From 96186aff87b9fd37ac12f3d90295a5fec2145121 Mon Sep 17 00:00:00 2001 From: Pinyao Ting Date: Mon, 20 Jul 2020 11:03:39 -0700 Subject: [PATCH 1/3] Minimal Phone Mode (part-3) Introduces a separate database for minimal device mode. When minimal device mode is enabled/disabled: 1. WellbeingModel receives onChange event from ContentObserver 2. WellbeingModel called DWB's ContentProvider for latest state in minimal device mode 3. Based on the state, WellbeingModel calls LauncherProvider to put launcher into normal/minimal mode. 4. When going from normal -> minimal, Launcher switches to a different database, namely minimal.db, then proceed to database initialization. 5. If the database hasn't been initialized yet, Launcher will call ContentResolver#openInputStream with following uri: content://com.google.android.apps.wellbeing.api/launcher_layout to get the default layout xml. 6. The default layout is then saved in database, and the database is considered initialized and doesn't need to go through step 5 again in the future. 7. In case of minimal -> normal, Launcher switches back to its original database (e.g. launcher.db if the grid size is 5x5), then reload launcher. Bug: 161462256 Change-Id: I6bafa66440da23281f63454b698ea56b15960022 --- .../launcher3/model/WellbeingModel.java | 48 ++++++++++++++--- .../android/launcher3/LauncherProvider.java | 53 ++++++++++++++++--- .../android/launcher3/LauncherSettings.java | 10 +++- 3 files changed, 97 insertions(+), 14 deletions(-) diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java index cc7b712842..810f4e3c59 100644 --- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java +++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java @@ -43,9 +43,13 @@ import android.util.ArrayMap; import android.util.Log; import androidx.annotation.MainThread; +import androidx.annotation.NonNull; import androidx.annotation.WorkerThread; import com.android.launcher3.BaseDraggingActivity; +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.LauncherProvider; +import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.ItemInfo; @@ -74,6 +78,9 @@ public final class WellbeingModel { private static final int MSG_PACKAGE_REMOVED = 2; private static final int MSG_FULL_REFRESH = 3; + private static final int UNKNOWN_MINIMAL_DEVICE_STATE = 0; + private static final int IN_MINIMAL_DEVICE = 2; + // Welbeing contract private static final String PATH_ACTIONS = "actions"; private static final String PATH_MINIMAL_DEVICE = "minimal_device"; @@ -84,6 +91,8 @@ public final class WellbeingModel { private static final String EXTRA_MAX_NUM_ACTIONS_SHOWN = "max_num_actions_shown"; private static final String EXTRA_PACKAGES = "packages"; private static final String EXTRA_SUCCESS = "success"; + private static final String EXTRA_MINIMAL_DEVICE_STATE = "minimal_device_state"; + private static final String DB_NAME_MINIMAL_DEVICE = "minimal.db"; public static final MainThreadInitializedObject INSTANCE = new MainThreadInitializedObject<>(WellbeingModel::new); @@ -121,11 +130,12 @@ public final class WellbeingModel { updateWellbeingData(); } else if (uri.getPath().contains(PATH_MINIMAL_DEVICE)) { // Wellbeing reports that minimal device state or config is changed. - updateLauncherModel(); + updateLauncherModel(context); } } }; - FeatureFlags.ENABLE_MINIMAL_DEVICE.addChangeListener(mContext, this::updateLauncherModel); + FeatureFlags.ENABLE_MINIMAL_DEVICE.addChangeListener(mContext, () -> + updateLauncherModel(context)); if (!TextUtils.isEmpty(mWellbeingProviderPkg)) { context.registerReceiver( @@ -170,7 +180,6 @@ public final class WellbeingModel { Log.e(TAG, "Failed to register content observer for " + actionsUri + ": " + e); if (mIsInTest) throw new RuntimeException(e); } - updateWellbeingData(); } @@ -208,10 +217,34 @@ public final class WellbeingModel { mWorkerHandler.sendEmptyMessage(MSG_FULL_REFRESH); } - private void updateLauncherModel() { - if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) return; + private void updateLauncherModel(@NonNull final Context context) { + if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) { + reloadLauncherInNormalMode(context); + return; + } + runWithMinimalDeviceConfigs((bundle) -> { + if (bundle.getInt(EXTRA_MINIMAL_DEVICE_STATE, UNKNOWN_MINIMAL_DEVICE_STATE) + == IN_MINIMAL_DEVICE) { + reloadLauncherInMinimalMode(context); + } else { + reloadLauncherInNormalMode(context); + } + }); + } - // TODO: init Launcher in minimal device / normal mode + private void reloadLauncherInNormalMode(@NonNull final Context context) { + LauncherSettings.Settings.call(context.getContentResolver(), + LauncherSettings.Settings.METHOD_SWITCH_DATABASE, + InvariantDeviceProfile.INSTANCE.get(context).dbFile); + } + + private void reloadLauncherInMinimalMode(@NonNull final Context context) { + final Bundle extras = new Bundle(); + extras.putString(LauncherProvider.KEY_LAYOUT_PROVIDER_AUTHORITY, + mWellbeingProviderPkg + ".api"); + LauncherSettings.Settings.call(context.getContentResolver(), + LauncherSettings.Settings.METHOD_SWITCH_DATABASE, + DB_NAME_MINIMAL_DEVICE, extras); } private Uri.Builder apiBuilder() { @@ -225,6 +258,9 @@ public final class WellbeingModel { */ @WorkerThread private void runWithMinimalDeviceConfigs(Consumer consumer) { + if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) { + return; + } if (DEBUG || mIsInTest) { Log.d(TAG, "runWithMinimalDeviceConfigs() called"); } diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index e8b5568783..19a314ffe2 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -100,10 +100,12 @@ public class LauncherProvider extends ContentProvider { public static final int SCHEMA_VERSION = 28; public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".settings"; + public static final String KEY_LAYOUT_PROVIDER_AUTHORITY = "KEY_LAYOUT_PROVIDER_AUTHORITY"; static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED"; protected DatabaseHelper mOpenHelper; + protected String mProviderAuthority; private long mLastRestoreTimestamp = 0L; @@ -367,7 +369,8 @@ public class LauncherProvider extends ContentProvider { case LauncherSettings.Settings.METHOD_WAS_EMPTY_DB_CREATED : { Bundle result = new Bundle(); result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE, - Utilities.getPrefs(getContext()).getBoolean(EMPTY_DATABASE_CREATED, false)); + Utilities.getPrefs(getContext()).getBoolean( + mOpenHelper.getKey(EMPTY_DATABASE_CREATED), false)); return result; } case LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS: { @@ -437,6 +440,7 @@ public class LauncherProvider extends ContentProvider { getContext(), true /* forMigration */))); return result; } + return null; } case LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW: { if (MULTI_DB_GRID_MIRATION_ALGO.get()) { @@ -450,6 +454,23 @@ public class LauncherProvider extends ContentProvider { () -> mOpenHelper)); return result; } + return null; + } + case LauncherSettings.Settings.METHOD_SWITCH_DATABASE: { + if (TextUtils.equals(arg, mOpenHelper.getDatabaseName())) return null; + final DatabaseHelper helper = mOpenHelper; + if (extras == null || !extras.containsKey(KEY_LAYOUT_PROVIDER_AUTHORITY)) { + mProviderAuthority = null; + } else { + mProviderAuthority = extras.getString(KEY_LAYOUT_PROVIDER_AUTHORITY); + } + mOpenHelper = DatabaseHelper.createDatabaseHelper( + getContext(), arg, false /* forMigration */); + helper.close(); + LauncherAppState app = LauncherAppState.getInstanceNoCreate(); + if (app == null) return null; + app.getModel().forceReload(); + return null; } } return null; @@ -492,7 +513,8 @@ public class LauncherProvider extends ContentProvider { } private void clearFlagEmptyDbCreated() { - Utilities.getPrefs(getContext()).edit().remove(EMPTY_DATABASE_CREATED).commit(); + Utilities.getPrefs(getContext()).edit() + .remove(mOpenHelper.getKey(EMPTY_DATABASE_CREATED)).commit(); } /** @@ -505,7 +527,7 @@ public class LauncherProvider extends ContentProvider { synchronized private void loadDefaultFavoritesIfNecessary() { SharedPreferences sp = Utilities.getPrefs(getContext()); - if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) { + if (sp.getBoolean(mOpenHelper.getKey(EMPTY_DATABASE_CREATED), false)) { Log.d(TAG, "loading default workspace"); AppWidgetHost widgetHost = mOpenHelper.newLauncherWidgetHost(); @@ -553,8 +575,13 @@ public class LauncherProvider extends ContentProvider { */ private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction(AppWidgetHost widgetHost) { Context ctx = getContext(); - String authority = Settings.Secure.getString(ctx.getContentResolver(), - "launcher3.layout.provider"); + final String authority; + if (!TextUtils.isEmpty(mProviderAuthority)) { + authority = mProviderAuthority; + } else { + authority = Settings.Secure.getString(ctx.getContentResolver(), + "launcher3.layout.provider"); + } if (TextUtils.isEmpty(authority)) { return null; } @@ -693,12 +720,26 @@ public class LauncherProvider extends ContentProvider { } } + /** + * Re-composite given key in respect to database. If the current db is + * {@link LauncherFiles#LAUNCHER_DB}, return the key as-is. Otherwise append the db name to + * given key. e.g. consider key="EMPTY_DATABASE_CREATED", dbName="minimal.db", the returning + * string will be "EMPTY_DATABASE_CREATED@minimal.db". + */ + String getKey(final String key) { + if (TextUtils.equals(getDatabaseName(), LauncherFiles.LAUNCHER_DB)) { + return key; + } + return key + "@" + getDatabaseName(); + } + /** * Overriden in tests. */ protected void onEmptyDbCreated() { // Set the flag for empty DB - Utilities.getPrefs(mContext).edit().putBoolean(EMPTY_DATABASE_CREATED, true).commit(); + Utilities.getPrefs(mContext).edit().putBoolean(getKey(EMPTY_DATABASE_CREATED), true) + .commit(); } public long getSerialNumberForUser(UserHandle user) { diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index 5512654390..58a418edec 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -354,14 +354,20 @@ public class LauncherSettings { public static final String METHOD_PREP_FOR_PREVIEW = "prep_for_preview"; + public static final String METHOD_SWITCH_DATABASE = "switch_database"; + public static final String EXTRA_VALUE = "value"; public static Bundle call(ContentResolver cr, String method) { - return call(cr, method, null); + return call(cr, method, null /* arg */); } public static Bundle call(ContentResolver cr, String method, String arg) { - return cr.call(CONTENT_URI, method, arg, null); + return call(cr, method, arg, null /* extras */); + } + + public static Bundle call(ContentResolver cr, String method, String arg, Bundle extras) { + return cr.call(CONTENT_URI, method, arg, extras); } } } From 375c602fc0a3cdd037f5bf23bdd9435cb5d1d5c5 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Wed, 12 Aug 2020 13:28:24 -0700 Subject: [PATCH 2/3] Set new scrimMultiplier instead of scrimProgress for overview -> home Animating this new property allows us to adjust the scrim without competing with other state animations that could be setting the scrim progress. Also reset scrimMultipler = 1 for state transitions. Test: Swipe from home to overview, then during that transition swipe from overview to home; when the former transition ends while the second is still in progress, ensure scrim value doesn't jump Bug: 144170434 Change-Id: I44b57ced1c6902e558fe1818bcbf11ceb4ff2f6f Merged-In: I44b57ced1c6902e558fe1818bcbf11ceb4ff2f6f --- .../NavBarToHomeTouchController.java | 12 ++++---- .../BaseRecentsViewStateController.java | 4 +++ .../launcher3/graphics/OverviewScrim.java | 28 +++++++++++++++++++ src/com/android/launcher3/graphics/Scrim.java | 6 +++- 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java index 61f6702dcc..9cc3e1a0ad 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java @@ -66,8 +66,8 @@ public class NavBarToHomeTouchController implements TouchController, SingleAxisSwipeDetector.Listener { private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL_3; - // How much of the overview scrim we can remove during the transition. - private static final float OVERVIEW_TO_HOME_SCRIM_PROGRESS = 0.5f; + // The min amount of overview scrim we keep during the transition. + private static final float OVERVIEW_TO_HOME_SCRIM_MULTIPLIER = 0.5f; private final Launcher mLauncher; private final SingleAxisSwipeDetector mSwipeDetector; @@ -163,11 +163,11 @@ public class NavBarToHomeTouchController implements TouchController, RecentsView recentsView = mLauncher.getOverviewPanel(); AnimatorControllerWithResistance.createRecentsResistanceFromOverviewAnim(mLauncher, builder); - float endScrimAlpha = Utilities.mapRange(OVERVIEW_TO_HOME_SCRIM_PROGRESS, - mStartState.getOverviewScrimAlpha(mLauncher), - mEndState.getOverviewScrimAlpha(mLauncher)); + builder.setFloat(mLauncher.getDragLayer().getOverviewScrim(), - OverviewScrim.SCRIM_PROGRESS, endScrimAlpha, PULLBACK_INTERPOLATOR); + OverviewScrim.SCRIM_MULTIPLIER, OVERVIEW_TO_HOME_SCRIM_MULTIPLIER, + PULLBACK_INTERPOLATOR); + if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { builder.addOnFrameCallback( () -> recentsView.redrawLiveTile(false /* mightNeedToRefill */)); diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java index 1b8e244ac1..aad7e17df0 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java @@ -18,6 +18,7 @@ package com.android.launcher3.uioverrides; import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT; import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.graphics.OverviewScrim.SCRIM_MULTIPLIER; import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL; @@ -70,6 +71,7 @@ public abstract class BaseRecentsViewStateController getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0); OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim(); SCRIM_PROGRESS.set(scrim, state.getOverviewScrimAlpha(mLauncher)); + SCRIM_MULTIPLIER.set(scrim, 1f); getTaskModalnessProperty().set(mRecentsView, state.getOverviewModalness()); } @@ -108,6 +110,8 @@ public abstract class BaseRecentsViewStateController OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim(); setter.setFloat(scrim, SCRIM_PROGRESS, toState.getOverviewScrimAlpha(mLauncher), config.getInterpolator(ANIM_OVERVIEW_SCRIM_FADE, LINEAR)); + setter.setFloat(scrim, SCRIM_MULTIPLIER, 1f, + config.getInterpolator(ANIM_OVERVIEW_SCRIM_FADE, LINEAR)); setter.setFloat( mRecentsView, getTaskModalnessProperty(), diff --git a/src/com/android/launcher3/graphics/OverviewScrim.java b/src/com/android/launcher3/graphics/OverviewScrim.java index 94acbfdcca..c0c3e5e9c2 100644 --- a/src/com/android/launcher3/graphics/OverviewScrim.java +++ b/src/com/android/launcher3/graphics/OverviewScrim.java @@ -22,6 +22,7 @@ import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; import static com.android.launcher3.LauncherState.OVERVIEW; import android.graphics.Rect; +import android.util.FloatProperty; import android.view.View; import android.view.ViewGroup; @@ -33,10 +34,25 @@ import androidx.annotation.Nullable; */ public class OverviewScrim extends Scrim { + public static final FloatProperty SCRIM_MULTIPLIER = + new FloatProperty("scrimMultiplier") { + @Override + public Float get(OverviewScrim scrim) { + return scrim.mScrimMultiplier; + } + + @Override + public void setValue(OverviewScrim scrim, float v) { + scrim.setScrimMultiplier(v); + } + }; + private @NonNull View mStableScrimmedView; // Might be higher up if mStableScrimmedView is invisible. private @Nullable View mCurrentScrimmedView; + private float mScrimMultiplier = 1f; + public OverviewScrim(View view) { super(view); mStableScrimmedView = mCurrentScrimmedView = mLauncher.getOverviewPanel(); @@ -68,4 +84,16 @@ public class OverviewScrim extends Scrim { public @Nullable View getScrimmedView() { return mCurrentScrimmedView; } + + private void setScrimMultiplier(float scrimMultiplier) { + if (Float.compare(mScrimMultiplier, scrimMultiplier) != 0) { + mScrimMultiplier = scrimMultiplier; + invalidate(); + } + } + + @Override + protected int getScrimAlpha() { + return Math.round(super.getScrimAlpha() * mScrimMultiplier); + } } diff --git a/src/com/android/launcher3/graphics/Scrim.java b/src/com/android/launcher3/graphics/Scrim.java index f90962d5d1..a151cba47d 100644 --- a/src/com/android/launcher3/graphics/Scrim.java +++ b/src/com/android/launcher3/graphics/Scrim.java @@ -61,7 +61,11 @@ public class Scrim implements View.OnAttachStateChangeListener, } public void draw(Canvas canvas) { - canvas.drawColor(setColorAlphaBound(mScrimColor, mScrimAlpha)); + canvas.drawColor(setColorAlphaBound(mScrimColor, getScrimAlpha())); + } + + protected int getScrimAlpha() { + return mScrimAlpha; } private void setScrimProgress(float progress) { From ff20eae383e40058bed5bb9c05d668982670bcd5 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Wed, 19 Aug 2020 17:21:53 -0700 Subject: [PATCH 3/3] Allow swipe up to go home from overivew during task dismiss animation Test: go to overview, dismiss a task, swipe up to go home during the transition (easier to try with animation duraation scaled up) Bug: 144170434 Change-Id: I28b846b43260179aeac877351a7069743f66f3ea Merged-In: I28b846b43260179aeac877351a7069743f66f3ea --- .../touchcontrollers/TaskViewTouchController.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java index 7bae2e5d86..2cfbc2bec6 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java @@ -83,7 +83,15 @@ public abstract class TaskViewTouchController mDetector = new SingleAxisSwipeDetector(activity, this, dir); } - private boolean canInterceptTouch() { + private boolean canInterceptTouch(MotionEvent ev) { + if ((ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) != 0) { + // Don't intercept swipes on the nav bar, as user might be trying to go home + // during a task dismiss animation. + if (mCurrentAnimation != null) { + mCurrentAnimation.getAnimationPlayer().end(); + } + return false; + } if (mCurrentAnimation != null) { mCurrentAnimation.forceFinishIfCloseToEnd(); } @@ -119,7 +127,7 @@ public abstract class TaskViewTouchController clearState(); } if (ev.getAction() == MotionEvent.ACTION_DOWN) { - mNoIntercept = !canInterceptTouch(); + mNoIntercept = !canInterceptTouch(ev); if (mNoIntercept) { return false; }