From e215fb730bb3d4a357a2c4bf0c082d3c0ad69495 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 5 May 2021 14:04:11 -0700 Subject: [PATCH] Moving taskbar lifecycle to TouchInteractionService > Inflating taskbar views with a non-launcher window context > Removing separate taskbar view in Launcher > Handling taskbar icon clicks directly instead of going via launcher > Fixing some relayouts during show/hide animation Bug: 187353581 Test: Manual Change-Id: Ia7d44acbb770bf0345922234b3daa5cc040a5843 --- quickstep/res/layout/taskbar.xml | 24 +- quickstep/res/layout/taskbar_view.xml | 26 - quickstep/res/values/dimens.xml | 1 + .../launcher3/BaseQuickstepLauncher.java | 92 +--- .../launcher3/QuickstepTransitionManager.java | 3 - .../launcher3/taskbar/ButtonProvider.java | 32 +- .../android/launcher3/taskbar/ImeBarView.java | 17 +- .../taskbar/TaskbarActivityContext.java | 230 ++++++++- .../taskbar/TaskbarAnimationController.java | 5 +- .../taskbar/TaskbarContainerView.java | 94 ++-- .../launcher3/taskbar/TaskbarController.java | 472 +++--------------- .../taskbar/TaskbarDragController.java | 13 +- .../taskbar/TaskbarHotseatController.java | 17 +- .../taskbar/TaskbarIconController.java | 163 ++++++ .../launcher3/taskbar/TaskbarManager.java | 157 ++++++ .../taskbar/TaskbarNavButtonController.java | 20 +- .../taskbar/TaskbarStateHandler.java | 41 +- .../taskbar/TaskbarUIController.java | 41 ++ .../launcher3/taskbar/TaskbarView.java | 262 ++++------ .../quickstep/BaseActivityInterface.java | 13 - .../quickstep/LauncherActivityInterface.java | 14 +- .../quickstep/TouchInteractionService.java | 61 +-- .../util/StaggeredWorkspaceAnim.java | 6 +- res/layout/taskbar_view.xml | 21 - src/com/android/launcher3/BubbleTextView.java | 4 - src/com/android/launcher3/Hotseat.java | 21 +- src/com/android/launcher3/Launcher.java | 14 - src/com/android/launcher3/Utilities.java | 10 +- .../launcher3/touch/ItemClickHandler.java | 56 ++- 29 files changed, 948 insertions(+), 982 deletions(-) delete mode 100644 quickstep/res/layout/taskbar_view.xml create mode 100644 quickstep/src/com/android/launcher3/taskbar/TaskbarIconController.java create mode 100644 quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java create mode 100644 quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java delete mode 100644 res/layout/taskbar_view.xml diff --git a/quickstep/res/layout/taskbar.xml b/quickstep/res/layout/taskbar.xml index 240fe556da..c4362214cb 100644 --- a/quickstep/res/layout/taskbar.xml +++ b/quickstep/res/layout/taskbar.xml @@ -22,9 +22,29 @@ + android:gravity="center" + android:forceHasOverlappingRendering="false" + android:layout_gravity="bottom" > + + + + + + - - - - diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index c45947205f..ad20c3013e 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -142,4 +142,5 @@ 8dp 16dp + 16dp diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index e777ee7354..bc2c125854 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -20,7 +20,6 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON; import static com.android.launcher3.LauncherState.FLAG_HIDE_BACK_BUTTON; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.NO_OFFSET; -import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS; import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE; @@ -30,7 +29,6 @@ import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.app.ActivityOptions; import android.content.ComponentName; -import android.content.Context; import android.content.Intent; import android.content.IntentSender; import android.content.ServiceConnection; @@ -51,13 +49,11 @@ import com.android.launcher3.proxy.StartActivityParams; import com.android.launcher3.statehandlers.BackButtonAlphaHandler; import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.statemanager.StateManager.StateHandler; -import com.android.launcher3.taskbar.TaskbarActivityContext; import com.android.launcher3.taskbar.TaskbarController; +import com.android.launcher3.taskbar.TaskbarManager; import com.android.launcher3.taskbar.TaskbarStateHandler; -import com.android.launcher3.taskbar.TaskbarView; import com.android.launcher3.uioverrides.RecentsViewStateController; import com.android.launcher3.util.ActivityOptionsWrapper; -import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.ObjectWrapper; import com.android.launcher3.util.UiThreadHelper; import com.android.quickstep.RecentsModel; @@ -67,6 +63,7 @@ import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.TaskUtils; import com.android.quickstep.TouchInteractionService; +import com.android.quickstep.TouchInteractionService.TISBinder; import com.android.quickstep.util.RemoteAnimationProvider; import com.android.quickstep.util.RemoteFadeOutAnimationListener; import com.android.quickstep.util.SplitSelectStateController; @@ -88,8 +85,6 @@ public abstract class BaseQuickstepLauncher extends Launcher private DepthController mDepthController = new DepthController(this); private QuickstepTransitionManager mAppTransitionManager; - private ServiceConnection mTisBinderConnection; - protected TouchInteractionService.TISBinder mTisBinder; /** * Reusable command for applying the back button alpha on the background thread. @@ -100,8 +95,20 @@ public abstract class BaseQuickstepLauncher extends Launcher private OverviewActionsView mActionsView; + private @Nullable TaskbarManager mTaskbarManager; private @Nullable TaskbarController mTaskbarController; + private final ServiceConnection mTisBinderConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName componentName, IBinder iBinder) { + mTaskbarManager = ((TISBinder) iBinder).getTaskbarManager(); + mTaskbarManager.setLauncher(BaseQuickstepLauncher.this); + } + + @Override + public void onServiceDisconnected(ComponentName componentName) { } + }; private final TaskbarStateHandler mTaskbarStateHandler = new TaskbarStateHandler(this); + // Will be updated when dragging from taskbar. private @Nullable DragOptions mNextWorkspaceDragOptions = null; private SplitPlaceholderView mSplitPlaceholderView; @@ -111,24 +118,6 @@ public abstract class BaseQuickstepLauncher extends Launcher super.onCreate(savedInstanceState); SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this); addMultiWindowModeChangedListener(mDepthController); - setupTouchInteractionServiceBinder(); - } - - private void setupTouchInteractionServiceBinder() { - Intent intent = new Intent(this, TouchInteractionService.class); - mTisBinderConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName componentName, IBinder binder) { - mTisBinder = ((TouchInteractionService.TISBinder) binder); - mTisBinder.setTaskbarOverviewProxyDelegate(mTaskbarController); - } - - @Override - public void onServiceDisconnected(ComponentName componentName) { - mTisBinder = null; - } - }; - bindService(intent, mTisBinderConnection, 0); } @Override @@ -136,15 +125,12 @@ public abstract class BaseQuickstepLauncher extends Launcher mAppTransitionManager.onActivityDestroyed(); SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this); - if (mTaskbarController != null) { - mTaskbarController.cleanup(); - mTaskbarController = null; - if (mTisBinder != null) { - mTisBinder.setTaskbarOverviewProxyDelegate(null); - unbindService(mTisBinderConnection); - } - } + + unbindService(mTisBinderConnection); + if (mTaskbarManager != null) { + mTaskbarManager.setLauncher(null); + } super.onDestroy(); } @@ -271,37 +257,12 @@ public abstract class BaseQuickstepLauncher extends Launcher mAppTransitionManager = new QuickstepTransitionManager(this); mAppTransitionManager.registerRemoteAnimations(); - addTaskbarIfNecessary(); - addOnDeviceProfileChangeListener(newDp -> addTaskbarIfNecessary()); + bindService(new Intent(this, TouchInteractionService.class), mTisBinderConnection, 0); + } - @Override - public void onDisplayInfoChanged(Context context, DisplayController.Info info, - int flags) { - super.onDisplayInfoChanged(context, info, flags); - if ((flags & CHANGE_ACTIVE_SCREEN) != 0) { - addTaskbarIfNecessary(); - } - } - - private void addTaskbarIfNecessary() { - if (mTaskbarController != null) { - mTaskbarController.cleanup(); - if (mTisBinder != null) { - mTisBinder.setTaskbarOverviewProxyDelegate(null); - } - mTaskbarController = null; - } - if (mDeviceProfile.isTaskbarPresent) { - TaskbarView taskbarViewOnHome = (TaskbarView) mHotseat.getTaskbarView(); - TaskbarActivityContext taskbarActivityContext = new TaskbarActivityContext(this); - mTaskbarController = new TaskbarController(this, - taskbarActivityContext.getTaskbarContainerView(), taskbarViewOnHome); - mTaskbarController.init(); - if (mTisBinder != null) { - mTisBinder.setTaskbarOverviewProxyDelegate(mTaskbarController); - } - } + public void setTaskbarController(TaskbarController taskbarController) { + mTaskbarController = taskbarController; } public T getActionsView() { @@ -340,14 +301,9 @@ public abstract class BaseQuickstepLauncher extends Launcher } @Override - public boolean isViewInTaskbar(View v) { - return mTaskbarController != null && mTaskbarController.isViewInTaskbar(v); - } - public boolean supportsAdaptiveIconAnimation(View clickedView) { return mAppTransitionManager.hasControlRemoteAppTransitionPermission() - && FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM.get() - && !isViewInTaskbar(clickedView); + && FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM.get(); } @Override diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 275dfda607..65dee55ca7 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -1256,7 +1256,6 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener final boolean launchingFromWidget = mV instanceof LauncherAppWidgetHostView; final boolean launchingFromRecents = isLaunchingFromRecents(mV, appTargets); - final boolean launchingFromTaskbar = mLauncher.isViewInTaskbar(mV); if (launchingFromWidget) { composeWidgetLaunchAnimator(anim, (LauncherAppWidgetHostView) mV, appTargets, wallpaperTargets, nonAppTargets); @@ -1267,8 +1266,6 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener launcherClosing); addCujInstrumentation( anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_RECENTS); - } else if (launchingFromTaskbar) { - // TODO } else { composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets, launcherClosing); diff --git a/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java b/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java index 0d4130d1bc..540f748313 100644 --- a/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java +++ b/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java @@ -16,12 +16,17 @@ package com.android.launcher3.taskbar; +import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK; +import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME; +import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH; +import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS; + import android.annotation.DrawableRes; -import android.content.Context; import android.view.View; import android.widget.ImageView; import com.android.launcher3.R; +import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton; /** * Creates Buttons for Taskbar for 3 button nav. @@ -29,47 +34,46 @@ import com.android.launcher3.R; */ public class ButtonProvider { - private int mMarginLeftRight; - private final Context mContext; + private final int mMarginLeftRight; + private final TaskbarActivityContext mContext; - public ButtonProvider(Context context) { + public ButtonProvider(TaskbarActivityContext context) { mContext = context; - } - - public void setMarginLeftRight(int margin) { - mMarginLeftRight = margin; + mMarginLeftRight = context.getResources() + .getDimensionPixelSize(R.dimen.taskbar_icon_spacing); } public View getBack() { // Back button - return getButtonForDrawable(R.drawable.ic_sysbar_back); + return getButtonForDrawable(R.drawable.ic_sysbar_back, BUTTON_BACK); } public View getDown() { // Ime down button - return getButtonForDrawable(R.drawable.ic_sysbar_back); + return getButtonForDrawable(R.drawable.ic_sysbar_back, BUTTON_BACK); } public View getHome() { // Home button - return getButtonForDrawable(R.drawable.ic_sysbar_home); + return getButtonForDrawable(R.drawable.ic_sysbar_home, BUTTON_HOME); } public View getRecents() { // Recents button - return getButtonForDrawable(R.drawable.ic_sysbar_recent); + return getButtonForDrawable(R.drawable.ic_sysbar_recent, BUTTON_RECENTS); } public View getImeSwitcher() { // IME Switcher Button - return getButtonForDrawable(R.drawable.ic_ime_switcher); + return getButtonForDrawable(R.drawable.ic_ime_switcher, BUTTON_IME_SWITCH); } - private View getButtonForDrawable(@DrawableRes int drawableId) { + private View getButtonForDrawable(@DrawableRes int drawableId, @TaskbarButton int buttonType) { ImageView buttonView = new ImageView(mContext); buttonView.setImageResource(drawableId); buttonView.setBackgroundResource(R.drawable.taskbar_icon_click_feedback_roundrect); buttonView.setPadding(mMarginLeftRight, 0, mMarginLeftRight, 0); + buttonView.setOnClickListener(view -> mContext.onNavigationButtonClick(buttonType)); return buttonView; } diff --git a/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java b/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java index bb3669beed..287caab44b 100644 --- a/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java @@ -16,9 +16,6 @@ package com.android.launcher3.taskbar; -import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK; -import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH; - import android.content.Context; import android.util.AttributeSet; import android.view.View; @@ -29,7 +26,6 @@ import com.android.launcher3.views.ActivityContext; public class ImeBarView extends RelativeLayout { private ButtonProvider mButtonProvider; - private TaskbarController.TaskbarViewCallbacks mControllerCallbacks; private View mImeView; public ImeBarView(Context context) { @@ -44,12 +40,9 @@ public class ImeBarView extends RelativeLayout { super(context, attrs, defStyleAttr); } - public void construct(ButtonProvider buttonProvider) { + public void init(ButtonProvider buttonProvider) { mButtonProvider = buttonProvider; - } - public void init(TaskbarController.TaskbarViewCallbacks taskbarCallbacks) { - mControllerCallbacks = taskbarCallbacks; ActivityContext context = getActivityContext(); RelativeLayout.LayoutParams imeParams = new RelativeLayout.LayoutParams( context.getDeviceProfile().iconSizePx, @@ -64,24 +57,16 @@ public class ImeBarView extends RelativeLayout { // Down Arrow View downView = mButtonProvider.getDown(); - downView.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick( - BUTTON_BACK)); downView.setLayoutParams(downParams); downView.setRotation(-90); addView(downView); // IME switcher button mImeView = mButtonProvider.getImeSwitcher(); - mImeView.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick( - BUTTON_IME_SWITCH)); mImeView.setLayoutParams(imeParams); addView(mImeView); } - public void cleanup() { - removeAllViews(); - } - public void setImeSwitcherVisibility(boolean show) { mImeView.setVisibility(show ? VISIBLE : GONE); } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 3af51d575d..70f278874a 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -15,56 +15,164 @@ */ package com.android.launcher3.taskbar; -import android.content.ContextWrapper; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + +import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_BOTTOM_TAPPABLE_ELEMENT; +import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR; + +import android.app.ActivityOptions; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.content.pm.LauncherApps; +import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.os.Process; +import android.os.SystemProperties; +import android.util.Log; +import android.view.ContextThemeWrapper; +import android.view.Display; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; +import android.view.WindowManager; +import android.widget.Toast; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.launcher3.BaseQuickstepLauncher; +import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget; +import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.R; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.dragndrop.DraggableView; +import com.android.launcher3.folder.Folder; +import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton; +import com.android.launcher3.touch.ItemClickHandler; +import com.android.launcher3.util.PackageManagerHelper; +import com.android.launcher3.util.Themes; +import com.android.launcher3.util.TraceHelper; import com.android.launcher3.views.ActivityContext; -import com.android.launcher3.views.BaseDragLayer; +import com.android.quickstep.SysUINavigationMode; +import com.android.quickstep.SysUINavigationMode.Mode; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.WindowManagerWrapper; /** * The {@link ActivityContext} with which we inflate Taskbar-related Views. This allows UI elements * that are used by both Launcher and Taskbar (such as Folder) to reference a generic * ActivityContext and BaseDragLayer instead of the Launcher activity and its DragLayer. */ -public class TaskbarActivityContext extends ContextWrapper implements ActivityContext { +public class TaskbarActivityContext extends ContextThemeWrapper implements ActivityContext { + + private static final boolean ENABLE_THREE_BUTTON_TASKBAR = + SystemProperties.getBoolean("persist.debug.taskbar_three_button", false); + private static final String TAG = "TaskbarActivityContext"; + + private static final String WINDOW_TITLE = "Taskbar"; private final DeviceProfile mDeviceProfile; private final LayoutInflater mLayoutInflater; private final TaskbarContainerView mTaskbarContainerView; + private final TaskbarIconController mIconController; private final MyDragController mDragController; - public TaskbarActivityContext(BaseQuickstepLauncher launcher) { - super(launcher); - mDeviceProfile = launcher.getDeviceProfile().copy(this); + private final WindowManager mWindowManager; + private WindowManager.LayoutParams mWindowLayoutParams; + + private final SysUINavigationMode.Mode mNavMode; + private final TaskbarNavButtonController mNavButtonController; + + private final boolean mIsSafeModeEnabled; + + @NonNull + private TaskbarUIController mUIController = TaskbarUIController.DEFAULT; + + private final View.OnClickListener mOnTaskbarIconClickListener; + private final View.OnLongClickListener mOnTaskbarIconLongClickListener; + + public TaskbarActivityContext(Context windowContext, DeviceProfile dp, + TaskbarNavButtonController buttonController) { + super(windowContext, Themes.getActivityThemeRes(windowContext)); + mDeviceProfile = dp; + mNavButtonController = buttonController; + mNavMode = SysUINavigationMode.getMode(windowContext); + mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode", + () -> getPackageManager().isSafeMode()); + + mOnTaskbarIconLongClickListener = + new TaskbarDragController(this)::startSystemDragOnLongClick; + mOnTaskbarIconClickListener = this::onTaskbarIconClicked; + float taskbarIconSize = getResources().getDimension(R.dimen.taskbar_icon_size); float iconScale = taskbarIconSize / mDeviceProfile.iconSizePx; mDeviceProfile.updateIconSize(iconScale, getResources()); mLayoutInflater = LayoutInflater.from(this).cloneInContext(this); - mTaskbarContainerView = (TaskbarContainerView) mLayoutInflater .inflate(R.layout.taskbar, null, false); + mIconController = new TaskbarIconController(this, mTaskbarContainerView); mDragController = new MyDragController(this); + + Display display = windowContext.getDisplay(); + Context c = display.getDisplayId() == Display.DEFAULT_DISPLAY + ? windowContext.getApplicationContext() + : windowContext.getApplicationContext().createDisplayContext(display); + mWindowManager = c.getSystemService(WindowManager.class); } - public TaskbarContainerView getTaskbarContainerView() { - return mTaskbarContainerView; + public void init() { + mWindowLayoutParams = new WindowManager.LayoutParams( + MATCH_PARENT, + mDeviceProfile.taskbarSize, + TYPE_APPLICATION_OVERLAY, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, + PixelFormat.TRANSLUCENT); + mWindowLayoutParams.setTitle(WINDOW_TITLE); + mWindowLayoutParams.packageName = getPackageName(); + mWindowLayoutParams.gravity = Gravity.BOTTOM; + mWindowLayoutParams.setFitInsetsTypes(0); + mWindowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; + mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + mWindowLayoutParams.setSystemApplicationOverlay(true); + + WindowManagerWrapper wmWrapper = WindowManagerWrapper.getInstance(); + wmWrapper.setProvidesInsetsTypes( + mWindowLayoutParams, + new int[] { ITYPE_EXTRA_NAVIGATION_BAR, ITYPE_BOTTOM_TAPPABLE_ELEMENT } + ); + + mIconController.init(mOnTaskbarIconClickListener, mOnTaskbarIconLongClickListener); + mWindowManager.addView(mTaskbarContainerView, mWindowLayoutParams); + } + + /** + * Updates the TaskbarContainer height (pass deviceProfile.taskbarSize to reset). + */ + public void setTaskbarWindowHeight(int height) { + if (mWindowLayoutParams.height == height) { + return; + } + mWindowLayoutParams.height = height; + mWindowManager.updateViewLayout(mTaskbarContainerView, mWindowLayoutParams); + } + + public boolean canShowNavButtons() { + return ENABLE_THREE_BUTTON_TASKBAR && mNavMode == Mode.THREE_BUTTONS; } @Override @@ -73,7 +181,7 @@ public class TaskbarActivityContext extends ContextWrapper implements ActivityCo } @Override - public BaseDragLayer getDragLayer() { + public TaskbarContainerView getDragLayer() { return mTaskbarContainerView; } @@ -92,6 +200,103 @@ public class TaskbarActivityContext extends ContextWrapper implements ActivityCo return mDragController; } + /** + * Sets a new data-source for this taskbar instance + */ + public void setUIController(@NonNull TaskbarUIController uiController) { + mUIController.onDestroy(); + mUIController = uiController; + mIconController.setUIController(mUIController); + mUIController.onCreate(); + } + + /** + * Called when this instance of taskbar is no longer needed + */ + public void onDestroy() { + setUIController(TaskbarUIController.DEFAULT); + mIconController.onDestroy(); + mWindowManager.removeViewImmediate(mTaskbarContainerView); + } + + void onNavigationButtonClick(@TaskbarButton int buttonType) { + mNavButtonController.onButtonClick(buttonType); + } + + public TaskbarIconController getIconController() { + return mIconController; + } + + /** + * Updates the TaskbarContainer to MATCH_PARENT vs original Taskbar size. + */ + protected void setTaskbarWindowFullscreen(boolean fullscreen) { + setTaskbarWindowHeight(fullscreen ? MATCH_PARENT : getDeviceProfile().taskbarSize); + } + + protected void onTaskbarIconClicked(View view) { + Object tag = view.getTag(); + if (tag instanceof Task) { + Task task = (Task) tag; + ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key, + ActivityOptions.makeBasic()); + } else if (tag instanceof FolderInfo) { + FolderIcon folderIcon = (FolderIcon) view; + Folder folder = folderIcon.getFolder(); + setTaskbarWindowFullscreen(true); + + getDragLayer().post(() -> { + folder.animateOpen(); + + folder.iterateOverItems((itemInfo, itemView) -> { + itemView.setOnClickListener(mOnTaskbarIconClickListener); + itemView.setOnLongClickListener(mOnTaskbarIconLongClickListener); + // To play haptic when dragging, like other Taskbar items do. + itemView.setHapticFeedbackEnabled(true); + return false; + }); + }); + } else if (tag instanceof WorkspaceItemInfo) { + WorkspaceItemInfo info = (WorkspaceItemInfo) tag; + if (info.isDisabled()) { + ItemClickHandler.handleDisabledItemClicked(info, this); + } else { + Intent intent = new Intent(info.getIntent()) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + try { + if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) { + Toast.makeText(this, R.string.safemode_shortcut_error, + Toast.LENGTH_SHORT).show(); + } else if (info.isPromise()) { + intent = new PackageManagerHelper(this) + .getMarketIntent(info.getTargetPackage()) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + + } else if (info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) { + String id = info.getDeepShortcutId(); + String packageName = intent.getPackage(); + getSystemService(LauncherApps.class) + .startShortcut(packageName, id, null, null, info.user); + } else if (info.user.equals(Process.myUserHandle())) { + startActivity(intent); + } else { + getSystemService(LauncherApps.class).startMainActivity( + intent.getComponent(), info.user, intent.getSourceBounds(), null); + } + } catch (NullPointerException | ActivityNotFoundException | SecurityException e) { + Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT) + .show(); + Log.e(TAG, "Unable to launch. tag=" + info + " intent=" + intent, e); + } + } + } else { + Log.e(TAG, "Unknown type clicked: " + tag); + } + + AbstractFloatingView.closeAllOpenViews(this); + } + private static class MyDragController extends DragController { MyDragController(TaskbarActivityContext activity) { super(activity); @@ -106,7 +311,8 @@ public class TaskbarActivityContext extends ContextWrapper implements ActivityCo } @Override - protected void exitDrag() { } + protected void exitDrag() { + } @Override protected DropTarget getDefaultDropTarget(int[] dropCoordinates) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java index 29f6935f8a..815efb9728 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java @@ -21,6 +21,7 @@ import android.animation.Animator; import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.Utilities; +import com.android.launcher3.taskbar.TaskbarController.TaskbarAnimationControllerCallbacks; import com.android.quickstep.AnimatedFloat; import com.android.quickstep.SystemUiProxy; import com.android.systemui.shared.system.QuickStepContract; @@ -34,7 +35,7 @@ public class TaskbarAnimationController { private static final long IME_VISIBILITY_ALPHA_DURATION = 120; private final BaseQuickstepLauncher mLauncher; - private final TaskbarController.TaskbarAnimationControllerCallbacks mTaskbarCallbacks; + private final TaskbarAnimationControllerCallbacks mTaskbarCallbacks; // Background alpha. private final AnimatedFloat mTaskbarBackgroundAlpha = new AnimatedFloat( @@ -55,7 +56,7 @@ public class TaskbarAnimationController { this::updateTranslationY); public TaskbarAnimationController(BaseQuickstepLauncher launcher, - TaskbarController.TaskbarAnimationControllerCallbacks taskbarCallbacks) { + TaskbarAnimationControllerCallbacks taskbarCallbacks) { mLauncher = launcher; mTaskbarCallbacks = taskbarCallbacks; } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java index 621bba7e85..5034791f0e 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java @@ -15,9 +15,6 @@ */ package com.android.launcher3.taskbar; -import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_FRAME; -import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION; - import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; @@ -32,22 +29,21 @@ import com.android.launcher3.R; import com.android.launcher3.util.TouchController; import com.android.launcher3.views.BaseDragLayer; import com.android.systemui.shared.system.ViewTreeObserverWrapper; +import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo; +import com.android.systemui.shared.system.ViewTreeObserverWrapper.OnComputeInsetsListener; /** * Top-level ViewGroup that hosts the TaskbarView as well as Views created by it such as Folder. */ public class TaskbarContainerView extends BaseDragLayer { - private final int[] mTempLoc = new int[2]; private final int mFolderMargin; private final Paint mTaskbarBackgroundPaint; - // Initialized in TaskbarController constructor. - private TaskbarController.TaskbarContainerViewCallbacks mControllerCallbacks; - - // Initialized in init. + private TaskbarIconController.Callbacks mControllerCallbacks; private TaskbarView mTaskbarView; - private ViewTreeObserverWrapper.OnComputeInsetsListener mTaskbarInsetsComputer; + + private final OnComputeInsetsListener mTaskbarInsetsComputer = this::onComputeTaskbarInsets; public TaskbarContainerView(@NonNull Context context) { this(context, null); @@ -68,15 +64,6 @@ public class TaskbarContainerView extends BaseDragLayer mFolderMargin = getResources().getDimensionPixelSize(R.dimen.taskbar_folder_margin); mTaskbarBackgroundPaint = new Paint(); mTaskbarBackgroundPaint.setColor(getResources().getColor(R.color.taskbar_background)); - } - - protected void construct(TaskbarController.TaskbarContainerViewCallbacks callbacks) { - mControllerCallbacks = callbacks; - } - - protected void init(TaskbarView taskbarView) { - mTaskbarView = taskbarView; - mTaskbarInsetsComputer = createTaskbarInsetsComputer(); recreateControllers(); } @@ -85,46 +72,24 @@ public class TaskbarContainerView extends BaseDragLayer mControllers = new TouchController[0]; } - private ViewTreeObserverWrapper.OnComputeInsetsListener createTaskbarInsetsComputer() { - return insetsInfo -> { - if (mControllerCallbacks.isTaskbarTouchable()) { - // Accept touches anywhere in our bounds. - insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME); - } else { - // Let touches pass through us. - insetsInfo.touchableRegion.setEmpty(); - insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION); - } - - // TaskbarContainerView provides insets to other apps based on contentInsets. These - // insets should stay consistent even if we expand TaskbarContainerView's bounds, e.g. - // to show a floating view like Folder. Thus, we set the contentInsets to be where - // mTaskbarView is, since its position never changes and insets rather than overlays. - int[] loc = mTempLoc; - float scale = mTaskbarView.getScaleX(); - float translationY = mTaskbarView.getTranslationY(); - mTaskbarView.setScaleX(1); - mTaskbarView.setScaleY(1); - mTaskbarView.setTranslationY(0); - mTaskbarView.getLocationInWindow(loc); - mTaskbarView.setScaleX(scale); - mTaskbarView.setScaleY(scale); - mTaskbarView.setTranslationY(translationY); - insetsInfo.contentInsets.left = loc[0]; - insetsInfo.contentInsets.top = loc[1]; - insetsInfo.contentInsets.right = getWidth() - (loc[0] + mTaskbarView.getWidth()); - insetsInfo.contentInsets.bottom = getHeight() - (loc[1] + mTaskbarView.getHeight()); - }; + public void init(TaskbarIconController.Callbacks callbacks, TaskbarView taskbarView) { + mControllerCallbacks = callbacks; + mTaskbarView = taskbarView; } - protected void cleanup() { + private void onComputeTaskbarInsets(InsetsInfo insetsInfo) { + if (mControllerCallbacks != null) { + mControllerCallbacks.updateInsetsTouchability(insetsInfo); + } + } + + protected void onDestroy() { ViewTreeObserverWrapper.removeOnComputeInsetsListener(mTaskbarInsetsComputer); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - ViewTreeObserverWrapper.addOnComputeInsetsListener(getViewTreeObserver(), mTaskbarInsetsComputer); } @@ -133,7 +98,7 @@ public class TaskbarContainerView extends BaseDragLayer protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - cleanup(); + onDestroy(); } @Override @@ -143,10 +108,25 @@ public class TaskbarContainerView extends BaseDragLayer return true; } + public void updateImeBarVisibilityAlpha(float alpha) { + if (mControllerCallbacks != null) { + mControllerCallbacks.updateImeBarVisibilityAlpha(alpha); + } + } + @Override public void onViewRemoved(View child) { super.onViewRemoved(child); - mControllerCallbacks.onViewRemoved(); + if (mControllerCallbacks != null) { + mControllerCallbacks.onContainerViewRemoved(); + } + } + + @Override + protected void dispatchDraw(Canvas canvas) { + canvas.drawRect(0, canvas.getHeight() - mTaskbarView.getHeight(), canvas.getWidth(), + canvas.getHeight(), mTaskbarBackgroundPaint); + super.dispatchDraw(canvas); } /** @@ -158,16 +138,6 @@ public class TaskbarContainerView extends BaseDragLayer return boundingBox; } - protected TaskbarActivityContext getTaskbarActivityContext() { - return mActivity; - } - - @Override - protected void dispatchDraw(Canvas canvas) { - canvas.drawRect(0, canvas.getHeight() - mTaskbarView.getHeight(), canvas.getWidth(), - canvas.getHeight(), mTaskbarBackgroundPaint); - super.dispatchDraw(canvas); - } /** * Sets the alpha of the background color behind all the Taskbar contents. diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java index 6084e10d85..cdae5beb9a 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java @@ -15,105 +15,83 @@ */ package com.android.launcher3.taskbar; -import static android.view.View.GONE; -import static android.view.View.INVISIBLE; -import static android.view.View.VISIBLE; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; - -import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_BOTTOM_TAPPABLE_ELEMENT; -import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR; - import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.app.ActivityOptions; -import android.graphics.PixelFormat; -import android.graphics.Point; import android.graphics.Rect; -import android.inputmethodservice.InputMethodService; -import android.view.Gravity; +import android.graphics.RectF; import android.view.MotionEvent; -import android.view.View; -import android.view.WindowManager; import androidx.annotation.Nullable; -import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherState; import com.android.launcher3.QuickstepTransitionManager; import com.android.launcher3.R; -import com.android.launcher3.anim.AlphaUpdateListener; +import com.android.launcher3.Utilities; import com.android.launcher3.anim.PendingAnimation; -import com.android.launcher3.folder.Folder; -import com.android.launcher3.folder.FolderIcon; -import com.android.launcher3.model.data.FolderInfo; -import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.states.StateAnimationConfig; -import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton; -import com.android.launcher3.touch.ItemClickHandler; -import com.android.launcher3.views.ActivityContext; -import com.android.quickstep.AnimatedFloat; -import com.android.quickstep.SysUINavigationMode; -import com.android.quickstep.TouchInteractionService.TaskbarOverviewProxyDelegate; -import com.android.systemui.shared.recents.model.Task; -import com.android.systemui.shared.system.ActivityManagerWrapper; -import com.android.systemui.shared.system.WindowManagerWrapper; + /** - * Interfaces with Launcher/WindowManager/SystemUI to determine what to show in TaskbarView. + * A data source which integrates with a Launcher instance + * TODO: Rename to have Launcher prefix */ -public class TaskbarController implements TaskbarOverviewProxyDelegate { - private static final String WINDOW_TITLE = "Taskbar"; - - private final TaskbarContainerView mTaskbarContainerView; - private final TaskbarView mTaskbarViewInApp; - private final TaskbarView mTaskbarViewOnHome; - private final ImeBarView mImeBarView; +public class TaskbarController extends TaskbarUIController { private final BaseQuickstepLauncher mLauncher; - private final WindowManager mWindowManager; - // Layout width and height of the Taskbar in the default state. - private final Point mTaskbarSize; private final TaskbarStateHandler mTaskbarStateHandler; private final TaskbarAnimationController mTaskbarAnimationController; private final TaskbarHotseatController mHotseatController; - private final TaskbarDragController mDragController; - private final TaskbarNavButtonController mNavButtonController; - // Initialized in init(). - private WindowManager.LayoutParams mWindowLayoutParams; - private SysUINavigationMode.Mode mNavMode = SysUINavigationMode.Mode.NO_BUTTON; - private final SysUINavigationMode.NavigationModeChangeListener mNavigationModeChangeListener = - this::onNavModeChanged; + private final TaskbarActivityContext mContext; + final TaskbarContainerView mTaskbarContainerView; + final TaskbarView mTaskbarView; private @Nullable Animator mAnimator; private boolean mIsAnimatingToLauncher; - public TaskbarController(BaseQuickstepLauncher launcher, - TaskbarContainerView taskbarContainerView, TaskbarView taskbarViewOnHome) { + public TaskbarController(BaseQuickstepLauncher launcher, TaskbarActivityContext context) { + mContext = context; + mTaskbarContainerView = context.getDragLayer(); + mTaskbarView = mTaskbarContainerView.findViewById(R.id.taskbar_view); + mLauncher = launcher; - mTaskbarContainerView = taskbarContainerView; - mTaskbarContainerView.construct(createTaskbarContainerViewCallbacks()); - ButtonProvider buttonProvider = new ButtonProvider(launcher); - mTaskbarViewInApp = mTaskbarContainerView.findViewById(R.id.taskbar_view); - mTaskbarViewInApp.construct(createTaskbarViewCallbacks(), buttonProvider); - mTaskbarViewOnHome = taskbarViewOnHome; - mTaskbarViewOnHome.construct(createTaskbarViewCallbacks(), buttonProvider); - mImeBarView = mTaskbarContainerView.findViewById(R.id.ime_bar_view); - mImeBarView.construct(buttonProvider); - mNavButtonController = new TaskbarNavButtonController(launcher); - mWindowManager = mLauncher.getWindowManager(); - mTaskbarSize = new Point(MATCH_PARENT, mLauncher.getDeviceProfile().taskbarSize); mTaskbarStateHandler = mLauncher.getTaskbarStateHandler(); mTaskbarAnimationController = new TaskbarAnimationController(mLauncher, createTaskbarAnimationControllerCallbacks()); - mHotseatController = new TaskbarHotseatController(mLauncher, - createTaskbarHotseatControllerCallbacks()); - mDragController = new TaskbarDragController(mLauncher); + mHotseatController = new TaskbarHotseatController( + mLauncher, mTaskbarView::updateHotseatItems); + } + + @Override + protected void onCreate() { + mTaskbarStateHandler.setAnimationController(mTaskbarAnimationController); + mTaskbarAnimationController.init(); + mHotseatController.init(); + setTaskbarViewVisible(!mLauncher.hasBeenResumed()); + alignRealHotseatWithTaskbar(); + mLauncher.setTaskbarController(this); + } + + @Override + protected void onDestroy() { + if (mAnimator != null) { + // End this first, in case it relies on properties that are about to be cleaned up. + mAnimator.end(); + } + mTaskbarStateHandler.setAnimationController(null); + mTaskbarAnimationController.cleanup(); + mHotseatController.cleanup(); + setTaskbarViewVisible(true); + mLauncher.getHotseat().setIconsAlpha(1f); + mLauncher.setTaskbarController(null); + } + + @Override + protected boolean isTaskbarTouchable() { + return !mIsAnimatingToLauncher; } private TaskbarAnimationControllerCallbacks createTaskbarAnimationControllerCallbacks() { @@ -125,245 +103,34 @@ public class TaskbarController implements TaskbarOverviewProxyDelegate { @Override public void updateTaskbarVisibilityAlpha(float alpha) { - mTaskbarViewInApp.setAlpha(alpha); - mTaskbarViewOnHome.setAlpha(alpha); + mTaskbarView.setAlpha(alpha); } @Override public void updateImeBarVisibilityAlpha(float alpha) { - if (mNavMode != SysUINavigationMode.Mode.THREE_BUTTONS) { - // TODO Remove sysui IME bar for gesture nav as well - return; - } - mImeBarView.setAlpha(alpha); - mImeBarView.setVisibility(alpha == 0 ? GONE : VISIBLE); + mTaskbarContainerView.updateImeBarVisibilityAlpha(alpha); } @Override public void updateTaskbarScale(float scale) { - mTaskbarViewInApp.setScaleX(scale); - mTaskbarViewInApp.setScaleY(scale); + mTaskbarView.setScaleX(scale); + mTaskbarView.setScaleY(scale); } @Override public void updateTaskbarTranslationY(float translationY) { if (translationY < 0) { // Resize to accommodate the max translation we'll reach. - setTaskbarWindowHeight(mTaskbarSize.y + mContext.setTaskbarWindowHeight(mContext.getDeviceProfile().taskbarSize + mLauncher.getHotseat().getTaskbarOffsetY()); } else { - setTaskbarWindowHeight(mTaskbarSize.y); + mContext.setTaskbarWindowHeight(mContext.getDeviceProfile().taskbarSize); } - mTaskbarViewInApp.setTranslationY(translationY); + mTaskbarView.setTranslationY(translationY); } }; } - private TaskbarContainerViewCallbacks createTaskbarContainerViewCallbacks() { - return new TaskbarContainerViewCallbacks() { - @Override - public void onViewRemoved() { - // Ensure no other children present (like Folders, etc) - for (int i = 0; i < mTaskbarContainerView.getChildCount(); i++) { - View v = mTaskbarContainerView.getChildAt(i); - if (!((v instanceof TaskbarView) || (v instanceof ImeBarView))){ - return; - } - } - setTaskbarWindowFullscreen(false); - } - - @Override - public boolean isTaskbarTouchable() { - return mTaskbarContainerView.getAlpha() > AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD - && (mTaskbarViewInApp.getVisibility() == VISIBLE - || mImeBarView.getVisibility() == VISIBLE) - && !mIsAnimatingToLauncher; - } - }; - } - - private TaskbarViewCallbacks createTaskbarViewCallbacks() { - return new TaskbarViewCallbacks() { - @Override - public View.OnClickListener getItemOnClickListener() { - return view -> { - Object tag = view.getTag(); - if (tag instanceof Task) { - Task task = (Task) tag; - ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key, - ActivityOptions.makeBasic()); - } else if (tag instanceof FolderInfo) { - FolderIcon folderIcon = (FolderIcon) view; - Folder folder = folderIcon.getFolder(); - - setTaskbarWindowFullscreen(true); - - mTaskbarContainerView.post(() -> { - folder.animateOpen(); - - folder.iterateOverItems((itemInfo, itemView) -> { - itemView.setOnClickListener(getItemOnClickListener()); - itemView.setOnLongClickListener(getItemOnLongClickListener()); - // To play haptic when dragging, like other Taskbar items do. - itemView.setHapticFeedbackEnabled(true); - return false; - }); - }); - } else { - ItemClickHandler.INSTANCE.onClick(view); - } - - AbstractFloatingView.closeAllOpenViews( - mTaskbarContainerView.getTaskbarActivityContext()); - }; - } - - @Override - public View.OnLongClickListener getItemOnLongClickListener() { - return mDragController::startSystemDragOnLongClick; - } - - @Override - public int getEmptyHotseatViewVisibility(TaskbarView taskbarView) { - // When on the home screen, we want the empty hotseat views to take up their full - // space so that the others line up with the home screen hotseat. - boolean isOnHomeScreen = taskbarView == mTaskbarViewOnHome - || mLauncher.hasBeenResumed() || mIsAnimatingToLauncher; - return isOnHomeScreen ? INVISIBLE : GONE; - } - - @Override - public float getNonIconScale(TaskbarView taskbarView) { - return taskbarView == mTaskbarViewOnHome ? getTaskbarScaleOnHome() : 1f; - } - - @Override - public void onItemPositionsChanged(TaskbarView taskbarView) { - if (taskbarView == mTaskbarViewOnHome) { - alignRealHotseatWithTaskbar(); - } - } - - @Override - public void onNavigationButtonClick(@TaskbarButton int buttonType) { - mNavButtonController.onButtonClick(buttonType); - } - }; - } - - private TaskbarHotseatControllerCallbacks createTaskbarHotseatControllerCallbacks() { - return new TaskbarHotseatControllerCallbacks() { - @Override - public void updateHotseatItems(ItemInfo[] hotseatItemInfos) { - mTaskbarViewInApp.updateHotseatItems(hotseatItemInfos); - } - }; - } - - /** - * Initializes the Taskbar, including adding it to the screen. - */ - public void init() { - mNavMode = SysUINavigationMode.INSTANCE.get(mLauncher) - .addModeChangeListener(mNavigationModeChangeListener); - mTaskbarViewInApp.init(mHotseatController.getNumHotseatIcons(), mNavMode); - mTaskbarViewOnHome.init(mHotseatController.getNumHotseatIcons(), mNavMode); - mTaskbarContainerView.init(mTaskbarViewInApp); - mImeBarView.init(createTaskbarViewCallbacks()); - addToWindowManager(); - mTaskbarStateHandler.setTaskbarCallbacks(createTaskbarStateHandlerCallbacks()); - mTaskbarAnimationController.init(); - mHotseatController.init(); - - setWhichTaskbarViewIsVisible(mLauncher.hasBeenResumed() - ? mTaskbarViewOnHome - : mTaskbarViewInApp); - } - - private TaskbarStateHandlerCallbacks createTaskbarStateHandlerCallbacks() { - return new TaskbarStateHandlerCallbacks() { - @Override - public AnimatedFloat getAlphaTarget() { - return mTaskbarAnimationController.getTaskbarVisibilityForLauncherState(); - } - - @Override - public AnimatedFloat getScaleTarget() { - return mTaskbarAnimationController.getTaskbarScaleForLauncherState(); - } - - @Override - public AnimatedFloat getTranslationYTarget() { - return mTaskbarAnimationController.getTaskbarTranslationYForLauncherState(); - } - }; - } - - /** - * Removes the Taskbar from the screen, and removes any obsolete listeners etc. - */ - public void cleanup() { - if (mAnimator != null) { - // End this first, in case it relies on properties that are about to be cleaned up. - mAnimator.end(); - } - - mTaskbarViewInApp.cleanup(); - mTaskbarViewOnHome.cleanup(); - mTaskbarContainerView.cleanup(); - mImeBarView.cleanup(); - removeFromWindowManager(); - mTaskbarStateHandler.setTaskbarCallbacks(null); - mTaskbarAnimationController.cleanup(); - mHotseatController.cleanup(); - - setWhichTaskbarViewIsVisible(null); - SysUINavigationMode.INSTANCE.get(mLauncher) - .removeModeChangeListener(mNavigationModeChangeListener); - } - - private void removeFromWindowManager() { - mWindowManager.removeViewImmediate(mTaskbarContainerView); - } - - private void addToWindowManager() { - final int gravity = Gravity.BOTTOM; - - mWindowLayoutParams = new WindowManager.LayoutParams( - mTaskbarSize.x, - mTaskbarSize.y, - TYPE_APPLICATION_OVERLAY, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, - PixelFormat.TRANSLUCENT); - mWindowLayoutParams.setTitle(WINDOW_TITLE); - mWindowLayoutParams.packageName = mLauncher.getPackageName(); - mWindowLayoutParams.gravity = gravity; - mWindowLayoutParams.setFitInsetsTypes(0); - mWindowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; - mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - mWindowLayoutParams.setSystemApplicationOverlay(true); - - WindowManagerWrapper wmWrapper = WindowManagerWrapper.getInstance(); - wmWrapper.setProvidesInsetsTypes( - mWindowLayoutParams, - new int[] { ITYPE_EXTRA_NAVIGATION_BAR, ITYPE_BOTTOM_TAPPABLE_ELEMENT } - ); - - TaskbarContainerView.LayoutParams taskbarLayoutParams = - new TaskbarContainerView.LayoutParams(mTaskbarSize.x, mTaskbarSize.y); - taskbarLayoutParams.gravity = gravity; - mTaskbarViewInApp.setLayoutParams(taskbarLayoutParams); - - mWindowManager.addView(mTaskbarContainerView, mWindowLayoutParams); - } - - private void onNavModeChanged(SysUINavigationMode.Mode newMode) { - mNavMode = newMode; - cleanup(); - init(); - } - /** * Should be called from onResume() and onPause(), and animates the Taskbar accordingly. */ @@ -401,13 +168,14 @@ public class TaskbarController implements TaskbarOverviewProxyDelegate { @Override public void onAnimationStart(Animator animation) { mIsAnimatingToLauncher = true; - mTaskbarViewInApp.updateHotseatItemsVisibility(); + mTaskbarView.setHolesAllowedInLayout(true); + mTaskbarView.updateHotseatItemsVisibility(); } @Override public void onAnimationEnd(Animator animation) { mIsAnimatingToLauncher = false; - setWhichTaskbarViewIsVisible(mTaskbarViewOnHome); + setTaskbarViewVisible(false); } }); @@ -420,44 +188,21 @@ public class TaskbarController implements TaskbarOverviewProxyDelegate { anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { - mTaskbarViewInApp.updateHotseatItemsVisibility(); - setWhichTaskbarViewIsVisible(mTaskbarViewInApp); + mTaskbarView.updateHotseatItemsVisibility(); + setTaskbarViewVisible(true); } @Override public void onAnimationEnd(Animator animation) { + mTaskbarView.setHolesAllowedInLayout(false); } }); return anim.buildAnim(); } - /** - * Should be called when the IME visibility changes, so we can hide/show Taskbar accordingly. - */ - public void setIsImeVisible(boolean isImeVisible) { - mTaskbarAnimationController.animateToVisibilityForIme(isImeVisible ? 0 : 1); - blockTaskbarTouchesForIme(isImeVisible); - } - - /** - * When in 3 button nav, the above doesn't get called since we prevent sysui nav bar from - * instantiating at all, which is what's responsible for sending sysui state flags over. - * - * @param vis IME visibility flag - * @param backDisposition Used to determine back button behavior for software keyboard - * See BACK_DISPOSITION_* constants in {@link InputMethodService} - */ - public void updateImeStatus(int displayId, int vis, int backDisposition, - boolean showImeSwitcher) { - if (displayId != mTaskbarContainerView.getContext().getDisplayId() || - mNavMode != SysUINavigationMode.Mode.THREE_BUTTONS) { - return; - } - - boolean imeVisible = (vis & InputMethodService.IME_VISIBLE) != 0; - mTaskbarAnimationController.animateToVisibilityForIme(imeVisible ? 0 : 1); - mImeBarView.setImeSwitcherVisibility(showImeSwitcher); - blockTaskbarTouchesForIme(imeVisible); + @Override + protected void onImeVisible(TaskbarContainerView containerView, boolean isVisible) { + mTaskbarAnimationController.animateToVisibilityForIme(isVisible ? 0 : 1); } /** @@ -472,24 +217,17 @@ public class TaskbarController implements TaskbarOverviewProxyDelegate { * @return Whether any Taskbar item could handle the given MotionEvent if given the chance. */ public boolean isEventOverAnyTaskbarItem(MotionEvent ev) { - return mTaskbarViewInApp.isEventOverAnyItem(ev); + return mTaskbarView.isEventOverAnyItem(ev); } public boolean isDraggingItem() { - return mTaskbarViewInApp.isDraggingItem() || mTaskbarViewOnHome.isDraggingItem(); - } - - /** - * @return Whether the given View is in the same window as Taskbar. - */ - public boolean isViewInTaskbar(View v) { - return mTaskbarContainerView.isAttachedToWindow() - && mTaskbarContainerView.getWindowId().equals(v.getWindowId()); + return mTaskbarView.isDraggingItem(); } /** * Pads the Hotseat to line up exactly with Taskbar's copy of the Hotseat. */ + @Override public void alignRealHotseatWithTaskbar() { Rect hotseatBounds = new Rect(); DeviceProfile grid = mLauncher.getDeviceProfile(); @@ -498,60 +236,28 @@ public class TaskbarController implements TaskbarOverviewProxyDelegate { int hotseatTopDiff = hotseatHeight - grid.taskbarSize - taskbarOffset; int hotseatBottomDiff = taskbarOffset; - mTaskbarViewOnHome.getHotseatBounds().roundOut(hotseatBounds); + RectF hotseatBoundsF = mTaskbarView.getHotseatBounds(); + Utilities.scaleRectFAboutPivot(hotseatBoundsF, getTaskbarScaleOnHome(), + mTaskbarView.getPivotX(), mTaskbarView.getPivotY()); + hotseatBoundsF.round(hotseatBounds); mLauncher.getHotseat().setPadding(hotseatBounds.left, hotseatBounds.top + hotseatTopDiff, - mTaskbarViewOnHome.getWidth() - hotseatBounds.right, - mTaskbarViewOnHome.getHeight() - hotseatBounds.bottom + hotseatBottomDiff); - } - - private void setWhichTaskbarViewIsVisible(@Nullable TaskbarView visibleTaskbar) { - mTaskbarViewInApp.setVisibility(visibleTaskbar == mTaskbarViewInApp - ? VISIBLE : INVISIBLE); - mTaskbarViewOnHome.setVisibility(visibleTaskbar == mTaskbarViewOnHome - ? VISIBLE : INVISIBLE); - mLauncher.getHotseat().setIconsAlpha(visibleTaskbar != mTaskbarViewInApp ? 1f : 0f); - } - - private void blockTaskbarTouchesForIme(boolean block) { - mTaskbarViewOnHome.setTouchesEnabled(!block); - mTaskbarViewInApp.setTouchesEnabled(!block); + mTaskbarView.getWidth() - hotseatBounds.right, + mTaskbarView.getHeight() - hotseatBounds.bottom + hotseatBottomDiff); } /** * Returns the ratio of the taskbar icon size on home vs in an app. */ public float getTaskbarScaleOnHome() { - DeviceProfile inAppDp = mTaskbarContainerView.getTaskbarActivityContext() - .getDeviceProfile(); - DeviceProfile onHomeDp = ActivityContext.lookupContext(mTaskbarViewOnHome.getContext()) - .getDeviceProfile(); + DeviceProfile inAppDp = mContext.getDeviceProfile(); + DeviceProfile onHomeDp = mLauncher.getDeviceProfile(); return (float) onHomeDp.cellWidthPx / inAppDp.cellWidthPx; } - /** - * Updates the TaskbarContainer to MATCH_PARENT vs original Taskbar size. - */ - private void setTaskbarWindowFullscreen(boolean fullscreen) { - setTaskbarWindowHeight(fullscreen ? MATCH_PARENT : mTaskbarSize.y); - } - - /** - * Updates the TaskbarContainer height (pass mTaskbarSize.y to reset). - */ - private void setTaskbarWindowHeight(int height) { - mWindowLayoutParams.width = mTaskbarSize.x; - mWindowLayoutParams.height = height; - mWindowManager.updateViewLayout(mTaskbarContainerView, mWindowLayoutParams); - } - - /** - * Contains methods that TaskbarStateHandler can call to interface with TaskbarController. - */ - protected interface TaskbarStateHandlerCallbacks { - AnimatedFloat getAlphaTarget(); - AnimatedFloat getScaleTarget(); - AnimatedFloat getTranslationYTarget(); + void setTaskbarViewVisible(boolean isVisible) { + mTaskbarView.setIconsVisibility(isVisible); + mLauncher.getHotseat().setIconsAlpha(isVisible ? 0f : 1f); } /** @@ -565,32 +271,4 @@ public class TaskbarController implements TaskbarOverviewProxyDelegate { void updateTaskbarScale(float scale); void updateTaskbarTranslationY(float translationY); } - - /** - * Contains methods that TaskbarContainerView can call to interface with TaskbarController. - */ - protected interface TaskbarContainerViewCallbacks { - void onViewRemoved(); - boolean isTaskbarTouchable(); - } - - /** - * Contains methods that TaskbarView can call to interface with TaskbarController. - */ - protected interface TaskbarViewCallbacks { - View.OnClickListener getItemOnClickListener(); - View.OnLongClickListener getItemOnLongClickListener(); - int getEmptyHotseatViewVisibility(TaskbarView taskbarView); - /** Returns how much to scale non-icon elements such as spacing and dividers. */ - float getNonIconScale(TaskbarView taskbarView); - void onItemPositionsChanged(TaskbarView taskbarView); - void onNavigationButtonClick(@TaskbarButton int buttonType); - } - - /** - * Contains methods that TaskbarHotseatController can call to interface with TaskbarController. - */ - protected interface TaskbarHotseatControllerCallbacks { - void updateHotseatItems(ItemInfo[] hotseatItemInfos); - } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java index 5eb34cb360..ee44927ce4 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java @@ -20,6 +20,7 @@ import static android.view.View.VISIBLE; import android.content.ClipData; import android.content.ClipDescription; +import android.content.Context; import android.content.Intent; import android.content.pm.LauncherApps; import android.content.res.Resources; @@ -29,7 +30,6 @@ import android.os.UserHandle; import android.view.DragEvent; import android.view.View; -import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.BubbleTextView; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; @@ -43,12 +43,12 @@ import com.android.systemui.shared.system.LauncherAppsCompat; */ public class TaskbarDragController { - private final BaseQuickstepLauncher mLauncher; + private final Context mContext; private final int mDragIconSize; - public TaskbarDragController(BaseQuickstepLauncher launcher) { - mLauncher = launcher; - Resources resources = mLauncher.getResources(); + public TaskbarDragController(Context context) { + mContext = context; + Resources resources = mContext.getResources(); mDragIconSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_drag_icon_size); } @@ -63,7 +63,6 @@ public class TaskbarDragController { } BubbleTextView btv = (BubbleTextView) view; - View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view) { @Override public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) { @@ -87,7 +86,7 @@ public class TaskbarDragController { Intent intent = null; if (tag instanceof WorkspaceItemInfo) { WorkspaceItemInfo item = (WorkspaceItemInfo) tag; - LauncherApps launcherApps = mLauncher.getSystemService(LauncherApps.class); + LauncherApps launcherApps = mContext.getSystemService(LauncherApps.class); clipDescription = new ClipDescription(item.title, new String[] { item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java index 68829cd0fb..91cf7efab5 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java @@ -26,6 +26,8 @@ import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.model.data.ItemInfo; +import java.util.function.Consumer; + /** * Works with TaskbarController to update the TaskbarView's Hotseat items. */ @@ -33,13 +35,12 @@ public class TaskbarHotseatController { private final BaseQuickstepLauncher mLauncher; private final Hotseat mHotseat; - private final TaskbarController.TaskbarHotseatControllerCallbacks mTaskbarCallbacks; + private final Consumer mTaskbarCallbacks; private final int mNumHotseatIcons; private final DragController.DragListener mDragListener = new DragController.DragListener() { @Override - public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { - } + public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { } @Override public void onDragEnd() { @@ -47,8 +48,8 @@ public class TaskbarHotseatController { } }; - public TaskbarHotseatController(BaseQuickstepLauncher launcher, - TaskbarController.TaskbarHotseatControllerCallbacks taskbarCallbacks) { + public TaskbarHotseatController( + BaseQuickstepLauncher launcher, Consumer taskbarCallbacks) { mLauncher = launcher; mHotseat = mLauncher.getHotseat(); mTaskbarCallbacks = taskbarCallbacks; @@ -85,10 +86,6 @@ public class TaskbarHotseatController { } } - mTaskbarCallbacks.updateHotseatItems(hotseatItemInfos); - } - - protected int getNumHotseatIcons() { - return mNumHotseatIcons; + mTaskbarCallbacks.accept(hotseatItemInfos); } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarIconController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarIconController.java new file mode 100644 index 0000000000..2a37915fcf --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarIconController.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2021 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.taskbar; + +import static android.view.View.GONE; +import static android.view.View.VISIBLE; + +import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_FRAME; +import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION; + +import android.graphics.Rect; +import android.inputmethodservice.InputMethodService; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnLongClickListener; + +import androidx.annotation.NonNull; + +import com.android.launcher3.R; +import com.android.launcher3.anim.AlphaUpdateListener; +import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo; + +/** + * Controller for taskbar icon UI + */ +public class TaskbarIconController { + + private final Rect mTempRect = new Rect(); + + private final TaskbarActivityContext mActivity; + private final TaskbarContainerView mContainerView; + + private final TaskbarView mTaskbarView; + private final ImeBarView mImeBarView; + + @NonNull + private TaskbarUIController mUIController = TaskbarUIController.DEFAULT; + + TaskbarIconController(TaskbarActivityContext activity, TaskbarContainerView containerView) { + mActivity = activity; + mContainerView = containerView; + mTaskbarView = mContainerView.findViewById(R.id.taskbar_view); + mImeBarView = mContainerView.findViewById(R.id.ime_bar_view); + } + + public void init(OnClickListener clickListener, OnLongClickListener longClickListener) { + mContainerView.addOnLayoutChangeListener((v, a, b, c, d, e, f, g, h) -> + mUIController.alignRealHotseatWithTaskbar()); + + ButtonProvider buttonProvider = new ButtonProvider(mActivity); + mImeBarView.init(buttonProvider); + mTaskbarView.construct(clickListener, longClickListener, buttonProvider); + mTaskbarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarSize; + + mContainerView.init(new Callbacks(), mTaskbarView); + } + + public void onDestroy() { + mContainerView.onDestroy(); + } + + public void setUIController(@NonNull TaskbarUIController uiController) { + mUIController = uiController; + } + + /** + * When in 3 button nav, the above doesn't get called since we prevent sysui nav bar from + * instantiating at all, which is what's responsible for sending sysui state flags over. + * + * @param vis IME visibility flag + */ + public void updateImeStatus(int displayId, int vis, boolean showImeSwitcher) { + if (displayId != mActivity.getDisplayId() || !mActivity.canShowNavButtons()) { + return; + } + + mImeBarView.setImeSwitcherVisibility(showImeSwitcher); + setImeIsVisible((vis & InputMethodService.IME_VISIBLE) != 0); + } + + /** + * Should be called when the IME visibility changes, so we can hide/show Taskbar accordingly. + */ + public void setImeIsVisible(boolean isImeVisible) { + mTaskbarView.setTouchesEnabled(!isImeVisible); + mUIController.onImeVisible(mContainerView, isImeVisible); + } + + /** + * Callbacks for {@link TaskbarContainerView} to interact with the icon controller + */ + public class Callbacks { + + /** + * Called to update the touchable insets + */ + public void updateInsetsTouchability(InsetsInfo insetsInfo) { + insetsInfo.touchableRegion.setEmpty(); + if (mContainerView.getAlpha() < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) { + // Let touches pass through us. + insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION); + } else if (mImeBarView.getVisibility() == VISIBLE) { + insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME); + } else if (!mUIController.isTaskbarTouchable()) { + // Let touches pass through us. + insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION); + } else if (mTaskbarView.areIconsVisible()) { + // Buttons are visible, take over the full taskbar area + insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME); + } else { + if (mTaskbarView.mSystemButtonContainer.getVisibility() == VISIBLE) { + mContainerView.getDescendantRectRelativeToSelf( + mTaskbarView.mSystemButtonContainer, mTempRect); + insetsInfo.touchableRegion.set(mTempRect); + } + insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION); + } + + // TaskbarContainerView provides insets to other apps based on contentInsets. These + // insets should stay consistent even if we expand TaskbarContainerView's bounds, e.g. + // to show a floating view like Folder. Thus, we set the contentInsets to be where + // mTaskbarView is, since its position never changes and insets rather than overlays. + insetsInfo.contentInsets.left = mTaskbarView.getLeft(); + insetsInfo.contentInsets.top = mTaskbarView.getTop(); + insetsInfo.contentInsets.right = mContainerView.getWidth() - mTaskbarView.getRight(); + insetsInfo.contentInsets.bottom = mContainerView.getHeight() - mTaskbarView.getBottom(); + } + + public void onContainerViewRemoved() { + int count = mContainerView.getChildCount(); + // Ensure no other children present (like Folders, etc) + for (int i = 0; i < count; i++) { + View v = mContainerView.getChildAt(i); + if (!((v instanceof TaskbarView) || (v instanceof ImeBarView))) { + return; + } + } + mActivity.setTaskbarWindowFullscreen(false); + } + + public void updateImeBarVisibilityAlpha(float alpha) { + if (!mActivity.canShowNavButtons()) { + // TODO Remove sysui IME bar for gesture nav as well + return; + } + mImeBarView.setAlpha(alpha); + mImeBarView.setVisibility(alpha == 0 ? GONE : VISIBLE); + } + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java new file mode 100644 index 0000000000..b9eec93687 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2021 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.taskbar; + +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + +import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN; +import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY; +import static com.android.launcher3.util.DisplayController.CHANGE_SUPPORTED_BOUNDS; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING; + +import android.content.Context; +import android.hardware.display.DisplayManager; +import android.inputmethodservice.InputMethodService; +import android.view.Display; + +import androidx.annotation.Nullable; + +import com.android.launcher3.BaseQuickstepLauncher; +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.util.DisplayController; +import com.android.launcher3.util.DisplayController.Info; +import com.android.quickstep.SysUINavigationMode; +import com.android.quickstep.SysUINavigationMode.Mode; +import com.android.quickstep.TouchInteractionService; + +/** + * Class to manager taskbar lifecycle + */ +public class TaskbarManager implements DisplayController.DisplayInfoChangeListener, + SysUINavigationMode.NavigationModeChangeListener { + + private final Context mContext; + private final DisplayController mDisplayController; + private final SysUINavigationMode mSysUINavigationMode; + private final TaskbarNavButtonController mNavButtonController; + + private TaskbarActivityContext mTaskbarActivityContext; + private BaseQuickstepLauncher mLauncher; + + private static final int CHANGE_FLAGS = + CHANGE_ACTIVE_SCREEN | CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS; + + public TaskbarManager(TouchInteractionService service) { + mDisplayController = DisplayController.INSTANCE.get(service); + mSysUINavigationMode = SysUINavigationMode.INSTANCE.get(service); + Display display = + service.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY); + mContext = service.createWindowContext(display, TYPE_APPLICATION_OVERLAY, null); + mNavButtonController = new TaskbarNavButtonController(service); + + mDisplayController.addChangeListener(this); + mSysUINavigationMode.addModeChangeListener(this); + recreateTaskbar(); + } + + @Override + public void onNavigationModeChanged(Mode newMode) { + recreateTaskbar(); + } + + @Override + public void onDisplayInfoChanged(Context context, Info info, int flags) { + if ((flags & CHANGE_FLAGS) != 0) { + recreateTaskbar(); + } + } + + private void destroyExistingTaskbar() { + if (mTaskbarActivityContext != null) { + mTaskbarActivityContext.onDestroy(); + mTaskbarActivityContext = null; + } + } + + /** + * Sets or clears a launcher to act as taskbar callback + */ + public void setLauncher(@Nullable BaseQuickstepLauncher launcher) { + mLauncher = launcher; + if (mTaskbarActivityContext != null) { + mTaskbarActivityContext.setUIController(mLauncher == null + ? TaskbarUIController.DEFAULT + : new TaskbarController(launcher, mTaskbarActivityContext)); + } + } + + private void recreateTaskbar() { + destroyExistingTaskbar(); + if (!FeatureFlags.ENABLE_TASKBAR.get()) { + return; + } + DeviceProfile dp = LauncherAppState.getIDP(mContext).getDeviceProfile(mContext); + if (!dp.isTaskbarPresent) { + return; + } + mTaskbarActivityContext = new TaskbarActivityContext( + mContext, dp.copy(mContext), mNavButtonController); + mTaskbarActivityContext.init(); + if (mLauncher != null) { + mTaskbarActivityContext.setUIController( + new TaskbarController(mLauncher, mTaskbarActivityContext)); + } + } + + /** + * See {@link com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags} + * @param systemUiStateFlags The latest SystemUiStateFlags + */ + public void onSystemUiFlagsChanged(int systemUiStateFlags) { + boolean isImeVisible = (systemUiStateFlags & SYSUI_STATE_IME_SHOWING) != 0; + if (mTaskbarActivityContext != null) { + mTaskbarActivityContext.getIconController().setImeIsVisible(isImeVisible); + } + } + + /** + * When in 3 button nav, the above doesn't get called since we prevent sysui nav bar from + * instantiating at all, which is what's responsible for sending sysui state flags over. + * + * @param vis IME visibility flag + * @param backDisposition Used to determine back button behavior for software keyboard + * See BACK_DISPOSITION_* constants in {@link InputMethodService} + */ + public void updateImeStatus(int displayId, int vis, int backDisposition, + boolean showImeSwitcher) { + if (mTaskbarActivityContext != null) { + mTaskbarActivityContext.getIconController() + .updateImeStatus(displayId, vis, showImeSwitcher); + } + } + + /** + * Called when the manager is no longer needed + */ + public void destroy() { + destroyExistingTaskbar(); + mDisplayController.removeChangeListener(this); + mSysUINavigationMode.removeModeChangeListener(this); + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java index 54e1610cd1..3b5afad691 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java @@ -16,7 +16,8 @@ package com.android.launcher3.taskbar; -import android.content.Context; +import static android.view.Display.DEFAULT_DISPLAY; + import android.content.Intent; import android.view.inputmethod.InputMethodManager; @@ -53,11 +54,10 @@ public class TaskbarNavButtonController { static final int BUTTON_RECENTS = BUTTON_HOME << 1; static final int BUTTON_IME_SWITCH = BUTTON_RECENTS << 1; + private final TouchInteractionService mService; - private final Context mContext; - - public TaskbarNavButtonController(Context context) { - mContext = context; + public TaskbarNavButtonController(TouchInteractionService service) { + mService = service; } public void onButtonClick(@TaskbarButton int buttonType) { @@ -78,13 +78,13 @@ public class TaskbarNavButtonController { } private void navigateHome() { - mContext.startActivity(new Intent(Intent.ACTION_MAIN) + mService.startActivity(new Intent(Intent.ACTION_MAIN) .addCategory(Intent.CATEGORY_HOME) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); } private void navigateToOverview() { - TouchInteractionService.getInstance().getOverviewCommandHelper() + mService.getOverviewCommandHelper() .addCommand(OverviewCommandHelper.TYPE_SHOW); } @@ -93,8 +93,8 @@ public class TaskbarNavButtonController { } private void showIMESwitcher() { - mContext.getSystemService(InputMethodManager.class).showInputMethodPickerFromSystem( - true /* showAuxiliarySubtypes */, mContext.getDisplayId()); + mService.getSystemService(InputMethodManager.class) + .showInputMethodPickerFromSystem(true /* showAuxiliarySubtypes */, + DEFAULT_DISPLAY); } - } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java index 6ea51fa5eb..a701aae088 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java @@ -24,59 +24,52 @@ import androidx.annotation.Nullable; import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.LauncherState; import com.android.launcher3.anim.PendingAnimation; +import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.states.StateAnimationConfig; import com.android.quickstep.AnimatedFloat; /** * StateHandler to animate Taskbar according to Launcher's state machine. Does nothing if Taskbar - * isn't present (i.e. {@link #setTaskbarCallbacks} is never called). + * isn't present (i.e. {@link #setAnimationController} is never called). */ public class TaskbarStateHandler implements StateManager.StateHandler { private final BaseQuickstepLauncher mLauncher; // Contains Taskbar-related methods and fields we should aniamte. If null, don't do anything. - private @Nullable TaskbarController.TaskbarStateHandlerCallbacks mTaskbarCallbacks = null; + private @Nullable TaskbarAnimationController mAnimationController = null; public TaskbarStateHandler(BaseQuickstepLauncher launcher) { mLauncher = launcher; } - public void setTaskbarCallbacks(TaskbarController.TaskbarStateHandlerCallbacks callbacks) { - mTaskbarCallbacks = callbacks; + public void setAnimationController(TaskbarAnimationController callbacks) { + mAnimationController = callbacks; } @Override public void setState(LauncherState state) { - if (mTaskbarCallbacks == null) { - return; - } - - AnimatedFloat alphaTarget = mTaskbarCallbacks.getAlphaTarget(); - AnimatedFloat scaleTarget = mTaskbarCallbacks.getScaleTarget(); - AnimatedFloat translationYTarget = mTaskbarCallbacks.getTranslationYTarget(); - boolean isTaskbarVisible = (state.getVisibleElements(mLauncher) & TASKBAR) != 0; - alphaTarget.updateValue(isTaskbarVisible ? 1f : 0f); - scaleTarget.updateValue(state.getTaskbarScale(mLauncher)); - translationYTarget.updateValue(state.getTaskbarTranslationY(mLauncher)); + setState(state, PropertySetter.NO_ANIM_PROPERTY_SETTER); } @Override public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config, PendingAnimation animation) { - if (mTaskbarCallbacks == null) { + setState(toState, animation); + } + + private void setState(LauncherState toState, PropertySetter setter) { + if (mAnimationController == null) { return; } - AnimatedFloat alphaTarget = mTaskbarCallbacks.getAlphaTarget(); - AnimatedFloat scaleTarget = mTaskbarCallbacks.getScaleTarget(); - AnimatedFloat translationYTarget = mTaskbarCallbacks.getTranslationYTarget(); boolean isTaskbarVisible = (toState.getVisibleElements(mLauncher) & TASKBAR) != 0; - animation.setFloat(alphaTarget, AnimatedFloat.VALUE, isTaskbarVisible ? 1f : 0f, LINEAR); - animation.setFloat(scaleTarget, AnimatedFloat.VALUE, toState.getTaskbarScale(mLauncher), - LINEAR); - animation.setFloat(translationYTarget, AnimatedFloat.VALUE, - toState.getTaskbarTranslationY(mLauncher), ACCEL_DEACCEL); + setter.setFloat(mAnimationController.getTaskbarVisibilityForLauncherState(), + AnimatedFloat.VALUE, isTaskbarVisible ? 1f : 0f, LINEAR); + setter.setFloat(mAnimationController.getTaskbarScaleForLauncherState(), + AnimatedFloat.VALUE, toState.getTaskbarScale(mLauncher), LINEAR); + setter.setFloat(mAnimationController.getTaskbarTranslationYForLauncherState(), + AnimatedFloat.VALUE, toState.getTaskbarTranslationY(mLauncher), ACCEL_DEACCEL); } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java new file mode 100644 index 0000000000..e16f5e65b0 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 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.taskbar; + +/** + * Base class for providing different taskbar UI + */ +public class TaskbarUIController { + + public static final TaskbarUIController DEFAULT = new TaskbarUIController(); + + /** + * Pads the Hotseat to line up exactly with Taskbar's copy of the Hotseat. + */ + public void alignRealHotseatWithTaskbar() { } + + protected void onCreate() { } + + protected void onDestroy() { } + + protected boolean isTaskbarTouchable() { + return true; + } + + protected void onImeVisible(TaskbarContainerView container, boolean isVisible) { + container.updateImeBarVisibilityAlpha(isVisible ? 1 : 0); + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java index 9e8013e63e..c6573a639c 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -15,20 +15,14 @@ */ package com.android.launcher3.taskbar; -import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK; -import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME; -import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS; +import static android.view.View.MeasureSpec.EXACTLY; +import static android.view.View.MeasureSpec.makeMeasureSpec; -import android.animation.Animator; -import android.animation.AnimatorSet; -import android.animation.LayoutTransition; -import android.animation.ValueAnimator; import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.RectF; -import android.os.SystemProperties; import android.util.AttributeSet; import android.view.DragEvent; import android.view.Gravity; @@ -51,17 +45,12 @@ import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.views.ActivityContext; -import com.android.quickstep.SysUINavigationMode; /** * Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps. */ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconParent, Insettable { - - private static final boolean ENABLE_THREE_BUTTON_TASKBAR = - SystemProperties.getBoolean("persist.debug.taskbar_three_button", false); - private final int mIconTouchSize; private final boolean mIsRtl; private final int mTouchSlop; @@ -69,17 +58,16 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa private final RectF mDelegateSlopBounds = new RectF(); private final int[] mTempOutLocation = new int[2]; - // Initialized in TaskbarController constructor. - private TaskbarController.TaskbarViewCallbacks mControllerCallbacks; - // Scale on elements that aren't icons. - private float mNonIconScale; - private int mItemMarginLeftRight; + private final int mItemMarginLeftRight; - // Initialized in init(). - private LayoutTransition mLayoutTransition; - private int mHotseatStartIndex; - private int mHotseatEndIndex; - private LinearLayout mButtonRegion; + private final TaskbarActivityContext mActivityContext; + + // Initialized in TaskbarController constructor. + private View.OnClickListener mIconClickListener; + private View.OnLongClickListener mIconLongClickListener; + + LinearLayout mSystemButtonContainer; + LinearLayout mHotseatIconsContainer; // Delegate touches to the closest view if within mIconTouchSize. private boolean mDelegateTargeted; @@ -91,10 +79,12 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa // Only non-null when the corresponding Folder is open. private @Nullable FolderIcon mLeaveBehindFolderIcon; - private int mNavButtonStartIndex; /** Provider of buttons added to taskbar in 3 button nav */ private ButtonProvider mButtonProvider; + private boolean mDisableRelayout; + private boolean mAreHolesAllowed; + public TaskbarView(@NonNull Context context) { this(context, null); } @@ -111,80 +101,58 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa public TaskbarView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); + mActivityContext = ActivityContext.lookupContext(context); Resources resources = getResources(); mIconTouchSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_touch_size); + mItemMarginLeftRight = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing); + mIsRtl = Utilities.isRtl(resources); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } - protected void construct(TaskbarController.TaskbarViewCallbacks taskbarViewCallbacks, - ButtonProvider buttonProvider) { - mControllerCallbacks = taskbarViewCallbacks; - mNonIconScale = mControllerCallbacks.getNonIconScale(this); - mItemMarginLeftRight = getResources().getDimensionPixelSize(R.dimen.taskbar_icon_spacing); - mItemMarginLeftRight = Math.round(mItemMarginLeftRight * mNonIconScale); - mButtonProvider = buttonProvider; - mButtonProvider.setMarginLeftRight(mItemMarginLeftRight); + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mSystemButtonContainer = findViewById(R.id.system_button_layout); + mHotseatIconsContainer = findViewById(R.id.hotseat_icons_layout); } - protected void init(int numHotseatIcons, SysUINavigationMode.Mode newMode) { - // TODO: check if buttons on left - if (newMode == SysUINavigationMode.Mode.THREE_BUTTONS && ENABLE_THREE_BUTTON_TASKBAR) { - // 3 button - mNavButtonStartIndex = 0; + protected void construct(OnClickListener clickListener, OnLongClickListener longClickListener, + ButtonProvider buttonProvider) { + mIconClickListener = clickListener; + mIconLongClickListener = longClickListener; + mButtonProvider = buttonProvider; + + if (mActivityContext.canShowNavButtons()) { createNavButtons(); } else { - mNavButtonStartIndex = -1; - removeNavButtons(); + mSystemButtonContainer.setVisibility(GONE); } - mHotseatStartIndex = mNavButtonStartIndex + 1; - mHotseatEndIndex = mHotseatStartIndex + numHotseatIcons - 1; + int numHotseatIcons = mActivityContext.getDeviceProfile().numShownHotseatIcons; updateHotseatItems(new ItemInfo[numHotseatIcons]); - - mLayoutTransition = new LayoutTransition(); - addUpdateListenerForAllLayoutTransitions(() -> { - if (getLayoutTransition() == mLayoutTransition) { - mControllerCallbacks.onItemPositionsChanged(this); - } - }); - setLayoutTransition(mLayoutTransition); } - private void addUpdateListenerForAllLayoutTransitions(Runnable onUpdate) { - addUpdateListenerForLayoutTransition(LayoutTransition.CHANGE_APPEARING, onUpdate); - addUpdateListenerForLayoutTransition(LayoutTransition.CHANGE_DISAPPEARING, onUpdate); - addUpdateListenerForLayoutTransition(LayoutTransition.CHANGING, onUpdate); - addUpdateListenerForLayoutTransition(LayoutTransition.APPEARING, onUpdate); - addUpdateListenerForLayoutTransition(LayoutTransition.DISAPPEARING, onUpdate); - } - - private void addUpdateListenerForLayoutTransition(int transitionType, Runnable onUpdate) { - Animator anim = mLayoutTransition.getAnimator(transitionType); - if (anim instanceof ValueAnimator) { - ((ValueAnimator) anim).addUpdateListener(valueAnimator -> onUpdate.run()); - } else { - AnimatorSet animSet = new AnimatorSet(); - ValueAnimator updateAnim = ValueAnimator.ofFloat(0, 1); - updateAnim.addUpdateListener(valueAnimator -> onUpdate.run()); - animSet.playTogether(anim, updateAnim); - mLayoutTransition.setAnimator(transitionType, animSet); + /** + * Enables/disables empty icons in taskbar so that the layout matches with Launcher + */ + public void setHolesAllowedInLayout(boolean areHolesAllowed) { + if (mAreHolesAllowed != areHolesAllowed) { + mAreHolesAllowed = areHolesAllowed; + updateHotseatItemsVisibility(); + // TODO: Add animation } } - protected void cleanup() { - endAllLayoutTransitionAnimators(); - setLayoutTransition(null); - removeAllViews(); - } - - private void endAllLayoutTransitionAnimators() { - mLayoutTransition.getAnimator(LayoutTransition.CHANGE_APPEARING).end(); - mLayoutTransition.getAnimator(LayoutTransition.CHANGE_DISAPPEARING).end(); - mLayoutTransition.getAnimator(LayoutTransition.CHANGING).end(); - mLayoutTransition.getAnimator(LayoutTransition.APPEARING).end(); - mLayoutTransition.getAnimator(LayoutTransition.DISAPPEARING).end(); + private void setHolesAllowedInLayoutNoAnimation(boolean areHolesAllowed) { + if (mAreHolesAllowed != areHolesAllowed) { + mAreHolesAllowed = areHolesAllowed; + updateHotseatItemsVisibility(); + onMeasure(makeMeasureSpec(getMeasuredWidth(), EXACTLY), + makeMeasureSpec(getMeasuredHeight(), EXACTLY)); + onLayout(false, getLeft(), getTop(), getRight(), getBottom()); + } } /** @@ -192,10 +160,9 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa */ protected void updateHotseatItems(ItemInfo[] hotseatItemInfos) { for (int i = 0; i < hotseatItemInfos.length; i++) { - ItemInfo hotseatItemInfo = hotseatItemInfos[!mIsRtl ? i - : hotseatItemInfos.length - i - 1]; - int hotseatIndex = mHotseatStartIndex + i; - View hotseatView = getChildAt(hotseatIndex); + ItemInfo hotseatItemInfo = hotseatItemInfos[ + !mIsRtl ? i : hotseatItemInfos.length - i - 1]; + View hotseatView = mHotseatIconsContainer.getChildAt(i); // Replace any Hotseat views with the appropriate type if it's not already that type. final int expectedLayoutResId; @@ -213,23 +180,23 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa } else { expectedLayoutResId = R.layout.taskbar_app_icon; } - if (hotseatView == null || hotseatView.getSourceLayoutResId() != expectedLayoutResId + if (hotseatView == null + || hotseatView.getSourceLayoutResId() != expectedLayoutResId || needsReinflate) { - removeView(hotseatView); - ActivityContext activityContext = getActivityContext(); + mHotseatIconsContainer.removeView(hotseatView); if (isFolder) { FolderInfo folderInfo = (FolderInfo) hotseatItemInfo; FolderIcon folderIcon = FolderIcon.inflateFolderAndIcon(expectedLayoutResId, - getActivityContext(), this, folderInfo); + mActivityContext, this, folderInfo); folderIcon.setTextVisible(false); hotseatView = folderIcon; } else { hotseatView = inflate(expectedLayoutResId); } - int iconSize = activityContext.getDeviceProfile().iconSizePx; + int iconSize = mActivityContext.getDeviceProfile().iconSizePx; LayoutParams lp = new LayoutParams(iconSize, iconSize); lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0); - addView(hotseatView, hotseatIndex, lp); + mHotseatIconsContainer.addView(hotseatView, i, lp); } // Apply the Hotseat ItemInfos, or hide the view if there is none for a given index. @@ -237,13 +204,11 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa && hotseatItemInfo instanceof WorkspaceItemInfo) { ((BubbleTextView) hotseatView).applyFromWorkspaceItem( (WorkspaceItemInfo) hotseatItemInfo); - hotseatView.setOnClickListener(mControllerCallbacks.getItemOnClickListener()); - hotseatView.setOnLongClickListener( - mControllerCallbacks.getItemOnLongClickListener()); + hotseatView.setOnClickListener(mIconClickListener); + hotseatView.setOnLongClickListener(mIconLongClickListener); } else if (isFolder) { - hotseatView.setOnClickListener(mControllerCallbacks.getItemOnClickListener()); - hotseatView.setOnLongClickListener( - mControllerCallbacks.getItemOnLongClickListener()); + hotseatView.setOnClickListener(mIconClickListener); + hotseatView.setOnLongClickListener(mIconLongClickListener); } else { hotseatView.setOnClickListener(null); hotseatView.setOnLongClickListener(null); @@ -254,24 +219,14 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa } protected void updateHotseatItemsVisibility() { - for (int i = mHotseatStartIndex; i <= mHotseatEndIndex; i++) { - updateHotseatItemVisibility(getChildAt(i)); + for (int i = mHotseatIconsContainer.getChildCount() - 1; i >= 0; i--) { + updateHotseatItemVisibility(mHotseatIconsContainer.getChildAt(i)); } } private void updateHotseatItemVisibility(View hotseatView) { - if (hotseatView.getTag() != null) { - hotseatView.setVisibility(VISIBLE); - } else { - int oldVisibility = hotseatView.getVisibility(); - int newVisibility = mControllerCallbacks.getEmptyHotseatViewVisibility(this); - hotseatView.setVisibility(newVisibility); - if (oldVisibility == GONE && newVisibility != GONE) { - // By default, the layout transition only runs when going to VISIBLE, - // but we want it to run when going to GONE to INVISIBLE as well. - getLayoutTransition().showChild(this, hotseatView, oldVisibility); - } - } + hotseatView.setVisibility( + hotseatView.getTag() != null ? VISIBLE : (mAreHolesAllowed ? INVISIBLE : GONE)); } @Override @@ -378,49 +333,20 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa return findDelegateView(xInOurCoordinates, yInOurCoorindates) != null; } - private void removeNavButtons() { - if (mButtonRegion != null) { - mButtonRegion.removeAllViews(); - removeView(mButtonRegion); - } // else We've never been in 3 button. Woah Scoob! - } - /** * Add back/home/recents buttons into a single ViewGroup that will be inserted at * {@param navButtonStartIndex} */ private void createNavButtons() { - ActivityContext context = getActivityContext(); - if (mButtonRegion == null) { - mButtonRegion = new LinearLayout(getContext()); - } else { - mButtonRegion.removeAllViews(); - } - mButtonRegion.setVisibility(VISIBLE); - LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams( - context.getDeviceProfile().iconSizePx, - context.getDeviceProfile().iconSizePx + mActivityContext.getDeviceProfile().iconSizePx, + mActivityContext.getDeviceProfile().iconSizePx ); buttonParams.gravity = Gravity.CENTER; - View backButton = mButtonProvider.getBack(); - backButton.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick( - BUTTON_BACK)); - mButtonRegion.addView(backButton, buttonParams); - - // Home button - View homeButton = mButtonProvider.getHome(); - homeButton.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick( - BUTTON_HOME)); - mButtonRegion.addView(homeButton, buttonParams); - - View recentsButton = mButtonProvider.getRecents(); - recentsButton.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick( - BUTTON_RECENTS)); - mButtonRegion.addView(recentsButton, buttonParams); - - addView(mButtonRegion, mNavButtonStartIndex); + mSystemButtonContainer.addView(mButtonProvider.getBack(), buttonParams); + mSystemButtonContainer.addView(mButtonProvider.getHome(), buttonParams); + mSystemButtonContainer.addView(mButtonProvider.getRecents(), buttonParams); } @Override @@ -428,7 +354,7 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa switch (event.getAction()) { case DragEvent.ACTION_DRAG_STARTED: mIsDraggingItem = true; - AbstractFloatingView.closeAllOpenViews(getActivityContext()); + AbstractFloatingView.closeAllOpenViews(mActivityContext); return true; case DragEvent.ACTION_DRAG_ENDED: mIsDraggingItem = false; @@ -445,26 +371,26 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa * @return The bounding box of where the hotseat elements are relative to this TaskbarView. */ protected RectF getHotseatBounds() { - View firstHotseatView = null, lastHotseatView = null; - for (int i = mHotseatStartIndex; i <= mHotseatEndIndex; i++) { - View child = getChildAt(i); - if (child.getVisibility() != GONE) { - if (firstHotseatView == null) { - firstHotseatView = child; - } - lastHotseatView = child; - } + RectF result; + mDisableRelayout = true; + boolean wereHolesAllowed = mAreHolesAllowed; + setHolesAllowedInLayoutNoAnimation(true); + result = new RectF( + mHotseatIconsContainer.getLeft(), + mHotseatIconsContainer.getTop(), + mHotseatIconsContainer.getRight(), + mHotseatIconsContainer.getBottom()); + setHolesAllowedInLayoutNoAnimation(wereHolesAllowed); + mDisableRelayout = false; + + return result; + } + + @Override + public void requestLayout() { + if (!mDisableRelayout) { + super.requestLayout(); } - if (firstHotseatView == null || lastHotseatView == null) { - return new RectF(); - } - View leftmostHotseatView = !mIsRtl ? firstHotseatView : lastHotseatView; - View rightmostHotseatView = !mIsRtl ? lastHotseatView : firstHotseatView; - return new RectF( - leftmostHotseatView.getLeft() - mItemMarginLeftRight, - leftmostHotseatView.getTop(), - rightmostHotseatView.getRight() + mItemMarginLeftRight, - rightmostHotseatView.getBottom()); } // FolderIconParent implemented methods. @@ -495,7 +421,7 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa } private View inflate(@LayoutRes int layoutResId) { - return getActivityContext().getLayoutInflater().inflate(layoutResId, this, false); + return mActivityContext.getLayoutInflater().inflate(layoutResId, this, false); } @Override @@ -503,7 +429,11 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa // Ignore, we just implement Insettable to draw behind system insets. } - private T getActivityContext() { - return ActivityContext.lookupContext(getContext()); + public void setIconsVisibility(boolean isVisible) { + mHotseatIconsContainer.setVisibility(isVisible ? VISIBLE : INVISIBLE); + } + + public boolean areIconsVisible() { + return mHotseatIconsContainer.getVisibility() == VISIBLE; } } diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java index f5ddd0e52f..86bf1194bb 100644 --- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java +++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java @@ -52,7 +52,6 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.statemanager.BaseState; import com.android.launcher3.statemanager.StatefulActivity; -import com.android.launcher3.taskbar.TaskbarController; import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.util.WindowBounds; import com.android.launcher3.views.ScrimView; @@ -123,11 +122,6 @@ public abstract class BaseActivityInterface { - if (mTaskbarOverviewProxyDelegate == null) { - return; - } - mTaskbarOverviewProxyDelegate - .updateImeStatus(displayId, vis, backDisposition, showImeSwitcher); - }); + int backDisposition, boolean showImeSwitcher) { + MAIN_EXECUTOR.execute(() -> mTaskbarManager.updateImeStatus( + displayId, vis, backDisposition, showImeSwitcher)); } - }; - public interface TaskbarOverviewProxyDelegate { - void updateImeStatus(int displayId, int vis, int backDisposition, - boolean showImeSwitcher); + public TaskbarManager getTaskbarManager() { + return mTaskbarManager; + } } private static boolean sConnected = false; - private static TouchInteractionService sInstance; private static boolean sIsInitialized = false; private RotationTouchHelper mRotationTouchHelper; - @Nullable - private TaskbarOverviewProxyDelegate mTaskbarOverviewProxyDelegate; public static boolean isConnected() { return sConnected; } - @Nullable - public static TouchInteractionService getInstance() { - return sInstance; - } public static boolean isInitialized() { return sIsInitialized; @@ -336,9 +308,7 @@ public class TouchInteractionService extends Service implements PluginListener - - \ No newline at end of file diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 3d044d68ce..322c6eed29 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -447,10 +447,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, * @param canvas The canvas to draw to. */ protected void drawDotIfNecessary(Canvas canvas) { - if (mActivity instanceof Launcher && ((Launcher) mActivity).isViewInTaskbar(this)) { - // TODO: support notification dots in Taskbar - return; - } if (!mForceHideDot && (hasDot() || mDotParams.scale > 0)) { getIconBounds(mDotParams.iconBounds); Utilities.scaleRectAboutCenter(mDotParams.iconBounds, diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index b2a9e75e50..ff380ce1ae 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -49,7 +49,6 @@ public class Hotseat extends CellLayout implements Insettable { private final View mQsb; private final int mQsbHeight; - private final View mTaskbarView; private final int mTaskbarViewHeight; public Hotseat(Context context) { @@ -67,10 +66,7 @@ public class Hotseat extends CellLayout implements Insettable { mQsbHeight = mQsb.getLayoutParams().height; addView(mQsb); - mTaskbarView = LayoutInflater.from(context).inflate(R.layout.taskbar_view, this, false); - mTaskbarViewHeight = mTaskbarView.getLayoutParams().height; - // We want taskbar in the back so its background applies to Hotseat as well. - addView(mTaskbarView, 0); + mTaskbarViewHeight = context.getResources().getDimensionPixelSize(R.dimen.taskbar_size); } /** @@ -187,8 +183,6 @@ public class Hotseat extends CellLayout implements Insettable { int width = getShortcutsAndWidgets().getMeasuredWidth(); mQsb.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(mQsbHeight, MeasureSpec.EXACTLY)); - mTaskbarView.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(mTaskbarViewHeight, MeasureSpec.EXACTLY)); } @Override @@ -202,13 +196,6 @@ public class Hotseat extends CellLayout implements Insettable { int bottom = b - t - getQsbOffsetY(); int top = bottom - mQsbHeight; mQsb.layout(left, top, right, bottom); - - int taskbarWidth = mTaskbarView.getMeasuredWidth(); - left = (r - l - taskbarWidth) / 2; - right = left + taskbarWidth; - bottom = b - t - getTaskbarOffsetY(); - top = bottom - mTaskbarViewHeight; - mTaskbarView.layout(left, top, right, bottom); } /** @@ -244,10 +231,4 @@ public class Hotseat extends CellLayout implements Insettable { return mQsb; } - /** - * Returns the Taskbar inside hotseat - */ - public View getTaskbarView() { - return mTaskbarView; - } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index cf902160ee..8889e60688 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1942,13 +1942,6 @@ public class Launcher extends StatefulActivity implements Launche @Override public boolean startActivitySafely(View v, Intent intent, ItemInfo item) { - if (isViewInTaskbar(v)) { - // Start the activity without the hacky workarounds below, which assume the View was - // clicked when Launcher was resumed and will be hidden until Launcher is re-resumed - // (this isn't the case for Taskbar). - return super.startActivitySafely(v, intent, item); - } - if (!hasBeenResumed()) { // Workaround an issue where the WM launch animation is clobbered when finishing the // recents animation into launcher. Defer launching the activity until Launcher is @@ -2860,13 +2853,6 @@ public class Launcher extends StatefulActivity implements Launche .start(); } - /** - * @return Whether the View is in the same window as the Taskbar window. - */ - public boolean isViewInTaskbar(View v) { - return false; - } - public boolean supportsAdaptiveIconAnimation(View clickedView) { return false; } diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 7ae729aa0c..2884fba08d 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -324,15 +324,17 @@ public final class Utilities { } public static void scaleRectFAboutCenter(RectF r, float scale) { + scaleRectFAboutPivot(r, scale, r.centerX(), r.centerY()); + } + + public static void scaleRectFAboutPivot(RectF r, float scale, float px, float py) { if (scale != 1.0f) { - float cx = r.centerX(); - float cy = r.centerY(); - r.offset(-cx, -cy); + r.offset(-px, -py); r.left = r.left * scale; r.top = r.top * scale ; r.right = r.right * scale; r.bottom = r.bottom * scale; - r.offset(cx, cy); + r.offset(px, py); } } diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java index ce7dc07d6d..b5dcd3a041 100644 --- a/src/com/android/launcher3/touch/ItemClickHandler.java +++ b/src/com/android/launcher3/touch/ItemClickHandler.java @@ -27,6 +27,7 @@ import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SU import android.app.AlertDialog; import android.app.PendingIntent; +import android.content.Context; import android.content.Intent; import android.content.IntentSender; import android.content.pm.LauncherApps; @@ -193,6 +194,35 @@ public class ItemClickHandler { launcher.startActivitySafely(v, intent, item); } + /** + * Handles clicking on a disabled shortcut + */ + public static void handleDisabledItemClicked(WorkspaceItemInfo shortcut, Context context) { + final int disabledFlags = shortcut.runtimeStatusFlags + & WorkspaceItemInfo.FLAG_DISABLED_MASK; + if ((disabledFlags + & ~FLAG_DISABLED_SUSPENDED + & ~FLAG_DISABLED_QUIET_USER) == 0) { + // If the app is only disabled because of the above flags, launch activity anyway. + // Framework will tell the user why the app is suspended. + } else { + if (!TextUtils.isEmpty(shortcut.disabledMessage)) { + // Use a message specific to this shortcut, if it has one. + Toast.makeText(context, shortcut.disabledMessage, Toast.LENGTH_SHORT).show(); + return; + } + // Otherwise just use a generic error message. + int error = R.string.activity_not_available; + if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_SAFEMODE) != 0) { + error = R.string.safemode_shortcut_error; + } else if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_BY_PUBLISHER) != 0 + || (shortcut.runtimeStatusFlags & FLAG_DISABLED_LOCKED_USER) != 0) { + error = R.string.shortcut_not_available; + } + Toast.makeText(context, error, Toast.LENGTH_SHORT).show(); + } + } + /** * Event handler for an app shortcut click. * @@ -200,30 +230,8 @@ public class ItemClickHandler { */ public static void onClickAppShortcut(View v, WorkspaceItemInfo shortcut, Launcher launcher) { if (shortcut.isDisabled()) { - final int disabledFlags = shortcut.runtimeStatusFlags - & WorkspaceItemInfo.FLAG_DISABLED_MASK; - if ((disabledFlags & - ~FLAG_DISABLED_SUSPENDED & - ~FLAG_DISABLED_QUIET_USER) == 0) { - // If the app is only disabled because of the above flags, launch activity anyway. - // Framework will tell the user why the app is suspended. - } else { - if (!TextUtils.isEmpty(shortcut.disabledMessage)) { - // Use a message specific to this shortcut, if it has one. - Toast.makeText(launcher, shortcut.disabledMessage, Toast.LENGTH_SHORT).show(); - return; - } - // Otherwise just use a generic error message. - int error = R.string.activity_not_available; - if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_SAFEMODE) != 0) { - error = R.string.safemode_shortcut_error; - } else if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_BY_PUBLISHER) != 0 || - (shortcut.runtimeStatusFlags & FLAG_DISABLED_LOCKED_USER) != 0) { - error = R.string.shortcut_not_available; - } - Toast.makeText(launcher, error, Toast.LENGTH_SHORT).show(); - return; - } + handleDisabledItemClicked(shortcut, launcher); + return; } // Check for abandoned promise