Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 57306e6b authored by John Spurlock's avatar John Spurlock
Browse files

Reveal hideybars using system gesture.

Monitor input events using a dedicated input channel created by
window manager.

This new input event receiver could be used in the future for other
system-wide gestures that do not consume events.

Change-Id: Ib1be2fee0015332279fcf10f5b669fffd61acdcd
parent c56b831a
Loading
Loading
Loading
Loading
+21 −24
Original line number Diff line number Diff line
@@ -152,8 +152,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    static public final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
    static public final String SYSTEM_DIALOG_REASON_ASSIST = "assist";

    static public final String ACTION_HIDEYBARS = "android.intent.action.HIDEYBARS";

    /**
     * These are the system UI flags that, when changing, can cause the layout
     * of the screen to change.
@@ -559,12 +557,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    private static final int HIDEYBARS_HIDING = 2;
    private int mHideybars;

    BroadcastReceiver mHideybarsReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
           receivedHideybars(intent.getAction());
        }
    };
    private InputChannel mSystemGestureInputChannel;
    private InputEventReceiver mSystemGestures;

    IStatusBarService getStatusBarService() {
        synchronized (mServiceAquireLock) {
@@ -918,10 +912,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
        context.registerReceiver(mMultiuserReceiver, filter);

        // register for hideybars
        filter = new IntentFilter();
        filter.addAction(ACTION_HIDEYBARS);
        context.registerReceiver(mHideybarsReceiver, filter);
        // monitor for system gestures
        mSystemGestureInputChannel = mWindowManagerFuncs.monitorInput("SystemGestures");
        mSystemGestures = new SystemGestures(mSystemGestureInputChannel,
                mHandler.getLooper(), context,
                new SystemGestures.Callbacks() {
                    @Override
                    public void onSwipeFromTop() {
                        showHideybars();
                    }});

        mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
        mLongPressVibePattern = getLongIntArray(mContext.getResources(),
@@ -4118,9 +4117,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        }
    };

    private void receivedHideybars(String action) {
    private void showHideybars() {
        synchronized(mLock) {
            if (action.equals(ACTION_HIDEYBARS)) {
            if (mHideybars == HIDEYBARS_SHOWING) {
                if (DEBUG) Slog.d(TAG, "Not showing hideybars, already shown");
                return;
@@ -4133,7 +4131,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
            updateSystemUiVisibilityLw();
        }
    }
    }

    @Override
    public void screenTurnedOff(int why) {
+159 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.policy.impl;

import android.content.Context;
import android.os.Looper;
import android.util.Slog;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.MotionEvent;

/*
 * Listens for system-wide input gestures, firing callbacks when detected.
 * @hide
 */
public class SystemGestures extends InputEventReceiver {
    private static final String TAG = "SystemGestures";
    private static final boolean DEBUG = false;
    private static final long SWIPE_TIMEOUT_MS = 500;
    private static final int MAX_TRACKED_POINTERS = 32;  // max per input system
    private static final int UNTRACKED_POINTER = -1;

    private final int mSwipeStartThreshold;
    private final int mSwipeEndThreshold;
    private final Callbacks mCallbacks;
    private final int[] mDownPointerId = new int[MAX_TRACKED_POINTERS];
    private final float[] mDownY = new float[MAX_TRACKED_POINTERS];
    private final long[] mDownTime = new long[MAX_TRACKED_POINTERS];

    private int mDownPointers;
    private boolean mSwipeFromTopFireable;

    public SystemGestures(InputChannel inputChannel, Looper looper,
            Context context, Callbacks callbacks) {
        super(inputChannel, looper);
        mCallbacks = checkNull("callbacks", callbacks);
        mSwipeStartThreshold = checkNull("context", context).getResources()
                .getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
        mSwipeEndThreshold = mSwipeStartThreshold * 2;
    }

    private static <T> T checkNull(String name, T arg) {
        if (arg == null) {
            throw new IllegalArgumentException(name + " must not be null");
        }
        return arg;
    }

    @Override
    public void onInputEvent(InputEvent event) {
        if (event instanceof MotionEvent && event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
            onPointerMotionEvent((MotionEvent) event);
        }
        finishInputEvent(event, false /*handled*/);
    }

    private void onPointerMotionEvent(MotionEvent event) {
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                mSwipeFromTopFireable = true;
                mDownPointers = 0;
                captureDown(event, 0);
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                captureDown(event, event.getActionIndex());
                break;
            case MotionEvent.ACTION_MOVE:
                if (mSwipeFromTopFireable && detectSwipeFromTop(event)) {
                    mSwipeFromTopFireable = false;
                    if (DEBUG) Slog.d(TAG, "Firing onSwipeFromTop");
                    mCallbacks.onSwipeFromTop();
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mSwipeFromTopFireable = false;
                break;
            default:
                if (DEBUG) Slog.d(TAG, "Ignoring " + event);
        }
    }

    private void captureDown(MotionEvent event, int pointerIndex) {
        final int pointerId = event.getPointerId(pointerIndex);
        final int i = findIndex(pointerId);
        if (DEBUG) Slog.d(TAG, "pointer " + pointerId +
                " down pointerIndex=" + pointerIndex + " trackingIndex=" + i);
        if (i != UNTRACKED_POINTER) {
            mDownY[i] = event.getY(pointerIndex);
            mDownTime[i] = event.getEventTime();
            if (DEBUG) Slog.d(TAG, "pointer " + pointerId + " down y=" + mDownY[i]);
        }
    }

    private int findIndex(int pointerId) {
        for (int i = 0; i < mDownPointers; i++) {
            if (mDownPointerId[i] == pointerId) {
                return i;
            }
        }
        if (mDownPointers == MAX_TRACKED_POINTERS || pointerId == MotionEvent.INVALID_POINTER_ID) {
            return UNTRACKED_POINTER;
        }
        mDownPointerId[mDownPointers++] = pointerId;
        return mDownPointers - 1;
    }

    private boolean detectSwipeFromTop(MotionEvent move) {
        final int historySize = move.getHistorySize();
        final int pointerCount = move.getPointerCount();
        for (int p = 0; p < pointerCount; p++) {
            final int pointerId = move.getPointerId(p);
            final int i = findIndex(pointerId);
            if (i != UNTRACKED_POINTER) {
                for (int h = 0; h < historySize; h++) {
                    final long time = move.getHistoricalEventTime(h);
                    final float y = move.getHistoricalY(p,  h);
                    if (detectSwipeFromTop(i, time, y)) {
                        return true;
                    }
                }
                if (detectSwipeFromTop(i, move.getEventTime(), move.getY(p))) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean detectSwipeFromTop(int i, long time, float y) {
        final float fromY = mDownY[i];
        final long elapsed = time - mDownTime[i];
        if (DEBUG) Slog.d(TAG, "pointer " + mDownPointerId[i]
                + " moved from.y=" + fromY + " to.y=" + y + " in " + elapsed);
        return fromY <= mSwipeStartThreshold
                && y > fromY + mSwipeEndThreshold
                && elapsed < SWIPE_TIMEOUT_MS;
    }

    interface Callbacks {
        void onSwipeFromTop();
    }
}