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

Commit 0f0a53a3 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Enable support for same page hotseat migration" into ub-launcher3-master

parents fe41c3af a421143d
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