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

Commit 49dc6d27 authored by Vinit Nayak's avatar Vinit Nayak
Browse files

Launch initial split from taskbar in overview app menu

* Consolidated init calls in SplitSelectStateController
* Also add support to launch from taskbar all apps
* Add logic in SplitSelectStateController to know whether
or not we need to dismiss existing TaskView vs relying
on mSplitHiddenTaskView null check
* Default click handling for SplitShortcut is to start
split selection mode

Bug: 251747761
Test: Initiated split from smart actions, thumbnail app
icon, home, taskbar in overview, all apps. Saw it choose
the latest thumbnail

Change-Id: Ib4f64e619c97615af458a19a9c0efd86c92979d9
parent 8995043d
Loading
Loading
Loading
Loading
+5 −133
Original line number Diff line number Diff line
@@ -15,38 +15,14 @@
 */
package com.android.launcher3.popup;

import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE;
import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.View;

import androidx.annotation.Nullable;

import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.R;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.splitscreen.SplitShortcut;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.quickstep.util.SplitSelectStateController;
import com.android.quickstep.views.FloatingTaskView;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.Task;

import java.util.function.Consumer;

/** {@link SystemShortcut.Factory} implementation to create workspace split shortcuts */
public interface QuickstepSystemShortcut {

    String TAG = QuickstepSystemShortcut.class.getSimpleName();
@@ -58,116 +34,12 @@ public interface QuickstepSystemShortcut {
                        originalView, position);
    }

    class SplitSelectSystemShortcut extends SystemShortcut<QuickstepLauncher> {

        private final int mSplitPlaceholderSize;
        private final int mSplitPlaceholderInset;

        private final Rect mTempRect = new Rect();
        private final SplitPositionOption mPosition;
    class SplitSelectSystemShortcut extends SplitShortcut<QuickstepLauncher> {

        public SplitSelectSystemShortcut(QuickstepLauncher launcher, ItemInfo itemInfo,
                View originalView, SplitPositionOption position) {
            super(position.iconResId, position.textResId, launcher, itemInfo, originalView);

            mPosition = position;

            mSplitPlaceholderSize = launcher.getResources().getDimensionPixelSize(
                    R.dimen.split_placeholder_size);
            mSplitPlaceholderInset = launcher.getResources().getDimensionPixelSize(
                    R.dimen.split_placeholder_inset);
        }

        @Override
        public void onClick(View view) {
            // Initiate splitscreen from the Home screen or Home All Apps
            Bitmap bitmap;
            Intent intent;
            if (mItemInfo instanceof WorkspaceItemInfo) {
                final WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) mItemInfo;
                bitmap = workspaceItemInfo.bitmap.icon;
                intent = workspaceItemInfo.intent;
            } else if (mItemInfo instanceof com.android.launcher3.model.data.AppInfo) {
                final com.android.launcher3.model.data.AppInfo appInfo =
                        (com.android.launcher3.model.data.AppInfo) mItemInfo;
                bitmap = appInfo.bitmap.icon;
                intent = appInfo.intent;
            } else {
                Log.e(TAG, "unknown item type");
                return;
            }

            StatsLogManager.EventEnum splitEvent = getLogEventForPosition(mPosition.stagePosition);
            RecentsView recentsView = mTarget.getOverviewPanel();
            // Check if there is already an instance of this app running, if so, initiate the split
            // using that.
            recentsView.findLastActiveTaskAndRunCallback(
                    intent.getComponent(),
                    (Consumer<Task>) foundTask -> {
                        SplitSelectSource source = new SplitSelectSource(mOriginalView,
                                new BitmapDrawable(bitmap), intent, mPosition, mItemInfo,
                                splitEvent, foundTask);
                        if (ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
                            startSplitToHome(source);
                        } else {
                            recentsView.initiateSplitSelect(source);
                        }
                    }
            );
        }

        private void startSplitToHome(SplitSelectSource source) {
            AbstractFloatingView.closeAllOpenViews(mTarget);

            SplitSelectStateController controller = mTarget.getSplitSelectStateController();
            controller.setInitialTaskSelect(source.intent, source.position.stagePosition,
                    source.itemInfo, source.splitEvent, source.alreadyRunningTask);

            RecentsView recentsView = mTarget.getOverviewPanel();
            recentsView.getPagedOrientationHandler().getInitialSplitPlaceholderBounds(
                    mSplitPlaceholderSize, mSplitPlaceholderInset, mTarget.getDeviceProfile(),
                    controller.getActiveSplitStagePosition(), mTempRect);

            PendingAnimation anim = new PendingAnimation(TABLET_HOME_TO_SPLIT.getDuration());
            RectF startingTaskRect = new RectF();
            final FloatingTaskView floatingTaskView = FloatingTaskView.getFloatingTaskView(mTarget,
                    source.view, null /* thumbnail */, source.drawable, startingTaskRect);
            floatingTaskView.setAlpha(1);
            floatingTaskView.addStagingAnimation(anim, startingTaskRect, mTempRect,
                    false /* fadeWithThumbnail */, true /* isStagedTask */);
            controller.setFirstFloatingTaskView(floatingTaskView);
            anim.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationCancel(Animator animation) {
                    mTarget.getDragLayer().removeView(floatingTaskView);
                    controller.resetState();
                }
            });
            anim.buildAnim().start();
        }
    }

    class SplitSelectSource {

        public final View view;
        public final Drawable drawable;
        public final Intent intent;
        public final SplitPositionOption position;
        public final ItemInfo itemInfo;
        public final StatsLogManager.EventEnum splitEvent;
        @Nullable
        public final Task alreadyRunningTask;

        public SplitSelectSource(View view, Drawable drawable, Intent intent,
                SplitPositionOption position, ItemInfo itemInfo,
                StatsLogManager.EventEnum splitEvent, @Nullable Task foundTask) {
            this.view = view;
            this.drawable = drawable;
            this.intent = intent;
            this.position = position;
            this.itemInfo = itemInfo;
            this.splitEvent = splitEvent;
            this.alreadyRunningTask = foundTask;
            super(position.iconResId, position.textResId, launcher, itemInfo, originalView,
                    position);
        }
    }
}
+93 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.splitscreen

