From 194f58525a28150fa0757fcbe831b03b4daa4180 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 15 Aug 2018 16:07:31 -0700 Subject: [PATCH 01/16] Adding a provider class for various popup actions so that it can be customized easily. Change-Id: I633846631ac3d4c70ebb3ea35a3edc2feee8339e --- res/values/config.xml | 16 +----- .../popup/PopupContainerWithArrow.java | 18 +++--- .../launcher3/popup/PopupDataProvider.java | 17 ------ .../popup/SystemShortcutFactory.java | 57 +++++++++++++++++++ src/com/android/launcher3/util/FlagOp.java | 28 +++------ 5 files changed, 76 insertions(+), 60 deletions(-) create mode 100644 src/com/android/launcher3/popup/SystemShortcutFactory.java diff --git a/res/values/config.xml b/res/values/config.xml index 2a6b25cfcc..9f9747834d 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -79,27 +79,15 @@ true - + - - - - - - - - - - - - + diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java index 10be925516..6877cc4ad5 100644 --- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java +++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java @@ -184,17 +184,10 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource, return null; } - PopupDataProvider popupDataProvider = launcher.getPopupDataProvider(); - List shortcutIds = popupDataProvider.getShortcutIdsForItem(itemInfo); - List notificationKeys = popupDataProvider - .getNotificationKeysForItem(itemInfo); - List systemShortcuts = popupDataProvider - .getEnabledSystemShortcutsForItem(itemInfo); - final PopupContainerWithArrow container = (PopupContainerWithArrow) launcher.getLayoutInflater().inflate( R.layout.popup_container, launcher.getDragLayer(), false); - container.populateAndShow(icon, shortcutIds, notificationKeys, systemShortcuts); + container.populateAndShow(icon, itemInfo, SystemShortcutFactory.INSTANCE.get(launcher)); return container; } @@ -219,6 +212,15 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource, } } + protected void populateAndShow( + BubbleTextView icon, ItemInfo item, SystemShortcutFactory factory) { + PopupDataProvider popupDataProvider = mLauncher.getPopupDataProvider(); + populateAndShow(icon, + popupDataProvider.getShortcutIdsForItem(item), + popupDataProvider.getNotificationKeysForItem(item), + factory.getEnabledShortcuts(mLauncher, item)); + } + @TargetApi(Build.VERSION_CODES.P) protected void populateAndShow(final BubbleTextView originalIcon, final List shortcutIds, final List notificationKeys, List systemShortcuts) { diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java index 3faec447a4..4d5a9c6e93 100644 --- a/src/com/android/launcher3/popup/PopupDataProvider.java +++ b/src/com/android/launcher3/popup/PopupDataProvider.java @@ -50,13 +50,6 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan private static final boolean LOGD = false; private static final String TAG = "PopupDataProvider"; - /** Note that these are in order of priority. */ - private static final SystemShortcut[] SYSTEM_SHORTCUTS = new SystemShortcut[] { - new SystemShortcut.AppInfo(), - new SystemShortcut.Widgets(), - new SystemShortcut.Install() - }; - private final Launcher mLauncher; /** Maps launcher activity components to their list of shortcut ids. */ @@ -192,16 +185,6 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan : notificationListener.getNotificationsForKeys(notificationKeys); } - public @NonNull List getEnabledSystemShortcutsForItem(ItemInfo info) { - List systemShortcuts = new ArrayList<>(); - for (SystemShortcut systemShortcut : SYSTEM_SHORTCUTS) { - if (systemShortcut.getOnClickListener(mLauncher, info) != null) { - systemShortcuts.add(systemShortcut); - } - } - return systemShortcuts; - } - public void cancelNotification(String notificationKey) { NotificationListener notificationListener = NotificationListener.getInstanceIfConnected(); if (notificationListener == null) { diff --git a/src/com/android/launcher3/popup/SystemShortcutFactory.java b/src/com/android/launcher3/popup/SystemShortcutFactory.java new file mode 100644 index 0000000000..516fafad54 --- /dev/null +++ b/src/com/android/launcher3/popup/SystemShortcutFactory.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2018 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.popup; + +import com.android.launcher3.ItemInfo; +import com.android.launcher3.Launcher; +import com.android.launcher3.R; +import com.android.launcher3.util.MainThreadInitializedObject; +import com.android.launcher3.util.ResourceBasedOverride; + +import java.util.ArrayList; +import java.util.List; + +import androidx.annotation.NonNull; + +public class SystemShortcutFactory implements ResourceBasedOverride { + + public static final MainThreadInitializedObject INSTANCE = + new MainThreadInitializedObject<>(c -> Overrides.getObject( + SystemShortcutFactory.class, c, R.string.system_shortcut_factory_class)); + + /** Note that these are in order of priority. */ + private final SystemShortcut[] mAllShortcuts; + + @SuppressWarnings("unused") + public SystemShortcutFactory() { + this(new SystemShortcut.AppInfo(), + new SystemShortcut.Widgets(), new SystemShortcut.Install()); + } + + protected SystemShortcutFactory(SystemShortcut... shortcuts) { + mAllShortcuts = shortcuts; + } + + public @NonNull List getEnabledShortcuts(Launcher launcher, ItemInfo info) { + List systemShortcuts = new ArrayList<>(); + for (SystemShortcut systemShortcut : mAllShortcuts) { + if (systemShortcut.getOnClickListener(launcher, info) != null) { + systemShortcuts.add(systemShortcut); + } + } + return systemShortcuts; + } +} diff --git a/src/com/android/launcher3/util/FlagOp.java b/src/com/android/launcher3/util/FlagOp.java index 5e26ed12fe..a012c86edc 100644 --- a/src/com/android/launcher3/util/FlagOp.java +++ b/src/com/android/launcher3/util/FlagOp.java @@ -1,30 +1,16 @@ package com.android.launcher3.util; -public abstract class FlagOp { +public interface FlagOp { - public static FlagOp NO_OP = new FlagOp() {}; + FlagOp NO_OP = i -> i; - private FlagOp() {} + int apply(int flags); - public int apply(int flags) { - return flags; + static FlagOp addFlag(int flag) { + return i -> i + flag; } - public static FlagOp addFlag(final int flag) { - return new FlagOp() { - @Override - public int apply(int flags) { - return flags | flag; - } - }; - } - - public static FlagOp removeFlag(final int flag) { - return new FlagOp() { - @Override - public int apply(int flags) { - return flags & ~flag; - } - }; + static FlagOp removeFlag(int flag) { + return i -> i & ~flag; } } From 24b209c65a953f6e906dd6f36da9a6c790bf196a Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Mon, 10 Sep 2018 16:48:47 -0700 Subject: [PATCH 02/16] Swipe down on status bar Bug: 111839343 Change-Id: I5332dc098af980b4d4ef45b095586d68975ad98c --- .../uioverrides/StatusBarTouchController.java | 115 ++++++++ .../launcher3/uioverrides/UiFactory.java | 31 +- .../android/launcher3/config/BaseFlags.java | 4 + .../AbstractStateChangeTouchController.java | 2 +- .../launcher3/touch/TouchEventTranslator.java | 274 ++++++++++++++++++ 5 files changed, 411 insertions(+), 15 deletions(-) create mode 100644 quickstep/src/com/android/launcher3/uioverrides/StatusBarTouchController.java create mode 100644 src/com/android/launcher3/touch/TouchEventTranslator.java 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 0000000000..d5f6b940ff --- /dev/null +++ b/quickstep/src/com/android/launcher3/uioverrides/StatusBarTouchController.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2018 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 static android.view.MotionEvent.ACTION_DOWN; +import static android.view.MotionEvent.ACTION_MOVE; + +import android.os.RemoteException; +import android.util.Log; +import android.view.MotionEvent; +import android.view.ViewConfiguration; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.touch.TouchEventTranslator; +import com.android.launcher3.util.TouchController; +import com.android.quickstep.RecentsModel; +import com.android.systemui.shared.recents.ISystemUiProxy; + +/** + * TouchController for handling touch events that get sent to the StatusBar. Once the + * Once the event delta y passes the touch slop, the events start getting forwarded. + * All events are offset by initial Y value of the pointer. + */ +public class StatusBarTouchController implements TouchController { + + private static final String TAG = "StatusBarController"; + + protected final Launcher mLauncher; + protected final TouchEventTranslator mTranslator; + private final float mTouchSlop; + private ISystemUiProxy mSysUiProxy; + + /* If {@code false}, this controller should not handle the input {@link MotionEvent}.*/ + private boolean mCanIntercept; + + public StatusBarTouchController(Launcher l) { + mLauncher = l; + mTouchSlop = ViewConfiguration.get(l).getScaledTouchSlop(); + mTranslator = new TouchEventTranslator((MotionEvent ev)-> dispatchTouchEvent(ev)); + } + + 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 final boolean onControllerInterceptTouchEvent(MotionEvent ev) { + int action = ev.getActionMasked(); + if (action == ACTION_DOWN) { + mCanIntercept = canInterceptTouch(ev); + if (!mCanIntercept) { + return false; + } + mTranslator.reset(); + mTranslator.setDownParameters(0, ev); + } else if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) { + // Check!! should only set it only when threshold is not entered. + mTranslator.setDownParameters(ev.getActionIndex(), ev); + } + if (!mCanIntercept) { + return false; + } + if (action == ACTION_MOVE) { + float dy = ev.getY() - mTranslator.getDownY(); + if (dy > mTouchSlop) { + mTranslator.dispatchDownEvents(ev); + mTranslator.processMotionEvent(ev); + return true; + } + } + return false; + } + + + @Override + public final boolean onControllerTouchEvent(MotionEvent ev) { + mTranslator.processMotionEvent(ev); + return true; + } + + private boolean canInterceptTouch(MotionEvent ev) { + if (!mLauncher.isInState(LauncherState.NORMAL)) { + return false; + } else { + // For NORMAL state, only listen if the event originated above the navbar height + DeviceProfile dp = mLauncher.getDeviceProfile(); + if (ev.getY() > (mLauncher.getDragLayer().getHeight() - dp.getInsets().bottom)) { + return false; + } + } + mSysUiProxy = RecentsModel.INSTANCE.get(mLauncher).getSystemUiProxy(); + return mSysUiProxy != null; + } +} diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java index c9393306f4..5980d85c0b 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java @@ -42,6 +42,7 @@ import com.android.launcher3.LauncherStateManager; import com.android.launcher3.LauncherStateManager.StateHandler; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.TouchController; import com.android.quickstep.OverviewInteractionState; import com.android.quickstep.RecentsModel; @@ -52,6 +53,7 @@ import com.android.systemui.shared.system.WindowManagerWrapper; import java.io.ByteArrayOutputStream; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.zip.Deflater; public class UiFactory { @@ -59,24 +61,25 @@ public class UiFactory { public static TouchController[] createTouchControllers(Launcher launcher) { boolean swipeUpEnabled = OverviewInteractionState.INSTANCE.get(launcher) .isSwipeUpGestureEnabled(); - if (!swipeUpEnabled) { - return new TouchController[] { - launcher.getDragController(), - new OverviewToAllAppsTouchController(launcher), - new LauncherTaskViewController(launcher)}; + ArrayList list = new ArrayList<>(); + list.add(launcher.getDragController()); + + if (!swipeUpEnabled || launcher.getDeviceProfile().isVerticalBarLayout()) { + list.add(new OverviewToAllAppsTouchController(launcher)); } + if (launcher.getDeviceProfile().isVerticalBarLayout()) { - return new TouchController[] { - launcher.getDragController(), - new OverviewToAllAppsTouchController(launcher), - new LandscapeEdgeSwipeController(launcher), - new LauncherTaskViewController(launcher)}; + list.add(new LandscapeEdgeSwipeController(launcher)); } else { - return new TouchController[] { - launcher.getDragController(), - new PortraitStatesTouchController(launcher), - new LauncherTaskViewController(launcher)}; + list.add(new PortraitStatesTouchController(launcher)); } + if (FeatureFlags.PULL_DOWN_STATUS_BAR && Utilities.IS_DEBUG_DEVICE + && !launcher.getDeviceProfile().isMultiWindowMode + && !launcher.getDeviceProfile().isVerticalBarLayout()) { + list.add(new StatusBarTouchController(launcher)); + } + list.add(new LauncherTaskViewController(launcher)); + return list.toArray(new TouchController[list.size()]); } public static void setOnTouchControllersChangedListener(Context context, Runnable listener) { diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java index 05fc33f35e..b67d35bd6c 100644 --- a/src/com/android/launcher3/config/BaseFlags.java +++ b/src/com/android/launcher3/config/BaseFlags.java @@ -36,6 +36,10 @@ abstract class BaseFlags { // Feature flag to enable moving the QSB on the 0th screen of the workspace. public static final boolean QSB_ON_FIRST_SCREEN = true; + + //Feature flag to enable pulling down navigation shade from workspace. + public static final boolean PULL_DOWN_STATUS_BAR = true; + // When enabled the all-apps icon is not added to the hotseat. public static final boolean NO_ALL_APPS_ICON = true; diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java index fd9157e218..7b09bcce71 100644 --- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java +++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2018 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. diff --git a/src/com/android/launcher3/touch/TouchEventTranslator.java b/src/com/android/launcher3/touch/TouchEventTranslator.java new file mode 100644 index 0000000000..8a5c93264c --- /dev/null +++ b/src/com/android/launcher3/touch/TouchEventTranslator.java @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2018 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.util.SparseLongArray; +import android.view.MotionEvent; +import android.view.MotionEvent.PointerCoords; +import android.view.MotionEvent.PointerProperties; + +import com.android.launcher3.Utilities.Consumer; + +/** + * To minimize the size of the MotionEvent, historic events are not copied and passed via the + * listener. + */ +public class TouchEventTranslator { + + private static final String TAG = "TouchEventTranslator"; + private static final boolean DEBUG = false; + + private class DownState { + long timeStamp; + float downY; + public DownState(long timeStamp, float downY) { + this.timeStamp = timeStamp; + this.downY = downY; + } + }; + private final DownState ZERO = new DownState(0, 0f); + + private final Consumer mListener; + + private final SparseArray mDownEvents; + private final SparseArray mFingers; + + private final SparseArray> mCache; + + public TouchEventTranslator(Consumer listener) { + mDownEvents = new SparseArray<>(); + mFingers = new SparseArray<>(); + mCache = new SparseArray<>(); + + mListener = listener; + } + + public void reset() { + mDownEvents.clear(); + mFingers.clear(); + } + + public float getDownY() { + return mDownEvents.get(0).downY; + } + + public void setDownParameters(int idx, MotionEvent e) { + DownState ev = new DownState(e.getEventTime(), e.getY(idx)); + mDownEvents.append(idx, ev); + } + + public void dispatchDownEvents(MotionEvent ev) { + for(int i = 0; i < ev.getPointerCount() && i < mDownEvents.size(); i++) { + int pid = ev.getPointerId(i); + put(pid, ev.getX(i), 0, mDownEvents.get(i).timeStamp, ev); + } + } + + public void processMotionEvent(MotionEvent ev) { + if (DEBUG) { + printSamples(TAG + " processMotionEvent", ev); + } + int index = ev.getActionIndex(); + float x = ev.getX(index); + float y = ev.getY(index) - mDownEvents.get(index, ZERO).downY; + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_POINTER_DOWN: + int pid = ev.getPointerId(index); + if(mFingers.get(pid, null) != null) { + for(int i=0; i < ev.getPointerCount(); i++) { + pid = ev.getPointerId(i); + position(pid, x, y); + } + generateEvent(ev.getAction(), ev); + } else { + put(pid, x, y, ev); + } + break; + case MotionEvent.ACTION_MOVE: + for(int i=0; i < ev.getPointerCount(); i++) { + pid = ev.getPointerId(i); + position(pid, x, y); + } + generateEvent(ev.getAction(), ev); + break; + case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_UP: + pid = ev.getPointerId(index); + lift(pid, index, x, y, ev); + break; + case MotionEvent.ACTION_CANCEL: + cancel(ev); + break; + default: + Log.v(TAG, "Didn't process "); + printSamples(TAG, ev); + + } + } + + private TouchEventTranslator put(int id, float x, float y, MotionEvent ev) { + return put(id, x, y, ev.getEventTime(), ev); + } + + private TouchEventTranslator put(int id, float x, float y, long ms, MotionEvent ev) { + checkFingerExistence(id, false); + boolean isInitialDown = (mFingers.size() == 0); + + 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)); + } + + int action; + if (isInitialDown) { + action = MotionEvent.ACTION_DOWN; + } else { + action = MotionEvent.ACTION_POINTER_DOWN; + // Set the id of the changed pointer. + action |= id << 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) { + checkFingerExistence(id, true); + boolean isFinalUp = (mFingers.size() == 1); + int action; + if (isFinalUp) { + action = MotionEvent.ACTION_UP; + } else { + action = MotionEvent.ACTION_POINTER_UP; + // Set the id of the changed pointer. + action |= 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"); + } + } + + + /** + * Used to debug MotionEvents being sent/received. + */ + public void printSamples(String msg, MotionEvent ev) { + System.out.printf("%s %s", msg, MotionEvent.actionToString(ev.getActionMasked())); + final int pointerCount = ev.getPointerCount(); + System.out.printf("#%d/%d", ev.getPointerId(ev.getActionIndex()), pointerCount); + System.out.printf(" t=%d:", ev.getEventTime()); + for (int p = 0; p < pointerCount; p++) { + System.out.printf(" id=%d: (%f,%f)", + ev.getPointerId(p), ev.getX(p), ev.getY(p)); + } + System.out.println(); + } + + private void generateEvent(int action, MotionEvent ev) { + generateEvent(action, ev.getEventTime(), ev); + } + + private void generateEvent(int action, long ms, MotionEvent ev) { + Pair state = getFingerState(); + MotionEvent event = MotionEvent.obtain( + mDownEvents.get(0).timeStamp, + ms, + action, + state.first.length, + state.first, + state.second, + ev.getMetaState(), + ev.getButtonState() /* buttonState */, + ev.getXPrecision() /* xPrecision */, + ev.getYPrecision() /* yPrecision */, + ev.getDeviceId(), + ev.getEdgeFlags(), + ev.getSource(), + ev.getFlags() /* flags */); + if (DEBUG) { + printSamples(TAG + " generateEvent", event); + } + mListener.accept(event); + event.recycle(); + } + + /** + * Returns the description of the fingers' state expected by MotionEvent. + */ + private Pair getFingerState() { + int nFingers = mFingers.size(); + + Pair result = mCache.get(nFingers); + PointerProperties[] properties = result.first; + PointerCoords[] coordinates = result.second; + + int index = 0; + 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 = MotionEvent.TOOL_TYPE_FINGER; + properties[index] = property; + + PointerCoords coordinate = coordinates[i]; + coordinate.x = location.x; + coordinate.y = location.y; + coordinate.pressure = 1.0f; + coordinates[index] = coordinate; + + index++; + } + return mCache.get(nFingers); + } +} From 1642f7173f71699fc2699e3fc20b0f37b47cddad Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 18 Sep 2018 11:40:35 -0700 Subject: [PATCH 03/16] Removing ViewScrim and cutom drawing code Bug: 109828640 Change-Id: I2cab0215a32c2ca6bc331d48083fcc00ada05c3b --- res/values/config.xml | 3 - .../launcher3/InsettableFrameLayout.java | 2 +- .../launcher3/dragndrop/DragLayer.java | 10 --- .../launcher3/graphics/ColorScrim.java | 65 ---------------- .../android/launcher3/graphics/ViewScrim.java | 77 ------------------- .../launcher3/widget/BaseWidgetSheet.java | 34 +++++++- .../launcher3/widget/WidgetsBottomSheet.java | 3 +- .../launcher3/widget/WidgetsFullSheet.java | 2 +- 8 files changed, 33 insertions(+), 163 deletions(-) delete mode 100644 src/com/android/launcher3/graphics/ColorScrim.java delete mode 100644 src/com/android/launcher3/graphics/ViewScrim.java diff --git a/res/values/config.xml b/res/values/config.xml index 9f9747834d..0efaccf194 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -101,9 +101,6 @@ - - - diff --git a/src/com/android/launcher3/InsettableFrameLayout.java b/src/com/android/launcher3/InsettableFrameLayout.java index 1db1fc0004..faa18b8050 100644 --- a/src/com/android/launcher3/InsettableFrameLayout.java +++ b/src/com/android/launcher3/InsettableFrameLayout.java @@ -68,7 +68,7 @@ public class InsettableFrameLayout extends FrameLayout implements Insettable { } public static class LayoutParams extends FrameLayout.LayoutParams { - boolean ignoreInsets = false; + public boolean ignoreInsets = false; public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index 6d2d3cb442..f005ce7bf7 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -47,7 +47,6 @@ import com.android.launcher3.Workspace; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; -import com.android.launcher3.graphics.ViewScrim; import com.android.launcher3.graphics.WorkspaceAndHotseatScrim; import com.android.launcher3.keyboard.ViewGroupFocusHelper; import com.android.launcher3.uioverrides.UiFactory; @@ -124,15 +123,6 @@ public class DragLayer extends BaseDragLayer { return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event); } - @Override - protected boolean drawChild(Canvas canvas, View child, long drawingTime) { - ViewScrim scrim = ViewScrim.get(child); - if (scrim != null) { - scrim.draw(canvas, getWidth(), getHeight()); - } - return super.drawChild(canvas, child, drawingTime); - } - @Override protected boolean findActiveController(MotionEvent ev) { if (mActivity.getStateManager().getState().disableInteraction) { diff --git a/src/com/android/launcher3/graphics/ColorScrim.java b/src/com/android/launcher3/graphics/ColorScrim.java deleted file mode 100644 index 5c1081ac3f..0000000000 --- a/src/com/android/launcher3/graphics/ColorScrim.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2018 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.graphics; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.view.View; -import android.view.animation.Interpolator; - -import com.android.launcher3.R; -import com.android.launcher3.anim.Interpolators; -import com.android.launcher3.uioverrides.WallpaperColorInfo; - -import androidx.core.graphics.ColorUtils; - -/** - * Simple scrim which draws a color - */ -public class ColorScrim extends ViewScrim { - - private final int mColor; - private final Interpolator mInterpolator; - private int mCurrentColor; - - public ColorScrim(View view, int color, Interpolator interpolator) { - super(view); - mColor = color; - mInterpolator = interpolator; - } - - @Override - protected void onProgressChanged() { - mCurrentColor = ColorUtils.setAlphaComponent(mColor, - Math.round(mInterpolator.getInterpolation(mProgress) * Color.alpha(mColor))); - } - - @Override - public void draw(Canvas canvas, int width, int height) { - if (mProgress > 0) { - canvas.drawColor(mCurrentColor); - } - } - - public static ColorScrim createExtractedColorScrim(View view) { - WallpaperColorInfo colors = WallpaperColorInfo.getInstance(view.getContext()); - int alpha = view.getResources().getInteger(R.integer.extracted_color_gradient_alpha); - ColorScrim scrim = new ColorScrim(view, ColorUtils.setAlphaComponent( - colors.getSecondaryColor(), alpha), Interpolators.LINEAR); - scrim.attach(); - return scrim; - } -} diff --git a/src/com/android/launcher3/graphics/ViewScrim.java b/src/com/android/launcher3/graphics/ViewScrim.java deleted file mode 100644 index e1727e0e5b..0000000000 --- a/src/com/android/launcher3/graphics/ViewScrim.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2018 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.graphics; - -import android.graphics.Canvas; -import android.util.Property; -import android.view.View; -import android.view.ViewParent; - -import com.android.launcher3.R; - -/** - * A utility class that can be used to draw a scrim behind a view - */ -public abstract class ViewScrim { - - public static Property PROGRESS = - new Property(Float.TYPE, "progress") { - @Override - public Float get(ViewScrim viewScrim) { - return viewScrim.mProgress; - } - - @Override - public void set(ViewScrim object, Float value) { - object.setProgress(value); - } - }; - - protected final T mView; - protected float mProgress = 0; - - public ViewScrim(T view) { - mView = view; - } - - public void attach() { - mView.setTag(R.id.view_scrim, this); - } - - public void setProgress(float progress) { - if (mProgress != progress) { - mProgress = progress; - onProgressChanged(); - invalidate(); - } - } - - public abstract void draw(Canvas canvas, int width, int height); - - protected void onProgressChanged() { } - - public void invalidate() { - ViewParent parent = mView.getParent(); - if (parent != null) { - ((View) parent).invalidate(); - } - } - - public static ViewScrim get(View view) { - return (ViewScrim) view.getTag(R.id.view_scrim); - } -} diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java index 20c8876d29..48c18f8538 100644 --- a/src/com/android/launcher3/widget/BaseWidgetSheet.java +++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.widget; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; + import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; import android.content.Context; @@ -31,13 +33,16 @@ import com.android.launcher3.ItemInfo; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.dragndrop.DragOptions; -import com.android.launcher3.graphics.ColorScrim; import com.android.launcher3.touch.ItemLongClickListener; +import com.android.launcher3.uioverrides.WallpaperColorInfo; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; import com.android.launcher3.util.SystemUiController; import com.android.launcher3.util.Themes; import com.android.launcher3.views.AbstractSlideInView; +import com.android.launcher3.views.BaseDragLayer; + +import androidx.core.graphics.ColorUtils; /** * Base class for various widgets popup @@ -49,11 +54,11 @@ abstract class BaseWidgetSheet extends AbstractSlideInView /* Touch handling related member variables. */ private Toast mWidgetInstructionToast; - protected final ColorScrim mColorScrim; + protected final View mColorScrim; public BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - mColorScrim = ColorScrim.createExtractedColorScrim(this); + mColorScrim = createColorScrim(context); } @Override @@ -80,9 +85,14 @@ abstract class BaseWidgetSheet extends AbstractSlideInView return true; } + protected void attachToContainer() { + getPopupContainer().addView(mColorScrim); + getPopupContainer().addView(this); + } + protected void setTranslationShift(float translationShift) { super.setTranslationShift(translationShift); - mColorScrim.setProgress(1 - mTranslationShift); + mColorScrim.setAlpha(1 - mTranslationShift); } private boolean beginDraggingWidget(WidgetCell v) { @@ -115,6 +125,7 @@ abstract class BaseWidgetSheet extends AbstractSlideInView protected void onCloseComplete() { super.onCloseComplete(); + getPopupContainer().removeView(mColorScrim); clearNavBarColor(); } @@ -148,4 +159,19 @@ abstract class BaseWidgetSheet extends AbstractSlideInView protected SystemUiController getSystemUiController() { return mLauncher.getSystemUiController(); } + + private static View createColorScrim(Context context) { + View view = new View(context); + view.forceHasOverlappingRendering(false); + + WallpaperColorInfo colors = WallpaperColorInfo.getInstance(context); + int alpha = context.getResources().getInteger(R.integer.extracted_color_gradient_alpha); + view.setBackgroundColor(ColorUtils.setAlphaComponent(colors.getSecondaryColor(), alpha)); + + BaseDragLayer.LayoutParams lp = new BaseDragLayer.LayoutParams(MATCH_PARENT, MATCH_PARENT); + lp.ignoreInsets = true; + view.setLayoutParams(lp); + + return view; + } } diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java index 4ba6b5b123..4bd6234bcd 100644 --- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java +++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java @@ -70,8 +70,7 @@ public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable { R.string.widgets_bottom_sheet_title, mOriginalItemInfo.title)); onWidgetsBound(); - - getPopupContainer().addView(this); + attachToContainer(); mIsOpen = false; animateOpen(); } diff --git a/src/com/android/launcher3/widget/WidgetsFullSheet.java b/src/com/android/launcher3/widget/WidgetsFullSheet.java index f7ff69abe3..11126861f8 100644 --- a/src/com/android/launcher3/widget/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/WidgetsFullSheet.java @@ -220,8 +220,8 @@ public class WidgetsFullSheet extends BaseWidgetSheet public static WidgetsFullSheet show(Launcher launcher, boolean animate) { WidgetsFullSheet sheet = (WidgetsFullSheet) launcher.getLayoutInflater() .inflate(R.layout.widgets_full_sheet, launcher.getDragLayer(), false); + sheet.attachToContainer(); sheet.mIsOpen = true; - sheet.getPopupContainer().addView(sheet); sheet.open(animate); return sheet; } From c59465efe018d6703120ff319c118baf55caf92d Mon Sep 17 00:00:00 2001 From: Vadim Tryshev Date: Tue, 18 Sep 2018 14:02:33 -0700 Subject: [PATCH 04/16] Disabling heads-up notifications in launcher tests. This prevents the tests from interacting with this popup. Bug: 110103162 Test: TaplTests Change-Id: I0215ddab634611d20a63ed728de11d4138456f96 --- .../com/android/launcher3/ui/AbstractLauncherUiTest.java | 7 +++++++ tests/src/com/android/launcher3/ui/WorkTabTest.java | 1 + .../com/android/launcher3/ui/widget/BindWidgetTest.java | 2 ++ 3 files changed, 10 insertions(+) diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index d45036607f..5a3d36d6d2 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -56,6 +56,7 @@ import com.android.launcher3.util.Condition; import com.android.launcher3.util.Wait; import com.android.launcher3.util.rule.LauncherActivityRule; +import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -94,6 +95,12 @@ public abstract class AbstractLauncherUiTest { mLauncher = new LauncherInstrumentation(getInstrumentation()); mTargetContext = InstrumentationRegistry.getTargetContext(); mTargetPackage = mTargetContext.getPackageName(); + mDevice.executeShellCommand("settings put global heads_up_notifications_enabled 0"); + } + + @After + public void tearDown() throws Exception { + mDevice.executeShellCommand("settings put global heads_up_notifications_enabled 1"); } protected void lockRotation(boolean naturalOrientation) throws RemoteException { diff --git a/tests/src/com/android/launcher3/ui/WorkTabTest.java b/tests/src/com/android/launcher3/ui/WorkTabTest.java index 044b7f3382..efbab0ce8d 100644 --- a/tests/src/com/android/launcher3/ui/WorkTabTest.java +++ b/tests/src/com/android/launcher3/ui/WorkTabTest.java @@ -54,6 +54,7 @@ public class WorkTabTest extends AbstractLauncherUiTest { @After public void removeWorkProfile() throws Exception { mDevice.executeShellCommand("pm remove-user " + mProfileUserId); + super.tearDown(); } @Test diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java index 0dc9b2e675..17fdd26922 100644 --- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java @@ -103,6 +103,8 @@ public class BindWidgetTest extends AbstractLauncherUiTest { if (mSessionId > -1) { mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId); } + + super.tearDown(); } @Test From 66408d90e14936dbe0d2fb8f06750fb5159af82a Mon Sep 17 00:00:00 2001 From: Vadim Tryshev Date: Tue, 18 Sep 2018 14:44:21 -0700 Subject: [PATCH 05/16] Adding logging pressHome() Will help understanding flakiness of platform smoke tests. Bug: 110103162 Test: TaplTests Change-Id: I7c112554f8281ebe0e62026644e5705a7d1218a2 --- .../com/android/launcher3/tapl/LauncherInstrumentation.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 441fc65bd3..9e4a61530b 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -225,7 +225,10 @@ public final class LauncherInstrumentation { // otherwise waitForIdle may return immediately in case when there was a big enough pause in // accessibility events prior to pressing Home. executeAndWaitForEvent( - () -> getSystemUiObject("home").click(), + () -> { + log("LauncherInstrumentation.pressHome before clicking"); + getSystemUiObject("home").click(); + }, event -> true, "Pressing Home didn't produce any events"); mDevice.waitForIdle(); From 3c6607e29814837d23222fe22994a7a4a142f5be Mon Sep 17 00:00:00 2001 From: Vadim Tryshev Date: Tue, 18 Sep 2018 20:01:43 -0700 Subject: [PATCH 06/16] Add more checks to AppIcon.launch() Apparently, one platform tests starts Messages, and Messages simply doesn't start. This check will help to hunt down the problem. Bug: 110103162 Test: affected tests Change-Id: I8d781647a2e680e31e96db40d94b89e7728aaf11 --- .../tapl/com/android/launcher3/tapl/AppIcon.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java index ea47503eb6..a11f6df0a2 100644 --- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java +++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java @@ -42,6 +42,7 @@ public final class AppIcon { /** * Clicks the icon to launch its app. */ + @Deprecated public Background launch() { LauncherInstrumentation.log("AppIcon.launch before click"); LauncherInstrumentation.assertTrue( @@ -50,6 +51,20 @@ public final class AppIcon { return new Background(mLauncher); } + /** + * Clicks the icon to launch its app. + */ + public Background launch(String packageName) { + LauncherInstrumentation.log("AppIcon.launch before click"); + LauncherInstrumentation.assertTrue( + "Launching an app didn't open a new window: " + mIcon.getText(), + mIcon.clickAndWait(Until.newWindow(), LauncherInstrumentation.WAIT_TIME_MS)); + LauncherInstrumentation.assertTrue( + "App didn't start: " + packageName, mLauncher.getDevice().wait(Until.hasObject( + By.pkg(packageName).depth(0)), LauncherInstrumentation.WAIT_TIME_MS)); + return new Background(mLauncher); + } + UiObject2 getIcon() { return mIcon; } From c0b1a7e58ec677c085009ad1f80c292d2a225e61 Mon Sep 17 00:00:00 2001 From: Vadim Tryshev Date: Wed, 19 Sep 2018 14:17:05 -0700 Subject: [PATCH 07/16] Removing unnecessarily calling parent's tearDown I'ts called automatically Bug: 110103162 Test: affected tests Change-Id: I32ac35a425dedadabea63e02be5a7dffa3cced25 --- tests/src/com/android/launcher3/ui/WorkTabTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/src/com/android/launcher3/ui/WorkTabTest.java b/tests/src/com/android/launcher3/ui/WorkTabTest.java index efbab0ce8d..044b7f3382 100644 --- a/tests/src/com/android/launcher3/ui/WorkTabTest.java +++ b/tests/src/com/android/launcher3/ui/WorkTabTest.java @@ -54,7 +54,6 @@ public class WorkTabTest extends AbstractLauncherUiTest { @After public void removeWorkProfile() throws Exception { mDevice.executeShellCommand("pm remove-user " + mProfileUserId); - super.tearDown(); } @Test From 5077aefc30fd3ad1a5b8fdfe4aada8fda471a37c Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 17 Sep 2018 15:22:13 -0700 Subject: [PATCH 08/16] Skip starting new fragment, if the manager is in paused state Bug: 111684268 Change-Id: Ice529356733d98181371e37beed7a34922313cc4 --- src/com/android/launcher3/SettingsActivity.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java index 8589b7ee63..1f802267c9 100644 --- a/src/com/android/launcher3/SettingsActivity.java +++ b/src/com/android/launcher3/SettingsActivity.java @@ -88,6 +88,11 @@ public class SettingsActivity extends Activity @Override public boolean onPreferenceStartFragment( PreferenceFragment preferenceFragment, Preference pref) { + if (getFragmentManager().isStateSaved()) { + // Sometimes onClick can come after onPause because of being posted on the handler. + // Skip starting new fragments in that case. + return false; + } Fragment f = Fragment.instantiate(this, pref.getFragment(), pref.getExtras()); if (f instanceof DialogFragment) { ((DialogFragment) f).show(getFragmentManager(), pref.getKey()); @@ -241,8 +246,7 @@ public class SettingsActivity extends Activity * Content observer which listens for system badging setting changes, * and updates the launcher badging setting subtext accordingly. */ - private static class IconBadgingObserver extends SettingsObserver.Secure - implements Preference.OnPreferenceClickListener { + private static class IconBadgingObserver extends SettingsObserver.Secure { private final ButtonPreference mBadgingPref; private final ContentResolver mResolver; @@ -275,16 +279,11 @@ public class SettingsActivity extends Activity } } mBadgingPref.setWidgetFrameVisible(!serviceEnabled); - mBadgingPref.setOnPreferenceClickListener(serviceEnabled ? null : this); + mBadgingPref.setFragment( + serviceEnabled ? null : NotificationAccessConfirmation.class.getName()); mBadgingPref.setSummary(summary); } - - @Override - public boolean onPreferenceClick(Preference preference) { - new NotificationAccessConfirmation().show(mFragmentManager, "notification_access"); - return true; - } } public static class NotificationAccessConfirmation From f58cf5e337796091047304c61932cf0b23ef42cf Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Wed, 19 Sep 2018 16:06:16 -0700 Subject: [PATCH 09/16] Disable scrolling noti shade on abstract floating view with swipe down interactions already doing something else. Bug: 116143342 Change-Id: If3716de0eb1f7b508c3b74dbe2593ba62fffcf74 --- .../launcher3/uioverrides/StatusBarTouchController.java | 5 ++++- src/com/android/launcher3/AbstractFloatingView.java | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/StatusBarTouchController.java index d5f6b940ff..4d567865e6 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/StatusBarTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/StatusBarTouchController.java @@ -23,6 +23,7 @@ import android.util.Log; import android.view.MotionEvent; import android.view.ViewConfiguration; +import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; @@ -100,7 +101,9 @@ public class StatusBarTouchController implements TouchController { } private boolean canInterceptTouch(MotionEvent ev) { - if (!mLauncher.isInState(LauncherState.NORMAL)) { + if (!mLauncher.isInState(LauncherState.NORMAL) || + AbstractFloatingView.getTopOpenViewWithType(mLauncher, + AbstractFloatingView.TYPE_STATUS_BAR_SWIPE_DOWN_DISALLOW) != null) { return false; } else { // For NORMAL state, only listen if the event originated above the navbar height diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java index 4fe60991cf..45751328a8 100644 --- a/src/com/android/launcher3/AbstractFloatingView.java +++ b/src/com/android/launcher3/AbstractFloatingView.java @@ -91,6 +91,11 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE & ~TYPE_QUICKSTEP_PREVIEW; + // These view all have particular operation associated with swipe down interaction. + public static final int TYPE_STATUS_BAR_SWIPE_DOWN_DISALLOW = TYPE_WIDGETS_BOTTOM_SHEET | + TYPE_WIDGETS_FULL_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_ON_BOARD_POPUP | + TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU ; + protected boolean mIsOpen; public AbstractFloatingView(Context context, AttributeSet attrs) { From dbac5a1d15620b46132751ac008cd2b4c63f9a7f Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 20 Sep 2018 15:32:57 -0700 Subject: [PATCH 10/16] Updating the gradle and build tools version Change-Id: Ica921edb152f158736136df59514c0b8469eab3b --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 7ac4127853..b59f264c83 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.0-beta05' + classpath 'com.android.tools.build:gradle:3.2.0-rc03' classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.6' } } @@ -16,7 +16,7 @@ apply plugin: 'com.google.protobuf' android { compileSdkVersion 28 - buildToolsVersion '28.0.0' + buildToolsVersion '28.0.2' defaultConfig { minSdkVersion 21 From 55bdeed7bb4ecf20f22a721b2c8e2e56b4f9f9f5 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 18 Sep 2018 16:17:22 -0700 Subject: [PATCH 11/16] Removing various reflection based animations, to allow for better proguarding Change-Id: If9df24ea4170e8a3d336057d1c3dc800934fc1ac --- proguard.flags | 74 ------------------- .../launcher3/AppWidgetResizeFrame.java | 13 +++- .../android/launcher3/ButtonDropTarget.java | 17 ++++- src/com/android/launcher3/CellLayout.java | 32 -------- .../android/launcher3/LauncherAnimUtils.java | 27 +++++++ src/com/android/launcher3/Workspace.java | 5 +- .../launcher3/views/BaseDragLayer.java | 59 +++++++-------- 7 files changed, 80 insertions(+), 147 deletions(-) diff --git a/proguard.flags b/proguard.flags index a312b9103c..bb52a6c9e9 100644 --- a/proguard.flags +++ b/proguard.flags @@ -2,80 +2,6 @@ *; } --keep class com.android.launcher3.allapps.AllAppsBackgroundDrawable { - public void setAlpha(int); - public int getAlpha(); -} - --keep class com.android.launcher3.BaseRecyclerViewFastScrollBar { - public void setThumbWidth(int); - public int getThumbWidth(); - public void setTrackWidth(int); - public int getTrackWidth(); -} - --keep class com.android.launcher3.BaseRecyclerViewFastScrollPopup { - public void setAlpha(float); - public float getAlpha(); -} - --keep class com.android.launcher3.ButtonDropTarget { - public int getTextColor(); -} - --keep class com.android.launcher3.CellLayout { - public float getBackgroundAlpha(); - public void setBackgroundAlpha(float); -} - --keep class com.android.launcher3.CellLayout$LayoutParams { - public void setWidth(int); - public int getWidth(); - public void setHeight(int); - public int getHeight(); - public void setX(int); - public int getX(); - public void setY(int); - public int getY(); -} - --keep class com.android.launcher3.views.BaseDragLayer$LayoutParams { - public void setWidth(int); - public int getWidth(); - public void setHeight(int); - public int getHeight(); - public void setX(int); - public int getX(); - public void setY(int); - public int getY(); -} - --keep class com.android.launcher3.FastBitmapDrawable { - public void setDesaturation(float); - public float getDesaturation(); - public void setBrightness(float); - public float getBrightness(); -} - --keep class com.android.launcher3.MemoryDumpActivity { - *; -} - --keep class com.android.launcher3.PreloadIconDrawable { - public float getAnimationProgress(); - public void setAnimationProgress(float); -} - --keep class com.android.launcher3.pageindicators.CaretDrawable { - public float getCaretProgress(); - public void setCaretProgress(float); -} - --keep class com.android.launcher3.Workspace { - public float getBackgroundAlpha(); - public void setBackgroundAlpha(float); -} - # Proguard will strip new callbacks in LauncherApps.Callback from # WrappedCallback if compiled against an older SDK. Don't let this happen. -keep class com.android.launcher3.compat.** { diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java index 0f5317be74..8e2ffe923d 100644 --- a/src/com/android/launcher3/AppWidgetResizeFrame.java +++ b/src/com/android/launcher3/AppWidgetResizeFrame.java @@ -1,5 +1,10 @@ package com.android.launcher3; +import static com.android.launcher3.LauncherAnimUtils.LAYOUT_HEIGHT; +import static com.android.launcher3.LauncherAnimUtils.LAYOUT_WIDTH; +import static com.android.launcher3.views.BaseDragLayer.LAYOUT_X; +import static com.android.launcher3.views.BaseDragLayer.LAYOUT_Y; + import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; @@ -429,10 +434,10 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O requestLayout(); } else { ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(lp, - PropertyValuesHolder.ofInt("width", lp.width, newWidth), - PropertyValuesHolder.ofInt("height", lp.height, newHeight), - PropertyValuesHolder.ofInt("x", lp.x, newX), - PropertyValuesHolder.ofInt("y", lp.y, newY)); + PropertyValuesHolder.ofInt(LAYOUT_WIDTH, lp.width, newWidth), + PropertyValuesHolder.ofInt(LAYOUT_HEIGHT, lp.height, newHeight), + PropertyValuesHolder.ofInt(LAYOUT_X, lp.x, newX), + PropertyValuesHolder.ofInt(LAYOUT_Y, lp.y, newY)); mFirstFrameAnimatorHelper.addTo(oa).addUpdateListener(a -> requestLayout()); AnimatorSet set = new AnimatorSet(); diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index dd63ebce0b..2b0da434a9 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -33,6 +33,7 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.Property; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; @@ -55,6 +56,20 @@ import com.android.launcher3.util.Thunk; public abstract class ButtonDropTarget extends TextView implements DropTarget, DragController.DragListener, OnClickListener { + private static final Property TEXT_COLOR = + new Property(Integer.TYPE, "textColor") { + + @Override + public Integer get(ButtonDropTarget target) { + return target.getTextColor(); + } + + @Override + public void set(ButtonDropTarget target, Integer value) { + target.setTextColor(value); + } + }; + private static final int[] sTempCords = new int[2]; private static final int DRAG_VIEW_DROP_DURATION = 285; @@ -206,7 +221,7 @@ public abstract class ButtonDropTarget extends TextView }); mCurrentColorAnim.play(anim1); - mCurrentColorAnim.play(ObjectAnimator.ofArgb(this, "textColor", targetColor)); + mCurrentColorAnim.play(ObjectAnimator.ofArgb(this, TEXT_COLOR, targetColor)); mCurrentColorAnim.start(); } diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index f6f1496310..a42238e5b2 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -2678,38 +2678,6 @@ public class CellLayout extends ViewGroup { public String toString() { return "(" + this.cellX + ", " + this.cellY + ")"; } - - public void setWidth(int width) { - this.width = width; - } - - public int getWidth() { - return width; - } - - public void setHeight(int height) { - this.height = height; - } - - public int getHeight() { - return height; - } - - public void setX(int x) { - this.x = x; - } - - public int getX() { - return x; - } - - public void setY(int y) { - this.y = y; - } - - public int getY() { - return y; - } } // This class stores info for two purposes: diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java index ac07e8887f..aad344976f 100644 --- a/src/com/android/launcher3/LauncherAnimUtils.java +++ b/src/com/android/launcher3/LauncherAnimUtils.java @@ -19,6 +19,7 @@ package com.android.launcher3; import android.graphics.drawable.Drawable; import android.util.Property; import android.view.View; +import android.view.ViewGroup.LayoutParams; public class LauncherAnimUtils { /** @@ -64,4 +65,30 @@ public class LauncherAnimUtils { public static int blockedFlingDurationFactor(float velocity) { return (int) Utilities.boundToRange(Math.abs(velocity) / 2, 2f, 6f); } + + public static final Property LAYOUT_WIDTH = + new Property(Integer.TYPE, "width") { + @Override + public Integer get(LayoutParams lp) { + return lp.width; + } + + @Override + public void set(LayoutParams lp, Integer width) { + lp.width = width; + } + }; + + public static final Property LAYOUT_HEIGHT = + new Property(Integer.TYPE, "height") { + @Override + public Integer get(LayoutParams lp) { + return lp.height; + } + + @Override + public void set(LayoutParams lp, Integer height) { + lp.height = height; + } + }; } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 353916fbb9..b5a770f6c3 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -671,9 +671,6 @@ public class Workspace extends PagedView private void fadeAndRemoveEmptyScreen(int delay, int duration, final Runnable onComplete, final boolean stripEmptyScreens) { // XXX: Do we need to update LM workspace screens below? - PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0f); - PropertyValuesHolder bgAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", 0f); - final CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID); mRemoveEmptyScreenRunnable = new Runnable() { @@ -692,7 +689,7 @@ public class Workspace extends PagedView } }; - ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(cl, alpha, bgAlpha); + ObjectAnimator oa = ObjectAnimator.ofFloat(cl, ALPHA, 0f); oa.setDuration(duration); oa.setStartDelay(delay); oa.addListener(new AnimatorListenerAdapter() { diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java index e8a879fccb..1faca152b6 100644 --- a/src/com/android/launcher3/views/BaseDragLayer.java +++ b/src/com/android/launcher3/views/BaseDragLayer.java @@ -21,6 +21,7 @@ import static com.android.launcher3.Utilities.SINGLE_FRAME_MS; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; +import android.util.Property; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; @@ -42,6 +43,32 @@ import java.util.ArrayList; public abstract class BaseDragLayer extends InsettableFrameLayout { + public static final Property LAYOUT_X = + new Property(Integer.TYPE, "x") { + @Override + public Integer get(LayoutParams lp) { + return lp.x; + } + + @Override + public void set(LayoutParams lp, Integer x) { + lp.x = x; + } + }; + + public static final Property LAYOUT_Y = + new Property(Integer.TYPE, "y") { + @Override + public Integer get(LayoutParams lp) { + return lp.y; + } + + @Override + public void set(LayoutParams lp, Integer y) { + lp.y = y; + } + }; + protected final int[] mTmpXY = new int[2]; protected final Rect mHitRect = new Rect(); @@ -307,38 +334,6 @@ public abstract class BaseDragLayer public LayoutParams(ViewGroup.LayoutParams lp) { super(lp); } - - public void setWidth(int width) { - this.width = width; - } - - public int getWidth() { - return width; - } - - public void setHeight(int height) { - this.height = height; - } - - public int getHeight() { - return height; - } - - public void setX(int x) { - this.x = x; - } - - public int getX() { - return x; - } - - public void setY(int y) { - this.y = y; - } - - public int getY() { - return y; - } } protected void onLayout(boolean changed, int l, int t, int r, int b) { From 802a28946c43f4f3e4d314838d019fdf6573f2fa Mon Sep 17 00:00:00 2001 From: Vadim Tryshev Date: Fri, 21 Sep 2018 14:31:21 -0700 Subject: [PATCH 12/16] Add support for testing in portrait mode Bug: 110103162 Test: TaplTests Change-Id: I7f633405c04984ea8f04acdc6dd4ad21019d3409 --- src/com/android/launcher3/Launcher.java | 11 -------- .../launcher3/states/RotationHelper.java | 9 ++++++- .../launcher3/ui/AbstractLauncherUiTest.java | 19 ++++++-------- .../tapl/LauncherInstrumentation.java | 26 +++++++++++++++++-- 4 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 55074f8134..151c76183c 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -867,17 +867,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, } } - public boolean hasSettings() { - if (mLauncherCallbacks != null) { - return mLauncherCallbacks.hasSettings(); - } else { - // On O and above we there is always some setting present settings (add icon to - // home screen or icon badging). On earlier APIs we will have the allow rotation - // setting, on devices with a locked orientation, - return Utilities.ATLEAST_OREO || !getResources().getBoolean(R.bool.allow_rotation); - } - } - public boolean isInState(LauncherState state) { return mStateManager.getState() == state; } diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java index e8664458d5..9c4a4eaa4b 100644 --- a/src/com/android/launcher3/states/RotationHelper.java +++ b/src/com/android/launcher3/states/RotationHelper.java @@ -56,7 +56,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { private final Activity mActivity; private final SharedPreferences mPrefs; - private final boolean mIgnoreAutoRotateSettings; + private boolean mIgnoreAutoRotateSettings; private boolean mAutoRotateEnabled; /** @@ -110,6 +110,13 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { } } + // Used by tests only. + public void forceAllowRotationForTesting(boolean allowRotation) { + mIgnoreAutoRotateSettings = + allowRotation || mActivity.getResources().getBoolean(R.bool.allow_rotation); + notifyChange(); + } + public void initialize() { if (!mInitialized) { mInitialized = true; diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 5a3d36d6d2..ba7d9c5237 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -52,7 +52,6 @@ import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.tapl.LauncherInstrumentation; import com.android.launcher3.testcomponent.AppWidgetNoConfig; import com.android.launcher3.testcomponent.AppWidgetWithConfig; -import com.android.launcher3.util.Condition; import com.android.launcher3.util.Wait; import com.android.launcher3.util.rule.LauncherActivityRule; @@ -79,20 +78,23 @@ public abstract class AbstractLauncherUiTest { public static final long DEFAULT_WORKER_TIMEOUT_SECS = 5; protected MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); - protected UiDevice mDevice; - protected LauncherInstrumentation mLauncher; + protected final UiDevice mDevice; + protected final LauncherInstrumentation mLauncher; protected Context mTargetContext; protected String mTargetPackage; private static final String TAG = "AbstractLauncherUiTest"; + protected AbstractLauncherUiTest() { + mDevice = UiDevice.getInstance(getInstrumentation()); + mLauncher = new LauncherInstrumentation(getInstrumentation()); + } + @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule(); @Before public void setUp() throws Exception { - mDevice = UiDevice.getInstance(getInstrumentation()); - mLauncher = new LauncherInstrumentation(getInstrumentation()); mTargetContext = InstrumentationRegistry.getTargetContext(); mTargetPackage = mTargetContext.getPackageName(); mDevice.executeShellCommand("settings put global heads_up_notifications_enabled 0"); @@ -285,12 +287,7 @@ public abstract class AbstractLauncherUiTest { // flakiness. protected boolean waitForLauncherCondition( Function condition, long timeout) { - return Wait.atMost(new Condition() { - @Override - public boolean isTrue() { - return getFromLauncher(condition); - } - }, timeout); + return Wait.atMost(() -> getFromLauncher(condition), timeout); } /** diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 9e4a61530b..7885e3cce4 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -25,6 +25,7 @@ import android.os.Bundle; import android.os.Parcelable; import android.provider.Settings; import android.util.Log; +import android.view.Surface; import android.view.accessibility.AccessibilityEvent; import androidx.annotation.NonNull; @@ -92,6 +93,7 @@ public final class LauncherInstrumentation { private final boolean mSwipeUpEnabled; private Boolean mSwipeUpEnabledOverride = null; private final Instrumentation mInstrumentation; + private int mExpectedRotation = Surface.ROTATION_0; /** * Constructs the root of TAPL hierarchy. You get all other objects from it. @@ -109,7 +111,7 @@ public final class LauncherInstrumentation { assertTrue("Device must run in a test harness", ActivityManager.isRunningInTestHarness()); } - // Used only by tests. + // Used only by TaplTests. public void overrideSwipeUpEnabled(Boolean swipeUpEnabledOverride) { mSwipeUpEnabledOverride = swipeUpEnabledOverride; } @@ -144,14 +146,30 @@ public final class LauncherInstrumentation { fail(message + ". " + "Actual: " + actual); } + static public void assertEquals(String message, int expected, int actual) { + if (expected != actual) { + fail(message + " expected: " + expected + " but was: " + actual); + } + } + static void assertNotEquals(String message, int unexpected, int actual) { if (unexpected == actual) { failEquals(message, actual); } } + public void setExpectedRotation(int expectedRotation) { + mExpectedRotation = expectedRotation; + } + private UiObject2 verifyContainerType(ContainerType containerType) { + assertEquals("Unexpected display rotation", + mExpectedRotation, mDevice.getDisplayRotation()); + assertTrue("Presence of recents button doesn't match isSwipeUpEnabled()", + isSwipeUpEnabled() == + (mDevice.findObject(By.res(SYSTEMUI_PACKAGE, "recent_apps")) == null)); log("verifyContainerType: " + containerType); + switch (containerType) { case WORKSPACE: { waitUntilGone(APPS_RES_ID); @@ -172,7 +190,11 @@ public final class LauncherInstrumentation { return waitForLauncherObject(APPS_RES_ID); } case OVERVIEW: { - waitForLauncherObject(APPS_RES_ID); + if (mDevice.isNaturalOrientation()) { + waitForLauncherObject(APPS_RES_ID); + } else { + waitUntilGone(APPS_RES_ID); + } waitUntilGone(WORKSPACE_RES_ID); waitUntilGone(WIDGETS_RES_ID); return waitForLauncherObject(OVERVIEW_RES_ID); From f840f10d500d3e11f8512a878c57f07bca635587 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 21 Sep 2018 14:41:05 -0700 Subject: [PATCH 13/16] Removing additional rpc due to icon cache update When launcher loads, it fetches the list of apps twice, once for loading all-apps and again for updating icons. Instead reusing the previously fetched apps list. Also moving the icon loading in a separate package for further generalization Change-Id: Ibd2dae56e6027a31b633da030bc6b43a90b27e1b --- src/com/android/launcher3/AllAppsList.java | 1 + src/com/android/launcher3/BubbleTextView.java | 4 +- src/com/android/launcher3/Launcher.java | 1 + .../android/launcher3/LauncherAppState.java | 1 + src/com/android/launcher3/LauncherModel.java | 1 + src/com/android/launcher3/ShortcutInfo.java | 1 + .../launcher3/WidgetPreviewLoader.java | 1 + .../compat/PackageInstallerCompatVL.java | 2 +- .../compat/ShortcutConfigActivityInfo.java | 2 +- .../PinShortcutRequestActivityInfo.java | 2 +- .../launcher3/graphics/LauncherIcons.java | 2 +- .../launcher3/{ => icons}/IconCache.java | 233 +++--------------- .../icons/IconCacheUpdateHandler.java | 223 +++++++++++++++++ .../launcher3/model/CacheDataUpdatedTask.java | 2 +- .../android/launcher3/model/LoaderCursor.java | 2 +- .../android/launcher3/model/LoaderTask.java | 29 ++- .../launcher3/model/PackageUpdatedTask.java | 2 +- .../android/launcher3/model/WidgetsModel.java | 2 +- .../widget/PendingAppWidgetHostView.java | 4 +- .../launcher3/widget/WidgetsDiffReporter.java | 2 +- .../launcher3/widget/WidgetsListAdapter.java | 2 +- .../model/BaseModelUpdateTaskTestCase.java | 2 +- .../launcher3/model/LoaderCursorTest.java | 2 +- .../widget/WidgetsListAdapterTest.java | 2 +- 24 files changed, 303 insertions(+), 222 deletions(-) rename src/com/android/launcher3/{ => icons}/IconCache.java (74%) create mode 100644 src/com/android/launcher3/icons/IconCacheUpdateHandler.java diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java index 3d60605459..733f29540f 100644 --- a/src/com/android/launcher3/AllAppsList.java +++ b/src/com/android/launcher3/AllAppsList.java @@ -26,6 +26,7 @@ import android.util.Log; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.PackageInstallerCompat; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.util.FlagOp; import com.android.launcher3.util.ItemInfoMatcher; diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 5d401433bf..8e6703cc5a 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -40,8 +40,8 @@ import android.view.ViewConfiguration; import android.view.ViewDebug; import android.widget.TextView; -import com.android.launcher3.IconCache.IconLoadRequest; -import com.android.launcher3.IconCache.ItemInfoUpdateReceiver; +import com.android.launcher3.icons.IconCache.IconLoadRequest; +import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver; import com.android.launcher3.Launcher.OnResumeCallback; import com.android.launcher3.badge.BadgeInfo; import com.android.launcher3.badge.BadgeRenderer; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 55074f8134..b5bbb9c680 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -87,6 +87,7 @@ import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.folder.FolderIconPreviewVerifier; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.keyboard.CustomActionsPopup; import com.android.launcher3.keyboard.ViewGroupFocusHelper; import com.android.launcher3.logging.FileLog; diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 5159de17af..b02182c571 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -29,6 +29,7 @@ import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.notification.NotificationListener; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.Preconditions; diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 7a90a55d45..df1c693382 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -39,6 +39,7 @@ import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.graphics.LauncherIcons; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.model.AddWorkspaceItemsTask; import com.android.launcher3.model.BaseModelUpdateTask; import com.android.launcher3.model.BgDataModel; diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java index a84bfd54ec..7717d91883 100644 --- a/src/com/android/launcher3/ShortcutInfo.java +++ b/src/com/android/launcher3/ShortcutInfo.java @@ -25,6 +25,7 @@ import android.text.TextUtils; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.shortcuts.ShortcutInfoCompat; import com.android.launcher3.util.ContentWriter; diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 84bad08583..cf497fd876 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -33,6 +33,7 @@ import com.android.launcher3.compat.ShortcutConfigActivityInfo; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.graphics.LauncherIcons; import com.android.launcher3.graphics.ShadowGenerator; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.model.WidgetItem; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.PackageUserKey; diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java index dd17916f0d..fe7b4e5d0d 100644 --- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java +++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java @@ -27,7 +27,7 @@ import android.os.UserHandle; import android.text.TextUtils; import android.util.SparseArray; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; import com.android.launcher3.config.FeatureFlags; diff --git a/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java b/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java index 31c0087cb0..d260e24e7e 100644 --- a/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java +++ b/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java @@ -32,7 +32,7 @@ import android.os.UserHandle; import android.util.Log; import android.widget.Toast; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; import com.android.launcher3.ShortcutInfo; diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java index 52167bb8bf..bd919bcbcb 100644 --- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java +++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java @@ -28,7 +28,7 @@ import android.os.Build; import android.os.Process; import com.android.launcher3.FastBitmapDrawable; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.LauncherAnimUtils; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherSettings; diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java index 087362cd42..7db67c9828 100644 --- a/src/com/android/launcher3/graphics/LauncherIcons.java +++ b/src/com/android/launcher3/graphics/LauncherIcons.java @@ -44,7 +44,7 @@ import android.os.UserHandle; import com.android.launcher3.AppInfo; import com.android.launcher3.FastBitmapDrawable; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.ItemInfoWithIcon; import com.android.launcher3.LauncherAppState; diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/icons/IconCache.java similarity index 74% rename from src/com/android/launcher3/IconCache.java rename to src/com/android/launcher3/icons/IconCache.java index 61fb3e38bf..2035d0e57d 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/icons/IconCache.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.launcher3; +package com.android.launcher3.icons; import static com.android.launcher3.graphics.BitmapInfo.LOW_RES_ICON; @@ -38,11 +38,19 @@ import android.graphics.drawable.Drawable; import android.os.Build.VERSION; import android.os.Handler; import android.os.Process; -import android.os.SystemClock; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; +import com.android.launcher3.AppInfo; +import com.android.launcher3.IconProvider; +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.ItemInfoWithIcon; +import com.android.launcher3.LauncherFiles; +import com.android.launcher3.LauncherModel; +import com.android.launcher3.MainThreadExecutor; +import com.android.launcher3.ShortcutInfo; +import com.android.launcher3.Utilities; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.graphics.BitmapInfo; @@ -54,14 +62,9 @@ import com.android.launcher3.util.InstantAppResolver; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.Provider; import com.android.launcher3.util.SQLiteCacheHelper; -import com.android.launcher3.util.Thunk; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.Stack; import androidx.annotation.NonNull; import androidx.core.graphics.ColorUtils; @@ -81,8 +84,6 @@ public class IconCache { private static final boolean DEBUG = false; private static final boolean DEBUG_IGNORE_CACHE = false; - @Thunk static final Object ICON_UPDATE_TOKEN = new Object(); - public static class CacheEntry extends BitmapInfo { public CharSequence title = ""; public CharSequence contentDescription = ""; @@ -93,20 +94,21 @@ public class IconCache { } private final HashMap mDefaultIcons = new HashMap<>(); - @Thunk final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); - private final Context mContext; - private final PackageManager mPackageManager; - private final IconProvider mIconProvider; - @Thunk final UserManagerCompat mUserManager; - private final LauncherAppsCompat mLauncherApps; + final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); + final Context mContext; + final PackageManager mPackageManager; + final IconProvider mIconProvider; + final UserManagerCompat mUserManager; + final LauncherAppsCompat mLauncherApps; + private final HashMap mCache = new HashMap<>(INITIAL_ICON_CACHE_CAPACITY); private final InstantAppResolver mInstantAppResolver; private final int mIconDpi; - @Thunk final IconDB mIconDb; - @Thunk final Handler mWorkerHandler; + final IconDB mIconDb; + final Handler mWorkerHandler; private final BitmapFactory.Options mDecodeOptions; @@ -247,115 +249,9 @@ public class IconCache { new String[]{packageName + "/%", Long.toString(userSerial)}); } - public void updateDbIcons(Set ignorePackagesForMainUser) { - // Remove all active icon update tasks. - mWorkerHandler.removeCallbacksAndMessages(ICON_UPDATE_TOKEN); - + public IconCacheUpdateHandler getUpdateHandler() { mIconProvider.updateSystemStateString(mContext); - for (UserHandle user : mUserManager.getUserProfiles()) { - // Query for the set of apps - final List apps = mLauncherApps.getActivityList(null, user); - // Fail if we don't have any apps - // TODO: Fix this. Only fail for the current user. - if (apps == null || apps.isEmpty()) { - return; - } - - // Update icon cache. This happens in segments and {@link #onPackageIconsUpdated} - // is called by the icon cache when the job is complete. - updateDBIcons(user, apps, Process.myUserHandle().equals(user) - ? ignorePackagesForMainUser : Collections.emptySet()); - } - } - - /** - * Updates the persistent DB, such that only entries corresponding to {@param apps} remain in - * the DB and are updated. - * @return The set of packages for which icons have updated. - */ - private void updateDBIcons(UserHandle user, List apps, - Set ignorePackages) { - long userSerial = mUserManager.getSerialNumberForUser(user); - PackageManager pm = mContext.getPackageManager(); - HashMap pkgInfoMap = new HashMap<>(); - for (PackageInfo info : pm.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) { - pkgInfoMap.put(info.packageName, info); - } - - HashMap componentMap = new HashMap<>(); - for (LauncherActivityInfo app : apps) { - componentMap.put(app.getComponentName(), app); - } - - HashSet itemsToRemove = new HashSet<>(); - Stack appsToUpdate = new Stack<>(); - - Cursor c = null; - try { - c = mIconDb.query( - new String[]{IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT, - IconDB.COLUMN_LAST_UPDATED, IconDB.COLUMN_VERSION, - IconDB.COLUMN_SYSTEM_STATE}, - IconDB.COLUMN_USER + " = ? ", - new String[]{Long.toString(userSerial)}); - - final int indexComponent = c.getColumnIndex(IconDB.COLUMN_COMPONENT); - final int indexLastUpdate = c.getColumnIndex(IconDB.COLUMN_LAST_UPDATED); - final int indexVersion = c.getColumnIndex(IconDB.COLUMN_VERSION); - final int rowIndex = c.getColumnIndex(IconDB.COLUMN_ROWID); - final int systemStateIndex = c.getColumnIndex(IconDB.COLUMN_SYSTEM_STATE); - - while (c.moveToNext()) { - String cn = c.getString(indexComponent); - ComponentName component = ComponentName.unflattenFromString(cn); - PackageInfo info = pkgInfoMap.get(component.getPackageName()); - if (info == null) { - if (!ignorePackages.contains(component.getPackageName())) { - remove(component, user); - itemsToRemove.add(c.getInt(rowIndex)); - } - continue; - } - if ((info.applicationInfo.flags & ApplicationInfo.FLAG_IS_DATA_ONLY) != 0) { - // Application is not present - continue; - } - - long updateTime = c.getLong(indexLastUpdate); - int version = c.getInt(indexVersion); - LauncherActivityInfo app = componentMap.remove(component); - if (version == info.versionCode && updateTime == info.lastUpdateTime && - TextUtils.equals(c.getString(systemStateIndex), - mIconProvider.getIconSystemState(info.packageName))) { - continue; - } - if (app == null) { - remove(component, user); - itemsToRemove.add(c.getInt(rowIndex)); - } else { - appsToUpdate.add(app); - } - } - } catch (SQLiteException e) { - Log.d(TAG, "Error reading icon cache", e); - // Continue updating whatever we have read so far - } finally { - if (c != null) { - c.close(); - } - } - if (!itemsToRemove.isEmpty()) { - mIconDb.delete( - Utilities.createDbSelectionQuery(IconDB.COLUMN_ROWID, itemsToRemove), null); - } - - // Insert remaining apps. - if (!componentMap.isEmpty() || !appsToUpdate.isEmpty()) { - Stack appsToAdd = new Stack<>(); - appsToAdd.addAll(componentMap.values()); - new SerializedIconUpdateTask(userSerial, pkgInfoMap, - appsToAdd, appsToUpdate).scheduleNext(); - } + return new IconCacheUpdateHandler(this); } /** @@ -363,8 +259,9 @@ public class IconCache { * @param replaceExisting if true, it will recreate the bitmap even if it already exists in * the memory. This is useful then the previous bitmap was created using * old data. + * package private */ - @Thunk synchronized void addIconToDBAndMemCache(LauncherActivityInfo app, + synchronized void addIconToDBAndMemCache(LauncherActivityInfo app, PackageInfo info, long userSerial, boolean replaceExisting) { final ComponentKey key = new ComponentKey(app.getComponentName(), app.getUser()); CacheEntry entry = null; @@ -744,81 +641,23 @@ public class IconCache { } } - /** - * A runnable that updates invalid icons and adds missing icons in the DB for the provided - * LauncherActivityInfo list. Items are updated/added one at a time, so that the - * worker thread doesn't get blocked. - */ - @Thunk class SerializedIconUpdateTask implements Runnable { - private final long mUserSerial; - private final HashMap mPkgInfoMap; - private final Stack mAppsToAdd; - private final Stack mAppsToUpdate; - private final HashSet mUpdatedPackages = new HashSet<>(); - - @Thunk SerializedIconUpdateTask(long userSerial, HashMap pkgInfoMap, - Stack appsToAdd, - Stack appsToUpdate) { - mUserSerial = userSerial; - mPkgInfoMap = pkgInfoMap; - mAppsToAdd = appsToAdd; - mAppsToUpdate = appsToUpdate; - } - - @Override - public void run() { - if (!mAppsToUpdate.isEmpty()) { - LauncherActivityInfo app = mAppsToUpdate.pop(); - String pkg = app.getComponentName().getPackageName(); - PackageInfo info = mPkgInfoMap.get(pkg); - addIconToDBAndMemCache(app, info, mUserSerial, true /*replace existing*/); - mUpdatedPackages.add(pkg); - - if (mAppsToUpdate.isEmpty() && !mUpdatedPackages.isEmpty()) { - // No more app to update. Notify model. - LauncherAppState.getInstance(mContext).getModel().onPackageIconsUpdated( - mUpdatedPackages, mUserManager.getUserForSerialNumber(mUserSerial)); - } - - // Let it run one more time. - scheduleNext(); - } else if (!mAppsToAdd.isEmpty()) { - LauncherActivityInfo app = mAppsToAdd.pop(); - PackageInfo info = mPkgInfoMap.get(app.getComponentName().getPackageName()); - // We do not check the mPkgInfoMap when generating the mAppsToAdd. Although every - // app should have package info, this is not guaranteed by the api - if (info != null) { - addIconToDBAndMemCache(app, info, mUserSerial, false /*replace existing*/); - } - - if (!mAppsToAdd.isEmpty()) { - scheduleNext(); - } - } - } - - public void scheduleNext() { - mWorkerHandler.postAtTime(this, ICON_UPDATE_TOKEN, SystemClock.uptimeMillis() + 1); - } - } - - private static final class IconDB extends SQLiteCacheHelper { + static final class IconDB extends SQLiteCacheHelper { private final static int RELEASE_VERSION = 25; - private final static String TABLE_NAME = "icons"; - private final static String COLUMN_ROWID = "rowid"; - private final static String COLUMN_COMPONENT = "componentName"; - private final static String COLUMN_USER = "profileId"; - private final static String COLUMN_LAST_UPDATED = "lastUpdated"; - private final static String COLUMN_VERSION = "version"; - private final static String COLUMN_ICON = "icon"; - private final static String COLUMN_ICON_COLOR = "icon_color"; - private final static String COLUMN_LABEL = "label"; - private final static String COLUMN_SYSTEM_STATE = "system_state"; + public final static String TABLE_NAME = "icons"; + public final static String COLUMN_ROWID = "rowid"; + public final static String COLUMN_COMPONENT = "componentName"; + public final static String COLUMN_USER = "profileId"; + public final static String COLUMN_LAST_UPDATED = "lastUpdated"; + public final static String COLUMN_VERSION = "version"; + public final static String COLUMN_ICON = "icon"; + public final static String COLUMN_ICON_COLOR = "icon_color"; + public final static String COLUMN_LABEL = "label"; + public final static String COLUMN_SYSTEM_STATE = "system_state"; - private final static String[] COLUMNS_HIGH_RES = new String[] { + public final static String[] COLUMNS_HIGH_RES = new String[] { IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL, IconDB.COLUMN_ICON }; - private final static String[] COLUMNS_LOW_RES = new String[] { + public final static String[] COLUMNS_LOW_RES = new String[] { IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL }; public IconDB(Context context, int iconPixelSize) { diff --git a/src/com/android/launcher3/icons/IconCacheUpdateHandler.java b/src/com/android/launcher3/icons/IconCacheUpdateHandler.java new file mode 100644 index 0000000000..04e29013ab --- /dev/null +++ b/src/com/android/launcher3/icons/IconCacheUpdateHandler.java @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2018 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.icons; + +import android.content.ComponentName; +import android.content.pm.ApplicationInfo; +import android.content.pm.LauncherActivityInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.database.sqlite.SQLiteException; +import android.os.SystemClock; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.Log; + +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.Utilities; +import com.android.launcher3.icons.IconCache.IconDB; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.Stack; + +/** + * Utility class to handle updating the Icon cache + */ +public class IconCacheUpdateHandler { + + private static final String TAG = "IconCacheUpdateHandler"; + + private static final Object ICON_UPDATE_TOKEN = new Object(); + + private final HashMap mPkgInfoMap; + private final IconCache mIconCache; + private final HashMap> mPackagesToIgnore = new HashMap<>(); + + IconCacheUpdateHandler(IconCache cache) { + mIconCache = cache; + + mPkgInfoMap = new HashMap<>(); + + // Remove all active icon update tasks. + mIconCache.mWorkerHandler.removeCallbacksAndMessages(ICON_UPDATE_TOKEN); + + createPackageInfoMap(); + } + + public void setPackagesToIgnore(UserHandle userHandle, Set packages) { + mPackagesToIgnore.put(userHandle, packages); + } + + private void createPackageInfoMap() { + PackageManager pm = mIconCache.mPackageManager; + HashMap pkgInfoMap = new HashMap<>(); + for (PackageInfo info : pm.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) { + pkgInfoMap.put(info.packageName, info); + } + } + + /** + * Updates the persistent DB, such that only entries corresponding to {@param apps} remain in + * the DB and are updated. + * @return The set of packages for which icons have updated. + */ + public void updateIcons(List apps) { + if (apps.isEmpty()) { + return; + } + UserHandle user = apps.get(0).getUser(); + + Set ignorePackages = mPackagesToIgnore.get(user); + if (ignorePackages == null) { + ignorePackages = Collections.emptySet(); + } + + long userSerial = mIconCache.mUserManager.getSerialNumberForUser(user); + HashMap componentMap = new HashMap<>(); + for (LauncherActivityInfo app : apps) { + componentMap.put(app.getComponentName(), app); + } + + HashSet itemsToRemove = new HashSet<>(); + Stack appsToUpdate = new Stack<>(); + + try (Cursor c = mIconCache.mIconDb.query( + new String[]{IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT, + IconDB.COLUMN_LAST_UPDATED, IconDB.COLUMN_VERSION, + IconDB.COLUMN_SYSTEM_STATE}, + IconDB.COLUMN_USER + " = ? ", + new String[]{Long.toString(userSerial)})) { + + final int indexComponent = c.getColumnIndex(IconDB.COLUMN_COMPONENT); + final int indexLastUpdate = c.getColumnIndex(IconDB.COLUMN_LAST_UPDATED); + final int indexVersion = c.getColumnIndex(IconDB.COLUMN_VERSION); + final int rowIndex = c.getColumnIndex(IconDB.COLUMN_ROWID); + final int systemStateIndex = c.getColumnIndex(IconDB.COLUMN_SYSTEM_STATE); + + while (c.moveToNext()) { + String cn = c.getString(indexComponent); + ComponentName component = ComponentName.unflattenFromString(cn); + PackageInfo info = mPkgInfoMap.get(component.getPackageName()); + if (info == null) { + if (!ignorePackages.contains(component.getPackageName())) { + mIconCache.remove(component, user); + itemsToRemove.add(c.getInt(rowIndex)); + } + continue; + } + if ((info.applicationInfo.flags & ApplicationInfo.FLAG_IS_DATA_ONLY) != 0) { + // Application is not present + continue; + } + + long updateTime = c.getLong(indexLastUpdate); + int version = c.getInt(indexVersion); + LauncherActivityInfo app = componentMap.remove(component); + if (version == info.versionCode && updateTime == info.lastUpdateTime && + TextUtils.equals(c.getString(systemStateIndex), + mIconCache.mIconProvider.getIconSystemState(info.packageName))) { + continue; + } + if (app == null) { + mIconCache.remove(component, user); + itemsToRemove.add(c.getInt(rowIndex)); + } else { + appsToUpdate.add(app); + } + } + } catch (SQLiteException e) { + Log.d(TAG, "Error reading icon cache", e); + // Continue updating whatever we have read so far + } + if (!itemsToRemove.isEmpty()) { + mIconCache.mIconDb.delete( + Utilities.createDbSelectionQuery(IconDB.COLUMN_ROWID, itemsToRemove), null); + } + + // Insert remaining apps. + if (!componentMap.isEmpty() || !appsToUpdate.isEmpty()) { + Stack appsToAdd = new Stack<>(); + appsToAdd.addAll(componentMap.values()); + new SerializedIconUpdateTask(userSerial, user, appsToAdd, appsToUpdate).scheduleNext(); + } + } + + + /** + * A runnable that updates invalid icons and adds missing icons in the DB for the provided + * LauncherActivityInfo list. Items are updated/added one at a time, so that the + * worker thread doesn't get blocked. + */ + private class SerializedIconUpdateTask implements Runnable { + private final long mUserSerial; + private final UserHandle mUserHandle; + private final Stack mAppsToAdd; + private final Stack mAppsToUpdate; + private final HashSet mUpdatedPackages = new HashSet<>(); + + SerializedIconUpdateTask(long userSerial, UserHandle userHandle, + Stack appsToAdd, Stack appsToUpdate) { + mUserHandle = userHandle; + mUserSerial = userSerial; + mAppsToAdd = appsToAdd; + mAppsToUpdate = appsToUpdate; + } + + @Override + public void run() { + if (!mAppsToUpdate.isEmpty()) { + LauncherActivityInfo app = mAppsToUpdate.pop(); + String pkg = app.getComponentName().getPackageName(); + PackageInfo info = mPkgInfoMap.get(pkg); + mIconCache.addIconToDBAndMemCache( + app, info, mUserSerial, true /*replace existing*/); + mUpdatedPackages.add(pkg); + + if (mAppsToUpdate.isEmpty() && !mUpdatedPackages.isEmpty()) { + // No more app to update. Notify model. + LauncherAppState.getInstance(mIconCache.mContext).getModel() + .onPackageIconsUpdated(mUpdatedPackages, mUserHandle); + } + + // Let it run one more time. + scheduleNext(); + } else if (!mAppsToAdd.isEmpty()) { + LauncherActivityInfo app = mAppsToAdd.pop(); + PackageInfo info = mPkgInfoMap.get(app.getComponentName().getPackageName()); + // We do not check the mPkgInfoMap when generating the mAppsToAdd. Although every + // app should have package info, this is not guaranteed by the api + if (info != null) { + mIconCache.addIconToDBAndMemCache( + app, info, mUserSerial, false /*replace existing*/); + } + + if (!mAppsToAdd.isEmpty()) { + scheduleNext(); + } + } + } + + public void scheduleNext() { + mIconCache.mWorkerHandler.postAtTime(this, ICON_UPDATE_TOKEN, + SystemClock.uptimeMillis() + 1); + } + } +} diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java index 54c0542764..be83d36921 100644 --- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java +++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java @@ -20,7 +20,7 @@ import android.os.UserHandle; import com.android.launcher3.AllAppsList; import com.android.launcher3.AppInfo; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel.CallbackTask; diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java index 744e98aeaa..77e9721bce 100644 --- a/src/com/android/launcher3/model/LoaderCursor.java +++ b/src/com/android/launcher3/model/LoaderCursor.java @@ -32,7 +32,7 @@ import android.util.Log; import android.util.LongSparseArray; import com.android.launcher3.AppInfo; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 8f8bc09f3c..4ccb8d8d6d 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -43,7 +43,8 @@ import android.util.MutableInt; import com.android.launcher3.AllAppsList; import com.android.launcher3.AppInfo; import com.android.launcher3.FolderInfo; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCacheUpdateHandler; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.InstallShortcutReceiver; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; @@ -182,7 +183,7 @@ public class LoaderTask implements Runnable { // second step TraceHelper.partitionSection(TAG, "step 2.1: loading all apps"); - loadAllApps(); + List> activityListPerUser = loadAllApps(); TraceHelper.partitionSection(TAG, "step 2.2: Binding all apps"); verifyNotStopped(); @@ -190,7 +191,9 @@ public class LoaderTask implements Runnable { verifyNotStopped(); TraceHelper.partitionSection(TAG, "step 2.3: Update icon cache"); - updateIconCache(); + IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler(); + setIgnorePackages(updateHandler); + updateIconCacheForApps(updateHandler, activityListPerUser); // Take a break TraceHelper.partitionSection(TAG, "step 2 completed, wait for idle"); @@ -774,7 +777,7 @@ public class LoaderTask implements Runnable { } } - private void updateIconCache() { + private void setIgnorePackages(IconCacheUpdateHandler updateHandler) { // Ignore packages which have a promise icon. HashSet packagesToIgnore = new HashSet<>(); synchronized (mBgDataModel) { @@ -792,12 +795,20 @@ public class LoaderTask implements Runnable { } } } - mIconCache.updateDbIcons(packagesToIgnore); + updateHandler.setPackagesToIgnore(Process.myUserHandle(), packagesToIgnore); } - private void loadAllApps() { - final List profiles = mUserManager.getUserProfiles(); + private void updateIconCacheForApps(IconCacheUpdateHandler updateHandler, + List> activityListPerUser) { + int userCount = activityListPerUser.size(); + for (int i = 0; i < userCount; i++) { + updateHandler.updateIcons(activityListPerUser.get(i)); + } + } + private List> loadAllApps() { + final List profiles = mUserManager.getUserProfiles(); + List> activityListPerUser = new ArrayList<>(); // Clear the list of apps mBgAllAppsList.clear(); for (UserHandle user : profiles) { @@ -806,7 +817,7 @@ public class LoaderTask implements Runnable { // Fail if we don't have any apps // TODO: Fix this. Only fail for the current user. if (apps == null || apps.isEmpty()) { - return; + return activityListPerUser; } boolean quietMode = mUserManager.isQuietModeEnabled(user); // Create the ApplicationInfos @@ -815,6 +826,7 @@ public class LoaderTask implements Runnable { // This builds the icon bitmaps. mBgAllAppsList.add(new AppInfo(app, user, quietMode), app); } + activityListPerUser.add(apps); } if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) { @@ -827,6 +839,7 @@ public class LoaderTask implements Runnable { } mBgAllAppsList.added = new ArrayList<>(); + return activityListPerUser; } private void loadDeepShortcuts() { diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java index c55608310c..0af53118d3 100644 --- a/src/com/android/launcher3/model/PackageUpdatedTask.java +++ b/src/com/android/launcher3/model/PackageUpdatedTask.java @@ -24,7 +24,7 @@ import android.util.Log; import com.android.launcher3.AllAppsList; import com.android.launcher3.AppInfo; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.InstallShortcutReceiver; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java index 448ff6c920..82f4fe1894 100644 --- a/src/com/android/launcher3/model/WidgetsModel.java +++ b/src/com/android/launcher3/model/WidgetsModel.java @@ -11,7 +11,7 @@ import android.os.UserHandle; import android.util.Log; import com.android.launcher3.AppFilter; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetProviderInfo; diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java index 29b4b0ba24..50db40fde8 100644 --- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java +++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java @@ -33,8 +33,8 @@ import android.view.View.OnClickListener; import com.android.launcher3.DeviceProfile; import com.android.launcher3.FastBitmapDrawable; -import com.android.launcher3.IconCache; -import com.android.launcher3.IconCache.ItemInfoUpdateReceiver; +import com.android.launcher3.icons.IconCache; +import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver; import com.android.launcher3.ItemInfoWithIcon; import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.R; diff --git a/src/com/android/launcher3/widget/WidgetsDiffReporter.java b/src/com/android/launcher3/widget/WidgetsDiffReporter.java index 2ba672d7af..435125bd83 100644 --- a/src/com/android/launcher3/widget/WidgetsDiffReporter.java +++ b/src/com/android/launcher3/widget/WidgetsDiffReporter.java @@ -18,7 +18,7 @@ package com.android.launcher3.widget; import android.util.Log; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.model.PackageItemInfo; import com.android.launcher3.widget.WidgetsListAdapter.WidgetListRowEntryComparator; diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index 1016d04005..a45521dee7 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -23,7 +23,7 @@ import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.R; import com.android.launcher3.WidgetPreviewLoader; import com.android.launcher3.model.WidgetItem; diff --git a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java index dd3e859fda..59b2da036f 100644 --- a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java +++ b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java @@ -24,7 +24,7 @@ import androidx.test.rule.provider.ProviderTestRule; import com.android.launcher3.AllAppsList; import com.android.launcher3.AppFilter; import com.android.launcher3.AppInfo; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java index 29e54b1374..42a2764010 100644 --- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java +++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java @@ -10,7 +10,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; diff --git a/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java b/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java index d5b14f2985..307a53eefd 100644 --- a/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java +++ b/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java @@ -29,7 +29,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import android.view.LayoutInflater; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.WidgetPreviewLoader; From 94001d6db098d1330b4efa4bb7db7eda1c691759 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sat, 22 Sep 2018 08:51:38 -0700 Subject: [PATCH 14/16] Import translations. DO NOT MERGE Change-Id: Ie0d34917778d73f08e441cc078bdcbabe752e6a4 Auto-generated-cl: translation import --- res/values-ta/strings.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml index 253e3afb50..ed0f09ddab 100644 --- a/res/values-ta/strings.xml +++ b/res/values-ta/strings.xml @@ -114,8 +114,7 @@ "இங்கு நகர்த்து" "முகப்புத் திரையில் சேர்க்கப்பட்டது" "அகற்றப்பட்டது" - - + "செயல்தவிர்" "நகர்த்து" "%1$s வரிசை, %2$s நெடுவரிசைக்கு நகர்த்து" "நிலை %1$sக்கு நகர்த்து" From 73a3d79d24ef1fab4eb185689b8d955b993d70bf Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 24 Sep 2018 11:30:08 -0700 Subject: [PATCH 15/16] Cancelling long press on workspace if touch point moves a lot Bug: 113695336 Change-Id: I9a2e5972b6718b31cffd88be218fe5744c702827 --- .../android/launcher3/touch/WorkspaceTouchListener.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java index 668892791c..4de082e90e 100644 --- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java +++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java @@ -17,6 +17,7 @@ package com.android.launcher3.touch; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_DOWN; +import static android.view.MotionEvent.ACTION_MOVE; import static android.view.MotionEvent.ACTION_POINTER_UP; import static android.view.MotionEvent.ACTION_UP; import static android.view.ViewConfiguration.getLongPressTimeout; @@ -29,6 +30,7 @@ import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; +import android.view.ViewConfiguration; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.CellLayout; @@ -60,12 +62,16 @@ public class WorkspaceTouchListener implements OnTouchListener, Runnable { private final Launcher mLauncher; private final Workspace mWorkspace; private final PointF mTouchDownPoint = new PointF(); + private final float mTouchSlop; private int mLongPressState = STATE_CANCELLED; public WorkspaceTouchListener(Launcher launcher, Workspace workspace) { mLauncher = launcher; mWorkspace = workspace; + // Use twice the touch slop as we are looking for long press which is more + // likely to cause movement. + mTouchSlop = 2 * ViewConfiguration.get(launcher).getScaledTouchSlop(); } @Override @@ -116,6 +122,9 @@ public class WorkspaceTouchListener implements OnTouchListener, Runnable { mWorkspace.onTouchEvent(ev); if (mWorkspace.isHandlingTouch()) { cancelLongPress(); + } else if (action == ACTION_MOVE && PointF.length( + mTouchDownPoint.x - ev.getX(), mTouchDownPoint.y - ev.getY()) > mTouchSlop) { + cancelLongPress(); } result = true; From 6379d5370b77af0d3c2e926ed38174144aebbac0 Mon Sep 17 00:00:00 2001 From: Tony Date: Mon, 24 Sep 2018 15:31:40 -0400 Subject: [PATCH 16/16] Use task insets rather than launcher insets when swiping down This fixes the issue where dragging down on full screen apps was offset by the status bar even though it shouldn't be. Bug: 77979532 Change-Id: I8cb17778c4ae66b1821e86dd757626f875a27d2d --- .../src/com/android/quickstep/util/ClipAnimationHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java index c3ce4399ef..8aaa40c6bb 100644 --- a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java +++ b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java @@ -239,7 +239,7 @@ public class ClipAnimationHelper { updateStackBoundsToMultiWindowTaskSize(activity); } else { mSourceStackBounds.set(mHomeStackBounds); - mSourceInsets.set(activity.getDeviceProfile().getInsets()); + mSourceInsets.set(ttv.getInsets()); } TransformedRect targetRect = new TransformedRect();