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

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

Enable support for same page hotseat migration

Bug: 142753423
Test: Manual
Change-Id: Ib53a64629a595c412e30ca5ff46077efc73a3f3e
parent d71c3b18
Loading
Loading
Loading
Loading
+6 −4
Original line number Diff line number Diff line
@@ -35,25 +35,27 @@

        <TextView
            style="@style/TextHeadline"
            android:id="@+id/hotseat_edu_heading"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="18dp"
            android:paddingLeft="@dimen/bottom_sheet_edu_padding"
            android:paddingRight="@dimen/bottom_sheet_edu_padding"
            android:text="@string/hotseat_migrate_title"
            android:text="@string/hotseat_edu_title_migrate"
            android:textAlignment="center"
            android:textColor="@android:color/white"
            android:textSize="20sp" />

        <TextView
            android:layout_width="match_parent"
            android:id="@+id/hotseat_edu_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="18dp"
            android:layout_marginBottom="18dp"
            android:fontFamily="roboto-medium"
            android:paddingLeft="@dimen/bottom_sheet_edu_padding"
            android:paddingRight="@dimen/bottom_sheet_edu_padding"
            android:text="@string/hotseat_migrate_message"
            android:text="@string/hotseat_edu_message_migrate"
            android:textAlignment="center"
            android:textColor="@android:color/white"
            android:textSize="16sp" />
@@ -83,7 +85,7 @@
                    android:layout_height="wrap_content"
                    android:layout_gravity="end"
                    android:background="?android:attr/selectableItemBackground"
                    android:text="@string/hotseat_migrate_accept"
                    android:text="@string/hotseat_edu_accept"
                    android:textAlignment="textEnd"
                    android:textColor="@android:color/white" />

@@ -91,7 +93,7 @@
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:id="@+id/no_thanks"
                    android:text="@string/hotseat_migrate_dismiss"
                    android:text="@string/hotseat_edu_dismiss"
                    android:layout_gravity="start"
                    android:background="?android:attr/selectableItemBackground"
                    android:textColor="@android:color/white" />
+20 −7
Original line number Diff line number Diff line
@@ -27,11 +27,15 @@ import android.view.ViewGroup;

import androidx.core.app.NotificationCompat;