import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.util.Log
import android.view.View
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.popup.QuickstepSystemShortcut
import com.android.launcher3.popup.SystemShortcut
import com.android.launcher3.util.SplitConfigurationOptions
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption
import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource
import com.android.launcher3.views.ActivityContext

/**
 * Shortcut to allow starting split. Default interaction for [onClick] is to launch
 * split selection mode
 */
abstract class SplitShortcut<T>(
    iconResId: Int,
    labelResId: Int,
    target: T,
    itemInfo: ItemInfo?,
    originalView: View?,
    protected val position: SplitPositionOption
) : SystemShortcut<T>(iconResId, labelResId, target, itemInfo, originalView) where
T : Context?,
T : ActivityContext? {
    private val TAG = SystemShortcut::class.java.simpleName

    // Initiate splitscreen from the Home screen or Home All Apps
    protected val splitSelectSource: SplitSelectSource?
        get() {
            // Initiate splitscreen from the Home screen or Home All Apps
            val bitmap: Bitmap
            val intent: Intent
            when (mItemInfo) {
                is WorkspaceItemInfo -> {
                    val workspaceItemInfo = mItemInfo
                    bitmap = workspaceItemInfo.bitmap.icon
                    intent = workspaceItemInfo.intent
                }
                is com.android.launcher3.model.data.AppInfo -> {
                    val appInfo = mItemInfo
                    bitmap = appInfo.bitmap.icon
                    intent = appInfo.intent
                }
                else -> {
                    Log.e(TAG, "unknown item type")
                    return null
                }
            }
            val splitEvent =
                SplitConfigurationOptions.getLogEventForPosition(position.stagePosition)
            return SplitSelectSource(
                mOriginalView,
                BitmapDrawable(bitmap),
                intent,
                position,
                mItemInfo,
                splitEvent
            )
        }

    /** Starts split selection on the provided [mTarget] */
    override fun onClick(view: View?) {
        val splitSelectSource = splitSelectSource
        if (splitSelectSource == null) {
            Log.w(QuickstepSystemShortcut.TAG, "no split selection source")
            return
        }
        mTarget!!.startSplitSelection(splitSelectSource)
    }
}
+7 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */
package com.android.launcher3.taskbar;

import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.content.pm.PackageManager.FEATURE_PC;
import static android.os.Trace.TRACE_TAG_APP;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
@@ -92,6 +93,7 @@ import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.ViewCache;
import com.android.launcher3.views.ActivityContext;
@@ -754,6 +756,11 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
        }
    }

    @Override
    public void startSplitSelection(SplitSelectSource splitSelectSource) {
        mControllers.uiController.startSplitSelection(splitSelectSource);
    }

    protected void onTaskbarIconClicked(View view) {
        Object tag = view.getTag();
        if (tag instanceof Task) {
+2 −1
Original line number Diff line number Diff line
@@ -125,7 +125,8 @@ import java.util.StringJoiner;
                    mControllers.taskbarDragController.setDisallowLongClick(disallowLongClick);
                    mControllers.taskbarAllAppsController.setDisallowGlobalDrag(disallowGlobalDrag);
                    mControllers.taskbarAllAppsController.setDisallowLongClick(disallowLongClick);
                    mControllers.taskbarPopupController.setHideSplitOptions(disallowGlobalDrag);
                    mControllers.taskbarPopupController.setAllowInitialSplitSelection(
                            disallowGlobalDrag);
                }
            };

