Search code examples
androidlive-wallpapertouch-event

Intercept Live Wallpaper Touch Events?


If I have an activity that displays above the user's wallpaper, is there any way to prevent the wallpaper from receiving touch events I've already handled? Returning true or false from onTouchEvent appears to make no difference.


Solution

  • I've figured it out!

    private View empty;
    
    @Override public void onCreate(Bundle savedState)
    {
        super.onCreate(savedState);
        empty = new View(this);
        setContentView(empty);
    }
    
    @Override public void onAttachedToWindow()
    {
        View topview = getLayoutInflater().inflate(R.layout.mylayout, null);
        PopupWindow pw = new PopupWindow(topview);
        pw.setWindowLayoutMode(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        pw.showAtLocation(empty, 0, 0, 0);
    }
    

    The key insight came from studying the source code of WindowManagerService.java; specifically, this part:

    if (srcWin != null
        && pointer.getAction() == MotionEvent.ACTION_DOWN
        && mWallpaperTarget == srcWin
        && srcWin.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) {
            sendPointerToWallpaperLocked(relWin, pointer, eventTime);
    }
    

    Here there are four criteria that must be met for a live wallpaper to receive touch events:

    1. srcWin (the window destined to receive the touch) must be non-null. I don't know when this check might fail, but I think it's safe to assume that this is not a solution.
    2. The event has to be ACTION_DOWN for these checks to be performed. Other code will forward touch events to the wallpaper without bothering with all these checks if the initial down has already been forwarded to the wallpaper. Also not a solution.
    3. mWallpaperTarget, the window the wallpaper is being displayed beneath, must be equal to srcWin, the window the touch is destined for.
    4. The window must not be a keyguard. Since Android no longer allows non-system apps to use TYPE_KEYGUARD, this is not a solution.

    The trick then is to simply layer another window on top! Thus, we now have a stack of windows in this order:

    • wallpaper window
    • the activity window, mWallpaperTarget, which has @android:style/Theme.Holo.Wallpaper.NoTitleBar or similiar.
    • srcWin, the PopupWindow

    Since Android only checks for strict equality, and mWallpaperTarget != srcWin, the touches are not forwarded to the wallpaper, and interaction is not allowed.