import com.android.launcher3.CellLayout;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.Workspace;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.WorkspaceLayoutManager;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.ActivityTracker;
import com.android.launcher3.util.Themes;
@@ -61,16 +65,25 @@ public class HotseatEduController {
        mNotification = createNotification();
    }

    void migrate() {
    boolean migrate() {
        Workspace workspace = mLauncher.getWorkspace();
        CellLayout firstScreen = workspace.getScreenWithId(WorkspaceLayoutManager.FIRST_SCREEN_ID);
        int toPage = Workspace.FIRST_SCREEN_ID;
        int toRow = mLauncher.getDeviceProfile().inv.numRows - 1;
        if (FeatureFlags.HOTSEAT_MIGRATE_NEW_PAGE.get()) {
            toPage = workspace.getScreenIdForPageIndex(workspace.getPageCount());
            toRow = 0;
        } else if (!firstScreen.makeSpaceForHotseatMigration(true)) {
            return false;
        }
        ViewGroup hotseatVG = mLauncher.getHotseat().getShortcutsAndWidgets();
        int workspacePageCount = mLauncher.getWorkspace().getPageCount();
        for (int i = 0; i < hotseatVG.getChildCount(); i++) {
            View child = hotseatVG.getChildAt(i);
            ItemInfo tag = (ItemInfo) child.getTag();
            mLauncher.getModelWriter().moveItemInDatabase(tag,
                    LauncherSettings.Favorites.CONTAINER_DESKTOP, workspacePageCount, tag.screenId,
                    0);
                    LauncherSettings.Favorites.CONTAINER_DESKTOP, toPage, tag.screenId, toRow);
        }
        return true;
    }

    void removeNotification() {
@@ -93,7 +106,7 @@ public class HotseatEduController {

    private void createNotificationChannel() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
        CharSequence name = mLauncher.getString(R.string.hotseat_migrate_title);
        CharSequence name = mLauncher.getString(R.string.hotseat_edu_prompt_title);
        int importance = NotificationManager.IMPORTANCE_LOW;
        NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, name,
                importance);
@@ -104,8 +117,8 @@ public class HotseatEduController {
        Intent intent = new Intent(mLauncher.getApplicationContext(), mLauncher.getClass());
        intent = new NotificationHandler().addToIntent(intent);

        CharSequence name = mLauncher.getString(R.string.hotseat_migrate_prompt_title);
        String description = mLauncher.getString(R.string.hotseat_migrate_prompt_content);
        CharSequence name = mLauncher.getString(R.string.hotseat_edu_prompt_title);
        String description = mLauncher.getString(R.string.hotseat_edu_prompt_content);
        NotificationCompat.Builder builder = new NotificationCompat.Builder(mLauncher,
                NOTIFICATION_CHANNEL_ID)
                .setContentTitle(name)
+74 −19
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.android.launcher3.CellLayout;
@@ -34,7 +35,9 @@ import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.WorkspaceLayoutManager;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.uioverrides.PredictedAppIcon;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -49,12 +52,24 @@ import java.util.List;
public class HotseatEduDialog extends AbstractSlideInView implements Insettable {

    private static final int DEFAULT_CLOSE_DURATION = 200;
    protected static final int FINAL_SCRIM_BG_COLOR = 0x88000000;

    // We don't migrate if user has more than SAME_PAGE_MAX_ROWS rows of item in their screen
    private static final int SAME_PAGE_MAX_ROWS = 2;

    private static final int MIGRATE_SAME_PAGE = 0;
    private static final int MIGRATE_NEW_PAGE = 1;
    private static final int MIGRATE_NO_MIGRATE = 2;

    public static boolean shown = false;

    private final Rect mInsets = new Rect();
    private View mHotseatWrapper;
    private CellLayout mSampleHotseat;
    private TextView mEduHeading;
    private TextView mEduContent;
    private Button mDismissBtn;

    private int mMigrationMode = MIGRATE_SAME_PAGE;

    public void setHotseatEduController(HotseatEduController hotseatEduController) {
        mHotseatEduController = hotseatEduController;
@@ -78,6 +93,8 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
        super.onFinishInflate();
        mHotseatWrapper = findViewById(R.id.hotseat_wrapper);
        mSampleHotseat = findViewById(R.id.sample_prediction);
        mEduHeading = findViewById(R.id.hotseat_edu_heading);
        mEduContent = findViewById(R.id.hotseat_edu_content);

        DeviceProfile grid = mLauncher.getDeviceProfile();
        Rect padding = grid.getHotseatLayoutPadding();
@@ -87,24 +104,27 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
        mSampleHotseat.setPadding(padding.left, 0, padding.right, 0);

        Button turnOnBtn = findViewById(R.id.turn_predictions_on);
        turnOnBtn.setOnClickListener(this::onMigrate);
        turnOnBtn.setOnClickListener(this::onAccept);

        Button learnMoreBtn = findViewById(R.id.no_thanks);
        learnMoreBtn.setOnClickListener(this::onKeepDefault);
        mDismissBtn = findViewById(R.id.no_thanks);
        mDismissBtn.setOnClickListener(this::onDismiss);

    }

    private void onMigrate(View v) {
        if (mHotseatEduController == null) return;
    private void onAccept(View v) {
        if (mMigrationMode == MIGRATE_NO_MIGRATE || !mHotseatEduController.migrate()) {
            onDismiss(v);
            return;
        }
        handleClose(true);
        mHotseatEduController.migrate();
        mHotseatEduController.finishOnboarding();
        logUserAction(true);
        Toast.makeText(mLauncher, R.string.hotseat_items_migrated, Toast.LENGTH_LONG).show();
        int toastStringRes = mMigrationMode == MIGRATE_SAME_PAGE
                ? R.string.hotseat_items_migrated : R.string.hotseat_items_migrated_alt;
        Toast.makeText(mLauncher, toastStringRes, Toast.LENGTH_LONG).show();
    }

    private void onKeepDefault(View v) {
        if (mHotseatEduController == null) return;
    private void onDismiss(View v) {
        Toast.makeText(getContext(), R.string.hotseat_no_migration, Toast.LENGTH_LONG).show();
        mHotseatEduController.finishOnboarding();
        logUserAction(false);
@@ -148,6 +168,8 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
        target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
        target.controlType = migrated ? LauncherLogProto.ControlType.HYBRID_HOTSEAT_ACCEPTED
                : HYBRID_HOTSEAT_CANCELED;
        // encoding migration type on pageIndex
        target.pageIndex = mMigrationMode;
        LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
        UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
    }
@@ -161,6 +183,7 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
        LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
        UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
    }

    private void animateOpen() {
        if (mIsOpen || mOpenCloseAnimator.isRunning()) {
            return;
@@ -183,25 +206,57 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
        handleClose(false);
    }

    @Override
    protected int getScrimColor(Context context) {
        return FINAL_SCRIM_BG_COLOR;
    }

    private void populatePreview(List<WorkspaceItemInfo> predictions) {
        for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
            WorkspaceItemInfo info = predictions.get(i);
            PredictedAppIcon icon = PredictedAppIcon.createIcon(mSampleHotseat, info);
            icon.setEnabled(false);
            icon.verifyHighRes();
            CellLayout.LayoutParams lp = new CellLayout.LayoutParams(i, 0, 1, 1);
            mSampleHotseat.addViewToCellLayout(icon, i, info.getViewId(), lp, true);
        }
    }

    @Override
    protected void attachToContainer() {
        super.attachToContainer();
        if (FeatureFlags.HOTSEAT_MIGRATE_NEW_PAGE.get()) {
            mEduContent.setText(R.string.hotseat_edu_message_migrate_alt);
            mMigrationMode = MIGRATE_NEW_PAGE;
            return;
        }
        CellLayout page = mLauncher.getWorkspace().getScreenWithId(
                WorkspaceLayoutManager.FIRST_SCREEN_ID);

        int maxItemsOnPage = SAME_PAGE_MAX_ROWS * mLauncher.getDeviceProfile().inv.numColumns
                + (FeatureFlags.QSB_ON_FIRST_SCREEN ? 1 : 0);
        if (page.getShortcutsAndWidgets().getChildCount() > maxItemsOnPage
                || !page.makeSpaceForHotseatMigration(false)) {
            mMigrationMode = MIGRATE_NO_MIGRATE;
            mEduContent.setText(R.string.hotseat_edu_message_no_migrate);
            mEduHeading.setText(R.string.hotseat_edu_title_no_migrate);
            mDismissBtn.setVisibility(GONE);
        }
    }

    /**
     * Opens User education dialog with a list of suggested apps
     */
    public void show(List<WorkspaceItemInfo> predictions) {
        if (getParent() != null
                || predictions.size() < mLauncher.getDeviceProfile().inv.numHotseatIcons) {
                || predictions.size() < mLauncher.getDeviceProfile().inv.numHotseatIcons
                || mHotseatEduController == null) {
            return;
        }
        attachToContainer();
        logOnBoardingSeen();
        animateOpen();
        for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
            WorkspaceItemInfo info = predictions.get(i);
            PredictedAppIcon icon = PredictedAppIcon.createIcon(mSampleHotseat, info);
            icon.setEnabled(false);
            icon.verifyHighRes();
            CellLayout.LayoutParams lp = new CellLayout.LayoutParams(i, 0, 1, 1);
            mSampleHotseat.addViewToCellLayout(icon, i, info.getViewId(), lp, true);
        }
        populatePreview(predictions);
    }

    /**
+19 −14
Original line number Diff line number Diff line
@@ -66,27 +66,32 @@
    <!-- Content description for a close button. [CHAR LIMIT=NONE] -->
    <string  name="back_gesture_tutorial_close_button_content_description" translatable="false">Close</string>


    <!-- Hotseat migration notification title -->
    <string translatable="false" name="hotseat_migrate_prompt_title">Easily access your most-used apps</string>
    <string translatable="false" name="hotseat_edu_prompt_title">Get app suggestions based on your routines</string>
    <!-- Hotseat migration notification content -->
    <string translatable="false" name="hotseat_migrate_prompt_content">Pixel suggests your favorite apps based on your routines. Tap to learn more.</string>
    <!-- Hotseat migration wizard title -->
    <string translatable="false" name="hotseat_migrate_title">Suggested apps replace the bottom row of apps</string>
    <!-- Hotseat migration wizard message -->
    <string translatable="false" name="hotseat_migrate_message">Your current apps will move to the last screen. To pin or block a suggested app, drag it off the bottom row.</string>
    <string translatable="false" name="hotseat_edu_prompt_content">Tap to set up</string>


    <!-- Hotseat educational strings for users who don't qualify for migration -->
    <string translatable="false" name="hotseat_edu_title_migrate">Suggested apps replace the bottom row of apps</string>
    <string translatable="false" name="hotseat_edu_message_migrate">Your hotseat items will be moved up on the homescreen</string>
    <string translatable="false" name="hotseat_edu_message_migrate_alt">Your hotseat items will be moved to the last page of your workspace</string>


    <!-- Hotseat educational strings for users who don't qualify -->
    <string translatable="false" name="hotseat_edu_title_no_migrate">Suggested apps will be found at the bottom row of your home screen</string>
    <string translatable="false" name="hotseat_edu_message_no_migrate">Drag one or many apps off the bottom row of home screen to see app suggestions</string>

    <!-- Toast message user sees after opting into fully predicted hybrid hotseat -->
    <string translatable="false" name="hotseat_items_migrated">Bottom row of apps moved to last screen</string>
    <string translatable="false" name="hotseat_items_migrated">Bottom row of apps moved up.</string>
    <string translatable="false" name="hotseat_items_migrated_alt">Bottom row of apps moved to last page.</string>
    <!-- Toast message user sees after opting into fully predicted hybrid hotseat -->
    <string translatable="false" name="hotseat_no_migration">Bottom row won\'t be replaced. Manually drag apps for predictions.</string>
    <!-- Button text to opt in for fully predicted hotseat -->
    <string translatable="false" name="hotseat_migrate_accept">Turn On</string>
    <string translatable="false" name="hotseat_edu_accept">Got it</string>
    <!-- Button text to dismiss opt in for fully predicted hotseat -->
    <string translatable="false" name="hotseat_migrate_dismiss">No thanks</string>
    <!-- Hotseat onboard notification title -->
    <string translatable="false" name="hotseat_onboard_notification_title">Your hotseat just got smarter</string>
    <!-- Hotseat onboard notification detail -->
    <string translatable="false" name="hotseat_onboard_notification_detail">Tap here to set it up</string>

    <string translatable="false" name="hotseat_edu_dismiss">No thanks</string>


    <!-- Title shown during interactive part of Back gesture tutorial for right edge. [CHAR LIMIT=30] -->
+20 −0
Original line number Diff line number Diff line
@@ -2783,6 +2783,26 @@ public class CellLayout extends ViewGroup implements Transposable {
        return false;
    }

    /**
     * Finds solution to accept hotseat migration to cell layout. commits solution if commitConfig
     */
    public boolean makeSpaceForHotseatMigration(boolean commitConfig) {
        if (FeatureFlags.HOTSEAT_MIGRATE_NEW_PAGE.get()) return false;
        int[] cellPoint = new int[2];
        int[] directionVector = new int[]{0, -1};
        cellToPoint(0, mCountY, cellPoint);
        ItemConfiguration configuration = new ItemConfiguration();
        if (findReorderSolution(cellPoint[0], cellPoint[1], mCountX, 1, mCountX, 1,
                directionVector, null, false, configuration).isSolution) {
            if (commitConfig) {
                copySolutionToTempState(configuration, null);
                commitTempPlacement();
            }
            return true;
        }
        return false;
    }

    public boolean isRegionVacant(int x, int y, int spanX, int spanY) {
        return mOccupied.isRegionVacant(x, y, spanX, spanY);
    }
Loading