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

Commit aa2aff5a authored by Samuel Fufa's avatar Samuel Fufa
Browse files

Personalize hotseat education

If a user has has 0 apps in the hotseat, jump directly to showing predications.
Otherwise show migration dialog as usual and if user rejects it, show different tips based on the number of available spots.

Bug: 142753423
Test: Manual
Change-Id: Ic5202caf074db2409f6468dd9373875571f3f3c1
parent 25486212
Loading
Loading
Loading
Loading
+37 −6
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.view.View;

import androidx.core.app.NotificationCompat;

import com.android.launcher3.ArrowTipView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.Hotseat;
@@ -42,6 +43,7 @@ import com.android.launcher3.util.ActivityTracker;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.Snackbar;

import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -56,6 +58,9 @@ public class HotseatEduController {
    private static final String NOTIFICATION_CHANNEL_ID = "launcher_onboarding";
    private static final int ONBOARDING_NOTIFICATION_ID = 7641;

    private static final String SETTINGS_ACTION =
            "android.settings.ACTION_CONTENT_SUGGESTIONS_SETTINGS";

    private final Launcher mLauncher;
    private final NotificationManager mNotificationManager;
    private final Notification mNotification;
@@ -65,9 +70,11 @@ public class HotseatEduController {
    private ArrayList<ItemInfo> mNewItems = new ArrayList<>();
    private IntArray mNewScreens = null;
    private Runnable mOnOnboardingComplete;
    private Hotseat mHotseat;

    HotseatEduController(Launcher launcher, Runnable runnable) {
        mLauncher = launcher;
        mHotseat = launcher.getHotseat();
        mOnOnboardingComplete = runnable;
        mNotificationManager = mLauncher.getSystemService(NotificationManager.class);
        createNotificationChannel();
@@ -98,7 +105,7 @@ public class HotseatEduController {

        //separate folders and items that can get in folders
        for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
            View view = mLauncher.getHotseat().getChildAt(i, 0);
            View view = mHotseat.getChildAt(i, 0);
            if (view == null) continue;
            ItemInfo info = (ItemInfo) view.getTag();
            if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
@@ -178,7 +185,6 @@ public class HotseatEduController {
     */
    private int migrateHotseatWhole() {
        Workspace workspace = mLauncher.getWorkspace();
        Hotseat hotseatVG = mLauncher.getHotseat();

        int pageId = -1;
        int toRow = 0;
@@ -196,7 +202,7 @@ public class HotseatEduController {
                    .getInt(LauncherSettings.Settings.EXTRA_VALUE);
        }
        for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
            View child = hotseatVG.getChildAt(i, 0);
            View child = mHotseat.getChildAt(i, 0);
            if (child == null || child.getTag() == null) continue;
            ItemInfo tag = (ItemInfo) child.getTag();
            mLauncher.getModelWriter().moveItemInDatabase(tag,
@@ -211,8 +217,8 @@ public class HotseatEduController {
        mNotificationManager.cancel(ONBOARDING_NOTIFICATION_ID);
    }

    void finishOnboarding() {
        mLauncher.getHotseat().removeAllViewsInLayout();
    void moveHotseatItems() {
        mHotseat.removeAllViewsInLayout();
        if (!mNewItems.isEmpty()) {
            int lastPage = mNewItems.get(mNewItems.size() - 1).screenId;
            ArrayList<ItemInfo> animated = new ArrayList<>();
@@ -227,11 +233,25 @@ public class HotseatEduController {
            }
            mLauncher.bindAppsAdded(mNewScreens, nonAnimated, animated);
        }
    }

    void finishOnboarding() {
        mOnOnboardingComplete.run();
        destroy();
        mLauncher.getSharedPrefs().edit().putBoolean(KEY_HOTSEAT_EDU_SEEN, true).apply();
    }

    void showDimissTip() {
        if (mHotseat.getShortcutsAndWidgets().getChildCount()
                < mLauncher.getDeviceProfile().inv.numHotseatIcons) {
            Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled, R.string.hotseat_turn_off,
                    null, () -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
        } else {
            new ArrowTipView(mLauncher).show(
                    mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop());
        }
    }

    void setPredictedApps(List<WorkspaceItemInfo> predictedApps) {
        mPredictedApps = predictedApps;
        if (!mPredictedApps.isEmpty()
@@ -275,6 +295,17 @@ public class HotseatEduController {
        }
    }

    void showEdu() {
        // hotseat is already empty and does not require migration. show edu tip
        if (mHotseat.getShortcutsAndWidgets().getChildCount() == 0) {
            new ArrowTipView(mLauncher).show(mLauncher.getString(R.string.hotseat_auto_enrolled),
                    mHotseat.getTop());
            finishOnboarding();
        } else {
            showDialog();
        }
    }

    void showDialog() {
        if (mPredictedApps == null || mPredictedApps.isEmpty()) {
            return;
@@ -291,7 +322,7 @@ public class HotseatEduController {
            ActivityTracker.SchedulerCallback<QuickstepLauncher> {
        @Override
        public boolean init(QuickstepLauncher activity, boolean alreadyOnHome) {
            activity.getHotseatPredictionController().showEduDialog();
            activity.getHotseatPredictionController().showEdu();
            return true;
        }
    }
+5 −8
Original line number Diff line number Diff line
@@ -16,7 +16,8 @@
package com.android.launcher3.hybridhotseat;

import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.HYBRID_HOTSEAT_CANCELED;
import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType
        .HYBRID_HOTSEAT_CANCELED;

import android.animation.PropertyValuesHolder;
import android.content.Context;
@@ -27,9 +28,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.android.launcher3.ArrowTipView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
@@ -109,18 +108,16 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
    private void onAccept(View v) {
        mHotseatEduController.migrate();
        handleClose(true);

        mHotseatEduController.moveHotseatItems();
        mHotseatEduController.finishOnboarding();
        //TODO: pass actual page index here.
        // Temporarily we're passing 1 for folder migration and 2 for page migration
        logUserAction(true, FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get() ? 1 : 2);
        int toastStringRes = !FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()
                ? R.string.hotseat_items_migrated : R.string.hotseat_items_migrated_alt;
        Toast.makeText(mLauncher, toastStringRes, Toast.LENGTH_LONG).show();
    }

    private void onDismiss(View v) {
        int top = mLauncher.getHotseat().getTop();
        new ArrowTipView(mLauncher).show(mLauncher.getString(R.string.hotseat_no_migration), top);
        mHotseatEduController.showDimissTip();
        mHotseatEduController.finishOnboarding();
        logUserAction(false, -1);
        handleClose(true);
+3 −3
Original line number Diff line number Diff line
@@ -147,12 +147,12 @@ public class HotseatPredictionController implements DragController.DragListener,
    }

    /**
     * Transitions to NORMAL workspace mode and shows edu dialog
     * Transitions to NORMAL workspace mode and shows edu
     */
    public void showEduDialog() {
    public void showEdu() {
        if (mHotseatEduController == null) return;
        mLauncher.getStateManager().goToState(LauncherState.NORMAL, true,
                () -> mHotseatEduController.showDialog());
                () -> mHotseatEduController.showEdu());
    }

    @Override
+10 −5
Original line number Diff line number Diff line
@@ -78,16 +78,21 @@
    <string name="hotseat_edu_message_migrate">Easily access your most-used apps right on the Home screen. Suggestions will change based on your routines. Apps on the bottom row will move up to your Home screen. </string>
    <string name="hotseat_edu_message_migrate_alt">Easily access your most-used apps, right on the Home screen. Suggestions will change based on your routines. Apps on the bottom row will move to a new folder.</string>

    <!-- Toast message user sees after opting into fully predicted hybrid hotseat -->
    <string name="hotseat_items_migrated">Your hotseat items have been moved up to your homescreen</string>
    <string name="hotseat_items_migrated_alt">Your hotseat items have been moved to a folder</string>
    <!-- Toast message user sees after opting into fully predicted hybrid hotseat -->
    <string name="hotseat_no_migration">Drag apps off the bottom row to see app suggestions</string>
    <!-- Button text to opt in for fully predicted hotseat -->
    <string name="hotseat_edu_accept">Get app suggestions</string>
    <!-- Button text to dismiss opt in for fully predicted hotseat -->
    <string name="hotseat_edu_dismiss">No thanks</string>

    <!-- action shown to turn of predictions after onboarding -->
    <string name="hotseat_turn_off">Settings</string>

    <!-- tip shown if user has no items in hotseat to migrate -->
    <string name="hotseat_auto_enrolled">Most-used apps appear here, and change based on routines</string>
    <!-- tip shown if user declines migration and has some open spots for prediction -->
    <string name="hotseat_tip_no_empty_slots">Drag apps off the bottom row to get app suggestions</string>
    <!-- tip shown if user declines migration and has no open spots for prediction -->
    <string name="hotseat_tip_gaps_filled">App suggestions added to empty space.</string>


    <!-- Title shown during interactive part of Back gesture tutorial for right edge. [CHAR LIMIT=30] -->
    <string name="back_gesture_tutorial_playground_title_swipe_inward_right_edge" translatable="false">Try the back gesture</string>