diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index 79796010fd..bc799f529b 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -58,7 +58,6 @@ import androidx.annotation.WorkerThread; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.model.AppLaunchTracker; @@ -68,6 +67,7 @@ import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.tracing.nano.LauncherTraceProto; import com.android.launcher3.tracing.nano.TouchInteractionServiceProto; import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; +import com.android.launcher3.util.OnboardingPrefs; import com.android.launcher3.util.TraceHelper; import com.android.quickstep.inputconsumers.AccessibilityInputConsumer; import com.android.quickstep.inputconsumers.AssistantInputConsumer; @@ -378,7 +378,7 @@ public class TouchInteractionService extends Service implements PluginListener { + + public QuickstepOnboardingPrefs(BaseQuickstepLauncher launcher, SharedPreferences sharedPrefs, + LauncherStateManager stateManager) { + super(launcher, sharedPrefs, stateManager); + + if (!getBoolean(HOME_BOUNCE_SEEN)) { + mStateManager.addStateListener(new StateListener() { + @Override + public void onStateTransitionStart(LauncherState toState) { } + + @Override + public void onStateTransitionComplete(LauncherState finalState) { + boolean swipeUpEnabled = SysUINavigationMode.INSTANCE + .get(mLauncher).getMode().hasGestures; + LauncherState prevState = mStateManager.getLastState(); + + if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled + && finalState == ALL_APPS && prevState == NORMAL) || + hasReachedMaxCount(HOME_BOUNCE_COUNT))) { + mSharedPrefs.edit().putBoolean(HOME_BOUNCE_SEEN, true).apply(); + mStateManager.removeStateListener(this); + } + } + }); + } + + if (!getBoolean(SHELF_BOUNCE_SEEN)) { + mStateManager.addStateListener(new StateListener() { + @Override + public void onStateTransitionStart(LauncherState toState) { } + + @Override + public void onStateTransitionComplete(LauncherState finalState) { + LauncherState prevState = mStateManager.getLastState(); + + if ((finalState == ALL_APPS && prevState == OVERVIEW) || + hasReachedMaxCount(SHELF_BOUNCE_COUNT)) { + mSharedPrefs.edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply(); + mStateManager.removeStateListener(this); + } + } + }); + } + } +} diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 48db5eaa32..f69940b92e 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -139,6 +139,7 @@ import com.android.launcher3.util.IntArray; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.MultiValueAlpha; import com.android.launcher3.util.MultiValueAlpha.AlphaProperty; +import com.android.launcher3.util.OnboardingPrefs; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.PendingRequestArgs; @@ -300,6 +301,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, // We only want to get the SharedPreferences once since it does an FS stat each time we get // it from the context. private SharedPreferences mSharedPrefs; + private OnboardingPrefs mOnboardingPrefs; // Activity result which needs to be processed after workspace has loaded. private ActivityResultInfo mPendingActivityResult; @@ -367,6 +369,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, mAllAppsController = new AllAppsTransitionController(this); mStateManager = new LauncherStateManager(this); + mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs, mStateManager); + mAppWidgetManager = new WidgetManagerHelper(this); mAppWidgetHost = new LauncherAppWidgetHost(this, appWidgetId -> getWorkspace().removeWidget(appWidgetId)); @@ -458,6 +462,15 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, return new LauncherOverlayManager() { }; } + protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs, + LauncherStateManager stateManager) { + return new OnboardingPrefs<>(this, sharedPrefs, stateManager); + } + + public OnboardingPrefs getOnboardingPrefs() { + return mOnboardingPrefs; + } + @Override public void onPluginConnected(OverlayPlugin overlayManager, Context context) { switchOverlay(() -> overlayManager.createOverlayManager(this, this)); diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java index 0f0fc3a249..fc29a30d74 100644 --- a/src/com/android/launcher3/allapps/DiscoveryBounce.java +++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java @@ -24,7 +24,6 @@ import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerTyp import android.animation.Animator; import android.animation.AnimatorInflater; import android.animation.AnimatorListenerAdapter; -import android.content.SharedPreferences; import android.os.Handler; import android.os.UserManager; import android.view.MotionEvent; @@ -35,6 +34,7 @@ import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager.StateListener; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.util.OnboardingPrefs; /** * Abstract base class of floating view responsible for showing discovery bounce animation @@ -43,13 +43,6 @@ public class DiscoveryBounce extends AbstractFloatingView { private static final long DELAY_MS = 450; - public static final String HOME_BOUNCE_SEEN = "launcher.apps_view_shown"; - public static final String SHELF_BOUNCE_SEEN = "launcher.shelf_bounce_seen"; - public static final String HOME_BOUNCE_COUNT = "launcher.home_bounce_count"; - public static final String SHELF_BOUNCE_COUNT = "launcher.shelf_bounce_count"; - - public static final int BOUNCE_MAX_COUNT = 3; - private final Launcher mLauncher; private final Animator mDiscoBounceAnimation; @@ -142,8 +135,9 @@ public class DiscoveryBounce extends AbstractFloatingView { } private static void showForHomeIfNeeded(Launcher launcher, boolean withDelay) { + OnboardingPrefs onboardingPrefs = launcher.getOnboardingPrefs(); if (!launcher.isInState(NORMAL) - || launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false) + || onboardingPrefs.getBoolean(OnboardingPrefs.HOME_BOUNCE_SEEN) || AbstractFloatingView.getTopOpenView(launcher) != null || launcher.getSystemService(UserManager.class).isDemoUser() || Utilities.IS_RUNNING_IN_TEST_HARNESS) { @@ -154,7 +148,7 @@ public class DiscoveryBounce extends AbstractFloatingView { new Handler().postDelayed(() -> showForHomeIfNeeded(launcher, false), DELAY_MS); return; } - incrementHomeBounceCount(launcher); + onboardingPrefs.incrementEventCount(OnboardingPrefs.HOME_BOUNCE_COUNT); new DiscoveryBounce(launcher, 0).show(HOTSEAT); } @@ -164,11 +158,12 @@ public class DiscoveryBounce extends AbstractFloatingView { } private static void showForOverviewIfNeeded(Launcher launcher, boolean withDelay) { + OnboardingPrefs onboardingPrefs = launcher.getOnboardingPrefs(); if (!launcher.isInState(OVERVIEW) || !launcher.hasBeenResumed() || launcher.isForceInvisible() || launcher.getDeviceProfile().isVerticalBarLayout() - || launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false) + || onboardingPrefs.getBoolean(OnboardingPrefs.SHELF_BOUNCE_SEEN) || launcher.getSystemService(UserManager.class).isDemoUser() || Utilities.IS_RUNNING_IN_TEST_HARNESS) { return; @@ -182,7 +177,7 @@ public class DiscoveryBounce extends AbstractFloatingView { // TODO: Move these checks to the top and call this method after invalidate handler. return; } - incrementShelfBounceCount(launcher); + onboardingPrefs.incrementEventCount(OnboardingPrefs.SHELF_BOUNCE_COUNT); new DiscoveryBounce(launcher, (1 - OVERVIEW.getVerticalProgress(launcher))) .show(PREDICTION); @@ -209,22 +204,4 @@ public class DiscoveryBounce extends AbstractFloatingView { mController.setProgress(progress - mDelta); } } - - private static void incrementShelfBounceCount(Launcher launcher) { - SharedPreferences sharedPrefs = launcher.getSharedPrefs(); - int count = sharedPrefs.getInt(SHELF_BOUNCE_COUNT, 0); - if (count > BOUNCE_MAX_COUNT) { - return; - } - sharedPrefs.edit().putInt(SHELF_BOUNCE_COUNT, count + 1).apply(); - } - - private static void incrementHomeBounceCount(Launcher launcher) { - SharedPreferences sharedPrefs = launcher.getSharedPrefs(); - int count = sharedPrefs.getInt(HOME_BOUNCE_COUNT, 0); - if (count > BOUNCE_MAX_COUNT) { - return; - } - sharedPrefs.edit().putInt(HOME_BOUNCE_COUNT, count + 1).apply(); - } } diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java new file mode 100644 index 0000000000..cdb60e9b63 --- /dev/null +++ b/src/com/android/launcher3/util/OnboardingPrefs.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2020 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.util; + +import android.content.SharedPreferences; +import android.util.ArrayMap; + +import androidx.annotation.StringDef; + +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherStateManager; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Collections; +import java.util.Map; + +/** + * Stores and retrieves onboarding-related data via SharedPreferences. + */ +public class OnboardingPrefs { + + public static final String HOME_BOUNCE_SEEN = "launcher.apps_view_shown"; + public static final String SHELF_BOUNCE_SEEN = "launcher.shelf_bounce_seen"; + public static final String HOME_BOUNCE_COUNT = "launcher.home_bounce_count"; + public static final String SHELF_BOUNCE_COUNT = "launcher.shelf_bounce_count"; + + /** + * Events that either have happened or have not (booleans). + */ + @StringDef(value = { + HOME_BOUNCE_SEEN, + SHELF_BOUNCE_SEEN, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EventBoolKey {} + + /** + * Events that occur multiple times, which we count up to a max defined in {@link #MAX_COUNTS}. + */ + @StringDef(value = { + HOME_BOUNCE_COUNT, + SHELF_BOUNCE_COUNT, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EventCountKey {} + + private static final Map MAX_COUNTS; + static { + Map maxCounts = new ArrayMap<>(2); + maxCounts.put(HOME_BOUNCE_COUNT, 3); + maxCounts.put(SHELF_BOUNCE_COUNT, 3); + MAX_COUNTS = Collections.unmodifiableMap(maxCounts); + } + + protected final T mLauncher; + protected final SharedPreferences mSharedPrefs; + protected final LauncherStateManager mStateManager; + + public OnboardingPrefs(T launcher, SharedPreferences sharedPrefs, + LauncherStateManager stateManager) { + mLauncher = launcher; + mSharedPrefs = sharedPrefs; + mStateManager = stateManager; + } + + /** @return The number of times we have seen the given event. */ + public int getCount(@EventCountKey String key) { + return mSharedPrefs.getInt(key, 0); + } + + /** @return Whether we have seen this event enough times, as defined by {@link #MAX_COUNTS}. */ + public boolean hasReachedMaxCount(@EventCountKey String eventKey) { + return hasReachedMaxCount(getCount(eventKey), eventKey); + } + + private boolean hasReachedMaxCount(int count, @EventCountKey String eventKey) { + return count >= MAX_COUNTS.get(eventKey); + } + + /** @return Whether we have seen the given event. */ + public boolean getBoolean(@EventBoolKey String key) { + return mSharedPrefs.getBoolean(key, false); + } + + /** + * Add 1 to the given event count, if we haven't already reached the max count. + */ + public void incrementEventCount(@EventCountKey String eventKey) { + int count = getCount(eventKey); + if (hasReachedMaxCount(count, eventKey)) { + return; + } + count++; + mSharedPrefs.edit().putInt(eventKey, count).apply(); + } +} diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java index a56801fa1c..313ea05a1f 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java @@ -15,8 +15,8 @@ */ package com.android.launcher3.uioverrides.states; -import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN; import static com.android.launcher3.anim.Interpolators.DEACCEL_2; +import static com.android.launcher3.util.OnboardingPrefs.HOME_BOUNCE_SEEN; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher;