Merge changes I46501369,Icd1ddd43 into sc-dev

* changes:
  Revert "Moving taskbar lifecycle to TouchInteractionService"
  Revert "Renaming TaskbarController to LauncherTaskbarUIController"
This commit is contained in:
Kholoud Mohamed 2021-05-20 15:30:34 +00:00 committed by Android (Google) Code Review
commit 2c2163329b
32 changed files with 1204 additions and 1171 deletions

View File

@ -22,29 +22,9 @@
<com.android.launcher3.taskbar.TaskbarView
android:id="@+id/taskbar_view"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:forceHasOverlappingRendering="false"
android:layout_gravity="bottom" >
<LinearLayout
android:id="@+id/system_button_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/taskbar_nav_buttons_spacing"
android:paddingRight="@dimen/taskbar_nav_buttons_spacing"
android:forceHasOverlappingRendering="false"
android:gravity="center" />
<LinearLayout
android:id="@+id/hotseat_icons_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:forceHasOverlappingRendering="false"
android:gravity="center" />
</com.android.launcher3.taskbar.TaskbarView>
android:gravity="center"/>
<com.android.launcher3.taskbar.ImeBarView
android:id="@+id/ime_bar_view"

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<com.android.launcher3.taskbar.TaskbarView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/taskbar_view"
android:layout_width="match_parent"
android:layout_height="@dimen/taskbar_size"
android:background="@android:color/transparent"
android:layout_gravity="bottom"
android:gravity="center"
android:visibility="gone" />

View File

@ -149,5 +149,4 @@
<!-- Note that this applies to both sides of all icons, so visible space is double this. -->
<dimen name="taskbar_icon_spacing">8dp</dimen>
<dimen name="taskbar_folder_margin">16dp</dimen>
<dimen name="taskbar_nav_buttons_spacing">16dp</dimen>
</resources>

View File

@ -20,6 +20,7 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
import static com.android.launcher3.LauncherState.FLAG_HIDE_BACK_BUTTON;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.NO_OFFSET;
import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
@ -29,6 +30,7 @@ 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;
@ -49,11 +51,13 @@ 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.LauncherTaskbarUIController;
import com.android.launcher3.taskbar.TaskbarManager;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarController;
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;
@ -63,7 +67,6 @@ 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;
@ -85,6 +88,8 @@ 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.
@ -95,20 +100,8 @@ public abstract class BaseQuickstepLauncher extends Launcher
private OverviewActionsView mActionsView;
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 @Nullable TaskbarController mTaskbarController;
private final TaskbarStateHandler mTaskbarStateHandler = new TaskbarStateHandler(this);
// Will be updated when dragging from taskbar.
private @Nullable DragOptions mNextWorkspaceDragOptions = null;
private SplitPlaceholderView mSplitPlaceholderView;
@ -118,6 +111,24 @@ 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
@ -125,12 +136,15 @@ public abstract class BaseQuickstepLauncher extends Launcher
mAppTransitionManager.onActivityDestroyed();
SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
unbindService(mTisBinderConnection);
if (mTaskbarManager != null) {
mTaskbarManager.setLauncher(null);
if (mTaskbarController != null) {
mTaskbarController.cleanup();
mTaskbarController = null;
if (mTisBinder != null) {
mTisBinder.setTaskbarOverviewProxyDelegate(null);
unbindService(mTisBinderConnection);
}
}
super.onDestroy();
}
@ -257,12 +271,37 @@ public abstract class BaseQuickstepLauncher extends Launcher
mAppTransitionManager = new QuickstepTransitionManager(this);
mAppTransitionManager.registerRemoteAnimations();
bindService(new Intent(this, TouchInteractionService.class), mTisBinderConnection, 0);
addTaskbarIfNecessary();
addOnDeviceProfileChangeListener(newDp -> addTaskbarIfNecessary());
}
public void setTaskbarUIController(LauncherTaskbarUIController taskbarUIController) {
mTaskbarUIController = taskbarUIController;
@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 <T extends OverviewActionsView> T getActionsView() {
@ -292,8 +331,8 @@ public abstract class BaseQuickstepLauncher extends Launcher
return mDepthController;
}
public @Nullable LauncherTaskbarUIController getTaskbarUIController() {
return mTaskbarUIController;
public @Nullable TaskbarController getTaskbarController() {
return mTaskbarController;
}
public TaskbarStateHandler getTaskbarStateHandler() {
@ -301,9 +340,14 @@ 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();
&& FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM.get()
&& !isViewInTaskbar(clickedView);
}
@Override
@ -349,8 +393,8 @@ public abstract class BaseQuickstepLauncher extends Launcher
@Override
public float getNormalTaskbarScale() {
if (mTaskbarUIController != null) {
return mTaskbarUIController.getTaskbarScaleOnHome();
if (mTaskbarController != null) {
return mTaskbarController.getTaskbarScaleOnHome();
}
return super.getNormalTaskbarScale();
}
@ -372,8 +416,8 @@ public abstract class BaseQuickstepLauncher extends Launcher
}
if ((changeBits & ACTIVITY_STATE_RESUMED) != 0) {
if (mTaskbarUIController != null) {
mTaskbarUIController.onLauncherResumedOrPaused(hasBeenResumed());
if (mTaskbarController != null) {
mTaskbarController.onLauncherResumedOrPaused(hasBeenResumed());
}
}

View File

@ -1252,6 +1252,7 @@ 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);
@ -1262,6 +1263,8 @@ 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);

View File

@ -263,8 +263,8 @@ public class HotseatPredictionController implements DragController.DragListener,
removeOutlineDrawings();
}
if (mLauncher.getTaskbarUIController() != null) {
mLauncher.getTaskbarUIController().onHotseatUpdated();
if (mLauncher.getTaskbarController() != null) {
mLauncher.getTaskbarController().onHotseatUpdated();
}
}

