diff options
author | Luca Stefani <luca.stefani.ge1@gmail.com> | 2019-02-01 22:08:39 +0100 |
---|---|---|
committer | Bruno Martins <bgcngm@gmail.com> | 2019-02-04 14:52:05 +0100 |
commit | e653623bfae6cbee7dc8b8029ef626113810ecb2 (patch) | |
tree | 291a7d47ec483bb756aada2994d602011e464512 | |
parent | 51fcc5b08f3a7aef613c6176e6351604cf7ca5eb (diff) | |
download | android_packages_apps_Trebuchet-e653623bfae6cbee7dc8b8029ef626113810ecb2.tar.gz android_packages_apps_Trebuchet-e653623bfae6cbee7dc8b8029ef626113810ecb2.tar.bz2 android_packages_apps_Trebuchet-e653623bfae6cbee7dc8b8029ef626113810ecb2.zip |
Trebuchet: Add reverse engineered Q swipe down gesture
Change-Id: I4b6fd72299ebd34fa17397eacbb14617b4cedc90
-rw-r--r-- | quickstep/libs/sysui_shared.jar | bin | 133150 -> 118069 bytes | |||
-rw-r--r-- | quickstep/src/com/android/launcher3/uioverrides/StatusBarTouchController.java | 116 | ||||
-rw-r--r-- | quickstep/src/com/android/launcher3/uioverrides/UiFactory.java | 9 | ||||
-rw-r--r-- | res/values/lineage_strings.xml | 3 | ||||
-rw-r--r-- | res/xml/launcher_preferences.xml | 6 | ||||
-rw-r--r-- | src/com/android/launcher3/AbstractFloatingView.java | 4 | ||||
-rw-r--r-- | src/com/android/launcher3/Utilities.java | 4 | ||||
-rw-r--r-- | src/com/android/launcher3/touch/TouchEventTranslator.java | 218 |
8 files changed, 356 insertions, 4 deletions
diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar Binary files differindex 27de1e907..4ed224193 100644 --- a/quickstep/libs/sysui_shared.jar +++ b/quickstep/libs/sysui_shared.jar diff --git a/quickstep/src/com/android/launcher3/uioverrides/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/StatusBarTouchController.java new file mode 100644 index 000000000..86ec3231f --- /dev/null +++ b/quickstep/src/com/android/launcher3/uioverrides/StatusBarTouchController.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2016 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.launcher3.uioverrides; + +import android.content.SharedPreferences; +import android.os.RemoteException; +import android.util.Log; +import android.view.MotionEvent; +import android.view.ViewConfiguration; + +import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.Utilities; +import com.android.launcher3.touch.TouchEventTranslator; +import com.android.launcher3.util.TouchController; +import com.android.quickstep.RecentsModel; +import com.android.systemui.shared.recents.ISystemUiProxy; + +public class StatusBarTouchController implements TouchController { + private static final String TAG = StatusBarTouchController.class.getSimpleName(); + private static final String PREF_STATUSBAR_EXPAND = "pref_expand_statusbar"; + + private boolean mCanIntercept; + private ISystemUiProxy mSysUiProxy; + + private final Launcher mLauncher; + private final SharedPreferences mSharedPreferences; + private final float mTouchSlop; + + protected final TouchEventTranslator mTranslator = + new TouchEventTranslator(this::dispatchTouchEvent); + + public StatusBarTouchController(Launcher launcher) { + mLauncher = launcher; + mSharedPreferences = Utilities.getPrefs(launcher); + mTouchSlop = ViewConfiguration.get(launcher).getScaledTouchSlop() * 2; + } + + private void dispatchTouchEvent(MotionEvent ev) { + try { + if (mSysUiProxy != null) { + mSysUiProxy.onStatusBarMotionEvent(ev); + } + } catch (RemoteException e) { + Log.e(TAG, "Remote exception on sysUiProxy.", e); + } + } + + @Override + public boolean onControllerInterceptTouchEvent(MotionEvent ev) { + int action = ev.getActionMasked(); + if (action == MotionEvent.ACTION_DOWN) { + mCanIntercept = canInterceptTouch(ev); + if (!mCanIntercept) { + return false; + } + mTranslator.reset(); + mTranslator.setDownParameters(0, ev); + } else if (action == MotionEvent.ACTION_POINTER_DOWN) { + mTranslator.setDownParameters(ev.getActionIndex(), ev); + } + + if (mCanIntercept && action == MotionEvent.ACTION_MOVE) { + float dy = ev.getY() - mTranslator.getDownY(); + float dx = ev.getX() - mTranslator.getDownX(); + if (dy > mTouchSlop && dy > Math.abs(dx)) { + mTranslator.dispatchDownEvents(ev); + mTranslator.processMotionEvent(ev); + return true; + } else if (Math.abs(dx) > mTouchSlop) { + mCanIntercept = false; + } + } + return false; + } + + @Override + public boolean onControllerTouchEvent(MotionEvent ev) { + mTranslator.processMotionEvent(ev); + return true; + } + + private boolean canInterceptTouch(MotionEvent ev) { + if (!mSharedPreferences.getBoolean(PREF_STATUSBAR_EXPAND, true)) { + return false; + } + + if (mLauncher.isInState(LauncherState.NORMAL)) { + if (AbstractFloatingView.getTopOpenViewWithType( + mLauncher, AbstractFloatingView.TYPE_STATUS_BAR_SWIPE_DOWN_DISALLOW) == null) { + if (ev.getY() > mLauncher.getDragLayer().getHeight() - + mLauncher.getDeviceProfile().getInsets().bottom) { + return false; + } + mSysUiProxy = RecentsModel.getInstance(mLauncher).getSystemUiProxy(); + return mSysUiProxy != null; + } + } + return false; + } +} diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java index ac9f8634e..47e67005f 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java @@ -63,19 +63,22 @@ public class UiFactory { return new TouchController[] { launcher.getDragController(), new OverviewToAllAppsTouchController(launcher), - new LauncherTaskViewController(launcher)}; + new LauncherTaskViewController(launcher), + new StatusBarTouchController(launcher)}; } if (launcher.getDeviceProfile().isVerticalBarLayout()) { return new TouchController[] { launcher.getDragController(), new OverviewToAllAppsTouchController(launcher), new LandscapeEdgeSwipeController(launcher), - new LauncherTaskViewController(launcher)}; + new LauncherTaskViewController(launcher), + new StatusBarTouchController(launcher)}; } else { return new TouchController[] { launcher.getDragController(), new PortraitStatesTouchController(launcher), - new LauncherTaskViewController(launcher)}; + new LauncherTaskViewController(launcher), + new StatusBarTouchController(launcher)}; } } diff --git a/res/values/lineage_strings.xml b/res/values/lineage_strings.xml index 14d6f83bc..828f2d465 100644 --- a/res/values/lineage_strings.xml +++ b/res/values/lineage_strings.xml @@ -62,6 +62,9 @@ <string name="google_title" translatable="false">Google</string> <string name="play_folder_title">Play</string> + <!-- Expand statusbar --> + <string name="statusbar_expand">Swipe down to show notifications</string> + <!-- Hidden apps --> <string name="hidden_apps_manager_name">Hidden apps</string> <string name="hidden_apps_auth_manager">Unlock to manage the hidden apps</string> diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml index a268116e1..e33f48511 100644 --- a/res/xml/launcher_preferences.xml +++ b/res/xml/launcher_preferences.xml @@ -57,6 +57,12 @@ android:title="@string/desktop_show_labels" android:defaultValue="true" android:persistent="true" /> + + <SwitchPreference + android:key="pref_expand_statusbar" + android:title="@string/statusbar_expand" + android:defaultValue="true" + android:persistent="true" /> </PreferenceCategory> <PreferenceCategory diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java index b614ec9d6..cf3f09889 100644 --- a/src/com/android/launcher3/AbstractFloatingView.java +++ b/src/com/android/launcher3/AbstractFloatingView.java @@ -52,6 +52,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch TYPE_WIDGETS_FULL_SHEET, TYPE_ON_BOARD_POPUP, TYPE_DISCOVERY_BOUNCE, + TYPE_STATUS_BAR_SWIPE_DOWN_DISALLOW, TYPE_QUICKSTEP_PREVIEW, TYPE_TASK_MENU, @@ -66,6 +67,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch public static final int TYPE_WIDGETS_FULL_SHEET = 1 << 4; public static final int TYPE_ON_BOARD_POPUP = 1 << 5; public static final int TYPE_DISCOVERY_BOUNCE = 1 << 6; + public static final int TYPE_STATUS_BAR_SWIPE_DOWN_DISALLOW = 1 << 10; // Popups related to quickstep UI public static final int TYPE_QUICKSTEP_PREVIEW = 1 << 7; @@ -75,7 +77,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET | TYPE_QUICKSTEP_PREVIEW | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU - | TYPE_OPTIONS_POPUP; + | TYPE_OPTIONS_POPUP | TYPE_STATUS_BAR_SWIPE_DOWN_DISALLOW; // Type of popups which should be kept open during launcher rebind public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 2330b10e3..67d70537a 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -136,6 +136,10 @@ public final class Utilities { return Log.isLoggable(propertyName, Log.VERBOSE); } + public interface Consumer<T> { + void accept(T t); + } + /** * Given a coordinate relative to the descendant, find the coordinate in a parent view's * coordinates. diff --git a/src/com/android/launcher3/touch/TouchEventTranslator.java b/src/com/android/launcher3/touch/TouchEventTranslator.java new file mode 100644 index 000000000..4228a120a --- /dev/null +++ b/src/com/android/launcher3/touch/TouchEventTranslator.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2016 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.launcher3.touch; + +import android.graphics.PointF; +import android.util.Log; +import android.util.Pair; +import android.util.SparseArray; +import android.view.MotionEvent; +import android.view.MotionEvent.PointerCoords; +import android.view.MotionEvent.PointerProperties; + +import com.android.launcher3.Utilities.Consumer; + +public class TouchEventTranslator { + private static final String TAG = TouchEventTranslator.class.getSimpleName(); + + private final DownState ZERO = new DownState(0, 0.0f, 0.0f); + private final SparseArray<Pair<PointerProperties[], PointerCoords[]>> mCache = + new SparseArray<>(); + private final SparseArray<DownState> mDownEvents = new SparseArray<>(); + private final SparseArray<PointF> mFingers = new SparseArray<>(); + private final Consumer<MotionEvent> mListener; + + private class DownState { + float downX; + float downY; + long timeStamp; + + public DownState(long timeStamp, float downX, float downY) { + this.timeStamp = timeStamp; + this.downX = downX; + this.downY = downY; + } + } + + public TouchEventTranslator(Consumer<MotionEvent> listener) { + mListener = listener; + } + + public void reset() { + mDownEvents.clear(); + mFingers.clear(); + } + + public float getDownX() { + return mDownEvents.get(0).downX; + } + + public float getDownY() { + return mDownEvents.get(0).downY; + } + + public void setDownParameters(int idx, MotionEvent e) { + mDownEvents.append(idx, new DownState(e.getEventTime(), e.getX(idx), e.getY(idx))); + } + + public void dispatchDownEvents(MotionEvent ev) { + for (int i = 0; i < ev.getPointerCount() && i < mDownEvents.size(); i++) { + put(ev.getPointerId(i), i, ev.getX(i), 0.0f, mDownEvents.get(i).timeStamp, ev); + } + } + + public void processMotionEvent(MotionEvent ev) { + int index = ev.getActionIndex(); + float x = ev.getX(index); + float y = ev.getY(index) - mDownEvents.get(index, ZERO).downY; + int actionMasked = ev.getActionMasked(); + if (actionMasked != MotionEvent.ACTION_UP) { + if (actionMasked == MotionEvent.ACTION_MOVE) { + for (int i = 0; i < ev.getPointerCount(); i++) { + position(ev.getPointerId(i), x, y); + } + generateEvent(ev.getAction(), ev); + return; + } else if (actionMasked == MotionEvent.ACTION_CANCEL) { + cancel(ev); + return; + } else if (actionMasked == MotionEvent.ACTION_POINTER_DOWN) { + int pid = ev.getPointerId(index); + if (mFingers.get(pid, null) != null) { + for (int i = 0; i < ev.getPointerCount(); i++) { + position(ev.getPointerId(i), x, y); + } + generateEvent(ev.getAction(), ev); + return; + } + put(pid, index, x, y, ev); + return; + } else if (actionMasked != MotionEvent.ACTION_POINTER_UP) { + Log.v(TAG, "Didn't process "); + return; + } + } + lift(ev.getPointerId(index), index, x, y, ev); + } + + private TouchEventTranslator put(int id, int index, float x, float y, MotionEvent ev) { + return put(id, index, x, y, ev.getEventTime(), ev); + } + + private TouchEventTranslator put(int id, int index, float x, float y, long ms, MotionEvent ev) { + int action; + checkFingerExistence(id, false); + mFingers.put(id, new PointF(x, y)); + int n = mFingers.size(); + if (mCache.get(n) == null) { + PointerProperties[] properties = new PointerProperties[n]; + PointerCoords[] coords = new PointerCoords[n]; + for (int i = 0; i < n; i++) { + properties[i] = new PointerProperties(); + coords[i] = new PointerCoords(); + } + mCache.put(n, new Pair<>(properties, coords)); + } + + boolean isInitialDown = mFingers.size() == 0; + action = isInitialDown ? + MotionEvent.ACTION_DOWN : + MotionEvent.ACTION_POINTER_DOWN | (index << MotionEvent.ACTION_POINTER_INDEX_SHIFT); + generateEvent(action, ms, ev); + return this; + } + + public TouchEventTranslator position(int id, float x, float y) { + checkFingerExistence(id, true); + mFingers.get(id).set(x, y); + return this; + } + + private TouchEventTranslator lift(int id, int index, MotionEvent ev) { + int action; + checkFingerExistence(id, true); + + boolean isFinalUp = mFingers.size() == 1; + action = isFinalUp ? + MotionEvent.ACTION_UP : + MotionEvent.ACTION_POINTER_UP | (index << MotionEvent.ACTION_POINTER_INDEX_SHIFT); + generateEvent(action, ev); + mFingers.remove(id); + return this; + } + + private TouchEventTranslator lift(int id, int index, float x, float y, MotionEvent ev) { + checkFingerExistence(id, true); + mFingers.get(id).set(x, y); + return lift(id, index, ev); + } + + public TouchEventTranslator cancel(MotionEvent ev) { + generateEvent(MotionEvent.ACTION_CANCEL, ev); + mFingers.clear(); + return this; + } + + private void checkFingerExistence(int id, boolean shouldExist) { + if (shouldExist == (mFingers.get(id, null) == null)) { + throw new IllegalArgumentException( + shouldExist ? "Finger does not exist" : "Finger already exists"); + } + } + + private void generateEvent(int action, MotionEvent ev) { + generateEvent(action, ev.getEventTime(), ev); + } + + private void generateEvent(int action, long ms, MotionEvent ev) { + Pair<PointerProperties[], PointerCoords[]> state = getFingerState(); + MotionEvent event = MotionEvent.obtain( + mDownEvents.get(0).timeStamp, ms, action, + state.first.length, state.first, state.second, + ev.getMetaState(), ev.getButtonState(), ev.getXPrecision(), + ev.getYPrecision(), ev.getDeviceId(), ev.getEdgeFlags(), + ev.getSource(), ev.getFlags()); + if (event.getPointerId(event.getActionIndex()) >= 0) { + mListener.accept(event); + event.recycle(); + return; + } + throw new IllegalStateException(String.valueOf(event.getActionIndex()) + + " not found in MotionEvent"); + } + + private Pair<PointerProperties[], PointerCoords[]> getFingerState() { + int nFingers = mFingers.size(); + Pair<PointerProperties[], PointerCoords[]> result = mCache.get(nFingers); + PointerProperties[] properties = result.first; + PointerCoords[] coordinates = result.second; + for (int i = 0; i < mFingers.size(); i++) { + int id = mFingers.keyAt(i); + PointF location = mFingers.get(id); + PointerProperties property = properties[i]; + property.id = id; + property.toolType = 1; + properties[i] = property; + PointerCoords coordinate = coordinates[i]; + coordinate.x = location.x; + coordinate.y = location.y; + coordinate.pressure = 1.0f; + coordinates[i] = coordinate; + } + return mCache.get(nFingers); + } +} |