Merge "Moving taskbar lifecycle to TouchInteractionService" into sc-dev am: 5bb515b6cc

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/14445701

Change-Id: Ibe2ec56b9d9e4137323777f05b02a0b72d5b88ee
This commit is contained in:
Sunny Goyal
2021-05-18 17:27:58 +00:00
committed by Automerger Merge Worker
29 changed files with 948 additions and 982 deletions
+22 -2
View File
@@ -22,9 +22,29 @@
<com.android.launcher3.taskbar.TaskbarView <com.android.launcher3.taskbar.TaskbarView
android:id="@+id/taskbar_view" android:id="@+id/taskbar_view"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center"/> android:gravity="center"
android:forceHasOverlappingRendering="false"
android:layout_gravity="bottom" >
<LinearLayout
android:id="@+id/system_button_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/taskbar_nav_buttons_spacing"
android:paddingRight="@dimen/taskbar_nav_buttons_spacing"
android:forceHasOverlappingRendering="false"
android:gravity="center" />
<LinearLayout
android:id="@+id/hotseat_icons_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:forceHasOverlappingRendering="false"
android:gravity="center" />
</com.android.launcher3.taskbar.TaskbarView>
<com.android.launcher3.taskbar.ImeBarView <com.android.launcher3.taskbar.ImeBarView
android:id="@+id/ime_bar_view" android:id="@+id/ime_bar_view"
-26
View File
@@ -1,26 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<com.android.launcher3.taskbar.TaskbarView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/taskbar_view"
android:layout_width="match_parent"
android:layout_height="@dimen/taskbar_size"
android:background="@android:color/transparent"
android:layout_gravity="bottom"
android:gravity="center"
android:visibility="gone" />
+1
View File
@@ -147,4 +147,5 @@
<!-- Note that this applies to both sides of all icons, so visible space is double this. --> <!-- Note that this applies to both sides of all icons, so visible space is double this. -->
<dimen name="taskbar_icon_spacing">8dp</dimen> <dimen name="taskbar_icon_spacing">8dp</dimen>
<dimen name="taskbar_folder_margin">16dp</dimen> <dimen name="taskbar_folder_margin">16dp</dimen>
<dimen name="taskbar_nav_buttons_spacing">16dp</dimen>
</resources> </resources>
@@ -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.FLAG_HIDE_BACK_BUTTON;
import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.NO_OFFSET; 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.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS; import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE; import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
@@ -30,7 +29,6 @@ import android.animation.AnimatorSet;
import android.animation.ValueAnimator; import android.animation.ValueAnimator;
import android.app.ActivityOptions; import android.app.ActivityOptions;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentSender; import android.content.IntentSender;
import android.content.ServiceConnection; 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.BackButtonAlphaHandler;
import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager.StateHandler; import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarController; import com.android.launcher3.taskbar.TaskbarController;
import com.android.launcher3.taskbar.TaskbarManager;
import com.android.launcher3.taskbar.TaskbarStateHandler; import com.android.launcher3.taskbar.TaskbarStateHandler;
import com.android.launcher3.taskbar.TaskbarView;
import com.android.launcher3.uioverrides.RecentsViewStateController; import com.android.launcher3.uioverrides.RecentsViewStateController;
import com.android.launcher3.util.ActivityOptionsWrapper; import com.android.launcher3.util.ActivityOptionsWrapper;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.ObjectWrapper; import com.android.launcher3.util.ObjectWrapper;
import com.android.launcher3.util.UiThreadHelper; import com.android.launcher3.util.UiThreadHelper;
import com.android.quickstep.RecentsModel; import com.android.quickstep.RecentsModel;
@@ -67,6 +63,7 @@ import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
import com.android.quickstep.SystemUiProxy; import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskUtils; import com.android.quickstep.TaskUtils;
import com.android.quickstep.TouchInteractionService; import com.android.quickstep.TouchInteractionService;
import com.android.quickstep.TouchInteractionService.TISBinder;
import com.android.quickstep.util.RemoteAnimationProvider; import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.RemoteFadeOutAnimationListener; import com.android.quickstep.util.RemoteFadeOutAnimationListener;
import com.android.quickstep.util.SplitSelectStateController; import com.android.quickstep.util.SplitSelectStateController;
@@ -88,8 +85,6 @@ public abstract class BaseQuickstepLauncher extends Launcher
private DepthController mDepthController = new DepthController(this); private DepthController mDepthController = new DepthController(this);
private QuickstepTransitionManager mAppTransitionManager; private QuickstepTransitionManager mAppTransitionManager;
private ServiceConnection mTisBinderConnection;
protected TouchInteractionService.TISBinder mTisBinder;
/** /**
* Reusable command for applying the back button alpha on the background thread. * 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 OverviewActionsView mActionsView;
private @Nullable TaskbarManager mTaskbarManager;
private @Nullable TaskbarController mTaskbarController; 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); private final TaskbarStateHandler mTaskbarStateHandler = new TaskbarStateHandler(this);
// Will be updated when dragging from taskbar. // Will be updated when dragging from taskbar.
private @Nullable DragOptions mNextWorkspaceDragOptions = null; private @Nullable DragOptions mNextWorkspaceDragOptions = null;
private SplitPlaceholderView mSplitPlaceholderView; private SplitPlaceholderView mSplitPlaceholderView;
@@ -111,24 +118,6 @@ public abstract class BaseQuickstepLauncher extends Launcher
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this); SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this);
addMultiWindowModeChangedListener(mDepthController); 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 @Override
@@ -136,15 +125,12 @@ public abstract class BaseQuickstepLauncher extends Launcher
mAppTransitionManager.onActivityDestroyed(); mAppTransitionManager.onActivityDestroyed();
SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this); 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(); super.onDestroy();
} }
@@ -271,37 +257,12 @@ public abstract class BaseQuickstepLauncher extends Launcher
mAppTransitionManager = new QuickstepTransitionManager(this); mAppTransitionManager = new QuickstepTransitionManager(this);
mAppTransitionManager.registerRemoteAnimations(); mAppTransitionManager.registerRemoteAnimations();
addTaskbarIfNecessary(); bindService(new Intent(this, TouchInteractionService.class), mTisBinderConnection, 0);
addOnDeviceProfileChangeListener(newDp -> addTaskbarIfNecessary());
} }
@Override public void setTaskbarController(TaskbarController taskbarController) {
public void onDisplayInfoChanged(Context context, DisplayController.Info info, mTaskbarController = taskbarController;
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 <T extends OverviewActionsView> T getActionsView() { public <T extends OverviewActionsView> T getActionsView() {
@@ -340,14 +301,9 @@ public abstract class BaseQuickstepLauncher extends Launcher
} }
@Override @Override
public boolean isViewInTaskbar(View v) {
return mTaskbarController != null && mTaskbarController.isViewInTaskbar(v);
}
public boolean supportsAdaptiveIconAnimation(View clickedView) { public boolean supportsAdaptiveIconAnimation(View clickedView) {
return mAppTransitionManager.hasControlRemoteAppTransitionPermission() return mAppTransitionManager.hasControlRemoteAppTransitionPermission()
&& FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM.get() && FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM.get();
&& !isViewInTaskbar(clickedView);
} }
@Override @Override
@@ -1252,7 +1252,6 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
final boolean launchingFromWidget = mV instanceof LauncherAppWidgetHostView; final boolean launchingFromWidget = mV instanceof LauncherAppWidgetHostView;
final boolean launchingFromRecents = isLaunchingFromRecents(mV, appTargets); final boolean launchingFromRecents = isLaunchingFromRecents(mV, appTargets);
final boolean launchingFromTaskbar = mLauncher.isViewInTaskbar(mV);
if (launchingFromWidget) { if (launchingFromWidget) {
composeWidgetLaunchAnimator(anim, (LauncherAppWidgetHostView) mV, appTargets, composeWidgetLaunchAnimator(anim, (LauncherAppWidgetHostView) mV, appTargets,
wallpaperTargets, nonAppTargets); wallpaperTargets, nonAppTargets);
@@ -1263,8 +1262,6 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
launcherClosing); launcherClosing);
addCujInstrumentation( addCujInstrumentation(
anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_RECENTS); anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_RECENTS);
} else if (launchingFromTaskbar) {
// TODO
} else { } else {
composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets, composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,
launcherClosing); launcherClosing);
@@ -16,12 +16,17 @@
package com.android.launcher3.taskbar; 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.annotation.DrawableRes;
import android.content.Context;
import android.view.View; import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton;
/** /**
* Creates Buttons for Taskbar for 3 button nav. * Creates Buttons for Taskbar for 3 button nav.
@@ -29,47 +34,46 @@ import com.android.launcher3.R;
*/ */
public class ButtonProvider { public class ButtonProvider {
private int mMarginLeftRight; private final int mMarginLeftRight;
private final Context mContext; private final TaskbarActivityContext mContext;
public ButtonProvider(Context context) { public ButtonProvider(TaskbarActivityContext context) {
mContext = context; mContext = context;
} mMarginLeftRight = context.getResources()
.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
public void setMarginLeftRight(int margin) {
mMarginLeftRight = margin;
} }
public View getBack() { public View getBack() {
// Back button // Back button
return getButtonForDrawable(R.drawable.ic_sysbar_back); return getButtonForDrawable(R.drawable.ic_sysbar_back, BUTTON_BACK);
} }
public View getDown() { public View getDown() {
// Ime down button // Ime down button
return getButtonForDrawable(R.drawable.ic_sysbar_back); return getButtonForDrawable(R.drawable.ic_sysbar_back, BUTTON_BACK);
} }
public View getHome() { public View getHome() {
// Home button // Home button
return getButtonForDrawable(R.drawable.ic_sysbar_home); return getButtonForDrawable(R.drawable.ic_sysbar_home, BUTTON_HOME);
} }
public View getRecents() { public View getRecents() {
// Recents button // Recents button
return getButtonForDrawable(R.drawable.ic_sysbar_recent); return getButtonForDrawable(R.drawable.ic_sysbar_recent, BUTTON_RECENTS);
} }
public View getImeSwitcher() { public View getImeSwitcher() {
// IME Switcher Button // 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); ImageView buttonView = new ImageView(mContext);
buttonView.setImageResource(drawableId); buttonView.setImageResource(drawableId);
buttonView.setBackgroundResource(R.drawable.taskbar_icon_click_feedback_roundrect); buttonView.setBackgroundResource(R.drawable.taskbar_icon_click_feedback_roundrect);
buttonView.setPadding(mMarginLeftRight, 0, mMarginLeftRight, 0); buttonView.setPadding(mMarginLeftRight, 0, mMarginLeftRight, 0);
buttonView.setOnClickListener(view -> mContext.onNavigationButtonClick(buttonType));
return buttonView; return buttonView;
} }
@@ -16,9 +16,6 @@
package com.android.launcher3.taskbar; 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.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View; import android.view.View;
@@ -29,7 +26,6 @@ import com.android.launcher3.views.ActivityContext;
public class ImeBarView extends RelativeLayout { public class ImeBarView extends RelativeLayout {
private ButtonProvider mButtonProvider; private ButtonProvider mButtonProvider;
private TaskbarController.TaskbarViewCallbacks mControllerCallbacks;
private View mImeView; private View mImeView;
public ImeBarView(Context context) { public ImeBarView(Context context) {
@@ -44,12 +40,9 @@ public class ImeBarView extends RelativeLayout {
super(context, attrs, defStyleAttr); super(context, attrs, defStyleAttr);
} }
public void construct(ButtonProvider buttonProvider) { public void init(ButtonProvider buttonProvider) {
mButtonProvider = buttonProvider; mButtonProvider = buttonProvider;
}
public void init(TaskbarController.TaskbarViewCallbacks taskbarCallbacks) {
mControllerCallbacks = taskbarCallbacks;
ActivityContext context = getActivityContext(); ActivityContext context = getActivityContext();
RelativeLayout.LayoutParams imeParams = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams imeParams = new RelativeLayout.LayoutParams(
context.getDeviceProfile().iconSizePx, context.getDeviceProfile().iconSizePx,
@@ -64,24 +57,16 @@ public class ImeBarView extends RelativeLayout {
// Down Arrow // Down Arrow
View downView = mButtonProvider.getDown(); View downView = mButtonProvider.getDown();
downView.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
BUTTON_BACK));
downView.setLayoutParams(downParams); downView.setLayoutParams(downParams);
downView.setRotation(-90); downView.setRotation(-90);
addView(downView); addView(downView);
// IME switcher button // IME switcher button
mImeView = mButtonProvider.getImeSwitcher(); mImeView = mButtonProvider.getImeSwitcher();
mImeView.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
BUTTON_IME_SWITCH));
mImeView.setLayoutParams(imeParams); mImeView.setLayoutParams(imeParams);
addView(mImeView); addView(mImeView);
} }
public void cleanup() {
removeAllViews();
}
public void setImeSwitcherVisibility(boolean show) { public void setImeSwitcherVisibility(boolean show) {
mImeView.setVisibility(show ? VISIBLE : GONE); mImeView.setVisibility(show ? VISIBLE : GONE);
} }
@@ -15,56 +15,164 @@
*/ */
package com.android.launcher3.taskbar; 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.Point;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.drawable.Drawable; 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.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.WindowManager;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DragSource; import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget; import com.android.launcher3.DropTarget;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.dragndrop.DraggableView; 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.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.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 * 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 * 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. * 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 DeviceProfile mDeviceProfile;
private final LayoutInflater mLayoutInflater; private final LayoutInflater mLayoutInflater;
private final TaskbarContainerView mTaskbarContainerView; private final TaskbarContainerView mTaskbarContainerView;
private final TaskbarIconController mIconController;
private final MyDragController mDragController; private final MyDragController mDragController;
public TaskbarActivityContext(BaseQuickstepLauncher launcher) { private final WindowManager mWindowManager;
super(launcher); private WindowManager.LayoutParams mWindowLayoutParams;
mDeviceProfile = launcher.getDeviceProfile().copy(this);
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 taskbarIconSize = getResources().getDimension(R.dimen.taskbar_icon_size);
float iconScale = taskbarIconSize / mDeviceProfile.iconSizePx; float iconScale = taskbarIconSize / mDeviceProfile.iconSizePx;
mDeviceProfile.updateIconSize(iconScale, getResources()); mDeviceProfile.updateIconSize(iconScale, getResources());
mLayoutInflater = LayoutInflater.from(this).cloneInContext(this); mLayoutInflater = LayoutInflater.from(this).cloneInContext(this);
mTaskbarContainerView = (TaskbarContainerView) mLayoutInflater mTaskbarContainerView = (TaskbarContainerView) mLayoutInflater
.inflate(R.layout.taskbar, null, false); .inflate(R.layout.taskbar, null, false);
mIconController = new TaskbarIconController(this, mTaskbarContainerView);
mDragController = new MyDragController(this); 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() { public void init() {
return mTaskbarContainerView; 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 @Override
@@ -73,7 +181,7 @@ public class TaskbarActivityContext extends ContextWrapper implements ActivityCo
} }
@Override @Override
public BaseDragLayer<TaskbarActivityContext> getDragLayer() { public TaskbarContainerView getDragLayer() {
return mTaskbarContainerView; return mTaskbarContainerView;
} }
@@ -92,6 +200,103 @@ public class TaskbarActivityContext extends ContextWrapper implements ActivityCo
return mDragController; 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<TaskbarActivityContext> { private static class MyDragController extends DragController<TaskbarActivityContext> {
MyDragController(TaskbarActivityContext activity) { MyDragController(TaskbarActivityContext activity) {
super(activity); super(activity);
@@ -106,7 +311,8 @@ public class TaskbarActivityContext extends ContextWrapper implements ActivityCo
} }
@Override @Override
protected void exitDrag() { } protected void exitDrag() {
}
@Override @Override
protected DropTarget getDefaultDropTarget(int[] dropCoordinates) { protected DropTarget getDefaultDropTarget(int[] dropCoordinates) {
@@ -21,6 +21,7 @@ import android.animation.Animator;
import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
import com.android.launcher3.taskbar.TaskbarController.TaskbarAnimationControllerCallbacks;
import com.android.quickstep.AnimatedFloat; import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.SystemUiProxy; import com.android.quickstep.SystemUiProxy;
import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.QuickStepContract;
@@ -34,7 +35,7 @@ public class TaskbarAnimationController {
private static final long IME_VISIBILITY_ALPHA_DURATION = 120; private static final long IME_VISIBILITY_ALPHA_DURATION = 120;
private final BaseQuickstepLauncher mLauncher; private final BaseQuickstepLauncher mLauncher;
private final TaskbarController.TaskbarAnimationControllerCallbacks mTaskbarCallbacks; private final TaskbarAnimationControllerCallbacks mTaskbarCallbacks;
// Background alpha. // Background alpha.
private final AnimatedFloat mTaskbarBackgroundAlpha = new AnimatedFloat( private final AnimatedFloat mTaskbarBackgroundAlpha = new AnimatedFloat(
@@ -55,7 +56,7 @@ public class TaskbarAnimationController {
this::updateTranslationY); this::updateTranslationY);
public TaskbarAnimationController(BaseQuickstepLauncher launcher, public TaskbarAnimationController(BaseQuickstepLauncher launcher,
TaskbarController.TaskbarAnimationControllerCallbacks taskbarCallbacks) { TaskbarAnimationControllerCallbacks taskbarCallbacks) {
mLauncher = launcher; mLauncher = launcher;
mTaskbarCallbacks = taskbarCallbacks; mTaskbarCallbacks = taskbarCallbacks;
} }
@@ -15,9 +15,6 @@
*/ */
package com.android.launcher3.taskbar; 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.content.Context;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
@@ -32,22 +29,21 @@ import com.android.launcher3.R;
import com.android.launcher3.util.TouchController; import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer; import com.android.launcher3.views.BaseDragLayer;
import com.android.systemui.shared.system.ViewTreeObserverWrapper; 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. * Top-level ViewGroup that hosts the TaskbarView as well as Views created by it such as Folder.
*/ */
public class TaskbarContainerView extends BaseDragLayer<TaskbarActivityContext> { public class TaskbarContainerView extends BaseDragLayer<TaskbarActivityContext> {
private final int[] mTempLoc = new int[2];
private final int mFolderMargin; private final int mFolderMargin;
private final Paint mTaskbarBackgroundPaint; private final Paint mTaskbarBackgroundPaint;
// Initialized in TaskbarController constructor. private TaskbarIconController.Callbacks mControllerCallbacks;
private TaskbarController.TaskbarContainerViewCallbacks mControllerCallbacks;
// Initialized in init.
private TaskbarView mTaskbarView; private TaskbarView mTaskbarView;
private ViewTreeObserverWrapper.OnComputeInsetsListener mTaskbarInsetsComputer;
private final OnComputeInsetsListener mTaskbarInsetsComputer = this::onComputeTaskbarInsets;
public TaskbarContainerView(@NonNull Context context) { public TaskbarContainerView(@NonNull Context context) {
this(context, null); this(context, null);
@@ -68,15 +64,6 @@ public class TaskbarContainerView extends BaseDragLayer<TaskbarActivityContext>
mFolderMargin = getResources().getDimensionPixelSize(R.dimen.taskbar_folder_margin); mFolderMargin = getResources().getDimensionPixelSize(R.dimen.taskbar_folder_margin);
mTaskbarBackgroundPaint = new Paint(); mTaskbarBackgroundPaint = new Paint();
mTaskbarBackgroundPaint.setColor(getResources().getColor(R.color.taskbar_background)); 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(); recreateControllers();
} }
@@ -85,46 +72,24 @@ public class TaskbarContainerView extends BaseDragLayer<TaskbarActivityContext>
mControllers = new TouchController[0]; mControllers = new TouchController[0];
} }
private ViewTreeObserverWrapper.OnComputeInsetsListener createTaskbarInsetsComputer() { public void init(TaskbarIconController.Callbacks callbacks, TaskbarView taskbarView) {
return insetsInfo -> { mControllerCallbacks = callbacks;
if (mControllerCallbacks.isTaskbarTouchable()) { mTaskbarView = taskbarView;
// 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());
};
} }
protected void cleanup() { private void onComputeTaskbarInsets(InsetsInfo insetsInfo) {
if (mControllerCallbacks != null) {
mControllerCallbacks.updateInsetsTouchability(insetsInfo);
}
}
protected void onDestroy() {
ViewTreeObserverWrapper.removeOnComputeInsetsListener(mTaskbarInsetsComputer); ViewTreeObserverWrapper.removeOnComputeInsetsListener(mTaskbarInsetsComputer);
} }
@Override @Override
protected void onAttachedToWindow() { protected void onAttachedToWindow() {
super.onAttachedToWindow(); super.onAttachedToWindow();
ViewTreeObserverWrapper.addOnComputeInsetsListener(getViewTreeObserver(), ViewTreeObserverWrapper.addOnComputeInsetsListener(getViewTreeObserver(),
mTaskbarInsetsComputer); mTaskbarInsetsComputer);
} }
@@ -133,7 +98,7 @@ public class TaskbarContainerView extends BaseDragLayer<TaskbarActivityContext>
protected void onDetachedFromWindow() { protected void onDetachedFromWindow() {
super.onDetachedFromWindow(); super.onDetachedFromWindow();
cleanup(); onDestroy();
} }
@Override @Override
@@ -143,10 +108,25 @@ public class TaskbarContainerView extends BaseDragLayer<TaskbarActivityContext>
return true; return true;
} }
public void updateImeBarVisibilityAlpha(float alpha) {
if (mControllerCallbacks != null) {
mControllerCallbacks.updateImeBarVisibilityAlpha(alpha);
}
}
@Override @Override
public void onViewRemoved(View child) { public void onViewRemoved(View child) {
super.onViewRemoved(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<TaskbarActivityContext>
return boundingBox; 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. * Sets the alpha of the background color behind all the Taskbar contents.
@@ -15,105 +15,83 @@
*/ */
package com.android.launcher3.taskbar; 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.Animator;
import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorListenerAdapter;
import android.app.ActivityOptions;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect; import android.graphics.Rect;
import android.inputmethodservice.InputMethodService; import android.graphics.RectF;
import android.view.Gravity;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherState;
import com.android.launcher3.QuickstepTransitionManager; import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.R; 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.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.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"; public class TaskbarController extends TaskbarUIController {
private final TaskbarContainerView mTaskbarContainerView;
private final TaskbarView mTaskbarViewInApp;
private final TaskbarView mTaskbarViewOnHome;
private final ImeBarView mImeBarView;
private final BaseQuickstepLauncher mLauncher; 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 TaskbarStateHandler mTaskbarStateHandler;
private final TaskbarAnimationController mTaskbarAnimationController; private final TaskbarAnimationController mTaskbarAnimationController;
private final TaskbarHotseatController mHotseatController; private final TaskbarHotseatController mHotseatController;
private final TaskbarDragController mDragController;
private final TaskbarNavButtonController mNavButtonController;
// Initialized in init(). private final TaskbarActivityContext mContext;
private WindowManager.LayoutParams mWindowLayoutParams; final TaskbarContainerView mTaskbarContainerView;
private SysUINavigationMode.Mode mNavMode = SysUINavigationMode.Mode.NO_BUTTON; final TaskbarView mTaskbarView;
private final SysUINavigationMode.NavigationModeChangeListener mNavigationModeChangeListener =
this::onNavModeChanged;
private @Nullable Animator mAnimator; private @Nullable Animator mAnimator;
private boolean mIsAnimatingToLauncher; private boolean mIsAnimatingToLauncher;
public TaskbarController(BaseQuickstepLauncher launcher, public TaskbarController(BaseQuickstepLauncher launcher, TaskbarActivityContext context) {
TaskbarContainerView taskbarContainerView, TaskbarView taskbarViewOnHome) { mContext = context;
mTaskbarContainerView = context.getDragLayer();
mTaskbarView = mTaskbarContainerView.findViewById(R.id.taskbar_view);
mLauncher = launcher; 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(); mTaskbarStateHandler = mLauncher.getTaskbarStateHandler();
mTaskbarAnimationController = new TaskbarAnimationController(mLauncher, mTaskbarAnimationController = new TaskbarAnimationController(mLauncher,
createTaskbarAnimationControllerCallbacks()); createTaskbarAnimationControllerCallbacks());
mHotseatController = new TaskbarHotseatController(mLauncher, mHotseatController = new TaskbarHotseatController(
createTaskbarHotseatControllerCallbacks()); mLauncher, mTaskbarView::updateHotseatItems);
mDragController = new TaskbarDragController(mLauncher); }
@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() { private TaskbarAnimationControllerCallbacks createTaskbarAnimationControllerCallbacks() {
@@ -125,245 +103,34 @@ public class TaskbarController implements TaskbarOverviewProxyDelegate {
@Override @Override
public void updateTaskbarVisibilityAlpha(float alpha) { public void updateTaskbarVisibilityAlpha(float alpha) {
mTaskbarViewInApp.setAlpha(alpha); mTaskbarView.setAlpha(alpha);
mTaskbarViewOnHome.setAlpha(alpha);
} }
@Override @Override
public void updateImeBarVisibilityAlpha(float alpha) { public void updateImeBarVisibilityAlpha(float alpha) {
if (mNavMode != SysUINavigationMode.Mode.THREE_BUTTONS) { mTaskbarContainerView.updateImeBarVisibilityAlpha(alpha);
// TODO Remove sysui IME bar for gesture nav as well
return;
}
mImeBarView.setAlpha(alpha);
mImeBarView.setVisibility(alpha == 0 ? GONE : VISIBLE);
} }
@Override @Override
public void updateTaskbarScale(float scale) { public void updateTaskbarScale(float scale) {
mTaskbarViewInApp.setScaleX(scale); mTaskbarView.setScaleX(scale);
mTaskbarViewInApp.setScaleY(scale); mTaskbarView.setScaleY(scale);
} }
@Override @Override
public void updateTaskbarTranslationY(float translationY) { public void updateTaskbarTranslationY(float translationY) {
if (translationY < 0) { if (translationY < 0) {
// Resize to accommodate the max translation we'll reach. // Resize to accommodate the max translation we'll reach.
setTaskbarWindowHeight(mTaskbarSize.y mContext.setTaskbarWindowHeight(mContext.getDeviceProfile().taskbarSize
+ mLauncher.getHotseat().getTaskbarOffsetY()); + mLauncher.getHotseat().getTaskbarOffsetY());
} else { } 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. * Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
*/ */
@@ -401,13 +168,14 @@ public class TaskbarController implements TaskbarOverviewProxyDelegate {
@Override @Override
public void onAnimationStart(Animator animation) { public void onAnimationStart(Animator animation) {
mIsAnimatingToLauncher = true; mIsAnimatingToLauncher = true;
mTaskbarViewInApp.updateHotseatItemsVisibility(); mTaskbarView.setHolesAllowedInLayout(true);
mTaskbarView.updateHotseatItemsVisibility();
} }
@Override @Override
public void onAnimationEnd(Animator animation) { public void onAnimationEnd(Animator animation) {
mIsAnimatingToLauncher = false; mIsAnimatingToLauncher = false;
setWhichTaskbarViewIsVisible(mTaskbarViewOnHome); setTaskbarViewVisible(false);
} }
}); });
@@ -420,44 +188,21 @@ public class TaskbarController implements TaskbarOverviewProxyDelegate {
anim.addListener(new AnimatorListenerAdapter() { anim.addListener(new AnimatorListenerAdapter() {
@Override @Override
public void onAnimationStart(Animator animation) { public void onAnimationStart(Animator animation) {
mTaskbarViewInApp.updateHotseatItemsVisibility(); mTaskbarView.updateHotseatItemsVisibility();
setWhichTaskbarViewIsVisible(mTaskbarViewInApp); setTaskbarViewVisible(true);
} }
@Override @Override
public void onAnimationEnd(Animator animation) { public void onAnimationEnd(Animator animation) {
mTaskbarView.setHolesAllowedInLayout(false);
} }
}); });
return anim.buildAnim(); return anim.buildAnim();
} }
/** @Override
* Should be called when the IME visibility changes, so we can hide/show Taskbar accordingly. protected void onImeVisible(TaskbarContainerView containerView, boolean isVisible) {
*/ mTaskbarAnimationController.animateToVisibilityForIme(isVisible ? 0 : 1);
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);
} }
/** /**
@@ -472,24 +217,17 @@ public class TaskbarController implements TaskbarOverviewProxyDelegate {
* @return Whether any Taskbar item could handle the given MotionEvent if given the chance. * @return Whether any Taskbar item could handle the given MotionEvent if given the chance.
*/ */
public boolean isEventOverAnyTaskbarItem(MotionEvent ev) { public boolean isEventOverAnyTaskbarItem(MotionEvent ev) {
return mTaskbarViewInApp.isEventOverAnyItem(ev); return mTaskbarView.isEventOverAnyItem(ev);
} }
public boolean isDraggingItem() { public boolean isDraggingItem() {
return mTaskbarViewInApp.isDraggingItem() || mTaskbarViewOnHome.isDraggingItem(); return mTaskbarView.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());
} }
/** /**
* Pads the Hotseat to line up exactly with Taskbar's copy of the Hotseat. * Pads the Hotseat to line up exactly with Taskbar's copy of the Hotseat.
*/ */
@Override
public void alignRealHotseatWithTaskbar() { public void alignRealHotseatWithTaskbar() {
Rect hotseatBounds = new Rect(); Rect hotseatBounds = new Rect();
DeviceProfile grid = mLauncher.getDeviceProfile(); DeviceProfile grid = mLauncher.getDeviceProfile();
@@ -498,60 +236,28 @@ public class TaskbarController implements TaskbarOverviewProxyDelegate {
int hotseatTopDiff = hotseatHeight - grid.taskbarSize - taskbarOffset; int hotseatTopDiff = hotseatHeight - grid.taskbarSize - taskbarOffset;
int hotseatBottomDiff = 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, mLauncher.getHotseat().setPadding(hotseatBounds.left,
hotseatBounds.top + hotseatTopDiff, hotseatBounds.top + hotseatTopDiff,
mTaskbarViewOnHome.getWidth() - hotseatBounds.right, mTaskbarView.getWidth() - hotseatBounds.right,
mTaskbarViewOnHome.getHeight() - hotseatBounds.bottom + hotseatBottomDiff); mTaskbarView.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);
} }
/** /**
* Returns the ratio of the taskbar icon size on home vs in an app. * Returns the ratio of the taskbar icon size on home vs in an app.
*/ */
public float getTaskbarScaleOnHome() { public float getTaskbarScaleOnHome() {
DeviceProfile inAppDp = mTaskbarContainerView.getTaskbarActivityContext() DeviceProfile inAppDp = mContext.getDeviceProfile();
.getDeviceProfile(); DeviceProfile onHomeDp = mLauncher.getDeviceProfile();
DeviceProfile onHomeDp = ActivityContext.lookupContext(mTaskbarViewOnHome.getContext())
.getDeviceProfile();
return (float) onHomeDp.cellWidthPx / inAppDp.cellWidthPx; return (float) onHomeDp.cellWidthPx / inAppDp.cellWidthPx;
} }
/** void setTaskbarViewVisible(boolean isVisible) {
* Updates the TaskbarContainer to MATCH_PARENT vs original Taskbar size. mTaskbarView.setIconsVisibility(isVisible);
*/ mLauncher.getHotseat().setIconsAlpha(isVisible ? 0f : 1f);
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();
} }
/** /**
@@ -565,32 +271,4 @@ public class TaskbarController implements TaskbarOverviewProxyDelegate {
void updateTaskbarScale(float scale); void updateTaskbarScale(float scale);
void updateTaskbarTranslationY(float translationY); 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);
}
} }
@@ -20,6 +20,7 @@ import static android.view.View.VISIBLE;
import android.content.ClipData; import android.content.ClipData;
import android.content.ClipDescription; import android.content.ClipDescription;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.LauncherApps; import android.content.pm.LauncherApps;
import android.content.res.Resources; import android.content.res.Resources;
@@ -29,7 +30,6 @@ import android.os.UserHandle;
import android.view.DragEvent; import android.view.DragEvent;
import android.view.View; import android.view.View;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.BubbleTextView; import com.android.launcher3.BubbleTextView;
import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R; import com.android.launcher3.R;
@@ -43,12 +43,12 @@ import com.android.systemui.shared.system.LauncherAppsCompat;
*/ */
public class TaskbarDragController { public class TaskbarDragController {
private final BaseQuickstepLauncher mLauncher; private final Context mContext;
private final int mDragIconSize; private final int mDragIconSize;
public TaskbarDragController(BaseQuickstepLauncher launcher) { public TaskbarDragController(Context context) {
mLauncher = launcher; mContext = context;
Resources resources = mLauncher.getResources(); Resources resources = mContext.getResources();
mDragIconSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_drag_icon_size); mDragIconSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_drag_icon_size);
} }
@@ -63,7 +63,6 @@ public class TaskbarDragController {
} }
BubbleTextView btv = (BubbleTextView) view; BubbleTextView btv = (BubbleTextView) view;
View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view) { View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view) {
@Override @Override
public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) { public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
@@ -87,7 +86,7 @@ public class TaskbarDragController {
Intent intent = null; Intent intent = null;
if (tag instanceof WorkspaceItemInfo) { if (tag instanceof WorkspaceItemInfo) {
WorkspaceItemInfo item = (WorkspaceItemInfo) tag; WorkspaceItemInfo item = (WorkspaceItemInfo) tag;
LauncherApps launcherApps = mLauncher.getSystemService(LauncherApps.class); LauncherApps launcherApps = mContext.getSystemService(LauncherApps.class);
clipDescription = new ClipDescription(item.title, clipDescription = new ClipDescription(item.title,
new String[] { new String[] {
item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
@@ -26,6 +26,8 @@ import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfo;
import java.util.function.Consumer;
/** /**
* Works with TaskbarController to update the TaskbarView's Hotseat items. * Works with TaskbarController to update the TaskbarView's Hotseat items.
*/ */
@@ -33,13 +35,12 @@ public class TaskbarHotseatController {
private final BaseQuickstepLauncher mLauncher; private final BaseQuickstepLauncher mLauncher;
private final Hotseat mHotseat; private final Hotseat mHotseat;
private final TaskbarController.TaskbarHotseatControllerCallbacks mTaskbarCallbacks; private final Consumer<ItemInfo[]> mTaskbarCallbacks;
private final int mNumHotseatIcons; private final int mNumHotseatIcons;
private final DragController.DragListener mDragListener = new DragController.DragListener() { private final DragController.DragListener mDragListener = new DragController.DragListener() {
@Override @Override
public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { }
}
@Override @Override
public void onDragEnd() { public void onDragEnd() {
@@ -47,8 +48,8 @@ public class TaskbarHotseatController {
} }
}; };
public TaskbarHotseatController(BaseQuickstepLauncher launcher, public TaskbarHotseatController(
TaskbarController.TaskbarHotseatControllerCallbacks taskbarCallbacks) { BaseQuickstepLauncher launcher, Consumer<ItemInfo[]> taskbarCallbacks) {
mLauncher = launcher; mLauncher = launcher;
mHotseat = mLauncher.getHotseat(); mHotseat = mLauncher.getHotseat();
mTaskbarCallbacks = taskbarCallbacks; mTaskbarCallbacks = taskbarCallbacks;
@@ -85,10 +86,6 @@ public class TaskbarHotseatController {
} }
} }
mTaskbarCallbacks.updateHotseatItems(hotseatItemInfos); mTaskbarCallbacks.accept(hotseatItemInfos);
}
protected int getNumHotseatIcons() {
return mNumHotseatIcons;
} }
} }
@@ -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);
}
}
}
@@ -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);
}
}
@@ -16,7 +16,8 @@
package com.android.launcher3.taskbar; package com.android.launcher3.taskbar;
import android.content.Context; import static android.view.Display.DEFAULT_DISPLAY;
import android.content.Intent; import android.content.Intent;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
@@ -53,11 +54,10 @@ public class TaskbarNavButtonController {
static final int BUTTON_RECENTS = BUTTON_HOME << 1; static final int BUTTON_RECENTS = BUTTON_HOME << 1;
static final int BUTTON_IME_SWITCH = BUTTON_RECENTS << 1; static final int BUTTON_IME_SWITCH = BUTTON_RECENTS << 1;
private final TouchInteractionService mService;
private final Context mContext; public TaskbarNavButtonController(TouchInteractionService service) {
mService = service;
public TaskbarNavButtonController(Context context) {
mContext = context;
} }
public void onButtonClick(@TaskbarButton int buttonType) { public void onButtonClick(@TaskbarButton int buttonType) {
@@ -78,13 +78,13 @@ public class TaskbarNavButtonController {
} }
private void navigateHome() { private void navigateHome() {
mContext.startActivity(new Intent(Intent.ACTION_MAIN) mService.startActivity(new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME) .addCategory(Intent.CATEGORY_HOME)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
} }
private void navigateToOverview() { private void navigateToOverview() {
TouchInteractionService.getInstance().getOverviewCommandHelper() mService.getOverviewCommandHelper()
.addCommand(OverviewCommandHelper.TYPE_SHOW); .addCommand(OverviewCommandHelper.TYPE_SHOW);
} }
@@ -93,8 +93,8 @@ public class TaskbarNavButtonController {
} }
private void showIMESwitcher() { private void showIMESwitcher() {
mContext.getSystemService(InputMethodManager.class).showInputMethodPickerFromSystem( mService.getSystemService(InputMethodManager.class)
true /* showAuxiliarySubtypes */, mContext.getDisplayId()); .showInputMethodPickerFromSystem(true /* showAuxiliarySubtypes */,
DEFAULT_DISPLAY);
} }
} }
@@ -24,59 +24,52 @@ import androidx.annotation.Nullable;
import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.states.StateAnimationConfig;
import com.android.quickstep.AnimatedFloat; import com.android.quickstep.AnimatedFloat;
/** /**
* StateHandler to animate Taskbar according to Launcher's state machine. Does nothing if Taskbar * 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<LauncherState> { public class TaskbarStateHandler implements StateManager.StateHandler<LauncherState> {
private final BaseQuickstepLauncher mLauncher; private final BaseQuickstepLauncher mLauncher;
// Contains Taskbar-related methods and fields we should aniamte. If null, don't do anything. // 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) { public TaskbarStateHandler(BaseQuickstepLauncher launcher) {
mLauncher = launcher; mLauncher = launcher;
} }
public void setTaskbarCallbacks(TaskbarController.TaskbarStateHandlerCallbacks callbacks) { public void setAnimationController(TaskbarAnimationController callbacks) {
mTaskbarCallbacks = callbacks; mAnimationController = callbacks;
} }
@Override @Override
public void setState(LauncherState state) { public void setState(LauncherState state) {
if (mTaskbarCallbacks == null) { setState(state, PropertySetter.NO_ANIM_PROPERTY_SETTER);
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));
} }
@Override @Override
public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config, public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
PendingAnimation animation) { PendingAnimation animation) {
if (mTaskbarCallbacks == null) { setState(toState, animation);
}
private void setState(LauncherState toState, PropertySetter setter) {
if (mAnimationController == null) {
return; return;
} }
AnimatedFloat alphaTarget = mTaskbarCallbacks.getAlphaTarget();
AnimatedFloat scaleTarget = mTaskbarCallbacks.getScaleTarget();
AnimatedFloat translationYTarget = mTaskbarCallbacks.getTranslationYTarget();
boolean isTaskbarVisible = (toState.getVisibleElements(mLauncher) & TASKBAR) != 0; boolean isTaskbarVisible = (toState.getVisibleElements(mLauncher) & TASKBAR) != 0;
animation.setFloat(alphaTarget, AnimatedFloat.VALUE, isTaskbarVisible ? 1f : 0f, LINEAR); setter.setFloat(mAnimationController.getTaskbarVisibilityForLauncherState(),
animation.setFloat(scaleTarget, AnimatedFloat.VALUE, toState.getTaskbarScale(mLauncher), AnimatedFloat.VALUE, isTaskbarVisible ? 1f : 0f, LINEAR);
LINEAR); setter.setFloat(mAnimationController.getTaskbarScaleForLauncherState(),
animation.setFloat(translationYTarget, AnimatedFloat.VALUE, AnimatedFloat.VALUE, toState.getTaskbarScale(mLauncher), LINEAR);
toState.getTaskbarTranslationY(mLauncher), ACCEL_DEACCEL); setter.setFloat(mAnimationController.getTaskbarTranslationYForLauncherState(),
AnimatedFloat.VALUE, toState.getTaskbarTranslationY(mLauncher), ACCEL_DEACCEL);
} }
} }
@@ -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);
}
}
@@ -15,20 +15,14 @@
*/ */
package com.android.launcher3.taskbar; package com.android.launcher3.taskbar;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK; import static android.view.View.MeasureSpec.EXACTLY;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME; import static android.view.View.MeasureSpec.makeMeasureSpec;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.LayoutTransition;
import android.animation.ValueAnimator;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.RectF; import android.graphics.RectF;
import android.os.SystemProperties;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.DragEvent; import android.view.DragEvent;
import android.view.Gravity; 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.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.views.ActivityContext; 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. * 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 { 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 int mIconTouchSize;
private final boolean mIsRtl; private final boolean mIsRtl;
private final int mTouchSlop; private final int mTouchSlop;
@@ -69,17 +58,16 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
private final RectF mDelegateSlopBounds = new RectF(); private final RectF mDelegateSlopBounds = new RectF();
private final int[] mTempOutLocation = new int[2]; private final int[] mTempOutLocation = new int[2];
// Initialized in TaskbarController constructor. private final int mItemMarginLeftRight;
private TaskbarController.TaskbarViewCallbacks mControllerCallbacks;
// Scale on elements that aren't icons.
private float mNonIconScale;
private int mItemMarginLeftRight;
// Initialized in init(). private final TaskbarActivityContext mActivityContext;
private LayoutTransition mLayoutTransition;
private int mHotseatStartIndex; // Initialized in TaskbarController constructor.
private int mHotseatEndIndex; private View.OnClickListener mIconClickListener;
private LinearLayout mButtonRegion; private View.OnLongClickListener mIconLongClickListener;
LinearLayout mSystemButtonContainer;
LinearLayout mHotseatIconsContainer;
// Delegate touches to the closest view if within mIconTouchSize. // Delegate touches to the closest view if within mIconTouchSize.
private boolean mDelegateTargeted; private boolean mDelegateTargeted;
@@ -91,10 +79,12 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
// Only non-null when the corresponding Folder is open. // Only non-null when the corresponding Folder is open.
private @Nullable FolderIcon mLeaveBehindFolderIcon; private @Nullable FolderIcon mLeaveBehindFolderIcon;
private int mNavButtonStartIndex;
/** Provider of buttons added to taskbar in 3 button nav */ /** Provider of buttons added to taskbar in 3 button nav */
private ButtonProvider mButtonProvider; private ButtonProvider mButtonProvider;
private boolean mDisableRelayout;
private boolean mAreHolesAllowed;
public TaskbarView(@NonNull Context context) { public TaskbarView(@NonNull Context context) {
this(context, null); 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, public TaskbarView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
int defStyleRes) { int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes); super(context, attrs, defStyleAttr, defStyleRes);
mActivityContext = ActivityContext.lookupContext(context);
Resources resources = getResources(); Resources resources = getResources();
mIconTouchSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_touch_size); mIconTouchSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_touch_size);
mItemMarginLeftRight = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
mIsRtl = Utilities.isRtl(resources); mIsRtl = Utilities.isRtl(resources);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
} }
protected void construct(TaskbarController.TaskbarViewCallbacks taskbarViewCallbacks, @Override
ButtonProvider buttonProvider) { protected void onFinishInflate() {
mControllerCallbacks = taskbarViewCallbacks; super.onFinishInflate();
mNonIconScale = mControllerCallbacks.getNonIconScale(this); mSystemButtonContainer = findViewById(R.id.system_button_layout);
mItemMarginLeftRight = getResources().getDimensionPixelSize(R.dimen.taskbar_icon_spacing); mHotseatIconsContainer = findViewById(R.id.hotseat_icons_layout);
mItemMarginLeftRight = Math.round(mItemMarginLeftRight * mNonIconScale);
mButtonProvider = buttonProvider;
mButtonProvider.setMarginLeftRight(mItemMarginLeftRight);
} }
protected void init(int numHotseatIcons, SysUINavigationMode.Mode newMode) { protected void construct(OnClickListener clickListener, OnLongClickListener longClickListener,
// TODO: check if buttons on left ButtonProvider buttonProvider) {
if (newMode == SysUINavigationMode.Mode.THREE_BUTTONS && ENABLE_THREE_BUTTON_TASKBAR) { mIconClickListener = clickListener;
// 3 button mIconLongClickListener = longClickListener;
mNavButtonStartIndex = 0; mButtonProvider = buttonProvider;
if (mActivityContext.canShowNavButtons()) {
createNavButtons(); createNavButtons();
} else { } else {
mNavButtonStartIndex = -1; mSystemButtonContainer.setVisibility(GONE);
removeNavButtons();
} }
mHotseatStartIndex = mNavButtonStartIndex + 1; int numHotseatIcons = mActivityContext.getDeviceProfile().numShownHotseatIcons;
mHotseatEndIndex = mHotseatStartIndex + numHotseatIcons - 1;
updateHotseatItems(new ItemInfo[numHotseatIcons]); 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); * Enables/disables empty icons in taskbar so that the layout matches with Launcher
addUpdateListenerForLayoutTransition(LayoutTransition.CHANGE_DISAPPEARING, onUpdate); */
addUpdateListenerForLayoutTransition(LayoutTransition.CHANGING, onUpdate); public void setHolesAllowedInLayout(boolean areHolesAllowed) {
addUpdateListenerForLayoutTransition(LayoutTransition.APPEARING, onUpdate); if (mAreHolesAllowed != areHolesAllowed) {
addUpdateListenerForLayoutTransition(LayoutTransition.DISAPPEARING, onUpdate); mAreHolesAllowed = areHolesAllowed;
} updateHotseatItemsVisibility();
// TODO: Add animation
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);
} }
} }
protected void cleanup() { private void setHolesAllowedInLayoutNoAnimation(boolean areHolesAllowed) {
endAllLayoutTransitionAnimators(); if (mAreHolesAllowed != areHolesAllowed) {
setLayoutTransition(null); mAreHolesAllowed = areHolesAllowed;
removeAllViews(); updateHotseatItemsVisibility();
} onMeasure(makeMeasureSpec(getMeasuredWidth(), EXACTLY),
makeMeasureSpec(getMeasuredHeight(), EXACTLY));
private void endAllLayoutTransitionAnimators() { onLayout(false, getLeft(), getTop(), getRight(), getBottom());
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();
} }
/** /**
@@ -192,10 +160,9 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
*/ */
protected void updateHotseatItems(ItemInfo[] hotseatItemInfos) { protected void updateHotseatItems(ItemInfo[] hotseatItemInfos) {
for (int i = 0; i < hotseatItemInfos.length; i++) { for (int i = 0; i < hotseatItemInfos.length; i++) {
ItemInfo hotseatItemInfo = hotseatItemInfos[!mIsRtl ? i ItemInfo hotseatItemInfo = hotseatItemInfos[
: hotseatItemInfos.length - i - 1]; !mIsRtl ? i : hotseatItemInfos.length - i - 1];
int hotseatIndex = mHotseatStartIndex + i; View hotseatView = mHotseatIconsContainer.getChildAt(i);
View hotseatView = getChildAt(hotseatIndex);
// Replace any Hotseat views with the appropriate type if it's not already that type. // Replace any Hotseat views with the appropriate type if it's not already that type.
final int expectedLayoutResId; final int expectedLayoutResId;
@@ -213,23 +180,23 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
} else { } else {
expectedLayoutResId = R.layout.taskbar_app_icon; expectedLayoutResId = R.layout.taskbar_app_icon;
} }
if (hotseatView == null || hotseatView.getSourceLayoutResId() != expectedLayoutResId if (hotseatView == null
|| hotseatView.getSourceLayoutResId() != expectedLayoutResId
|| needsReinflate) { || needsReinflate) {
removeView(hotseatView); mHotseatIconsContainer.removeView(hotseatView);
ActivityContext activityContext = getActivityContext();
if (isFolder) { if (isFolder) {
FolderInfo folderInfo = (FolderInfo) hotseatItemInfo; FolderInfo folderInfo = (FolderInfo) hotseatItemInfo;
FolderIcon folderIcon = FolderIcon.inflateFolderAndIcon(expectedLayoutResId, FolderIcon folderIcon = FolderIcon.inflateFolderAndIcon(expectedLayoutResId,
getActivityContext(), this, folderInfo); mActivityContext, this, folderInfo);
folderIcon.setTextVisible(false); folderIcon.setTextVisible(false);
hotseatView = folderIcon; hotseatView = folderIcon;
} else { } else {
hotseatView = inflate(expectedLayoutResId); hotseatView = inflate(expectedLayoutResId);
} }
int iconSize = activityContext.getDeviceProfile().iconSizePx; int iconSize = mActivityContext.getDeviceProfile().iconSizePx;
LayoutParams lp = new LayoutParams(iconSize, iconSize); LayoutParams lp = new LayoutParams(iconSize, iconSize);
lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0); 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. // 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) { && hotseatItemInfo instanceof WorkspaceItemInfo) {
((BubbleTextView) hotseatView).applyFromWorkspaceItem( ((BubbleTextView) hotseatView).applyFromWorkspaceItem(
(WorkspaceItemInfo) hotseatItemInfo); (WorkspaceItemInfo) hotseatItemInfo);
hotseatView.setOnClickListener(mControllerCallbacks.getItemOnClickListener()); hotseatView.setOnClickListener(mIconClickListener);
hotseatView.setOnLongClickListener( hotseatView.setOnLongClickListener(mIconLongClickListener);
mControllerCallbacks.getItemOnLongClickListener());
} else if (isFolder) { } else if (isFolder) {
hotseatView.setOnClickListener(mControllerCallbacks.getItemOnClickListener()); hotseatView.setOnClickListener(mIconClickListener);
hotseatView.setOnLongClickListener( hotseatView.setOnLongClickListener(mIconLongClickListener);
mControllerCallbacks.getItemOnLongClickListener());
} else { } else {
hotseatView.setOnClickListener(null); hotseatView.setOnClickListener(null);
hotseatView.setOnLongClickListener(null); hotseatView.setOnLongClickListener(null);
@@ -254,24 +219,14 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
} }
protected void updateHotseatItemsVisibility() { protected void updateHotseatItemsVisibility() {
for (int i = mHotseatStartIndex; i <= mHotseatEndIndex; i++) { for (int i = mHotseatIconsContainer.getChildCount() - 1; i >= 0; i--) {
updateHotseatItemVisibility(getChildAt(i)); updateHotseatItemVisibility(mHotseatIconsContainer.getChildAt(i));
} }
} }
private void updateHotseatItemVisibility(View hotseatView) { private void updateHotseatItemVisibility(View hotseatView) {
if (hotseatView.getTag() != null) { hotseatView.setVisibility(
hotseatView.setVisibility(VISIBLE); hotseatView.getTag() != null ? VISIBLE : (mAreHolesAllowed ? INVISIBLE : GONE));
} 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);
}
}
} }
@Override @Override
@@ -378,49 +333,20 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
return findDelegateView(xInOurCoordinates, yInOurCoorindates) != null; 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 * Add back/home/recents buttons into a single ViewGroup that will be inserted at
* {@param navButtonStartIndex} * {@param navButtonStartIndex}
*/ */
private void createNavButtons() { 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( LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams(
context.getDeviceProfile().iconSizePx, mActivityContext.getDeviceProfile().iconSizePx,
context.getDeviceProfile().iconSizePx mActivityContext.getDeviceProfile().iconSizePx
); );
buttonParams.gravity = Gravity.CENTER; buttonParams.gravity = Gravity.CENTER;
View backButton = mButtonProvider.getBack(); mSystemButtonContainer.addView(mButtonProvider.getBack(), buttonParams);
backButton.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick( mSystemButtonContainer.addView(mButtonProvider.getHome(), buttonParams);
BUTTON_BACK)); mSystemButtonContainer.addView(mButtonProvider.getRecents(), buttonParams);
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);
} }
@Override @Override
@@ -428,7 +354,7 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
switch (event.getAction()) { switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED: case DragEvent.ACTION_DRAG_STARTED:
mIsDraggingItem = true; mIsDraggingItem = true;
AbstractFloatingView.closeAllOpenViews(getActivityContext()); AbstractFloatingView.closeAllOpenViews(mActivityContext);
return true; return true;
case DragEvent.ACTION_DRAG_ENDED: case DragEvent.ACTION_DRAG_ENDED:
mIsDraggingItem = false; 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. * @return The bounding box of where the hotseat elements are relative to this TaskbarView.
*/ */
protected RectF getHotseatBounds() { protected RectF getHotseatBounds() {
View firstHotseatView = null, lastHotseatView = null; RectF result;
for (int i = mHotseatStartIndex; i <= mHotseatEndIndex; i++) { mDisableRelayout = true;
View child = getChildAt(i); boolean wereHolesAllowed = mAreHolesAllowed;
if (child.getVisibility() != GONE) { setHolesAllowedInLayoutNoAnimation(true);
if (firstHotseatView == null) { result = new RectF(
firstHotseatView = child; mHotseatIconsContainer.getLeft(),
} mHotseatIconsContainer.getTop(),
lastHotseatView = child; 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. // FolderIconParent implemented methods.
@@ -495,7 +421,7 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
} }
private View inflate(@LayoutRes int layoutResId) { private View inflate(@LayoutRes int layoutResId) {
return getActivityContext().getLayoutInflater().inflate(layoutResId, this, false); return mActivityContext.getLayoutInflater().inflate(layoutResId, this, false);
} }
@Override @Override
@@ -503,7 +429,11 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
// Ignore, we just implement Insettable to draw behind system insets. // Ignore, we just implement Insettable to draw behind system insets.
} }
private <T extends Context & ActivityContext> T getActivityContext() { public void setIconsVisibility(boolean isVisible) {
return ActivityContext.lookupContext(getContext()); mHotseatIconsContainer.setVisibility(isVisible ? VISIBLE : INVISIBLE);
}
public boolean areIconsVisible() {
return mHotseatIconsContainer.getVisibility() == VISIBLE;
} }
} }
@@ -52,7 +52,6 @@ import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.BaseState; import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.taskbar.TaskbarController;
import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.WindowBounds; import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.views.ScrimView; import com.android.launcher3.views.ScrimView;
@@ -123,11 +122,6 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
return null; return null;
} }
@Nullable
public TaskbarController getTaskbarController() {
return null;
}
public final boolean isResumed() { public final boolean isResumed() {
ACTIVITY_TYPE activity = getCreatedActivity(); ACTIVITY_TYPE activity = getCreatedActivity();
return activity != null && activity.hasBeenResumed(); return activity != null && activity.hasBeenResumed();
@@ -367,13 +361,6 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
*/ */
protected abstract int getOverviewScrimColorForState(ACTIVITY_TYPE activity, STATE_TYPE state); protected abstract int getOverviewScrimColorForState(ACTIVITY_TYPE activity, STATE_TYPE state);
/**
* See {@link com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags}
* @param systemUiStateFlags The latest SystemUiStateFlags
*/
public void onSystemUiFlagsChanged(int systemUiStateFlags) {
}
/** /**
* Returns the expected STATE_TYPE from the provided GestureEndTarget. * Returns the expected STATE_TYPE from the provided GestureEndTarget.
*/ */
@@ -22,7 +22,6 @@ import static com.android.launcher3.LauncherState.QUICK_SWITCH;
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback; import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorSet; import android.animation.AnimatorSet;
@@ -164,8 +163,7 @@ public final class LauncherActivityInterface extends
} }
@Nullable @Nullable
@Override private TaskbarController getTaskbarController() {
public TaskbarController getTaskbarController() {
BaseQuickstepLauncher launcher = getCreatedActivity(); BaseQuickstepLauncher launcher = getCreatedActivity();
if (launcher == null) { if (launcher == null) {
return null; return null;
@@ -300,16 +298,6 @@ public final class LauncherActivityInterface extends
return state.getWorkspaceScrimColor(launcher); return state.getWorkspaceScrimColor(launcher);
} }
@Override
public void onSystemUiFlagsChanged(int systemUiStateFlags) {
TaskbarController taskbarController = getTaskbarController();
if (taskbarController == null) {
return;
}
boolean isImeVisible = (systemUiStateFlags & SYSUI_STATE_IME_SHOWING) != 0;
taskbarController.setIsImeVisible(isImeVisible);
}
@Override @Override
public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) { public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
TaskbarController taskbarController = getTaskbarController(); TaskbarController taskbarController = getTaskbarController();
@@ -52,7 +52,6 @@ import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.os.Looper; import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.SystemProperties; import android.os.SystemProperties;
import android.util.Log; import android.util.Log;
@@ -75,6 +74,7 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.provider.RestoreDbTask; import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.taskbar.TaskbarManager;
import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.tracing.LauncherTraceProto; import com.android.launcher3.tracing.LauncherTraceProto;
@@ -147,21 +147,9 @@ public class TouchInteractionService extends Service implements PluginListener<O
private OverscrollPlugin mOverscrollPlugin; private OverscrollPlugin mOverscrollPlugin;
/** /**
* Extension of OverviewProxy aidl interface without needing to modify the actual interface. * Local IOverviewProxy implementation with some methods for local components
* This is for methods that need only need local access and not intended to make IPC calls.
*/ */
public abstract static class TISBinder extends IOverviewProxy.Stub { public class TISBinder extends IOverviewProxy.Stub {
public abstract void setTaskbarOverviewProxyDelegate(
@Nullable TaskbarOverviewProxyDelegate i);
}
private final TISBinder mMyBinder = new TISBinder() {
public void setTaskbarOverviewProxyDelegate(
@Nullable TaskbarOverviewProxyDelegate delegate) {
mTaskbarOverviewProxyDelegate = delegate;
}
@BinderThread @BinderThread
public void onInitialize(Bundle bundle) { public void onInitialize(Bundle bundle) {
@@ -274,40 +262,24 @@ public class TouchInteractionService extends Service implements PluginListener<O
@Override @Override
public void onImeWindowStatusChanged(int displayId, IBinder token, int vis, public void onImeWindowStatusChanged(int displayId, IBinder token, int vis,
int backDisposition, boolean showImeSwitcher) throws RemoteException { int backDisposition, boolean showImeSwitcher) {
if (mTaskbarOverviewProxyDelegate == null) { MAIN_EXECUTOR.execute(() -> mTaskbarManager.updateImeStatus(
return; displayId, vis, backDisposition, showImeSwitcher));
}
MAIN_EXECUTOR.execute(() -> {
if (mTaskbarOverviewProxyDelegate == null) {
return;
}
mTaskbarOverviewProxyDelegate
.updateImeStatus(displayId, vis, backDisposition, showImeSwitcher);
});
} }
};
public interface TaskbarOverviewProxyDelegate { public TaskbarManager getTaskbarManager() {
void updateImeStatus(int displayId, int vis, int backDisposition, return mTaskbarManager;
boolean showImeSwitcher); }
} }
private static boolean sConnected = false; private static boolean sConnected = false;
private static TouchInteractionService sInstance;
private static boolean sIsInitialized = false; private static boolean sIsInitialized = false;
private RotationTouchHelper mRotationTouchHelper; private RotationTouchHelper mRotationTouchHelper;
@Nullable
private TaskbarOverviewProxyDelegate mTaskbarOverviewProxyDelegate;
public static boolean isConnected() { public static boolean isConnected() {
return sConnected; return sConnected;
} }
@Nullable
public static TouchInteractionService getInstance() {
return sInstance;
}
public static boolean isInitialized() { public static boolean isInitialized() {
return sIsInitialized; return sIsInitialized;
@@ -336,9 +308,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
private DisplayManager mDisplayManager; private DisplayManager mDisplayManager;
public TouchInteractionService() { private TaskbarManager mTaskbarManager;
sInstance = this;
}
@Override @Override
public void onCreate() { public void onCreate() {
@@ -348,13 +318,14 @@ public class TouchInteractionService extends Service implements PluginListener<O
mMainChoreographer = Choreographer.getInstance(); mMainChoreographer = Choreographer.getInstance();
mAM = ActivityManagerWrapper.getInstance(); mAM = ActivityManagerWrapper.getInstance();
mDeviceState = new RecentsAnimationDeviceState(this, true); mDeviceState = new RecentsAnimationDeviceState(this, true);
mDisplayManager = getSystemService(DisplayManager.class);
mTaskbarManager = new TaskbarManager(this);
mRotationTouchHelper = mDeviceState.getRotationTouchHelper(); mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged); mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
mDeviceState.addOneHandedModeChangedCallback(this::onOneHandedModeOverlayChanged); mDeviceState.addOneHandedModeChangedCallback(this::onOneHandedModeOverlayChanged);
mDeviceState.runOnUserUnlocked(this::onUserUnlocked); mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
ProtoTracer.INSTANCE.get(this).add(this); ProtoTracer.INSTANCE.get(this).add(this);
mDisplayManager = getSystemService(DisplayManager.class);
sConnected = true; sConnected = true;
} }
@@ -468,8 +439,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
int systemUiStateFlags = mDeviceState.getSystemUiStateFlags(); int systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags); SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
mOverviewComponentObserver.onSystemUiStateChanged(); mOverviewComponentObserver.onSystemUiStateChanged();
mOverviewComponentObserver.getActivityInterface().onSystemUiFlagsChanged( mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags);
systemUiStateFlags);
if ((lastSysUIFlags & SYSUI_STATE_TRACING_ENABLED) != if ((lastSysUIFlags & SYSUI_STATE_TRACING_ENABLED) !=
(systemUiStateFlags & SYSUI_STATE_TRACING_ENABLED)) { (systemUiStateFlags & SYSUI_STATE_TRACING_ENABLED)) {
@@ -512,6 +482,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
getSystemService(AccessibilityManager.class) getSystemService(AccessibilityManager.class)
.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS); .unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
mTaskbarManager.destroy();
sConnected = false; sConnected = false;
super.onDestroy(); super.onDestroy();
} }
@@ -519,7 +490,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
Log.d(TAG, "Touch service connected: user=" + getUserId()); Log.d(TAG, "Touch service connected: user=" + getUserId());
return mMyBinder; return new TISBinder();
} }
private void onInputEvent(InputEvent ev) { private void onInputEvent(InputEvent ev) {
@@ -116,7 +116,7 @@ public class StaggeredWorkspaceAnim {
addStaggeredAnimationForView(child, lp.cellY + 1, totalRows); addStaggeredAnimationForView(child, lp.cellY + 1, totalRows);
} }
} else { } else {
final int hotseatRow, qsbRow, taskbarRow; final int hotseatRow, qsbRow;
if (grid.isTaskbarPresent) { if (grid.isTaskbarPresent) {
qsbRow = grid.inv.numRows + 1; qsbRow = grid.inv.numRows + 1;
hotseatRow = grid.inv.numRows + 2; hotseatRow = grid.inv.numRows + 2;
@@ -124,16 +124,12 @@ public class StaggeredWorkspaceAnim {
hotseatRow = grid.inv.numRows + 1; hotseatRow = grid.inv.numRows + 1;
qsbRow = grid.inv.numRows + 2; qsbRow = grid.inv.numRows + 2;
} }
// Taskbar and hotseat overlap.
taskbarRow = hotseatRow;
for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) { for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) {
View child = hotseatIcons.getChildAt(i); View child = hotseatIcons.getChildAt(i);
addStaggeredAnimationForView(child, hotseatRow, totalRows); addStaggeredAnimationForView(child, hotseatRow, totalRows);
} }
addStaggeredAnimationForView(hotseat.getQsb(), qsbRow, totalRows); addStaggeredAnimationForView(hotseat.getQsb(), qsbRow, totalRows);
addStaggeredAnimationForView(hotseat.getTaskbarView(), taskbarRow, totalRows);
} }
mAnimators.addListener(new AnimatorListenerAdapter() { mAnimators.addListener(new AnimatorListenerAdapter() {
-21
View File
@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<Space
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="gone" />
@@ -447,10 +447,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
* @param canvas The canvas to draw to. * @param canvas The canvas to draw to.
*/ */
protected void drawDotIfNecessary(Canvas canvas) { 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)) { if (!mForceHideDot && (hasDot() || mDotParams.scale > 0)) {
getIconBounds(mDotParams.iconBounds); getIconBounds(mDotParams.iconBounds);
Utilities.scaleRectAboutCenter(mDotParams.iconBounds, Utilities.scaleRectAboutCenter(mDotParams.iconBounds,
+1 -20
View File
@@ -49,7 +49,6 @@ public class Hotseat extends CellLayout implements Insettable {
private final View mQsb; private final View mQsb;
private final int mQsbHeight; private final int mQsbHeight;
private final View mTaskbarView;
private final int mTaskbarViewHeight; private final int mTaskbarViewHeight;
public Hotseat(Context context) { public Hotseat(Context context) {
@@ -67,10 +66,7 @@ public class Hotseat extends CellLayout implements Insettable {
mQsbHeight = mQsb.getLayoutParams().height; mQsbHeight = mQsb.getLayoutParams().height;
addView(mQsb); addView(mQsb);
mTaskbarView = LayoutInflater.from(context).inflate(R.layout.taskbar_view, this, false); mTaskbarViewHeight = context.getResources().getDimensionPixelSize(R.dimen.taskbar_size);
mTaskbarViewHeight = mTaskbarView.getLayoutParams().height;
// We want taskbar in the back so its background applies to Hotseat as well.
addView(mTaskbarView, 0);
} }
/** /**
@@ -187,8 +183,6 @@ public class Hotseat extends CellLayout implements Insettable {
int width = getShortcutsAndWidgets().getMeasuredWidth(); int width = getShortcutsAndWidgets().getMeasuredWidth();
mQsb.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), mQsb.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(mQsbHeight, MeasureSpec.EXACTLY)); MeasureSpec.makeMeasureSpec(mQsbHeight, MeasureSpec.EXACTLY));
mTaskbarView.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(mTaskbarViewHeight, MeasureSpec.EXACTLY));
} }
@Override @Override
@@ -202,13 +196,6 @@ public class Hotseat extends CellLayout implements Insettable {
int bottom = b - t - getQsbOffsetY(); int bottom = b - t - getQsbOffsetY();
int top = bottom - mQsbHeight; int top = bottom - mQsbHeight;
mQsb.layout(left, top, right, bottom); 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; return mQsb;
} }
/**
* Returns the Taskbar inside hotseat
*/
public View getTaskbarView() {
return mTaskbarView;
}
} }
-14
View File
@@ -1942,13 +1942,6 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
@Override @Override
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) { 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()) { if (!hasBeenResumed()) {
// Workaround an issue where the WM launch animation is clobbered when finishing the // Workaround an issue where the WM launch animation is clobbered when finishing the
// recents animation into launcher. Defer launching the activity until Launcher is // recents animation into launcher. Defer launching the activity until Launcher is
@@ -2860,13 +2853,6 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
.start(); .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) { public boolean supportsAdaptiveIconAnimation(View clickedView) {
return false; return false;
} }
+6 -4
View File
@@ -324,15 +324,17 @@ public final class Utilities {
} }
public static void scaleRectFAboutCenter(RectF r, float scale) { 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) { if (scale != 1.0f) {
float cx = r.centerX(); r.offset(-px, -py);
float cy = r.centerY();
r.offset(-cx, -cy);
r.left = r.left * scale; r.left = r.left * scale;
r.top = r.top * scale ; r.top = r.top * scale ;
r.right = r.right * scale; r.right = r.right * scale;
r.bottom = r.bottom * scale; r.bottom = r.bottom * scale;
r.offset(cx, cy); r.offset(px, py);
} }
} }
@@ -27,6 +27,7 @@ import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SU
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentSender; import android.content.IntentSender;
import android.content.pm.LauncherApps; import android.content.pm.LauncherApps;
@@ -193,6 +194,35 @@ public class ItemClickHandler {
launcher.startActivitySafely(v, intent, item); 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. * Event handler for an app shortcut click.
* *
@@ -200,30 +230,8 @@ public class ItemClickHandler {
*/ */
public static void onClickAppShortcut(View v, WorkspaceItemInfo shortcut, Launcher launcher) { public static void onClickAppShortcut(View v, WorkspaceItemInfo shortcut, Launcher launcher) {
if (shortcut.isDisabled()) { if (shortcut.isDisabled()) {
final int disabledFlags = shortcut.runtimeStatusFlags handleDisabledItemClicked(shortcut, launcher);
& WorkspaceItemInfo.FLAG_DISABLED_MASK; return;
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;
}
} }
// Check for abandoned promise // Check for abandoned promise