View File

@ -16,17 +16,12 @@
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.
@ -34,46 +29,47 @@ import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton;
*/
public class ButtonProvider {
private final int mMarginLeftRight;
private final TaskbarActivityContext mContext;
private int mMarginLeftRight;
private final Context mContext;
public ButtonProvider(TaskbarActivityContext context) {
public ButtonProvider(Context context) {
mContext = context;
mMarginLeftRight = context.getResources()
.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
}
public void setMarginLeftRight(int margin) {
mMarginLeftRight = margin;
}
public View getBack() {
// Back button
return getButtonForDrawable(R.drawable.ic_sysbar_back, BUTTON_BACK);
return getButtonForDrawable(R.drawable.ic_sysbar_back);
}
public View getDown() {
// Ime down button
return getButtonForDrawable(R.drawable.ic_sysbar_back, BUTTON_BACK);
return getButtonForDrawable(R.drawable.ic_sysbar_back);
}
public View getHome() {
// Home button
return getButtonForDrawable(R.drawable.ic_sysbar_home, BUTTON_HOME);
return getButtonForDrawable(R.drawable.ic_sysbar_home);
}
public View getRecents() {
// Recents button
return getButtonForDrawable(R.drawable.ic_sysbar_recent, BUTTON_RECENTS);
return getButtonForDrawable(R.drawable.ic_sysbar_recent);
}
public View getImeSwitcher() {
// IME Switcher Button
return getButtonForDrawable(R.drawable.ic_ime_switcher, BUTTON_IME_SWITCH);
return getButtonForDrawable(R.drawable.ic_ime_switcher);
}
private View getButtonForDrawable(@DrawableRes int drawableId, @TaskbarButton int buttonType) {
private View getButtonForDrawable(@DrawableRes int drawableId) {
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;
}

View File

@ -16,6 +16,9 @@
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;
@ -26,6 +29,7 @@ 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) {
@ -40,9 +44,12 @@ public class ImeBarView extends RelativeLayout {
super(context, attrs, defStyleAttr);
}
public void init(ButtonProvider buttonProvider) {
public void construct(ButtonProvider buttonProvider) {
mButtonProvider = buttonProvider;
}
public void init(TaskbarController.TaskbarViewCallbacks taskbarCallbacks) {
mControllerCallbacks = taskbarCallbacks;
ActivityContext context = getActivityContext();
RelativeLayout.LayoutParams imeParams = new RelativeLayout.LayoutParams(
context.getDeviceProfile().iconSizePx,
@ -57,16 +64,24 @@ 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);
}

View File

@ -1,275 +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 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);
}
}

View File

@ -15,164 +15,56 @@
*/
package com.android.launcher3.taskbar;
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.content.ContextWrapper;
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.AbstractFloatingView;
import com.android.launcher3.BaseQuickstepLauncher;
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.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;
import com.android.launcher3.views.BaseDragLayer;
/**
* 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 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";
public class TaskbarActivityContext extends ContextWrapper implements ActivityContext {
private final DeviceProfile mDeviceProfile;
private final LayoutInflater mLayoutInflater;
private final TaskbarContainerView mTaskbarContainerView;
private final TaskbarIconController mIconController;
private final MyDragController mDragController;
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;
public TaskbarActivityContext(BaseQuickstepLauncher launcher) {
super(launcher);
mDeviceProfile = launcher.getDeviceProfile().copy(this);
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 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;
public TaskbarContainerView getTaskbarContainerView() {
return mTaskbarContainerView;
}
@Override
@ -181,7 +73,7 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ
}
@Override
public TaskbarContainerView getDragLayer() {
public BaseDragLayer<TaskbarActivityContext> getDragLayer() {
return mTaskbarContainerView;
}
@ -200,103 +92,6 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ
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);
@ -311,8 +106,7 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ
}
@Override
protected void exitDrag() {
}
protected void exitDrag() { }
@Override
protected DropTarget getDefaultDropTarget(int[] dropCoordinates) {

View File

@ -21,7 +21,6 @@ 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;
@ -35,7 +34,7 @@ public class TaskbarAnimationController {
private static final long IME_VISIBILITY_ALPHA_DURATION = 120;
private final BaseQuickstepLauncher mLauncher;
private final TaskbarAnimationControllerCallbacks mTaskbarCallbacks;
private final TaskbarController.TaskbarAnimationControllerCallbacks mTaskbarCallbacks;
// Background alpha.
private final AnimatedFloat mTaskbarBackgroundAlpha = new AnimatedFloat(
@ -56,7 +55,7 @@ public class TaskbarAnimationController {
this::updateTranslationY);
public TaskbarAnimationController(BaseQuickstepLauncher launcher,
TaskbarAnimationControllerCallbacks taskbarCallbacks) {
TaskbarController.TaskbarAnimationControllerCallbacks taskbarCallbacks) {
mLauncher = launcher;
mTaskbarCallbacks = taskbarCallbacks;
}

View File

@ -15,6 +15,9 @@
*/
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;
@ -29,21 +32,22 @@ 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;
private TaskbarIconController.Callbacks mControllerCallbacks;
private TaskbarView mTaskbarView;
// Initialized in TaskbarController constructor.
private TaskbarController.TaskbarContainerViewCallbacks mControllerCallbacks;
private final OnComputeInsetsListener mTaskbarInsetsComputer = this::onComputeTaskbarInsets;
// Initialized in init.
private TaskbarView mTaskbarView;
private ViewTreeObserverWrapper.OnComputeInsetsListener mTaskbarInsetsComputer;
public TaskbarContainerView(@NonNull Context context) {
this(context, null);
@ -64,6 +68,15 @@ 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();
}
@ -72,24 +85,46 @@ public class TaskbarContainerView extends BaseDragLayer<TaskbarActivityContext>
mControllers = new TouchController[0];
}
public void init(TaskbarIconController.Callbacks callbacks, TaskbarView taskbarView) {
mControllerCallbacks = callbacks;
mTaskbarView = taskbarView;
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());
};
}
private void onComputeTaskbarInsets(InsetsInfo insetsInfo) {
if (mControllerCallbacks != null) {
mControllerCallbacks.updateInsetsTouchability(insetsInfo);
}
}
protected void onDestroy() {
protected void cleanup() {
ViewTreeObserverWrapper.removeOnComputeInsetsListener(mTaskbarInsetsComputer);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
ViewTreeObserverWrapper.addOnComputeInsetsListener(getViewTreeObserver(),
mTaskbarInsetsComputer);
}
@ -98,7 +133,7 @@ public class TaskbarContainerView extends BaseDragLayer<TaskbarActivityContext>
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
onDestroy();
cleanup();
}
@Override
@ -108,25 +143,10 @@ 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);
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);
mControllerCallbacks.onViewRemoved();
}
/**
@ -138,6 +158,16 @@ 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.

View File

@ -0,0 +1,596 @@
/*
* 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);
}
}

View File

@ -20,7 +20,6 @@ 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;
@ -30,6 +29,7 @@ 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 Context mContext;
private final BaseQuickstepLauncher mLauncher;
private final int mDragIconSize;
public TaskbarDragController(Context context) {
mContext = context;
Resources resources = mContext.getResources();
public TaskbarDragController(BaseQuickstepLauncher launcher) {
mLauncher = launcher;
Resources resources = mLauncher.getResources();
mDragIconSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_drag_icon_size);
}
@ -63,6 +63,7 @@ public class TaskbarDragController {
}
BubbleTextView btv = (BubbleTextView) view;
View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view) {
@Override
public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
@ -86,7 +87,7 @@ public class TaskbarDragController {
Intent intent = null;
if (tag instanceof WorkspaceItemInfo) {
WorkspaceItemInfo item = (WorkspaceItemInfo) tag;
LauncherApps launcherApps = mContext.getSystemService(LauncherApps.class);
LauncherApps launcherApps = mLauncher.getSystemService(LauncherApps.class);
clipDescription = new ClipDescription(item.title,
new String[] {
item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT

View File

@ -26,8 +26,6 @@ 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.
*/
@ -35,12 +33,13 @@ public class TaskbarHotseatController {
private final BaseQuickstepLauncher mLauncher;
private final Hotseat mHotseat;
private final Consumer<ItemInfo[]> mTaskbarCallbacks;
private final TaskbarController.TaskbarHotseatControllerCallbacks 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() {
@ -48,8 +47,8 @@ public class TaskbarHotseatController {
}
};
public TaskbarHotseatController(
BaseQuickstepLauncher launcher, Consumer<ItemInfo[]> taskbarCallbacks) {
public TaskbarHotseatController(BaseQuickstepLauncher launcher,
TaskbarController.TaskbarHotseatControllerCallbacks taskbarCallbacks) {
mLauncher = launcher;
mHotseat = mLauncher.getHotseat();
mTaskbarCallbacks = taskbarCallbacks;
@ -86,6 +85,10 @@ public class TaskbarHotseatController {
}
}
mTaskbarCallbacks.accept(hotseatItemInfos);
mTaskbarCallbacks.updateHotseatItems(hotseatItemInfos);
}
protected int getNumHotseatIcons() {
return mNumHotseatIcons;
}
}

