Merge changes from topic "revert-14664190-JGHJCCJYPM" into sc-dev am: 67914aff50 am: 8d996187ea

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

Change-Id: I9d647d893cb955091ecef0b4c289597a0dc39632
This commit is contained in:
Sunny Goyal
2021-05-21 16:22:15 +00:00
committed by Automerger Merge Worker
32 changed files with 1186 additions and 1205 deletions
@@ -21,7 +21,6 @@ import static com.android.launcher3.LauncherState.FLAG_HIDE_BACK_BUTTON;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.NO_OFFSET;
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
@@ -31,7 +30,6 @@ import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.app.ActivityOptions;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.ServiceConnection;
@@ -52,13 +50,11 @@ import com.android.launcher3.proxy.StartActivityParams;
import com.android.launcher3.statehandlers.BackButtonAlphaHandler;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarController;
import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.taskbar.TaskbarManager;
import com.android.launcher3.taskbar.TaskbarStateHandler;
import com.android.launcher3.taskbar.TaskbarView;
import com.android.launcher3.uioverrides.RecentsViewStateController;
import com.android.launcher3.util.ActivityOptionsWrapper;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.ObjectWrapper;
import com.android.launcher3.util.UiThreadHelper;
import com.android.quickstep.RecentsModel;
@@ -68,6 +64,7 @@ import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.TouchInteractionService;
import com.android.quickstep.TouchInteractionService.TISBinder;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.RemoteFadeOutAnimationListener;
import com.android.quickstep.util.SplitSelectStateController;
@@ -89,8 +86,6 @@ public abstract class BaseQuickstepLauncher extends Launcher
private DepthController mDepthController = new DepthController(this);
private QuickstepTransitionManager mAppTransitionManager;
private ServiceConnection mTisBinderConnection;
protected TouchInteractionService.TISBinder mTisBinder;
/**
* Reusable command for applying the back button alpha on the background thread.
@@ -101,8 +96,20 @@ public abstract class BaseQuickstepLauncher extends Launcher
private OverviewActionsView mActionsView;
private @Nullable TaskbarController mTaskbarController;
private @Nullable TaskbarManager mTaskbarManager;
private @Nullable LauncherTaskbarUIController mTaskbarUIController;
private final ServiceConnection mTisBinderConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mTaskbarManager = ((TISBinder) iBinder).getTaskbarManager();
mTaskbarManager.setLauncher(BaseQuickstepLauncher.this);
}
@Override
public void onServiceDisconnected(ComponentName componentName) { }
};
private final TaskbarStateHandler mTaskbarStateHandler = new TaskbarStateHandler(this);
// Will be updated when dragging from taskbar.
private @Nullable DragOptions mNextWorkspaceDragOptions = null;
private SplitPlaceholderView mSplitPlaceholderView;
@@ -112,24 +119,6 @@ public abstract class BaseQuickstepLauncher extends Launcher
super.onCreate(savedInstanceState);
SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this);
addMultiWindowModeChangedListener(mDepthController);
setupTouchInteractionServiceBinder();
}
private void setupTouchInteractionServiceBinder() {
Intent intent = new Intent(this, TouchInteractionService.class);
mTisBinderConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder binder) {
mTisBinder = ((TouchInteractionService.TISBinder) binder);
mTisBinder.setTaskbarOverviewProxyDelegate(mTaskbarController);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mTisBinder = null;
}
};
bindService(intent, mTisBinderConnection, 0);
}
@Override
@@ -137,15 +126,12 @@ public abstract class BaseQuickstepLauncher extends Launcher
mAppTransitionManager.onActivityDestroyed();
SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
if (mTaskbarController != null) {
mTaskbarController.cleanup();
mTaskbarController = null;
if (mTisBinder != null) {
mTisBinder.setTaskbarOverviewProxyDelegate(null);
unbindService(mTisBinderConnection);
}
}
unbindService(mTisBinderConnection);
if (mTaskbarManager != null) {
mTaskbarManager.setLauncher(null);
}
super.onDestroy();
}
@@ -272,37 +258,12 @@ public abstract class BaseQuickstepLauncher extends Launcher
mAppTransitionManager = new QuickstepTransitionManager(this);
mAppTransitionManager.registerRemoteAnimations();
addTaskbarIfNecessary();
addOnDeviceProfileChangeListener(newDp -> addTaskbarIfNecessary());
bindService(new Intent(this, TouchInteractionService.class), mTisBinderConnection, 0);
}
@Override
public void onDisplayInfoChanged(Context context, DisplayController.Info info,
int flags) {
super.onDisplayInfoChanged(context, info, flags);
if ((flags & CHANGE_ACTIVE_SCREEN) != 0) {
addTaskbarIfNecessary();
}
}
private void addTaskbarIfNecessary() {
if (mTaskbarController != null) {
mTaskbarController.cleanup();
if (mTisBinder != null) {
mTisBinder.setTaskbarOverviewProxyDelegate(null);
}
mTaskbarController = null;
}
if (mDeviceProfile.isTaskbarPresent) {
TaskbarView taskbarViewOnHome = (TaskbarView) mHotseat.getTaskbarView();
TaskbarActivityContext taskbarActivityContext = new TaskbarActivityContext(this);
mTaskbarController = new TaskbarController(this,
taskbarActivityContext.getTaskbarContainerView(), taskbarViewOnHome);
mTaskbarController.init();
if (mTisBinder != null) {
mTisBinder.setTaskbarOverviewProxyDelegate(mTaskbarController);
}
}
public void setTaskbarUIController(LauncherTaskbarUIController taskbarUIController) {
mTaskbarUIController = taskbarUIController;
}
public <T extends OverviewActionsView> T getActionsView() {
@@ -332,8 +293,8 @@ public abstract class BaseQuickstepLauncher extends Launcher
return mDepthController;
}
public @Nullable TaskbarController getTaskbarController() {
return mTaskbarController;
public @Nullable LauncherTaskbarUIController getTaskbarUIController() {
return mTaskbarUIController;
}
public TaskbarStateHandler getTaskbarStateHandler() {
@@ -341,14 +302,9 @@ public abstract class BaseQuickstepLauncher extends Launcher
}
@Override
public boolean isViewInTaskbar(View v) {
return mTaskbarController != null && mTaskbarController.isViewInTaskbar(v);
}
public boolean supportsAdaptiveIconAnimation(View clickedView) {
return mAppTransitionManager.hasControlRemoteAppTransitionPermission()
&& FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM.get()
&& !isViewInTaskbar(clickedView);
&& FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM.get();
}
@Override
@@ -394,8 +350,8 @@ public abstract class BaseQuickstepLauncher extends Launcher
@Override
public float getNormalTaskbarScale() {
if (mTaskbarController != null) {
return mTaskbarController.getTaskbarScaleOnHome();
if (mTaskbarUIController != null) {
return mTaskbarUIController.getTaskbarScaleOnHome();
}
return super.getNormalTaskbarScale();
}
@@ -417,8 +373,8 @@ public abstract class BaseQuickstepLauncher extends Launcher
}
if ((changeBits & ACTIVITY_STATE_RESUMED) != 0) {
if (mTaskbarController != null) {
mTaskbarController.onLauncherResumedOrPaused(hasBeenResumed());
if (mTaskbarUIController != null) {
mTaskbarUIController.onLauncherResumedOrPaused(hasBeenResumed());
}
}
@@ -1252,7 +1252,6 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
final boolean launchingFromWidget = mV instanceof LauncherAppWidgetHostView;
final boolean launchingFromRecents = isLaunchingFromRecents(mV, appTargets);
final boolean launchingFromTaskbar = mLauncher.isViewInTaskbar(mV);
if (launchingFromWidget) {
composeWidgetLaunchAnimator(anim, (LauncherAppWidgetHostView) mV, appTargets,
wallpaperTargets, nonAppTargets);
@@ -1263,8 +1262,6 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
launcherClosing);
addCujInstrumentation(
anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_RECENTS);
} else if (launchingFromTaskbar) {
// TODO
} else {
composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,
launcherClosing);
@@ -263,8 +263,8 @@ public class HotseatPredictionController implements DragController.DragListener,
removeOutlineDrawings();
}
if (mLauncher.getTaskbarController() != null) {
mLauncher.getTaskbarController().onHotseatUpdated();
if (mLauncher.getTaskbarUIController() != null) {
mLauncher.getTaskbarUIController().onHotseatUpdated();
}
}
@@ -16,12 +16,17 @@
package com.android.launcher3.taskbar;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
import android.annotation.DrawableRes;
import android.content.Context;
import android.view.View;
import android.widget.ImageView;
import com.android.launcher3.R;
import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton;
/**
* Creates Buttons for Taskbar for 3 button nav.
@@ -29,47 +34,46 @@ import com.android.launcher3.R;
*/
public class ButtonProvider {
private int mMarginLeftRight;
private final Context mContext;
private final int mMarginLeftRight;
private final TaskbarActivityContext mContext;
public ButtonProvider(Context context) {
public ButtonProvider(TaskbarActivityContext context) {
mContext = context;
}
public void setMarginLeftRight(int margin) {
mMarginLeftRight = margin;
mMarginLeftRight = context.getResources()
.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
}
public View getBack() {
// Back button
return getButtonForDrawable(R.drawable.ic_sysbar_back);
return getButtonForDrawable(R.drawable.ic_sysbar_back, BUTTON_BACK);
}
public View getDown() {
// Ime down button
return getButtonForDrawable(R.drawable.ic_sysbar_back);
return getButtonForDrawable(R.drawable.ic_sysbar_back, BUTTON_BACK);
}
public View getHome() {
// Home button
return getButtonForDrawable(R.drawable.ic_sysbar_home);
return getButtonForDrawable(R.drawable.ic_sysbar_home, BUTTON_HOME);
}
public View getRecents() {
// Recents button
return getButtonForDrawable(R.drawable.ic_sysbar_recent);
return getButtonForDrawable(R.drawable.ic_sysbar_recent, BUTTON_RECENTS);
}
public View getImeSwitcher() {
// IME Switcher Button
return getButtonForDrawable(R.drawable.ic_ime_switcher);
return getButtonForDrawable(R.drawable.ic_ime_switcher, BUTTON_IME_SWITCH);
}
private View getButtonForDrawable(@DrawableRes int drawableId) {
private View getButtonForDrawable(@DrawableRes int drawableId, @TaskbarButton int buttonType) {
ImageView buttonView = new ImageView(mContext);
buttonView.setImageResource(drawableId);
buttonView.setBackgroundResource(R.drawable.taskbar_icon_click_feedback_roundrect);
buttonView.setPadding(mMarginLeftRight, 0, mMarginLeftRight, 0);
buttonView.setOnClickListener(view -> mContext.onNavigationButtonClick(buttonType));
return buttonView;
}
@@ -16,9 +16,6 @@
package com.android.launcher3.taskbar;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
@@ -29,7 +26,6 @@ import com.android.launcher3.views.ActivityContext;
public class ImeBarView extends RelativeLayout {
private ButtonProvider mButtonProvider;
private TaskbarController.TaskbarViewCallbacks mControllerCallbacks;
private View mImeView;
public ImeBarView(Context context) {
@@ -44,12 +40,9 @@ public class ImeBarView extends RelativeLayout {
super(context, attrs, defStyleAttr);
}
public void construct(ButtonProvider buttonProvider) {
public void init(ButtonProvider buttonProvider) {
mButtonProvider = buttonProvider;
}
public void init(TaskbarController.TaskbarViewCallbacks taskbarCallbacks) {
mControllerCallbacks = taskbarCallbacks;
ActivityContext context = getActivityContext();
RelativeLayout.LayoutParams imeParams = new RelativeLayout.LayoutParams(
context.getDeviceProfile().iconSizePx,
@@ -64,24 +57,16 @@ public class ImeBarView extends RelativeLayout {
// Down Arrow
View downView = mButtonProvider.getDown();
downView.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
BUTTON_BACK));
downView.setLayoutParams(downParams);
downView.setRotation(-90);
addView(downView);
// IME switcher button
mImeView = mButtonProvider.getImeSwitcher();
mImeView.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
BUTTON_IME_SWITCH));
mImeView.setLayoutParams(imeParams);
addView(mImeView);
}
public void cleanup() {
removeAllViews();
}
public void setImeSwitcherVisibility(boolean show) {
mImeView.setVisibility(show ? VISIBLE : GONE);
}
@@ -0,0 +1,275 @@
/*
* 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 android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.graphics.Rect;
import android.graphics.RectF;
import android.view.MotionEvent;
import androidx.annotation.Nullable;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherState;
import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.states.StateAnimationConfig;
/**
* A data source which integrates with a Launcher instance
* TODO: Rename to have Launcher prefix
*/
public class LauncherTaskbarUIController extends TaskbarUIController {
private final BaseQuickstepLauncher mLauncher;
private final TaskbarStateHandler mTaskbarStateHandler;
private final TaskbarAnimationController mTaskbarAnimationController;
private final TaskbarHotseatController mHotseatController;
private final TaskbarActivityContext mContext;
final TaskbarContainerView mTaskbarContainerView;
final TaskbarView mTaskbarView;
private @Nullable Animator mAnimator;
private boolean mIsAnimatingToLauncher;
public LauncherTaskbarUIController(
BaseQuickstepLauncher launcher, TaskbarActivityContext context) {
mContext = context;
mTaskbarContainerView = context.getDragLayer();
mTaskbarView = mTaskbarContainerView.findViewById(R.id.taskbar_view);
mLauncher = launcher;
mTaskbarStateHandler = mLauncher.getTaskbarStateHandler();
mTaskbarAnimationController = new TaskbarAnimationController(mLauncher,
createTaskbarAnimationControllerCallbacks());
mHotseatController = new TaskbarHotseatController(
mLauncher, mTaskbarView::updateHotseatItems);
}
@Override
protected void onCreate() {
mTaskbarStateHandler.setAnimationController(mTaskbarAnimationController);
mTaskbarAnimationController.init();
mHotseatController.init();
setTaskbarViewVisible(!mLauncher.hasBeenResumed());
alignRealHotseatWithTaskbar();
mLauncher.setTaskbarUIController(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.setTaskbarUIController(null);
}
@Override
protected boolean isTaskbarTouchable() {
return !mIsAnimatingToLauncher;
}
private TaskbarAnimationControllerCallbacks createTaskbarAnimationControllerCallbacks() {
return new TaskbarAnimationControllerCallbacks() {
@Override
public void updateTaskbarBackgroundAlpha(float alpha) {
mTaskbarContainerView.setTaskbarBackgroundAlpha(alpha);
}
@Override
public void updateTaskbarVisibilityAlpha(float alpha) {
mTaskbarView.setAlpha(alpha);
}
@Override
public void updateImeBarVisibilityAlpha(float alpha) {
mTaskbarContainerView.updateImeBarVisibilityAlpha(alpha);
}
@Override
public void updateTaskbarScale(float scale) {
mTaskbarView.setScaleX(scale);
mTaskbarView.setScaleY(scale);
}
@Override
public void updateTaskbarTranslationY(float translationY) {
if (translationY < 0) {
// Resize to accommodate the max translation we'll reach.
mContext.setTaskbarWindowHeight(mContext.getDeviceProfile().taskbarSize
+ mLauncher.getHotseat().getTaskbarOffsetY());
} else {
mContext.setTaskbarWindowHeight(mContext.getDeviceProfile().taskbarSize);
}
mTaskbarView.setTranslationY(translationY);
}
};
}
/**
* Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
*/
public void onLauncherResumedOrPaused(boolean isResumed) {
long duration = QuickstepTransitionManager.CONTENT_ALPHA_DURATION;
if (mAnimator != null) {
mAnimator.cancel();
}
if (isResumed) {
mAnimator = createAnimToLauncher(null, duration);
} else {
mAnimator = createAnimToApp(duration);
}
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mAnimator = null;
}
});
mAnimator.start();
}
/**
* Create Taskbar animation when going from an app to Launcher.
* @param toState If known, the state we will end up in when reaching Launcher.
*/
public Animator createAnimToLauncher(@Nullable LauncherState toState, long duration) {
PendingAnimation anim = new PendingAnimation(duration);
anim.add(mTaskbarAnimationController.createAnimToBackgroundAlpha(0, duration));
if (toState != null) {
mTaskbarStateHandler.setStateWithAnimation(toState, new StateAnimationConfig(), anim);
}
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mIsAnimatingToLauncher = true;
mTaskbarView.setHolesAllowedInLayout(true);
mTaskbarView.updateHotseatItemsVisibility();
}
@Override
public void onAnimationEnd(Animator animation) {
mIsAnimatingToLauncher = false;
setTaskbarViewVisible(false);
}
});
return anim.buildAnim();
}
private Animator createAnimToApp(long duration) {
PendingAnimation anim = new PendingAnimation(duration);
anim.add(mTaskbarAnimationController.createAnimToBackgroundAlpha(1, duration));
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mTaskbarView.updateHotseatItemsVisibility();
setTaskbarViewVisible(true);
}
@Override
public void onAnimationEnd(Animator animation) {
mTaskbarView.setHolesAllowedInLayout(false);
}
});
return anim.buildAnim();
}
@Override
protected void onImeVisible(TaskbarContainerView containerView, boolean isVisible) {
mTaskbarAnimationController.animateToVisibilityForIme(isVisible ? 0 : 1);
}
/**
* Should be called when one or more items in the Hotseat have changed.
*/
public void onHotseatUpdated() {
mHotseatController.onHotseatUpdated();
}
/**
* @param ev MotionEvent in screen coordinates.
* @return Whether any Taskbar item could handle the given MotionEvent if given the chance.
*/
public boolean isEventOverAnyTaskbarItem(MotionEvent ev) {
return mTaskbarView.isEventOverAnyItem(ev);
}
public boolean isDraggingItem() {
return mTaskbarView.isDraggingItem();
}
/**
* Pads the Hotseat to line up exactly with Taskbar's copy of the Hotseat.
*/
@Override
public void alignRealHotseatWithTaskbar() {
Rect hotseatBounds = new Rect();
DeviceProfile grid = mLauncher.getDeviceProfile();
int hotseatHeight = grid.workspacePadding.bottom + grid.taskbarSize;
int taskbarOffset = mLauncher.getHotseat().getTaskbarOffsetY();
int hotseatTopDiff = hotseatHeight - grid.taskbarSize - taskbarOffset;
int hotseatBottomDiff = taskbarOffset;
RectF hotseatBoundsF = mTaskbarView.getHotseatBounds();
Utilities.scaleRectFAboutPivot(hotseatBoundsF, getTaskbarScaleOnHome(),
mTaskbarView.getPivotX(), mTaskbarView.getPivotY());
hotseatBoundsF.round(hotseatBounds);
mLauncher.getHotseat().setPadding(hotseatBounds.left,
hotseatBounds.top + hotseatTopDiff,
mTaskbarView.getWidth() - hotseatBounds.right,
mTaskbarView.getHeight() - hotseatBounds.bottom + hotseatBottomDiff);
}
/**
* Returns the ratio of the taskbar icon size on home vs in an app.
*/
public float getTaskbarScaleOnHome() {
DeviceProfile inAppDp = mContext.getDeviceProfile();
DeviceProfile onHomeDp = mLauncher.getDeviceProfile();
return (float) onHomeDp.cellWidthPx / inAppDp.cellWidthPx;
}
void setTaskbarViewVisible(boolean isVisible) {
mTaskbarView.setIconsVisibility(isVisible);
mLauncher.getHotseat().setIconsAlpha(isVisible ? 0f : 1f);
}
/**
* Contains methods that TaskbarAnimationController can call to interface with
* TaskbarController.
*/
protected interface TaskbarAnimationControllerCallbacks {
void updateTaskbarBackgroundAlpha(float alpha);
void updateTaskbarVisibilityAlpha(float alpha);
void updateImeBarVisibilityAlpha(float alpha);
void updateTaskbarScale(float scale);
void updateTaskbarTranslationY(float translationY);
}
}
@@ -15,56 +15,164 @@
*/
package com.android.launcher3.taskbar;
import android.content.ContextWrapper;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR;
import android.app.ActivityOptions;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Process;
import android.os.SystemProperties;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.Display;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.R;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.WindowManagerWrapper;
/**
* The {@link ActivityContext} with which we inflate Taskbar-related Views. This allows UI elements
* that are used by both Launcher and Taskbar (such as Folder) to reference a generic
* ActivityContext and BaseDragLayer instead of the Launcher activity and its DragLayer.
*/
public class TaskbarActivityContext extends ContextWrapper implements ActivityContext {
public class TaskbarActivityContext extends ContextThemeWrapper implements ActivityContext {
private static final boolean ENABLE_THREE_BUTTON_TASKBAR =
SystemProperties.getBoolean("persist.debug.taskbar_three_button", false);
private static final String TAG = "TaskbarActivityContext";
private static final String WINDOW_TITLE = "Taskbar";
private final DeviceProfile mDeviceProfile;
private final LayoutInflater mLayoutInflater;
private final TaskbarContainerView mTaskbarContainerView;
private final TaskbarIconController mIconController;
private final MyDragController mDragController;
public TaskbarActivityContext(BaseQuickstepLauncher launcher) {
super(launcher);
mDeviceProfile = launcher.getDeviceProfile().copy(this);
private final WindowManager mWindowManager;
private WindowManager.LayoutParams mWindowLayoutParams;
private final SysUINavigationMode.Mode mNavMode;
private final TaskbarNavButtonController mNavButtonController;
private final boolean mIsSafeModeEnabled;
@NonNull
private TaskbarUIController mUIController = TaskbarUIController.DEFAULT;
private final View.OnClickListener mOnTaskbarIconClickListener;
private final View.OnLongClickListener mOnTaskbarIconLongClickListener;
public TaskbarActivityContext(Context windowContext, DeviceProfile dp,
TaskbarNavButtonController buttonController) {
super(windowContext, Themes.getActivityThemeRes(windowContext));
mDeviceProfile = dp;
mNavButtonController = buttonController;
mNavMode = SysUINavigationMode.getMode(windowContext);
mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
() -> getPackageManager().isSafeMode());
mOnTaskbarIconLongClickListener =
new TaskbarDragController(this)::startSystemDragOnLongClick;
mOnTaskbarIconClickListener = this::onTaskbarIconClicked;
float taskbarIconSize = getResources().getDimension(R.dimen.taskbar_icon_size);
float iconScale = taskbarIconSize / mDeviceProfile.iconSizePx;
mDeviceProfile.updateIconSize(iconScale, getResources());
mLayoutInflater = LayoutInflater.from(this).cloneInContext(this);
mTaskbarContainerView = (TaskbarContainerView) mLayoutInflater
.inflate(R.layout.taskbar, null, false);
mIconController = new TaskbarIconController(this, mTaskbarContainerView);
mDragController = new MyDragController(this);
Display display = windowContext.getDisplay();
Context c = display.getDisplayId() == Display.DEFAULT_DISPLAY
? windowContext.getApplicationContext()
: windowContext.getApplicationContext().createDisplayContext(display);
mWindowManager = c.getSystemService(WindowManager.class);
}
public TaskbarContainerView getTaskbarContainerView() {
return mTaskbarContainerView;
public void init() {
mWindowLayoutParams = new WindowManager.LayoutParams(
MATCH_PARENT,
mDeviceProfile.taskbarSize,
TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
mWindowLayoutParams.setTitle(WINDOW_TITLE);
mWindowLayoutParams.packageName = getPackageName();
mWindowLayoutParams.gravity = Gravity.BOTTOM;
mWindowLayoutParams.setFitInsetsTypes(0);
mWindowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mWindowLayoutParams.setSystemApplicationOverlay(true);
WindowManagerWrapper wmWrapper = WindowManagerWrapper.getInstance();
wmWrapper.setProvidesInsetsTypes(
mWindowLayoutParams,
new int[] { ITYPE_EXTRA_NAVIGATION_BAR, ITYPE_BOTTOM_TAPPABLE_ELEMENT }
);
mIconController.init(mOnTaskbarIconClickListener, mOnTaskbarIconLongClickListener);
mWindowManager.addView(mTaskbarContainerView, mWindowLayoutParams);
}
/**
* Updates the TaskbarContainer height (pass deviceProfile.taskbarSize to reset).
*/
public void setTaskbarWindowHeight(int height) {
if (mWindowLayoutParams.height == height) {
return;
}
mWindowLayoutParams.height = height;
mWindowManager.updateViewLayout(mTaskbarContainerView, mWindowLayoutParams);
}
public boolean canShowNavButtons() {
return ENABLE_THREE_BUTTON_TASKBAR && mNavMode == Mode.THREE_BUTTONS;
}
@Override
@@ -73,7 +181,7 @@ public class TaskbarActivityContext extends ContextWrapper implements ActivityCo
}
@Override
public BaseDragLayer<TaskbarActivityContext> getDragLayer() {
public TaskbarContainerView getDragLayer() {
return mTaskbarContainerView;
}
@@ -92,6 +200,103 @@ public class TaskbarActivityContext extends ContextWrapper implements ActivityCo
return mDragController;
}
/**
* Sets a new data-source for this taskbar instance
*/
public void setUIController(@NonNull TaskbarUIController uiController) {
mUIController.onDestroy();
mUIController = uiController;
mIconController.setUIController(mUIController);
mUIController.onCreate();
}
/**
* Called when this instance of taskbar is no longer needed
*/
public void onDestroy() {
setUIController(TaskbarUIController.DEFAULT);
mIconController.onDestroy();
mWindowManager.removeViewImmediate(mTaskbarContainerView);
}
void onNavigationButtonClick(@TaskbarButton int buttonType) {
mNavButtonController.onButtonClick(buttonType);
}
public TaskbarIconController getIconController() {
return mIconController;
}
/**
* Updates the TaskbarContainer to MATCH_PARENT vs original Taskbar size.
*/
protected void setTaskbarWindowFullscreen(boolean fullscreen) {
setTaskbarWindowHeight(fullscreen ? MATCH_PARENT : getDeviceProfile().taskbarSize);
}
protected void onTaskbarIconClicked(View view) {
Object tag = view.getTag();
if (tag instanceof Task) {
Task task = (Task) tag;
ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key,
ActivityOptions.makeBasic());
} else if (tag instanceof FolderInfo) {
FolderIcon folderIcon = (FolderIcon) view;
Folder folder = folderIcon.getFolder();
setTaskbarWindowFullscreen(true);
getDragLayer().post(() -> {
folder.animateOpen();
folder.iterateOverItems((itemInfo, itemView) -> {
itemView.setOnClickListener(mOnTaskbarIconClickListener);
itemView.setOnLongClickListener(mOnTaskbarIconLongClickListener);
// To play haptic when dragging, like other Taskbar items do.
itemView.setHapticFeedbackEnabled(true);
return false;
});
});
} else if (tag instanceof WorkspaceItemInfo) {
WorkspaceItemInfo info = (WorkspaceItemInfo) tag;
if (info.isDisabled()) {
ItemClickHandler.handleDisabledItemClicked(info, this);
} else {
Intent intent = new Intent(info.getIntent())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
Toast.makeText(this, R.string.safemode_shortcut_error,
Toast.LENGTH_SHORT).show();
} else if (info.isPromise()) {
intent = new PackageManagerHelper(this)
.getMarketIntent(info.getTargetPackage())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} else if (info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
String id = info.getDeepShortcutId();
String packageName = intent.getPackage();
getSystemService(LauncherApps.class)
.startShortcut(packageName, id, null, null, info.user);
} else if (info.user.equals(Process.myUserHandle())) {
startActivity(intent);
} else {
getSystemService(LauncherApps.class).startMainActivity(
intent.getComponent(), info.user, intent.getSourceBounds(), null);
}
} catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT)
.show();
Log.e(TAG, "Unable to launch. tag=" + info + " intent=" + intent, e);
}
}
} else {
Log.e(TAG, "Unknown type clicked: " + tag);
}
AbstractFloatingView.closeAllOpenViews(this);
}
private static class MyDragController extends DragController<TaskbarActivityContext> {
MyDragController(TaskbarActivityContext activity) {
super(activity);
@@ -106,7 +311,8 @@ public class TaskbarActivityContext extends ContextWrapper implements ActivityCo
}
@Override
protected void exitDrag() { }
protected void exitDrag() {
}
@Override
protected DropTarget getDefaultDropTarget(int[] dropCoordinates) {
@@ -21,6 +21,7 @@ import android.animation.Animator;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.Utilities;
import com.android.launcher3.taskbar.LauncherTaskbarUIController.TaskbarAnimationControllerCallbacks;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.SystemUiProxy;
import com.android.systemui.shared.system.QuickStepContract;
@@ -34,7 +35,7 @@ public class TaskbarAnimationController {
private static final long IME_VISIBILITY_ALPHA_DURATION = 120;
private final BaseQuickstepLauncher mLauncher;
private final TaskbarController.TaskbarAnimationControllerCallbacks mTaskbarCallbacks;
private final TaskbarAnimationControllerCallbacks mTaskbarCallbacks;
// Background alpha.
private final AnimatedFloat mTaskbarBackgroundAlpha = new AnimatedFloat(
@@ -55,7 +56,7 @@ public class TaskbarAnimationController {
this::updateTranslationY);
public TaskbarAnimationController(BaseQuickstepLauncher launcher,
TaskbarController.TaskbarAnimationControllerCallbacks taskbarCallbacks) {
TaskbarAnimationControllerCallbacks taskbarCallbacks) {
mLauncher = launcher;
mTaskbarCallbacks = taskbarCallbacks;
}
@@ -15,9 +15,6 @@
*/
package com.android.launcher3.taskbar;
import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_FRAME;
import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
@@ -32,22 +29,21 @@ import com.android.launcher3.R;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
import com.android.systemui.shared.system.ViewTreeObserverWrapper;
import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo;
import com.android.systemui.shared.system.ViewTreeObserverWrapper.OnComputeInsetsListener;
/**
* Top-level ViewGroup that hosts the TaskbarView as well as Views created by it such as Folder.
*/
public class TaskbarContainerView extends BaseDragLayer<TaskbarActivityContext> {
private final int[] mTempLoc = new int[2];
private final int mFolderMargin;
private final Paint mTaskbarBackgroundPaint;
// Initialized in TaskbarController constructor.
private TaskbarController.TaskbarContainerViewCallbacks mControllerCallbacks;
// Initialized in init.
private TaskbarIconController.Callbacks mControllerCallbacks;
private TaskbarView mTaskbarView;
private ViewTreeObserverWrapper.OnComputeInsetsListener mTaskbarInsetsComputer;
private final OnComputeInsetsListener mTaskbarInsetsComputer = this::onComputeTaskbarInsets;
public TaskbarContainerView(@NonNull Context context) {
this(context, null);
@@ -68,15 +64,6 @@ public class TaskbarContainerView extends BaseDragLayer<TaskbarActivityContext>
mFolderMargin = getResources().getDimensionPixelSize(R.dimen.taskbar_folder_margin);
mTaskbarBackgroundPaint = new Paint();
mTaskbarBackgroundPaint.setColor(getResources().getColor(R.color.taskbar_background));
}
protected void construct(TaskbarController.TaskbarContainerViewCallbacks callbacks) {
mControllerCallbacks = callbacks;
}
protected void init(TaskbarView taskbarView) {
mTaskbarView = taskbarView;
mTaskbarInsetsComputer = createTaskbarInsetsComputer();
recreateControllers();
}
@@ -85,46 +72,24 @@ public class TaskbarContainerView extends BaseDragLayer<TaskbarActivityContext>
mControllers = new TouchController[0];
}
private ViewTreeObserverWrapper.OnComputeInsetsListener createTaskbarInsetsComputer() {
return insetsInfo -> {
if (mControllerCallbacks.isTaskbarTouchable()) {
// Accept touches anywhere in our bounds.
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME);
} else {
// Let touches pass through us.
insetsInfo.touchableRegion.setEmpty();
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
}
// TaskbarContainerView provides insets to other apps based on contentInsets. These
// insets should stay consistent even if we expand TaskbarContainerView's bounds, e.g.
// to show a floating view like Folder. Thus, we set the contentInsets to be where
// mTaskbarView is, since its position never changes and insets rather than overlays.
int[] loc = mTempLoc;
float scale = mTaskbarView.getScaleX();
float translationY = mTaskbarView.getTranslationY();
mTaskbarView.setScaleX(1);
mTaskbarView.setScaleY(1);
mTaskbarView.setTranslationY(0);
mTaskbarView.getLocationInWindow(loc);
mTaskbarView.setScaleX(scale);
mTaskbarView.setScaleY(scale);
mTaskbarView.setTranslationY(translationY);
insetsInfo.contentInsets.left = loc[0];
insetsInfo.contentInsets.top = loc[1];
insetsInfo.contentInsets.right = getWidth() - (loc[0] + mTaskbarView.getWidth());
insetsInfo.contentInsets.bottom = getHeight() - (loc[1] + mTaskbarView.getHeight());
};
public void init(TaskbarIconController.Callbacks callbacks, TaskbarView taskbarView) {
mControllerCallbacks = callbacks;
mTaskbarView = taskbarView;
}
protected void cleanup() {
private void onComputeTaskbarInsets(InsetsInfo insetsInfo) {
if (mControllerCallbacks != null) {
mControllerCallbacks.updateInsetsTouchability(insetsInfo);
}
}
protected void onDestroy() {
ViewTreeObserverWrapper.removeOnComputeInsetsListener(mTaskbarInsetsComputer);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
ViewTreeObserverWrapper.addOnComputeInsetsListener(getViewTreeObserver(),
mTaskbarInsetsComputer);
}
@@ -133,7 +98,7 @@ public class TaskbarContainerView extends BaseDragLayer<TaskbarActivityContext>
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
cleanup();
onDestroy();
}
@Override
@@ -143,10 +108,25 @@ public class TaskbarContainerView extends BaseDragLayer<TaskbarActivityContext>
return true;
}
public void updateImeBarVisibilityAlpha(float alpha) {
if (mControllerCallbacks != null) {
mControllerCallbacks.updateImeBarVisibilityAlpha(alpha);
}
}
@Override
public void onViewRemoved(View child) {
super.onViewRemoved(child);
mControllerCallbacks.onViewRemoved();
if (mControllerCallbacks != null) {
mControllerCallbacks.onContainerViewRemoved();
}
}
@Override
protected void dispatchDraw(Canvas canvas) {
canvas.drawRect(0, canvas.getHeight() - mTaskbarView.getHeight(), canvas.getWidth(),
canvas.getHeight(), mTaskbarBackgroundPaint);
super.dispatchDraw(canvas);
}
/**
@@ -158,16 +138,6 @@ public class TaskbarContainerView extends BaseDragLayer<TaskbarActivityContext>
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.
@@ -1,596 +0,0 @@
/*
* 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.INVISIBLE;
import static android.view.View.VISIBLE;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.ActivityOptions;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherState;
import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.R;
import com.android.launcher3.anim.AlphaUpdateListener;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.TouchInteractionService.TaskbarOverviewProxyDelegate;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.WindowManagerWrapper;
/**
* Interfaces with Launcher/WindowManager/SystemUI to determine what to show in TaskbarView.
*/
public class TaskbarController implements TaskbarOverviewProxyDelegate {
private static final String WINDOW_TITLE = "Taskbar";
private final TaskbarContainerView mTaskbarContainerView;
private final TaskbarView mTaskbarViewInApp;
private final TaskbarView mTaskbarViewOnHome;
private final ImeBarView mImeBarView;
private final BaseQuickstepLauncher mLauncher;
private final WindowManager mWindowManager;
// Layout width and height of the Taskbar in the default state.
private final Point mTaskbarSize;
private final TaskbarStateHandler mTaskbarStateHandler;
private final TaskbarAnimationController mTaskbarAnimationController;
private final TaskbarHotseatController mHotseatController;
private final TaskbarDragController mDragController;
private final TaskbarNavButtonController mNavButtonController;
// Initialized in init().
private WindowManager.LayoutParams mWindowLayoutParams;
private SysUINavigationMode.Mode mNavMode = SysUINavigationMode.Mode.NO_BUTTON;
private final SysUINavigationMode.NavigationModeChangeListener mNavigationModeChangeListener =
this::onNavModeChanged;
private @Nullable Animator mAnimator;
private boolean mIsAnimatingToLauncher;
public TaskbarController(BaseQuickstepLauncher launcher,
TaskbarContainerView taskbarContainerView, TaskbarView taskbarViewOnHome) {
mLauncher = launcher;
mTaskbarContainerView = taskbarContainerView;
mTaskbarContainerView.construct(createTaskbarContainerViewCallbacks());
ButtonProvider buttonProvider = new ButtonProvider(launcher);
mTaskbarViewInApp = mTaskbarContainerView.findViewById(R.id.taskbar_view);
mTaskbarViewInApp.construct(createTaskbarViewCallbacks(), buttonProvider);
mTaskbarViewOnHome = taskbarViewOnHome;
mTaskbarViewOnHome.construct(createTaskbarViewCallbacks(), buttonProvider);
mImeBarView = mTaskbarContainerView.findViewById(R.id.ime_bar_view);
mImeBarView.construct(buttonProvider);
mNavButtonController = new TaskbarNavButtonController(launcher);
mWindowManager = mLauncher.getWindowManager();
mTaskbarSize = new Point(MATCH_PARENT, mLauncher.getDeviceProfile().taskbarSize);
mTaskbarStateHandler = mLauncher.getTaskbarStateHandler();
mTaskbarAnimationController = new TaskbarAnimationController(mLauncher,
createTaskbarAnimationControllerCallbacks());
mHotseatController = new TaskbarHotseatController(mLauncher,
createTaskbarHotseatControllerCallbacks());
mDragController = new TaskbarDragController(mLauncher);
}
private TaskbarAnimationControllerCallbacks createTaskbarAnimationControllerCallbacks() {
return new TaskbarAnimationControllerCallbacks() {
@Override
public void updateTaskbarBackgroundAlpha(float alpha) {
mTaskbarContainerView.setTaskbarBackgroundAlpha(alpha);
}
@Override
public void updateTaskbarVisibilityAlpha(float alpha) {
mTaskbarViewInApp.setAlpha(alpha);
mTaskbarViewOnHome.setAlpha(alpha);
}
@Override
public void updateImeBarVisibilityAlpha(float alpha) {
if (mNavMode != SysUINavigationMode.Mode.THREE_BUTTONS) {
// TODO Remove sysui IME bar for gesture nav as well
return;
}
mImeBarView.setAlpha(alpha);
mImeBarView.setVisibility(alpha == 0 ? GONE : VISIBLE);
}
@Override
public void updateTaskbarScale(float scale) {
mTaskbarViewInApp.setScaleX(scale);
mTaskbarViewInApp.setScaleY(scale);
}
@Override
public void updateTaskbarTranslationY(float translationY) {
if (translationY < 0) {
// Resize to accommodate the max translation we'll reach.
setTaskbarWindowHeight(mTaskbarSize.y
+ mLauncher.getHotseat().getTaskbarOffsetY());
} else {
setTaskbarWindowHeight(mTaskbarSize.y);
}
mTaskbarViewInApp.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.
*/
public void onLauncherResumedOrPaused(boolean isResumed) {
long duration = QuickstepTransitionManager.CONTENT_ALPHA_DURATION;
if (mAnimator != null) {
mAnimator.cancel();
}
if (isResumed) {
mAnimator = createAnimToLauncher(null, duration);
} else {
mAnimator = createAnimToApp(duration);
}
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mAnimator = null;
}
});
mAnimator.start();
}
/**
* Create Taskbar animation when going from an app to Launcher.
* @param toState If known, the state we will end up in when reaching Launcher.
*/
public Animator createAnimToLauncher(@Nullable LauncherState toState, long duration) {
PendingAnimation anim = new PendingAnimation(duration);
anim.add(mTaskbarAnimationController.createAnimToBackgroundAlpha(0, duration));
if (toState != null) {
mTaskbarStateHandler.setStateWithAnimation(toState, new StateAnimationConfig(), anim);
}
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mIsAnimatingToLauncher = true;
mTaskbarViewInApp.updateHotseatItemsVisibility();
}
@Override
public void onAnimationEnd(Animator animation) {
mIsAnimatingToLauncher = false;
setWhichTaskbarViewIsVisible(mTaskbarViewOnHome);
}
});
return anim.buildAnim();
}
private Animator createAnimToApp(long duration) {
PendingAnimation anim = new PendingAnimation(duration);
anim.add(mTaskbarAnimationController.createAnimToBackgroundAlpha(1, duration));
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mTaskbarViewInApp.updateHotseatItemsVisibility();
setWhichTaskbarViewIsVisible(mTaskbarViewInApp);
}
@Override
public void onAnimationEnd(Animator animation) {
}
});
return anim.buildAnim();
}
/**
* Should be called when the IME visibility changes, so we can hide/show Taskbar accordingly.
*/
public void setIsImeVisible(boolean isImeVisible) {
mTaskbarAnimationController.animateToVisibilityForIme(isImeVisible ? 0 : 1);
blockTaskbarTouchesForIme(isImeVisible);
}
/**
* When in 3 button nav, the above doesn't get called since we prevent sysui nav bar from
* instantiating at all, which is what's responsible for sending sysui state flags over.
*
* @param vis IME visibility flag
* @param backDisposition Used to determine back button behavior for software keyboard
* See BACK_DISPOSITION_* constants in {@link InputMethodService}
*/
public void updateImeStatus(int displayId, int vis, int backDisposition,
boolean showImeSwitcher) {
if (displayId != mTaskbarContainerView.getContext().getDisplayId() ||
mNavMode != SysUINavigationMode.Mode.THREE_BUTTONS) {
return;
}
boolean imeVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
mTaskbarAnimationController.animateToVisibilityForIme(imeVisible ? 0 : 1);
mImeBarView.setImeSwitcherVisibility(showImeSwitcher);
blockTaskbarTouchesForIme(imeVisible);
}
/**
* Should be called when one or more items in the Hotseat have changed.
*/
public void onHotseatUpdated() {
mHotseatController.onHotseatUpdated();
}
/**
* @param ev MotionEvent in screen coordinates.
* @return Whether any Taskbar item could handle the given MotionEvent if given the chance.
*/
public boolean isEventOverAnyTaskbarItem(MotionEvent ev) {
return mTaskbarViewInApp.isEventOverAnyItem(ev);
}
public boolean isDraggingItem() {
return mTaskbarViewInApp.isDraggingItem() || mTaskbarViewOnHome.isDraggingItem();
}
/**
* @return Whether the given View is in the same window as Taskbar.
*/
public boolean isViewInTaskbar(View v) {
return mTaskbarContainerView.isAttachedToWindow()
&& mTaskbarContainerView.getWindowId().equals(v.getWindowId());
}
/**
* Pads the Hotseat to line up exactly with Taskbar's copy of the Hotseat.
*/
public void alignRealHotseatWithTaskbar() {
Rect hotseatBounds = new Rect();
DeviceProfile grid = mLauncher.getDeviceProfile();
int hotseatHeight = grid.workspacePadding.bottom + grid.taskbarSize;
int taskbarOffset = mLauncher.getHotseat().getTaskbarOffsetY();
int hotseatTopDiff = hotseatHeight - grid.taskbarSize - taskbarOffset;
int hotseatBottomDiff = taskbarOffset;
mTaskbarViewOnHome.getHotseatBounds().roundOut(hotseatBounds);
mLauncher.getHotseat().setPadding(hotseatBounds.left,
hotseatBounds.top + hotseatTopDiff,
mTaskbarViewOnHome.getWidth() - hotseatBounds.right,
mTaskbarViewOnHome.getHeight() - hotseatBounds.bottom + hotseatBottomDiff);
}
private void setWhichTaskbarViewIsVisible(@Nullable TaskbarView visibleTaskbar) {
mTaskbarViewInApp.setVisibility(visibleTaskbar == mTaskbarViewInApp
? VISIBLE : INVISIBLE);
mTaskbarViewOnHome.setVisibility(visibleTaskbar == mTaskbarViewOnHome
? VISIBLE : INVISIBLE);
mLauncher.getHotseat().setIconsAlpha(visibleTaskbar != mTaskbarViewInApp ? 1f : 0f);
}
private void blockTaskbarTouchesForIme(boolean block) {
mTaskbarViewOnHome.setTouchesEnabled(!block);
mTaskbarViewInApp.setTouchesEnabled(!block);
}
/**
* Returns the ratio of the taskbar icon size on home vs in an app.
*/
public float getTaskbarScaleOnHome() {
DeviceProfile inAppDp = mTaskbarContainerView.getTaskbarActivityContext()
.getDeviceProfile();
DeviceProfile onHomeDp = ActivityContext.lookupContext(mTaskbarViewOnHome.getContext())
.getDeviceProfile();
return (float) onHomeDp.cellWidthPx / inAppDp.cellWidthPx;
}
/**
* Updates the TaskbarContainer to MATCH_PARENT vs original Taskbar size.
*/
private void setTaskbarWindowFullscreen(boolean fullscreen) {
setTaskbarWindowHeight(fullscreen ? MATCH_PARENT : mTaskbarSize.y);
}
/**
* Updates the TaskbarContainer height (pass mTaskbarSize.y to reset).
*/
private void setTaskbarWindowHeight(int height) {
mWindowLayoutParams.width = mTaskbarSize.x;
mWindowLayoutParams.height = height;
mWindowManager.updateViewLayout(mTaskbarContainerView, mWindowLayoutParams);
}
/**
* Contains methods that TaskbarStateHandler can call to interface with TaskbarController.
*/
protected interface TaskbarStateHandlerCallbacks {
AnimatedFloat getAlphaTarget();
AnimatedFloat getScaleTarget();
AnimatedFloat getTranslationYTarget();
}
/**
* Contains methods that TaskbarAnimationController can call to interface with
* TaskbarController.
*/
protected interface TaskbarAnimationControllerCallbacks {
void updateTaskbarBackgroundAlpha(float alpha);
void updateTaskbarVisibilityAlpha(float alpha);
void updateImeBarVisibilityAlpha(float alpha);
void updateTaskbarScale(float scale);
void updateTaskbarTranslationY(float translationY);
}
/**
* Contains methods that TaskbarContainerView can call to interface with TaskbarController.
*/
protected interface TaskbarContainerViewCallbacks {
void onViewRemoved();
boolean isTaskbarTouchable();
}
/**
* Contains methods that TaskbarView can call to interface with TaskbarController.
*/
protected interface TaskbarViewCallbacks {
View.OnClickListener getItemOnClickListener();
View.OnLongClickListener getItemOnLongClickListener();
int getEmptyHotseatViewVisibility(TaskbarView taskbarView);
/** Returns how much to scale non-icon elements such as spacing and dividers. */
float getNonIconScale(TaskbarView taskbarView);
void onItemPositionsChanged(TaskbarView taskbarView);
void onNavigationButtonClick(@TaskbarButton int buttonType);
}
/**
* Contains methods that TaskbarHotseatController can call to interface with TaskbarController.
*/
protected interface TaskbarHotseatControllerCallbacks {
void updateHotseatItems(ItemInfo[] hotseatItemInfos);
}
}
@@ -20,6 +20,7 @@ import static android.view.View.VISIBLE;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.res.Resources;
@@ -29,7 +30,6 @@ import android.os.UserHandle;
import android.view.DragEvent;
import android.view.View;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
@@ -43,12 +43,12 @@ import com.android.systemui.shared.system.LauncherAppsCompat;
*/
public class TaskbarDragController {
private final BaseQuickstepLauncher mLauncher;
private final Context mContext;
private final int mDragIconSize;
public TaskbarDragController(BaseQuickstepLauncher launcher) {
mLauncher = launcher;
Resources resources = mLauncher.getResources();
public TaskbarDragController(Context context) {
mContext = context;
Resources resources = mContext.getResources();
mDragIconSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_drag_icon_size);
}
@@ -63,7 +63,6 @@ public class TaskbarDragController {
}
BubbleTextView btv = (BubbleTextView) view;
View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view) {
@Override
public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
@@ -87,7 +86,7 @@ public class TaskbarDragController {
Intent intent = null;
if (tag instanceof WorkspaceItemInfo) {
WorkspaceItemInfo item = (WorkspaceItemInfo) tag;
LauncherApps launcherApps = mLauncher.getSystemService(LauncherApps.class);
LauncherApps launcherApps = mContext.getSystemService(LauncherApps.class);
clipDescription = new ClipDescription(item.title,
new String[] {
item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
@@ -26,6 +26,8 @@ import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.model.data.ItemInfo;
import java.util.function.Consumer;
/**
* Works with TaskbarController to update the TaskbarView's Hotseat items.
*/
@@ -33,13 +35,12 @@ public class TaskbarHotseatController {
private final BaseQuickstepLauncher mLauncher;
private final Hotseat mHotseat;
private final TaskbarController.TaskbarHotseatControllerCallbacks mTaskbarCallbacks;
private final Consumer<ItemInfo[]> mTaskbarCallbacks;
private final int mNumHotseatIcons;
private final DragController.DragListener mDragListener = new DragController.DragListener() {
@Override
public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
}
public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { }
@Override
public void onDragEnd() {
@@ -47,8 +48,8 @@ public class TaskbarHotseatController {
}
};
public TaskbarHotseatController(BaseQuickstepLauncher launcher,
TaskbarController.TaskbarHotseatControllerCallbacks taskbarCallbacks) {
public TaskbarHotseatController(
BaseQuickstepLauncher launcher, Consumer<ItemInfo[]> taskbarCallbacks) {
mLauncher = launcher;
mHotseat = mLauncher.getHotseat();
mTaskbarCallbacks = taskbarCallbacks;
@@ -85,10 +86,6 @@ public class TaskbarHotseatController {
}
}
mTaskbarCallbacks.updateHotseatItems(hotseatItemInfos);
}
protected int getNumHotseatIcons() {
return mNumHotseatIcons;
mTaskbarCallbacks.accept(hotseatItemInfos);
}
}
@@ -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,170 @@
/*
* 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;
private boolean mUserUnlocked = false;
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;
}
}
/**
* Called when the user is unlocked
*/
public void onUserUnlocked() {
mUserUnlocked = true;
recreateTaskbar();
}
/**
* 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 LauncherTaskbarUIController(launcher, mTaskbarActivityContext));
}
}
private void recreateTaskbar() {
destroyExistingTaskbar();
if (!FeatureFlags.ENABLE_TASKBAR.get()) {
return;
}
if (!mUserUnlocked) {
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 LauncherTaskbarUIController(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;
import android.content.Context;
import static android.view.Display.DEFAULT_DISPLAY;
import android.content.Intent;
import android.view.inputmethod.InputMethodManager;
@@ -53,11 +54,10 @@ public class TaskbarNavButtonController {
static final int BUTTON_RECENTS = BUTTON_HOME << 1;
static final int BUTTON_IME_SWITCH = BUTTON_RECENTS << 1;
private final TouchInteractionService mService;
private final Context mContext;
public TaskbarNavButtonController(Context context) {
mContext = context;
public TaskbarNavButtonController(TouchInteractionService service) {
mService = service;
}
public void onButtonClick(@TaskbarButton int buttonType) {
@@ -78,13 +78,13 @@ public class TaskbarNavButtonController {
}
private void navigateHome() {
mContext.startActivity(new Intent(Intent.ACTION_MAIN)
mService.startActivity(new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
private void navigateToOverview() {
TouchInteractionService.getInstance().getOverviewCommandHelper()
mService.getOverviewCommandHelper()
.addCommand(OverviewCommandHelper.TYPE_SHOW);
}
@@ -93,8 +93,8 @@ public class TaskbarNavButtonController {
}
private void showIMESwitcher() {
mContext.getSystemService(InputMethodManager.class).showInputMethodPickerFromSystem(
true /* showAuxiliarySubtypes */, mContext.getDisplayId());
mService.getSystemService(InputMethodManager.class)
.showInputMethodPickerFromSystem(true /* showAuxiliarySubtypes */,
DEFAULT_DISPLAY);
}
}
@@ -24,59 +24,52 @@ import androidx.annotation.Nullable;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.quickstep.AnimatedFloat;
/**
* StateHandler to animate Taskbar according to Launcher's state machine. Does nothing if Taskbar
* isn't present (i.e. {@link #setTaskbarCallbacks} is never called).
* isn't present (i.e. {@link #setAnimationController} is never called).
*/
public class TaskbarStateHandler implements StateManager.StateHandler<LauncherState> {
private final BaseQuickstepLauncher mLauncher;
// Contains Taskbar-related methods and fields we should aniamte. If null, don't do anything.
private @Nullable TaskbarController.TaskbarStateHandlerCallbacks mTaskbarCallbacks = null;
private @Nullable TaskbarAnimationController mAnimationController = null;
public TaskbarStateHandler(BaseQuickstepLauncher launcher) {
mLauncher = launcher;
}
public void setTaskbarCallbacks(TaskbarController.TaskbarStateHandlerCallbacks callbacks) {
mTaskbarCallbacks = callbacks;
public void setAnimationController(TaskbarAnimationController callbacks) {
mAnimationController = callbacks;
}
@Override
public void setState(LauncherState state) {
if (mTaskbarCallbacks == null) {
return;
}
AnimatedFloat alphaTarget = mTaskbarCallbacks.getAlphaTarget();
AnimatedFloat scaleTarget = mTaskbarCallbacks.getScaleTarget();
AnimatedFloat translationYTarget = mTaskbarCallbacks.getTranslationYTarget();
boolean isTaskbarVisible = (state.getVisibleElements(mLauncher) & TASKBAR) != 0;
alphaTarget.updateValue(isTaskbarVisible ? 1f : 0f);
scaleTarget.updateValue(state.getTaskbarScale(mLauncher));
translationYTarget.updateValue(state.getTaskbarTranslationY(mLauncher));
setState(state, PropertySetter.NO_ANIM_PROPERTY_SETTER);
}
@Override
public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
PendingAnimation animation) {
if (mTaskbarCallbacks == null) {
setState(toState, animation);
}
private void setState(LauncherState toState, PropertySetter setter) {
if (mAnimationController == null) {
return;
}
AnimatedFloat alphaTarget = mTaskbarCallbacks.getAlphaTarget();
AnimatedFloat scaleTarget = mTaskbarCallbacks.getScaleTarget();
AnimatedFloat translationYTarget = mTaskbarCallbacks.getTranslationYTarget();
boolean isTaskbarVisible = (toState.getVisibleElements(mLauncher) & TASKBAR) != 0;
animation.setFloat(alphaTarget, AnimatedFloat.VALUE, isTaskbarVisible ? 1f : 0f, LINEAR);
animation.setFloat(scaleTarget, AnimatedFloat.VALUE, toState.getTaskbarScale(mLauncher),
LINEAR);
animation.setFloat(translationYTarget, AnimatedFloat.VALUE,
toState.getTaskbarTranslationY(mLauncher), ACCEL_DEACCEL);
setter.setFloat(mAnimationController.getTaskbarVisibilityForLauncherState(),
AnimatedFloat.VALUE, isTaskbarVisible ? 1f : 0f, LINEAR);
setter.setFloat(mAnimationController.getTaskbarScaleForLauncherState(),
AnimatedFloat.VALUE, toState.getTaskbarScale(mLauncher), LINEAR);
setter.setFloat(mAnimationController.getTaskbarTranslationYForLauncherState(),
AnimatedFloat.VALUE, toState.getTaskbarTranslationY(mLauncher), ACCEL_DEACCEL);
}
}
@@ -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;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.makeMeasureSpec;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.LayoutTransition;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.SystemProperties;
import android.util.AttributeSet;
import android.view.DragEvent;
import android.view.Gravity;
@@ -51,17 +45,12 @@ import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.SysUINavigationMode;
/**
* Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
*/
public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconParent, Insettable {
private static final boolean ENABLE_THREE_BUTTON_TASKBAR =
SystemProperties.getBoolean("persist.debug.taskbar_three_button", false);
private final int mIconTouchSize;
private final boolean mIsRtl;
private final int mTouchSlop;
@@ -69,17 +58,16 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
private final RectF mDelegateSlopBounds = new RectF();
private final int[] mTempOutLocation = new int[2];
// Initialized in TaskbarController constructor.
private TaskbarController.TaskbarViewCallbacks mControllerCallbacks;
// Scale on elements that aren't icons.
private float mNonIconScale;
private int mItemMarginLeftRight;
private final int mItemMarginLeftRight;
// Initialized in init().
private LayoutTransition mLayoutTransition;
private int mHotseatStartIndex;
private int mHotseatEndIndex;
private LinearLayout mButtonRegion;
private final TaskbarActivityContext mActivityContext;
// Initialized in TaskbarController constructor.
private View.OnClickListener mIconClickListener;
private View.OnLongClickListener mIconLongClickListener;
LinearLayout mSystemButtonContainer;
LinearLayout mHotseatIconsContainer;
// Delegate touches to the closest view if within mIconTouchSize.
private boolean mDelegateTargeted;
@@ -91,10 +79,12 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
// Only non-null when the corresponding Folder is open.
private @Nullable FolderIcon mLeaveBehindFolderIcon;
private int mNavButtonStartIndex;
/** Provider of buttons added to taskbar in 3 button nav */
private ButtonProvider mButtonProvider;
private boolean mDisableRelayout;
private boolean mAreHolesAllowed;
public TaskbarView(@NonNull Context context) {
this(context, null);
}
@@ -111,80 +101,58 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
public TaskbarView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mActivityContext = ActivityContext.lookupContext(context);
Resources resources = getResources();
mIconTouchSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_touch_size);
mItemMarginLeftRight = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
mIsRtl = Utilities.isRtl(resources);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
protected void construct(TaskbarController.TaskbarViewCallbacks taskbarViewCallbacks,
ButtonProvider buttonProvider) {
mControllerCallbacks = taskbarViewCallbacks;
mNonIconScale = mControllerCallbacks.getNonIconScale(this);
mItemMarginLeftRight = getResources().getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
mItemMarginLeftRight = Math.round(mItemMarginLeftRight * mNonIconScale);
mButtonProvider = buttonProvider;
mButtonProvider.setMarginLeftRight(mItemMarginLeftRight);
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mSystemButtonContainer = findViewById(R.id.system_button_layout);
mHotseatIconsContainer = findViewById(R.id.hotseat_icons_layout);
}
protected void init(int numHotseatIcons, SysUINavigationMode.Mode newMode) {
// TODO: check if buttons on left
if (newMode == SysUINavigationMode.Mode.THREE_BUTTONS && ENABLE_THREE_BUTTON_TASKBAR) {
// 3 button
mNavButtonStartIndex = 0;
protected void construct(OnClickListener clickListener, OnLongClickListener longClickListener,
ButtonProvider buttonProvider) {
mIconClickListener = clickListener;
mIconLongClickListener = longClickListener;
mButtonProvider = buttonProvider;
if (mActivityContext.canShowNavButtons()) {
createNavButtons();
} else {
mNavButtonStartIndex = -1;
removeNavButtons();
mSystemButtonContainer.setVisibility(GONE);
}
mHotseatStartIndex = mNavButtonStartIndex + 1;
mHotseatEndIndex = mHotseatStartIndex + numHotseatIcons - 1;
int numHotseatIcons = mActivityContext.getDeviceProfile().numShownHotseatIcons;
updateHotseatItems(new ItemInfo[numHotseatIcons]);
mLayoutTransition = new LayoutTransition();
addUpdateListenerForAllLayoutTransitions(() -> {
if (getLayoutTransition() == mLayoutTransition) {
mControllerCallbacks.onItemPositionsChanged(this);
}
});
setLayoutTransition(mLayoutTransition);
}
private void addUpdateListenerForAllLayoutTransitions(Runnable onUpdate) {
addUpdateListenerForLayoutTransition(LayoutTransition.CHANGE_APPEARING, onUpdate);
addUpdateListenerForLayoutTransition(LayoutTransition.CHANGE_DISAPPEARING, onUpdate);
addUpdateListenerForLayoutTransition(LayoutTransition.CHANGING, onUpdate);
addUpdateListenerForLayoutTransition(LayoutTransition.APPEARING, onUpdate);
addUpdateListenerForLayoutTransition(LayoutTransition.DISAPPEARING, onUpdate);
}
private void addUpdateListenerForLayoutTransition(int transitionType, Runnable onUpdate) {
Animator anim = mLayoutTransition.getAnimator(transitionType);
if (anim instanceof ValueAnimator) {
((ValueAnimator) anim).addUpdateListener(valueAnimator -> onUpdate.run());
} else {
AnimatorSet animSet = new AnimatorSet();
ValueAnimator updateAnim = ValueAnimator.ofFloat(0, 1);
updateAnim.addUpdateListener(valueAnimator -> onUpdate.run());
animSet.playTogether(anim, updateAnim);
mLayoutTransition.setAnimator(transitionType, animSet);
/**
* Enables/disables empty icons in taskbar so that the layout matches with Launcher
*/
public void setHolesAllowedInLayout(boolean areHolesAllowed) {
if (mAreHolesAllowed != areHolesAllowed) {
mAreHolesAllowed = areHolesAllowed;
updateHotseatItemsVisibility();
// TODO: Add animation
}
}
protected void cleanup() {
endAllLayoutTransitionAnimators();
setLayoutTransition(null);
removeAllViews();
}
private void endAllLayoutTransitionAnimators() {
mLayoutTransition.getAnimator(LayoutTransition.CHANGE_APPEARING).end();
mLayoutTransition.getAnimator(LayoutTransition.CHANGE_DISAPPEARING).end();
mLayoutTransition.getAnimator(LayoutTransition.CHANGING).end();
mLayoutTransition.getAnimator(LayoutTransition.APPEARING).end();
mLayoutTransition.getAnimator(LayoutTransition.DISAPPEARING).end();
private void setHolesAllowedInLayoutNoAnimation(boolean areHolesAllowed) {
if (mAreHolesAllowed != areHolesAllowed) {
mAreHolesAllowed = areHolesAllowed;
updateHotseatItemsVisibility();
onMeasure(makeMeasureSpec(getMeasuredWidth(), EXACTLY),
makeMeasureSpec(getMeasuredHeight(), EXACTLY));
onLayout(false, getLeft(), getTop(), getRight(), getBottom());
}
}
/**
@@ -192,10 +160,9 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
*/
protected void updateHotseatItems(ItemInfo[] hotseatItemInfos) {
for (int i = 0; i < hotseatItemInfos.length; i++) {
ItemInfo hotseatItemInfo = hotseatItemInfos[!mIsRtl ? i
: hotseatItemInfos.length - i - 1];
int hotseatIndex = mHotseatStartIndex + i;
View hotseatView = getChildAt(hotseatIndex);
ItemInfo hotseatItemInfo = hotseatItemInfos[
!mIsRtl ? i : hotseatItemInfos.length - i - 1];
View hotseatView = mHotseatIconsContainer.getChildAt(i);
// Replace any Hotseat views with the appropriate type if it's not already that type.
final int expectedLayoutResId;
@@ -213,23 +180,23 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
} else {
expectedLayoutResId = R.layout.taskbar_app_icon;
}
if (hotseatView == null || hotseatView.getSourceLayoutResId() != expectedLayoutResId
if (hotseatView == null
|| hotseatView.getSourceLayoutResId() != expectedLayoutResId
|| needsReinflate) {
removeView(hotseatView);
ActivityContext activityContext = getActivityContext();
mHotseatIconsContainer.removeView(hotseatView);
if (isFolder) {
FolderInfo folderInfo = (FolderInfo) hotseatItemInfo;
FolderIcon folderIcon = FolderIcon.inflateFolderAndIcon(expectedLayoutResId,
getActivityContext(), this, folderInfo);
mActivityContext, this, folderInfo);
folderIcon.setTextVisible(false);
hotseatView = folderIcon;
} else {
hotseatView = inflate(expectedLayoutResId);
}
int iconSize = activityContext.getDeviceProfile().iconSizePx;
int iconSize = mActivityContext.getDeviceProfile().iconSizePx;
LayoutParams lp = new LayoutParams(iconSize, iconSize);
lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0);
addView(hotseatView, hotseatIndex, lp);
mHotseatIconsContainer.addView(hotseatView, i, lp);
}
// Apply the Hotseat ItemInfos, or hide the view if there is none for a given index.
@@ -237,13 +204,11 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
&& hotseatItemInfo instanceof WorkspaceItemInfo) {
((BubbleTextView) hotseatView).applyFromWorkspaceItem(
(WorkspaceItemInfo) hotseatItemInfo);
hotseatView.setOnClickListener(mControllerCallbacks.getItemOnClickListener());
hotseatView.setOnLongClickListener(
mControllerCallbacks.getItemOnLongClickListener());
hotseatView.setOnClickListener(mIconClickListener);
hotseatView.setOnLongClickListener(mIconLongClickListener);
} else if (isFolder) {
hotseatView.setOnClickListener(mControllerCallbacks.getItemOnClickListener());
hotseatView.setOnLongClickListener(
mControllerCallbacks.getItemOnLongClickListener());
hotseatView.setOnClickListener(mIconClickListener);
hotseatView.setOnLongClickListener(mIconLongClickListener);
} else {
hotseatView.setOnClickListener(null);
hotseatView.setOnLongClickListener(null);
@@ -254,24 +219,14 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
}
protected void updateHotseatItemsVisibility() {
for (int i = mHotseatStartIndex; i <= mHotseatEndIndex; i++) {
updateHotseatItemVisibility(getChildAt(i));
for (int i = mHotseatIconsContainer.getChildCount() - 1; i >= 0; i--) {
updateHotseatItemVisibility(mHotseatIconsContainer.getChildAt(i));
}
}
private void updateHotseatItemVisibility(View hotseatView) {
if (hotseatView.getTag() != null) {
hotseatView.setVisibility(VISIBLE);
} else {
int oldVisibility = hotseatView.getVisibility();
int newVisibility = mControllerCallbacks.getEmptyHotseatViewVisibility(this);
hotseatView.setVisibility(newVisibility);
if (oldVisibility == GONE && newVisibility != GONE) {
// By default, the layout transition only runs when going to VISIBLE,
// but we want it to run when going to GONE to INVISIBLE as well.
getLayoutTransition().showChild(this, hotseatView, oldVisibility);
}
}
hotseatView.setVisibility(
hotseatView.getTag() != null ? VISIBLE : (mAreHolesAllowed ? INVISIBLE : GONE));
}
@Override
@@ -378,49 +333,20 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
return findDelegateView(xInOurCoordinates, yInOurCoorindates) != null;
}
private void removeNavButtons() {
if (mButtonRegion != null) {
mButtonRegion.removeAllViews();
removeView(mButtonRegion);
} // else We've never been in 3 button. Woah Scoob!
}
/**
* Add back/home/recents buttons into a single ViewGroup that will be inserted at
* {@param navButtonStartIndex}
*/
private void createNavButtons() {
ActivityContext context = getActivityContext();
if (mButtonRegion == null) {
mButtonRegion = new LinearLayout(getContext());
} else {
mButtonRegion.removeAllViews();
}
mButtonRegion.setVisibility(VISIBLE);
LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams(
context.getDeviceProfile().iconSizePx,
context.getDeviceProfile().iconSizePx
mActivityContext.getDeviceProfile().iconSizePx,
mActivityContext.getDeviceProfile().iconSizePx
);
buttonParams.gravity = Gravity.CENTER;
View backButton = mButtonProvider.getBack();
backButton.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
BUTTON_BACK));
mButtonRegion.addView(backButton, buttonParams);
// Home button
View homeButton = mButtonProvider.getHome();
homeButton.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
BUTTON_HOME));
mButtonRegion.addView(homeButton, buttonParams);
View recentsButton = mButtonProvider.getRecents();
recentsButton.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
BUTTON_RECENTS));
mButtonRegion.addView(recentsButton, buttonParams);
addView(mButtonRegion, mNavButtonStartIndex);
mSystemButtonContainer.addView(mButtonProvider.getBack(), buttonParams);
mSystemButtonContainer.addView(mButtonProvider.getHome(), buttonParams);
mSystemButtonContainer.addView(mButtonProvider.getRecents(), buttonParams);
}
@Override
@@ -428,7 +354,7 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
mIsDraggingItem = true;
AbstractFloatingView.closeAllOpenViews(getActivityContext());
AbstractFloatingView.closeAllOpenViews(mActivityContext);
return true;
case DragEvent.ACTION_DRAG_ENDED:
mIsDraggingItem = false;
@@ -445,26 +371,26 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
* @return The bounding box of where the hotseat elements are relative to this TaskbarView.
*/
protected RectF getHotseatBounds() {
View firstHotseatView = null, lastHotseatView = null;
for (int i = mHotseatStartIndex; i <= mHotseatEndIndex; i++) {
View child = getChildAt(i);
if (child.getVisibility() != GONE) {
if (firstHotseatView == null) {
firstHotseatView = child;
}
lastHotseatView = child;
}
RectF result;
mDisableRelayout = true;
boolean wereHolesAllowed = mAreHolesAllowed;
setHolesAllowedInLayoutNoAnimation(true);
result = new RectF(
mHotseatIconsContainer.getLeft(),
mHotseatIconsContainer.getTop(),
mHotseatIconsContainer.getRight(),
mHotseatIconsContainer.getBottom());
setHolesAllowedInLayoutNoAnimation(wereHolesAllowed);
mDisableRelayout = false;
return result;
}
@Override
public void requestLayout() {
if (!mDisableRelayout) {
super.requestLayout();
}
if (firstHotseatView == null || lastHotseatView == null) {
return new RectF();
}
View leftmostHotseatView = !mIsRtl ? firstHotseatView : lastHotseatView;
View rightmostHotseatView = !mIsRtl ? lastHotseatView : firstHotseatView;
return new RectF(
leftmostHotseatView.getLeft() - mItemMarginLeftRight,
leftmostHotseatView.getTop(),
rightmostHotseatView.getRight() + mItemMarginLeftRight,
rightmostHotseatView.getBottom());
}
// FolderIconParent implemented methods.
@@ -495,7 +421,7 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
}
private View inflate(@LayoutRes int layoutResId) {
return getActivityContext().getLayoutInflater().inflate(layoutResId, this, false);
return mActivityContext.getLayoutInflater().inflate(layoutResId, this, false);
}
@Override
@@ -503,7 +429,11 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
// Ignore, we just implement Insettable to draw behind system insets.
}
private <T extends Context & ActivityContext> T getActivityContext() {
return ActivityContext.lookupContext(getContext());
public void setIconsVisibility(boolean isVisible) {
mHotseatIconsContainer.setVisibility(isVisible ? VISIBLE : INVISIBLE);
}
public boolean areIconsVisible() {
return mHotseatIconsContainer.getVisibility() == VISIBLE;
}
}
@@ -165,7 +165,7 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
@Override
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
// Only pause is taskbar controller is not present
mHotseatPredictionController.setPauseUIUpdate(getTaskbarController() == null);
mHotseatPredictionController.setPauseUIUpdate(getTaskbarUIController() == null);
return super.startActivitySafely(v, intent, item);
}
@@ -233,9 +233,9 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
@Override
public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) {
super.bindWorkspaceItemsChanged(updated);
if (getTaskbarController() != null && updated.stream()
if (getTaskbarUIController() != null && updated.stream()
.filter(w -> w.container == CONTAINER_HOTSEAT).findFirst().isPresent()) {
getTaskbarController().onHotseatUpdated();
getTaskbarUIController().onHotseatUpdated();
}
}
@@ -52,7 +52,6 @@ import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.taskbar.TaskbarController;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.views.ScrimView;
@@ -123,11 +122,6 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
return null;
}
@Nullable
public TaskbarController getTaskbarController() {
return null;
}
public final boolean isResumed() {
ACTIVITY_TYPE activity = getCreatedActivity();
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);
/**
* 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.
*/
@@ -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.Interpolators.LINEAR;
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.AnimatorSet;
@@ -42,7 +41,7 @@ import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statehandlers.DepthController.ClampedDepthProperty;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.taskbar.TaskbarController;
import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.SysUINavigationMode.Mode;
@@ -164,13 +163,12 @@ public final class LauncherActivityInterface extends
}
@Nullable
@Override
public TaskbarController getTaskbarController() {
private LauncherTaskbarUIController getTaskbarController() {
BaseQuickstepLauncher launcher = getCreatedActivity();
if (launcher == null) {
return null;
}
return launcher.getTaskbarController();
return launcher.getTaskbarUIController();
}
@Nullable
@@ -278,13 +276,13 @@ public final class LauncherActivityInterface extends
@Override
public @Nullable Animator getParallelAnimationToLauncher(GestureEndTarget endTarget,
long duration) {
TaskbarController taskbarController = getTaskbarController();
LauncherTaskbarUIController uiController = getTaskbarController();
Animator superAnimator = super.getParallelAnimationToLauncher(endTarget, duration);
if (taskbarController == null) {
if (uiController == null) {
return superAnimator;
}
LauncherState toState = stateFromGestureEndTarget(endTarget);
Animator taskbarAnimator = taskbarController.createAnimToLauncher(toState, duration);
Animator taskbarAnimator = uiController.createAnimToLauncher(toState, duration);
if (superAnimator == null) {
return taskbarAnimator;
} else {
@@ -300,32 +298,22 @@ public final class LauncherActivityInterface extends
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
public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
TaskbarController taskbarController = getTaskbarController();
if (taskbarController == null) {
LauncherTaskbarUIController uiController = getTaskbarController();
if (uiController == null) {
return super.deferStartingActivity(deviceState, ev);
}
return taskbarController.isEventOverAnyTaskbarItem(ev);
return uiController.isEventOverAnyTaskbarItem(ev);
}
@Override
public boolean shouldCancelCurrentGesture() {
TaskbarController taskbarController = getTaskbarController();
if (taskbarController == null) {
LauncherTaskbarUIController uiController = getTaskbarController();
if (uiController == null) {
return super.shouldCancelCurrentGesture();
}
return taskbarController.isDraggingItem();
return uiController.isDraggingItem();
}
@Override
@@ -52,7 +52,6 @@ import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.Log;
@@ -75,6 +74,7 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.taskbar.TaskbarManager;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.tracing.LauncherTraceProto;
@@ -147,21 +147,9 @@ public class TouchInteractionService extends Service implements PluginListener<O
private OverscrollPlugin mOverscrollPlugin;
/**
* Extension of OverviewProxy aidl interface without needing to modify the actual interface.
* This is for methods that need only need local access and not intended to make IPC calls.
* Local IOverviewProxy implementation with some methods for local components
*/
public abstract static 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;
}
public class TISBinder extends IOverviewProxy.Stub {
@BinderThread
public void onInitialize(Bundle bundle) {
@@ -274,40 +262,24 @@ public class TouchInteractionService extends Service implements PluginListener<O
@Override
public void onImeWindowStatusChanged(int displayId, IBinder token, int vis,
int backDisposition, boolean showImeSwitcher) throws RemoteException {
if (mTaskbarOverviewProxyDelegate == null) {
return;
}
MAIN_EXECUTOR.execute(() -> {
if (mTaskbarOverviewProxyDelegate == null) {
return;
}
mTaskbarOverviewProxyDelegate
.updateImeStatus(displayId, vis, backDisposition, showImeSwitcher);
});
int backDisposition, boolean showImeSwitcher) {
MAIN_EXECUTOR.execute(() -> mTaskbarManager.updateImeStatus(
displayId, vis, backDisposition, showImeSwitcher));
}
};
public interface TaskbarOverviewProxyDelegate {
void updateImeStatus(int displayId, int vis, int backDisposition,
boolean showImeSwitcher);
public TaskbarManager getTaskbarManager() {
return mTaskbarManager;
}
}
private static boolean sConnected = false;
private static TouchInteractionService sInstance;
private static boolean sIsInitialized = false;
private RotationTouchHelper mRotationTouchHelper;
@Nullable
private TaskbarOverviewProxyDelegate mTaskbarOverviewProxyDelegate;
public static boolean isConnected() {
return sConnected;
}
@Nullable
public static TouchInteractionService getInstance() {
return sInstance;
}
public static boolean isInitialized() {
return sIsInitialized;
@@ -336,9 +308,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
private DisplayManager mDisplayManager;
public TouchInteractionService() {
sInstance = this;
}
private TaskbarManager mTaskbarManager;
@Override
public void onCreate() {
@@ -348,13 +318,15 @@ public class TouchInteractionService extends Service implements PluginListener<O
mMainChoreographer = Choreographer.getInstance();
mAM = ActivityManagerWrapper.getInstance();
mDeviceState = new RecentsAnimationDeviceState(this, true);
mDisplayManager = getSystemService(DisplayManager.class);
mTaskbarManager = new TaskbarManager(this);
mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
mDeviceState.addOneHandedModeChangedCallback(this::onOneHandedModeOverlayChanged);
mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
mDeviceState.runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
ProtoTracer.INSTANCE.get(this).add(this);
mDisplayManager = getSystemService(DisplayManager.class);
sConnected = true;
}
@@ -468,8 +440,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
int systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
mOverviewComponentObserver.onSystemUiStateChanged();
mOverviewComponentObserver.getActivityInterface().onSystemUiFlagsChanged(
systemUiStateFlags);
mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags);
if ((lastSysUIFlags & SYSUI_STATE_TRACING_ENABLED) !=
(systemUiStateFlags & SYSUI_STATE_TRACING_ENABLED)) {
@@ -512,6 +483,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
getSystemService(AccessibilityManager.class)
.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
mTaskbarManager.destroy();
sConnected = false;
super.onDestroy();
}
@@ -519,7 +491,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "Touch service connected: user=" + getUserId());
return mMyBinder;
return new TISBinder();
}
private void onInputEvent(InputEvent ev) {
@@ -116,7 +116,7 @@ public class StaggeredWorkspaceAnim {
addStaggeredAnimationForView(child, lp.cellY + 1, totalRows);
}
} else {
final int hotseatRow, qsbRow, taskbarRow;
final int hotseatRow, qsbRow;
if (grid.isTaskbarPresent) {
qsbRow = grid.inv.numRows + 1;
hotseatRow = grid.inv.numRows + 2;
@@ -124,16 +124,12 @@ public class StaggeredWorkspaceAnim {
hotseatRow = grid.inv.numRows + 1;
qsbRow = grid.inv.numRows + 2;
}
// Taskbar and hotseat overlap.
taskbarRow = hotseatRow;
for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) {
View child = hotseatIcons.getChildAt(i);
addStaggeredAnimationForView(child, hotseatRow, totalRows);
}
addStaggeredAnimationForView(hotseat.getQsb(), qsbRow, totalRows);
addStaggeredAnimationForView(hotseat.getTaskbarView(), taskbarRow, totalRows);
}
mAnimators.addListener(new AnimatorListenerAdapter() {