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

Commit d7107738 authored by Vinit Nayak's avatar Vinit Nayak Committed by Android (Google) Code Review
Browse files

Merge "Launch initial split from taskbar in overview app menu" into tm-qpr-dev

parents 275b80e6 49dc6d27
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