View File

@ -1,163 +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.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);
}
}
}

View File

@ -1,157 +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.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
import static com.android.launcher3.util.DisplayController.CHANGE_SUPPORTED_BOUNDS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.inputmethodservice.InputMethodService;
import android.view.Display;
import androidx.annotation.Nullable;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.Info;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.TouchInteractionService;
/**
* Class to manager taskbar lifecycle
*/
public class TaskbarManager implements DisplayController.DisplayInfoChangeListener,
SysUINavigationMode.NavigationModeChangeListener {
private final Context mContext;
private final DisplayController mDisplayController;
private final SysUINavigationMode mSysUINavigationMode;
private final TaskbarNavButtonController mNavButtonController;
private TaskbarActivityContext mTaskbarActivityContext;
private BaseQuickstepLauncher mLauncher;
private static final int CHANGE_FLAGS =
CHANGE_ACTIVE_SCREEN | CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS;
public TaskbarManager(TouchInteractionService service) {
mDisplayController = DisplayController.INSTANCE.get(service);
mSysUINavigationMode = SysUINavigationMode.INSTANCE.get(service);
Display display =
service.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY);
mContext = service.createWindowContext(display, TYPE_APPLICATION_OVERLAY, null);
mNavButtonController = new TaskbarNavButtonController(service);
mDisplayController.addChangeListener(this);
mSysUINavigationMode.addModeChangeListener(this);
recreateTaskbar();
}
@Override
public void onNavigationModeChanged(Mode newMode) {
recreateTaskbar();
}
@Override
public void onDisplayInfoChanged(Context context, Info info, int flags) {
if ((flags & CHANGE_FLAGS) != 0) {
recreateTaskbar();
}
}
private void destroyExistingTaskbar() {
if (mTaskbarActivityContext != null) {
mTaskbarActivityContext.onDestroy();
mTaskbarActivityContext = null;
}
}
/**
* Sets or clears a launcher to act as taskbar callback
*/
public void setLauncher(@Nullable BaseQuickstepLauncher launcher) {
mLauncher = launcher;
if (mTaskbarActivityContext != null) {
mTaskbarActivityContext.setUIController(mLauncher == null
? TaskbarUIController.DEFAULT
: new LauncherTaskbarUIController(launcher, mTaskbarActivityContext));
}
}
private void recreateTaskbar() {
destroyExistingTaskbar();
if (!FeatureFlags.ENABLE_TASKBAR.get()) {
return;
}
DeviceProfile dp = LauncherAppState.getIDP(mContext).getDeviceProfile(mContext);
if (!dp.isTaskbarPresent) {
return;
}
mTaskbarActivityContext = new TaskbarActivityContext(
mContext, dp.copy(mContext), mNavButtonController);
mTaskbarActivityContext.init();
if (mLauncher != null) {
mTaskbarActivityContext.setUIController(
new 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);
}
}