+28 −22
Original line number Diff line number Diff line
@@ -17,11 +17,9 @@ package com.android.launcher3.taskbar;

import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;

import android.content.ClipDescription;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.graphics.Point;
import android.os.Bundle;
import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
@@ -46,6 +44,7 @@ import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.popup.PopupLiveUpdateHandler;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.splitscreen.SplitShortcut;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.LauncherBindableItemsContainer;
import com.android.launcher3.util.PackageUserKey;
@@ -75,7 +74,7 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba

    // Initialized in init.
    private TaskbarControllers mControllers;
    private boolean mHideSplitOptions;
    private boolean mAllowInitialSplitSelection;

    public TaskbarPopupController(TaskbarActivityContext context) {
        mContext = context;
@@ -101,8 +100,8 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba
        mPopupDataProvider.setDeepShortcutMap(deepShortcutMapCopy);
    }

    public void setHideSplitOptions(boolean hideSplitOptions) {
        mHideSplitOptions = hideSplitOptions;
    public void setAllowInitialSplitSelection(boolean allowInitialSplitSelection) {
        mAllowInitialSplitSelection = allowInitialSplitSelection;
    }

    private void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
@@ -188,13 +187,9 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba
    }

    // Create a Stream of all applicable system shortcuts
    // TODO(b/227800345): Add "Split bottom" option when tablet is in portrait mode.
    private Stream<SystemShortcut.Factory> getSystemShortcuts() {
        // concat a Stream of split options with a Stream of APP_INFO
        Stream<SystemShortcut.Factory> appInfo = Stream.of(APP_INFO);
        if (mHideSplitOptions) {
            return appInfo;
        }

        return Stream.concat(
                Utilities.getSplitPositionOptions(mContext.getDeviceProfile())
@@ -261,7 +256,7 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba
    private SystemShortcut.Factory<BaseTaskbarContext> createSplitShortcutFactory(
            SplitPositionOption position) {
        return (context, itemInfo, originalView) -> new TaskbarSplitShortcut(context, itemInfo,
                originalView, position);
                originalView, position, mAllowInitialSplitSelection);
    }

     /**
@@ -269,32 +264,43 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba
     * from the taskbar, as if the user performed a drag and drop split.
     * Includes an onClick method that initiates the actual split.
     */
    private static class TaskbarSplitShortcut extends SystemShortcut<BaseTaskbarContext> {
        private final SplitPositionOption mPosition;
    private static class TaskbarSplitShortcut extends
             SplitShortcut<BaseTaskbarContext> {
         /**
          * If {@code true}, clicking this shortcut will not attempt to start a split app directly,
          * but be the first app in split selection mode
          */
         private final boolean mAllowInitialSplitSelection;

         TaskbarSplitShortcut(BaseTaskbarContext context, ItemInfo itemInfo, View originalView,
                SplitPositionOption position) {
            super(position.iconResId, position.textResId, context, itemInfo, originalView);
            mPosition = position;
                SplitPositionOption position, boolean allowInitialSplitSelection) {
             super(position.iconResId, position.textResId, context, itemInfo, originalView,
                     position);
             mAllowInitialSplitSelection = allowInitialSplitSelection;
         }

        @Override
        public void onClick(View view) {
            AbstractFloatingView.closeAllOpenViews(mTarget);
             if (mAllowInitialSplitSelection) {
                 super.onClick(view);
                 return;
             }

            // Initiate splitscreen from the in-app Taskbar or Taskbar All Apps
            Pair<InstanceId, com.android.launcher3.logging.InstanceId> instanceIds =
                    LogUtils.getShellShareableInstanceId();
            mTarget.getStatsLogManager().logger()
                    .withItemInfo(mItemInfo)
                    .withInstanceId(instanceIds.second)
                    .log(getLogEventForPosition(mPosition.stagePosition));
                    .log(getLogEventForPosition(getPosition().stagePosition));

            AbstractFloatingView.closeAllOpenViews(mTarget);
            if (mItemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
                WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) mItemInfo;
                SystemUiProxy.INSTANCE.get(mTarget).startShortcut(
                        workspaceItemInfo.getIntent().getPackage(),
                        workspaceItemInfo.getDeepShortcutId(),
                        mPosition.stagePosition,
                        getPosition().stagePosition,
                        null,
                        workspaceItemInfo.user,
                        instanceIds.first);
@@ -305,7 +311,7 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba
                                null,
                                mItemInfo.user),
                        new Intent(),
                        mPosition.stagePosition,
                        getPosition().stagePosition,
                        null,
                        instanceIds.first);
            }
Loading