View File

@ -16,8 +16,7 @@
package com.android.launcher3.taskbar;
import static android.view.Display.DEFAULT_DISPLAY;
import android.content.Context;
import android.content.Intent;
import android.view.inputmethod.InputMethodManager;
@ -54,10 +53,11 @@ public class TaskbarNavButtonController {
static final int BUTTON_RECENTS = BUTTON_HOME << 1;
static final int BUTTON_IME_SWITCH = BUTTON_RECENTS << 1;
private final TouchInteractionService mService;
public TaskbarNavButtonController(TouchInteractionService service) {
mService = service;
private final Context mContext;
public TaskbarNavButtonController(Context context) {
mContext = context;
}
public void onButtonClick(@TaskbarButton int buttonType) {
@ -78,13 +78,13 @@ public class TaskbarNavButtonController {
}
private void navigateHome() {
mService.startActivity(new Intent(Intent.ACTION_MAIN)
mContext.startActivity(new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
private void navigateToOverview() {
mService.getOverviewCommandHelper()
TouchInteractionService.getInstance().getOverviewCommandHelper()
.addCommand(OverviewCommandHelper.TYPE_SHOW);
}
@ -93,8 +93,8 @@ public class TaskbarNavButtonController {
}
private void showIMESwitcher() {
mService.getSystemService(InputMethodManager.class)
.showInputMethodPickerFromSystem(true /* showAuxiliarySubtypes */,
DEFAULT_DISPLAY);
mContext.getSystemService(InputMethodManager.class).showInputMethodPickerFromSystem(
true /* showAuxiliarySubtypes */, mContext.getDisplayId());
}
}

View File

@ -24,52 +24,59 @@ 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 #setAnimationController} is never called).
* isn't present (i.e. {@link #setTaskbarCallbacks} 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 TaskbarAnimationController mAnimationController = null;
private @Nullable TaskbarController.TaskbarStateHandlerCallbacks mTaskbarCallbacks = null;
public TaskbarStateHandler(BaseQuickstepLauncher launcher) {
mLauncher = launcher;
}
public void setAnimationController(TaskbarAnimationController callbacks) {
mAnimationController = callbacks;
public void setTaskbarCallbacks(TaskbarController.TaskbarStateHandlerCallbacks callbacks) {
mTaskbarCallbacks = callbacks;
}
@Override
public void setState(LauncherState state) {
setState(state, PropertySetter.NO_ANIM_PROPERTY_SETTER);
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));
}
@Override
public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
PendingAnimation animation) {
setState(toState, animation);
}
private void setState(LauncherState toState, PropertySetter setter) {
if (mAnimationController == null) {
if (mTaskbarCallbacks == null) {
return;
}
AnimatedFloat alphaTarget = mTaskbarCallbacks.getAlphaTarget();
AnimatedFloat scaleTarget = mTaskbarCallbacks.getScaleTarget();
AnimatedFloat translationYTarget = mTaskbarCallbacks.getTranslationYTarget();
boolean isTaskbarVisible = (toState.getVisibleElements(mLauncher) & TASKBAR) != 0;
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);
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);
}
}

View File

@ -1,41 +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;
/**
* 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);
}
}

View File

@ -15,14 +15,20 @@
*/
package com.android.launcher3.taskbar;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.makeMeasureSpec;
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 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;
@ -45,12 +51,17 @@ 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;
@ -58,16 +69,17 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
private final RectF mDelegateSlopBounds = new RectF();
private final int[] mTempOutLocation = new int[2];
private final int mItemMarginLeftRight;
private final TaskbarActivityContext mActivityContext;
// Initialized in TaskbarController constructor.
private View.OnClickListener mIconClickListener;
private View.OnLongClickListener mIconLongClickListener;
private TaskbarController.TaskbarViewCallbacks mControllerCallbacks;
// Scale on elements that aren't icons.
private float mNonIconScale;
private int mItemMarginLeftRight;
LinearLayout mSystemButtonContainer;
LinearLayout mHotseatIconsContainer;
// Initialized in init().
private LayoutTransition mLayoutTransition;
private int mHotseatStartIndex;
private int mHotseatEndIndex;
private LinearLayout mButtonRegion;
// Delegate touches to the closest view if within mIconTouchSize.
private boolean mDelegateTargeted;
@ -79,12 +91,10 @@ 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);
}
@ -101,58 +111,80 @@ 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();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mSystemButtonContainer = findViewById(R.id.system_button_layout);
mHotseatIconsContainer = findViewById(R.id.hotseat_icons_layout);
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);
}
protected void construct(OnClickListener clickListener, OnLongClickListener longClickListener,
ButtonProvider buttonProvider) {
mIconClickListener = clickListener;
mIconLongClickListener = longClickListener;
mButtonProvider = buttonProvider;
if (mActivityContext.canShowNavButtons()) {
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;
createNavButtons();
} else {
mSystemButtonContainer.setVisibility(GONE);
mNavButtonStartIndex = -1;
removeNavButtons();
}
int numHotseatIcons = mActivityContext.getDeviceProfile().numShownHotseatIcons;
mHotseatStartIndex = mNavButtonStartIndex + 1;
mHotseatEndIndex = mHotseatStartIndex + numHotseatIcons - 1;
updateHotseatItems(new ItemInfo[numHotseatIcons]);
mLayoutTransition = new LayoutTransition();
addUpdateListenerForAllLayoutTransitions(() -> {
if (getLayoutTransition() == mLayoutTransition) {
mControllerCallbacks.onItemPositionsChanged(this);
}
});
setLayoutTransition(mLayoutTransition);
}
/**
* 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
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);
}
}
private void setHolesAllowedInLayoutNoAnimation(boolean areHolesAllowed) {
if (mAreHolesAllowed != areHolesAllowed) {
mAreHolesAllowed = areHolesAllowed;
updateHotseatItemsVisibility();
onMeasure(makeMeasureSpec(getMeasuredWidth(), EXACTLY),
makeMeasureSpec(getMeasuredHeight(), EXACTLY));
onLayout(false, getLeft(), getTop(), getRight(), getBottom());
}
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();
}
/**
@ -160,9 +192,10 @@ 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];
View hotseatView = mHotseatIconsContainer.getChildAt(i);
ItemInfo hotseatItemInfo = hotseatItemInfos[!mIsRtl ? i
: hotseatItemInfos.length - i - 1];
int hotseatIndex = mHotseatStartIndex + i;
View hotseatView = getChildAt(hotseatIndex);
// Replace any Hotseat views with the appropriate type if it's not already that type.
final int expectedLayoutResId;
@ -180,23 +213,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) {
mHotseatIconsContainer.removeView(hotseatView);
removeView(hotseatView);
ActivityContext activityContext = getActivityContext();
if (isFolder) {
FolderInfo folderInfo = (FolderInfo) hotseatItemInfo;
FolderIcon folderIcon = FolderIcon.inflateFolderAndIcon(expectedLayoutResId,
mActivityContext, this, folderInfo);
getActivityContext(), this, folderInfo);
folderIcon.setTextVisible(false);
hotseatView = folderIcon;
} else {
hotseatView = inflate(expectedLayoutResId);
}
int iconSize = mActivityContext.getDeviceProfile().iconSizePx;
int iconSize = activityContext.getDeviceProfile().iconSizePx;
LayoutParams lp = new LayoutParams(iconSize, iconSize);
lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0);
mHotseatIconsContainer.addView(hotseatView, i, lp);
addView(hotseatView, hotseatIndex, lp);
}
// Apply the Hotseat ItemInfos, or hide the view if there is none for a given index.
@ -204,11 +237,13 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
&& hotseatItemInfo instanceof WorkspaceItemInfo) {
((BubbleTextView) hotseatView).applyFromWorkspaceItem(
(WorkspaceItemInfo) hotseatItemInfo);
hotseatView.setOnClickListener(mIconClickListener);
hotseatView.setOnLongClickListener(mIconLongClickListener);
hotseatView.setOnClickListener(mControllerCallbacks.getItemOnClickListener());
hotseatView.setOnLongClickListener(
mControllerCallbacks.getItemOnLongClickListener());
} else if (isFolder) {
hotseatView.setOnClickListener(mIconClickListener);
hotseatView.setOnLongClickListener(mIconLongClickListener);
hotseatView.setOnClickListener(mControllerCallbacks.getItemOnClickListener());
hotseatView.setOnLongClickListener(
mControllerCallbacks.getItemOnLongClickListener());
} else {
hotseatView.setOnClickListener(null);
hotseatView.setOnLongClickListener(null);
@ -219,14 +254,24 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
}
protected void updateHotseatItemsVisibility() {
for (int i = mHotseatIconsContainer.getChildCount() - 1; i >= 0; i--) {
updateHotseatItemVisibility(mHotseatIconsContainer.getChildAt(i));
for (int i = mHotseatStartIndex; i <= mHotseatEndIndex; i++) {
updateHotseatItemVisibility(getChildAt(i));
}
}
private void updateHotseatItemVisibility(View hotseatView) {
hotseatView.setVisibility(
hotseatView.getTag() != null ? VISIBLE : (mAreHolesAllowed ? INVISIBLE : GONE));
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);
}
}
}
@Override
@ -333,20 +378,49 @@ 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(
mActivityContext.getDeviceProfile().iconSizePx,
mActivityContext.getDeviceProfile().iconSizePx
context.getDeviceProfile().iconSizePx,
context.getDeviceProfile().iconSizePx
);
buttonParams.gravity = Gravity.CENTER;
mSystemButtonContainer.addView(mButtonProvider.getBack(), buttonParams);
mSystemButtonContainer.addView(mButtonProvider.getHome(), buttonParams);
mSystemButtonContainer.addView(mButtonProvider.getRecents(), buttonParams);
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);
}
@Override
@ -354,7 +428,7 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
mIsDraggingItem = true;
AbstractFloatingView.closeAllOpenViews(mActivityContext);
AbstractFloatingView.closeAllOpenViews(getActivityContext());
return true;
case DragEvent.ACTION_DRAG_ENDED:
mIsDraggingItem = false;
@ -371,26 +445,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() {
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();
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;
}
}
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.
@ -421,7 +495,7 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
}
private View inflate(@LayoutRes int layoutResId) {
return mActivityContext.getLayoutInflater().inflate(layoutResId, this, false);
return getActivityContext().getLayoutInflater().inflate(layoutResId, this, false);
}
@Override
@ -429,11 +503,7 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
// Ignore, we just implement Insettable to draw behind system insets.
}
public void setIconsVisibility(boolean isVisible) {
mHotseatIconsContainer.setVisibility(isVisible ? VISIBLE : INVISIBLE);
}
public boolean areIconsVisible() {
return mHotseatIconsContainer.getVisibility() == VISIBLE;
private <T extends Context & ActivityContext> T getActivityContext() {
return ActivityContext.lookupContext(getContext());
}
}

View File

@ -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(getTaskbarUIController() == null);
mHotseatPredictionController.setPauseUIUpdate(getTaskbarController() == 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 (getTaskbarUIController() != null && updated.stream()
if (getTaskbarController() != null && updated.stream()
.filter(w -> w.container == CONTAINER_HOTSEAT).findFirst().isPresent()) {
getTaskbarUIController().onHotseatUpdated();
getTaskbarController().onHotseatUpdated();
}
}

View File

@ -52,6 +52,7 @@ 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;
@ -122,6 +123,11 @@ 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();
@ -361,6 +367,13 @@ 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.
*/

View File

@ -22,6 +22,7 @@ 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;
@ -41,7 +42,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.LauncherTaskbarUIController;
import com.android.launcher3.taskbar.TaskbarController;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.SysUINavigationMode.Mode;
@ -163,12 +164,13 @@ public final class LauncherActivityInterface extends
}
@Nullable
private LauncherTaskbarUIController getTaskbarController() {
@Override
public TaskbarController getTaskbarController() {
BaseQuickstepLauncher launcher = getCreatedActivity();
if (launcher == null) {
return null;
}
return launcher.getTaskbarUIController();
return launcher.getTaskbarController();
}
@Nullable
@ -276,13 +278,13 @@ public final class LauncherActivityInterface extends
@Override
public @Nullable Animator getParallelAnimationToLauncher(GestureEndTarget endTarget,
long duration) {
LauncherTaskbarUIController uiController = getTaskbarController();
TaskbarController taskbarController = getTaskbarController();
Animator superAnimator = super.getParallelAnimationToLauncher(endTarget, duration);
if (uiController == null) {
if (taskbarController == null) {
return superAnimator;
}
LauncherState toState = stateFromGestureEndTarget(endTarget);
Animator taskbarAnimator = uiController.createAnimToLauncher(toState, duration);
Animator taskbarAnimator = taskbarController.createAnimToLauncher(toState, duration);
if (superAnimator == null) {
return taskbarAnimator;
} else {
@ -298,22 +300,32 @@ 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) {
LauncherTaskbarUIController uiController = getTaskbarController();
if (uiController == null) {
TaskbarController taskbarController = getTaskbarController();
if (taskbarController == null) {
return super.deferStartingActivity(deviceState, ev);
}
return uiController.isEventOverAnyTaskbarItem(ev);
return taskbarController.isEventOverAnyTaskbarItem(ev);
}
@Override
public boolean shouldCancelCurrentGesture() {
LauncherTaskbarUIController uiController = getTaskbarController();
if (uiController == null) {
TaskbarController taskbarController = getTaskbarController();
if (taskbarController == null) {
return super.shouldCancelCurrentGesture();
}
return uiController.isDraggingItem();
return taskbarController.isDraggingItem();
}
@Override

View File

@ -52,6 +52,7 @@ 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;
@ -74,7 +75,6 @@ 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,9 +147,21 @@ public class TouchInteractionService extends Service implements PluginListener<O
private OverscrollPlugin mOverscrollPlugin;
/**
* Local IOverviewProxy implementation with some methods for local components
* 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.
*/
public class TISBinder extends IOverviewProxy.Stub {
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;
}
@BinderThread
public void onInitialize(Bundle bundle) {
@ -262,24 +274,40 @@ public class TouchInteractionService extends Service implements PluginListener<O
@Override
public void onImeWindowStatusChanged(int displayId, IBinder token, int vis,
int backDisposition, boolean showImeSwitcher) {
MAIN_EXECUTOR.execute(() -> mTaskbarManager.updateImeStatus(
displayId, vis, backDisposition, showImeSwitcher));
int backDisposition, boolean showImeSwitcher) throws RemoteException {
if (mTaskbarOverviewProxyDelegate == null) {
return;
}
MAIN_EXECUTOR.execute(() -> {
if (mTaskbarOverviewProxyDelegate == null) {
return;
}
mTaskbarOverviewProxyDelegate
.updateImeStatus(displayId, vis, backDisposition, showImeSwitcher);
});
}
};
public TaskbarManager getTaskbarManager() {
return mTaskbarManager;
}
public interface TaskbarOverviewProxyDelegate {
void updateImeStatus(int displayId, int vis, int backDisposition,
boolean showImeSwitcher);
}
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;
@ -308,7 +336,9 @@ public class TouchInteractionService extends Service implements PluginListener<O
private DisplayManager mDisplayManager;
private TaskbarManager mTaskbarManager;
public TouchInteractionService() {
sInstance = this;
}
@Override
public void onCreate() {
@ -318,14 +348,13 @@ 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);
ProtoTracer.INSTANCE.get(this).add(this);
mDisplayManager = getSystemService(DisplayManager.class);
sConnected = true;
}
@ -439,7 +468,8 @@ public class TouchInteractionService extends Service implements PluginListener<O
int systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
mOverviewComponentObserver.onSystemUiStateChanged();
mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags);
mOverviewComponentObserver.getActivityInterface().onSystemUiFlagsChanged(
systemUiStateFlags);
if ((lastSysUIFlags & SYSUI_STATE_TRACING_ENABLED) !=
(systemUiStateFlags & SYSUI_STATE_TRACING_ENABLED)) {
@ -482,7 +512,6 @@ public class TouchInteractionService extends Service implements PluginListener<O
getSystemService(AccessibilityManager.class)
.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
mTaskbarManager.destroy();
sConnected = false;
super.onDestroy();
}
@ -490,7 +519,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "Touch service connected: user=" + getUserId());
return new TISBinder();
return mMyBinder;
}
private void onInputEvent(InputEvent ev) {

View File

@ -116,7 +116,7 @@ public class StaggeredWorkspaceAnim {
addStaggeredAnimationForView(child, lp.cellY + 1, totalRows);
}
} else {
final int hotseatRow, qsbRow;
final int hotseatRow, qsbRow, taskbarRow;
if (grid.isTaskbarPresent) {
qsbRow = grid.inv.numRows + 1;
hotseatRow = grid.inv.numRows + 2;
@ -124,12 +124,16 @@ 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() {

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<Space
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="gone" />

View File

@ -447,6 +447,10 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
* @param canvas The canvas to draw to.
*/
protected void drawDotIfNecessary(Canvas canvas) {
if (mActivity instanceof Launcher && ((Launcher) mActivity).isViewInTaskbar(this)) {
// TODO: support notification dots in Taskbar
return;
}
if (!mForceHideDot && (hasDot() || mDotParams.scale > 0)) {
getIconBounds(mDotParams.iconBounds);
Utilities.scaleRectAboutCenter(mDotParams.iconBounds,

View File

@ -49,6 +49,7 @@ public class Hotseat extends CellLayout implements Insettable {
private final View mQsb;
private final int mQsbHeight;
private final View mTaskbarView;
private final int mTaskbarViewHeight;
public Hotseat(Context context) {
@ -66,7 +67,10 @@ public class Hotseat extends CellLayout implements Insettable {
mQsbHeight = mQsb.getLayoutParams().height;
addView(mQsb);
mTaskbarViewHeight = context.getResources().getDimensionPixelSize(R.dimen.taskbar_size);
mTaskbarView = LayoutInflater.from(context).inflate(R.layout.taskbar_view, this, false);
mTaskbarViewHeight = mTaskbarView.getLayoutParams().height;
// We want taskbar in the back so its background applies to Hotseat as well.
addView(mTaskbarView, 0);
}
/**
@ -183,6 +187,8 @@ public class Hotseat extends CellLayout implements Insettable {
int width = getShortcutsAndWidgets().getMeasuredWidth();
mQsb.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(mQsbHeight, MeasureSpec.EXACTLY));
mTaskbarView.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(mTaskbarViewHeight, MeasureSpec.EXACTLY));
}
@Override
@ -196,6 +202,13 @@ public class Hotseat extends CellLayout implements Insettable {
int bottom = b - t - getQsbOffsetY();
int top = bottom - mQsbHeight;
mQsb.layout(left, top, right, bottom);
int taskbarWidth = mTaskbarView.getMeasuredWidth();
left = (r - l - taskbarWidth) / 2;
right = left + taskbarWidth;
bottom = b - t - getTaskbarOffsetY();
top = bottom - mTaskbarViewHeight;
mTaskbarView.layout(left, top, right, bottom);
}
/**
@ -231,4 +244,10 @@ public class Hotseat extends CellLayout implements Insettable {
return mQsb;
}
/**
* Returns the Taskbar inside hotseat
*/
public View getTaskbarView() {
return mTaskbarView;
}
}

View File

@ -1942,6 +1942,13 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
@Override
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
if (isViewInTaskbar(v)) {
// Start the activity without the hacky workarounds below, which assume the View was
// clicked when Launcher was resumed and will be hidden until Launcher is re-resumed
// (this isn't the case for Taskbar).
return super.startActivitySafely(v, intent, item);
}
if (!hasBeenResumed()) {
// Workaround an issue where the WM launch animation is clobbered when finishing the
// recents animation into launcher. Defer launching the activity until Launcher is
@ -2853,6 +2860,13 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
.start();
}
/**
* @return Whether the View is in the same window as the Taskbar window.
*/
public boolean isViewInTaskbar(View v) {
return false;
}
public boolean supportsAdaptiveIconAnimation(View clickedView) {
return false;
}

View File

@ -324,17 +324,15 @@ public final class Utilities {
}
public static void scaleRectFAboutCenter(RectF r, float scale) {
scaleRectFAboutPivot(r, scale, r.centerX(), r.centerY());
}
public static void scaleRectFAboutPivot(RectF r, float scale, float px, float py) {
if (scale != 1.0f) {
r.offset(-px, -py);
float cx = r.centerX();
float cy = r.centerY();
r.offset(-cx, -cy);
r.left = r.left * scale;
r.top = r.top * scale ;
r.right = r.right * scale;
r.bottom = r.bottom * scale;
r.offset(px, py);
r.offset(cx, cy);
}
}

View File

@ -27,7 +27,6 @@ import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SU
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.LauncherApps;
@ -194,35 +193,6 @@ public class ItemClickHandler {
launcher.startActivitySafely(v, intent, item);
}
/**
* Handles clicking on a disabled shortcut
*/
public static void handleDisabledItemClicked(WorkspaceItemInfo shortcut, Context context) {
final int disabledFlags = shortcut.runtimeStatusFlags
& WorkspaceItemInfo.FLAG_DISABLED_MASK;
if ((disabledFlags
& ~FLAG_DISABLED_SUSPENDED
& ~FLAG_DISABLED_QUIET_USER) == 0) {
// If the app is only disabled because of the above flags, launch activity anyway.
// Framework will tell the user why the app is suspended.
} else {
if (!TextUtils.isEmpty(shortcut.disabledMessage)) {
// Use a message specific to this shortcut, if it has one.
Toast.makeText(context, shortcut.disabledMessage, Toast.LENGTH_SHORT).show();
return;
}
// Otherwise just use a generic error message.
int error = R.string.activity_not_available;
if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_SAFEMODE) != 0) {
error = R.string.safemode_shortcut_error;
} else if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_BY_PUBLISHER) != 0
|| (shortcut.runtimeStatusFlags & FLAG_DISABLED_LOCKED_USER) != 0) {
error = R.string.shortcut_not_available;
}
Toast.makeText(context, error, Toast.LENGTH_SHORT).show();
}
}
/**
* Event handler for an app shortcut click.
*
@ -230,8 +200,30 @@ public class ItemClickHandler {
*/
public static void onClickAppShortcut(View v, WorkspaceItemInfo shortcut, Launcher launcher) {
if (shortcut.isDisabled()) {
handleDisabledItemClicked(shortcut, launcher);
return;
final int disabledFlags = shortcut.runtimeStatusFlags
& WorkspaceItemInfo.FLAG_DISABLED_MASK;
if ((disabledFlags &
~FLAG_DISABLED_SUSPENDED &
~FLAG_DISABLED_QUIET_USER) == 0) {
// If the app is only disabled because of the above flags, launch activity anyway.
// Framework will tell the user why the app is suspended.
} else {
if (!TextUtils.isEmpty(shortcut.disabledMessage)) {
// Use a message specific to this shortcut, if it has one.
Toast.makeText(launcher, shortcut.disabledMessage, Toast.LENGTH_SHORT).show();
return;
}
// Otherwise just use a generic error message.
int error = R.string.activity_not_available;
if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_SAFEMODE) != 0) {
error = R.string.safemode_shortcut_error;
} else if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_BY_PUBLISHER) != 0 ||
(shortcut.runtimeStatusFlags & FLAG_DISABLED_LOCKED_USER) != 0) {
error = R.string.shortcut_not_available;
}
Toast.makeText(launcher, error, Toast.LENGTH_SHORT).show();
return;
}
}
// Check for abandoned promise