Merge "Fix PathParser error when parsing app close interpolator in RTL languages." into sc-dev
diff --git a/quickstep/res/layout/taskbar.xml b/quickstep/res/layout/taskbar.xml
index 240fe55..e680233 100644
--- a/quickstep/res/layout/taskbar.xml
+++ b/quickstep/res/layout/taskbar.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
-<com.android.launcher3.taskbar.TaskbarContainerView
+<com.android.launcher3.taskbar.TaskbarDragLayer
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/taskbar_container"
android:layout_width="wrap_content"
@@ -22,9 +22,29 @@
<com.android.launcher3.taskbar.TaskbarView
android:id="@+id/taskbar_view"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:gravity="center"/>
+ android:gravity="center"
+ android:forceHasOverlappingRendering="false"
+ android:layout_gravity="bottom" >
+
+ <LinearLayout
+ android:id="@+id/system_button_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="@dimen/taskbar_nav_buttons_spacing"
+ android:paddingRight="@dimen/taskbar_nav_buttons_spacing"
+ android:forceHasOverlappingRendering="false"
+ android:gravity="center" />
+
+ <LinearLayout
+ android:id="@+id/hotseat_icons_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:forceHasOverlappingRendering="false"
+ android:gravity="center" />
+
+ </com.android.launcher3.taskbar.TaskbarView>
<com.android.launcher3.taskbar.ImeBarView
android:id="@+id/ime_bar_view"
@@ -32,4 +52,4 @@
android:layout_height="wrap_content"
android:visibility="gone"/>
-</com.android.launcher3.taskbar.TaskbarContainerView>
\ No newline at end of file
+</com.android.launcher3.taskbar.TaskbarDragLayer>
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar_view.xml b/quickstep/res/layout/taskbar_view.xml
deleted file mode 100644
index 34a88ea..0000000
--- a/quickstep/res/layout/taskbar_view.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<com.android.launcher3.taskbar.TaskbarView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/taskbar_view"
- android:layout_width="match_parent"
- android:layout_height="@dimen/taskbar_size"
- android:background="@android:color/transparent"
- android:layout_gravity="bottom"
- android:gravity="center"
- android:visibility="gone" />
-
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 735cb24..50453ac 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -149,4 +149,5 @@
<!-- 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>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 25e2359..0b41f15 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -21,7 +21,6 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.NO_OFFSET;
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
-import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
@@ -31,7 +30,6 @@
import android.animation.ValueAnimator;
import android.app.ActivityOptions;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.ServiceConnection;
@@ -52,13 +50,11 @@
import com.android.launcher3.statehandlers.BackButtonAlphaHandler;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager.StateHandler;
-import com.android.launcher3.taskbar.TaskbarActivityContext;
-import com.android.launcher3.taskbar.TaskbarController;
+import com.android.launcher3.taskbar.LauncherTaskbarUIController;
+import com.android.launcher3.taskbar.TaskbarManager;
import com.android.launcher3.taskbar.TaskbarStateHandler;
-import com.android.launcher3.taskbar.TaskbarView;
import com.android.launcher3.uioverrides.RecentsViewStateController;
import com.android.launcher3.util.ActivityOptionsWrapper;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.ObjectWrapper;
import com.android.launcher3.util.UiThreadHelper;
import com.android.quickstep.RecentsModel;
@@ -68,6 +64,7 @@
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.TouchInteractionService;
+import com.android.quickstep.TouchInteractionService.TISBinder;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.RemoteFadeOutAnimationListener;
import com.android.quickstep.util.SplitSelectStateController;
@@ -89,8 +86,6 @@
private DepthController mDepthController = new DepthController(this);
private QuickstepTransitionManager mAppTransitionManager;
- private ServiceConnection mTisBinderConnection;
- protected TouchInteractionService.TISBinder mTisBinder;
/**
* Reusable command for applying the back button alpha on the background thread.
@@ -101,8 +96,20 @@
private OverviewActionsView mActionsView;
- private @Nullable TaskbarController mTaskbarController;
+ private @Nullable TaskbarManager mTaskbarManager;
+ private @Nullable LauncherTaskbarUIController mTaskbarUIController;
+ private final ServiceConnection mTisBinderConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
+ mTaskbarManager = ((TISBinder) iBinder).getTaskbarManager();
+ mTaskbarManager.setLauncher(BaseQuickstepLauncher.this);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName componentName) { }
+ };
private final TaskbarStateHandler mTaskbarStateHandler = new TaskbarStateHandler(this);
+
// Will be updated when dragging from taskbar.
private @Nullable DragOptions mNextWorkspaceDragOptions = null;
private SplitPlaceholderView mSplitPlaceholderView;
@@ -112,24 +119,6 @@
super.onCreate(savedInstanceState);
SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this);
addMultiWindowModeChangedListener(mDepthController);
- setupTouchInteractionServiceBinder();
- }
-
- private void setupTouchInteractionServiceBinder() {
- Intent intent = new Intent(this, TouchInteractionService.class);
- mTisBinderConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName componentName, IBinder binder) {
- mTisBinder = ((TouchInteractionService.TISBinder) binder);
- mTisBinder.setTaskbarOverviewProxyDelegate(mTaskbarController);
- }
-
- @Override
- public void onServiceDisconnected(ComponentName componentName) {
- mTisBinder = null;
- }
- };
- bindService(intent, mTisBinderConnection, 0);
}
@Override
@@ -137,15 +126,12 @@
mAppTransitionManager.onActivityDestroyed();
SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
- if (mTaskbarController != null) {
- mTaskbarController.cleanup();
- mTaskbarController = null;
- if (mTisBinder != null) {
- mTisBinder.setTaskbarOverviewProxyDelegate(null);
- unbindService(mTisBinderConnection);
- }
- }
+
+ unbindService(mTisBinderConnection);
+ if (mTaskbarManager != null) {
+ mTaskbarManager.setLauncher(null);
+ }
super.onDestroy();
}
@@ -272,37 +258,12 @@
mAppTransitionManager = new QuickstepTransitionManager(this);
mAppTransitionManager.registerRemoteAnimations();
- addTaskbarIfNecessary();
- addOnDeviceProfileChangeListener(newDp -> addTaskbarIfNecessary());
+ bindService(new Intent(this, TouchInteractionService.class), mTisBinderConnection, 0);
+
}
- @Override
- public void onDisplayInfoChanged(Context context, DisplayController.Info info,
- int flags) {
- super.onDisplayInfoChanged(context, info, flags);
- if ((flags & CHANGE_ACTIVE_SCREEN) != 0) {
- addTaskbarIfNecessary();
- }
- }
-
- private void addTaskbarIfNecessary() {
- if (mTaskbarController != null) {
- mTaskbarController.cleanup();
- if (mTisBinder != null) {
- mTisBinder.setTaskbarOverviewProxyDelegate(null);
- }
- mTaskbarController = null;
- }
- if (mDeviceProfile.isTaskbarPresent) {
- TaskbarView taskbarViewOnHome = (TaskbarView) mHotseat.getTaskbarView();
- TaskbarActivityContext taskbarActivityContext = new TaskbarActivityContext(this);
- mTaskbarController = new TaskbarController(this,
- taskbarActivityContext.getTaskbarContainerView(), taskbarViewOnHome);
- mTaskbarController.init();
- if (mTisBinder != null) {
- mTisBinder.setTaskbarOverviewProxyDelegate(mTaskbarController);
- }
- }
+ public void setTaskbarUIController(LauncherTaskbarUIController taskbarUIController) {
+ mTaskbarUIController = taskbarUIController;
}
public <T extends OverviewActionsView> T getActionsView() {
@@ -332,8 +293,8 @@
return mDepthController;
}
- public @Nullable TaskbarController getTaskbarController() {
- return mTaskbarController;
+ public @Nullable LauncherTaskbarUIController getTaskbarUIController() {
+ return mTaskbarUIController;
}
public TaskbarStateHandler getTaskbarStateHandler() {
@@ -341,14 +302,9 @@
}
@Override
- public boolean isViewInTaskbar(View v) {
- return mTaskbarController != null && mTaskbarController.isViewInTaskbar(v);
- }
-
public boolean supportsAdaptiveIconAnimation(View clickedView) {
return mAppTransitionManager.hasControlRemoteAppTransitionPermission()
- && FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM.get()
- && !isViewInTaskbar(clickedView);
+ && FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM.get();
}
@Override
@@ -394,8 +350,8 @@
@Override
public float getNormalTaskbarScale() {
- if (mTaskbarController != null) {
- return mTaskbarController.getTaskbarScaleOnHome();
+ if (mTaskbarUIController != null) {
+ return mTaskbarUIController.getTaskbarScaleOnHome();
}
return super.getNormalTaskbarScale();
}
@@ -417,8 +373,8 @@
}
if ((changeBits & ACTIVITY_STATE_RESUMED) != 0) {
- if (mTaskbarController != null) {
- mTaskbarController.onLauncherResumedOrPaused(hasBeenResumed());
+ if (mTaskbarUIController != null) {
+ mTaskbarUIController.onLauncherResumedOrPaused(hasBeenResumed());
}
}
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 1b8fcb3..80754a0 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -1252,7 +1252,6 @@
final boolean launchingFromWidget = mV instanceof LauncherAppWidgetHostView;
final boolean launchingFromRecents = isLaunchingFromRecents(mV, appTargets);
- final boolean launchingFromTaskbar = mLauncher.isViewInTaskbar(mV);
if (launchingFromWidget) {
composeWidgetLaunchAnimator(anim, (LauncherAppWidgetHostView) mV, appTargets,
wallpaperTargets, nonAppTargets);
@@ -1263,8 +1262,6 @@
launcherClosing);
addCujInstrumentation(
anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_RECENTS);
- } else if (launchingFromTaskbar) {
- // TODO
} else {
composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,
launcherClosing);
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index 5dcf84c..85e5ab0 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -263,8 +263,8 @@
removeOutlineDrawings();
}
- if (mLauncher.getTaskbarController() != null) {
- mLauncher.getTaskbarController().onHotseatUpdated();
+ if (mLauncher.getTaskbarUIController() != null) {
+ mLauncher.getTaskbarUIController().onHotseatUpdated();
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java b/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java
index 0d4130d..540f748 100644
--- a/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java
+++ b/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java
@@ -16,12 +16,17 @@
package com.android.launcher3.taskbar;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
+
import android.annotation.DrawableRes;
-import android.content.Context;
import android.view.View;
import android.widget.ImageView;
import com.android.launcher3.R;
+import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton;
/**
* Creates Buttons for Taskbar for 3 button nav.
@@ -29,47 +34,46 @@
*/
public class ButtonProvider {
- private int mMarginLeftRight;
- private final Context mContext;
+ private final int mMarginLeftRight;
+ private final TaskbarActivityContext mContext;
- public ButtonProvider(Context context) {
+ public ButtonProvider(TaskbarActivityContext context) {
mContext = context;
- }
-
- public void setMarginLeftRight(int margin) {
- mMarginLeftRight = margin;
+ mMarginLeftRight = context.getResources()
+ .getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
}
public View getBack() {
// Back button
- return getButtonForDrawable(R.drawable.ic_sysbar_back);
+ return getButtonForDrawable(R.drawable.ic_sysbar_back, BUTTON_BACK);
}
public View getDown() {
// Ime down button
- return getButtonForDrawable(R.drawable.ic_sysbar_back);
+ return getButtonForDrawable(R.drawable.ic_sysbar_back, BUTTON_BACK);
}
public View getHome() {
// Home button
- return getButtonForDrawable(R.drawable.ic_sysbar_home);
+ return getButtonForDrawable(R.drawable.ic_sysbar_home, BUTTON_HOME);
}
public View getRecents() {
// Recents button
- return getButtonForDrawable(R.drawable.ic_sysbar_recent);
+ return getButtonForDrawable(R.drawable.ic_sysbar_recent, BUTTON_RECENTS);
}
public View getImeSwitcher() {
// IME Switcher Button
- return getButtonForDrawable(R.drawable.ic_ime_switcher);
+ return getButtonForDrawable(R.drawable.ic_ime_switcher, BUTTON_IME_SWITCH);
}
- private View getButtonForDrawable(@DrawableRes int drawableId) {
+ private View getButtonForDrawable(@DrawableRes int drawableId, @TaskbarButton int buttonType) {
ImageView buttonView = new ImageView(mContext);
buttonView.setImageResource(drawableId);
buttonView.setBackgroundResource(R.drawable.taskbar_icon_click_feedback_roundrect);
buttonView.setPadding(mMarginLeftRight, 0, mMarginLeftRight, 0);
+ buttonView.setOnClickListener(view -> mContext.onNavigationButtonClick(buttonType));
return buttonView;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java b/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java
index bb3669b..287caab 100644
--- a/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java
@@ -16,9 +16,6 @@
package com.android.launcher3.taskbar;
-import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
-import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH;
-
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
@@ -29,7 +26,6 @@
public class ImeBarView extends RelativeLayout {
private ButtonProvider mButtonProvider;
- private TaskbarController.TaskbarViewCallbacks mControllerCallbacks;
private View mImeView;
public ImeBarView(Context context) {
@@ -44,12 +40,9 @@
super(context, attrs, defStyleAttr);
}
- public void construct(ButtonProvider buttonProvider) {
+ public void init(ButtonProvider buttonProvider) {
mButtonProvider = buttonProvider;
- }
- public void init(TaskbarController.TaskbarViewCallbacks taskbarCallbacks) {
- mControllerCallbacks = taskbarCallbacks;
ActivityContext context = getActivityContext();
RelativeLayout.LayoutParams imeParams = new RelativeLayout.LayoutParams(
context.getDeviceProfile().iconSizePx,
@@ -64,24 +57,16 @@
// Down Arrow
View downView = mButtonProvider.getDown();
- downView.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
- BUTTON_BACK));
downView.setLayoutParams(downParams);
downView.setRotation(-90);
addView(downView);
// IME switcher button
mImeView = mButtonProvider.getImeSwitcher();
- mImeView.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
- BUTTON_IME_SWITCH));
mImeView.setLayoutParams(imeParams);
addView(mImeView);
}
- public void cleanup() {
- removeAllViews();
- }
-
public void setImeSwitcherVisibility(boolean show) {
mImeView.setVisibility(show ? VISIBLE : GONE);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
new file mode 100644
index 0000000..c2d107c
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.view.MotionEvent;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.QuickstepTransitionManager;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.states.StateAnimationConfig;
+
+
+/**
+ * A data source which integrates with a Launcher instance
+ * TODO: Rename to have Launcher prefix
+ */
+
+public class LauncherTaskbarUIController extends TaskbarUIController {
+
+ private final BaseQuickstepLauncher mLauncher;
+ private final TaskbarStateHandler mTaskbarStateHandler;
+ private final TaskbarAnimationController mTaskbarAnimationController;
+ private final TaskbarHotseatController mHotseatController;
+
+ private final TaskbarActivityContext mContext;
+ final TaskbarDragLayer mTaskbarDragLayer;
+ final TaskbarView mTaskbarView;
+
+ private @Nullable Animator mAnimator;
+ private boolean mIsAnimatingToLauncher;
+
+ public LauncherTaskbarUIController(
+ BaseQuickstepLauncher launcher, TaskbarActivityContext context) {
+ mContext = context;
+ mTaskbarDragLayer = context.getDragLayer();
+ mTaskbarView = mTaskbarDragLayer.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) {
+ mTaskbarDragLayer.setTaskbarBackgroundAlpha(alpha);
+ }
+
+ @Override
+ public void updateTaskbarVisibilityAlpha(float alpha) {
+ mTaskbarView.setAlpha(alpha);
+ }
+
+ @Override
+ public void updateImeBarVisibilityAlpha(float alpha) {
+ mTaskbarDragLayer.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(TaskbarDragLayer 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);
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 3af51d5..8c3d453 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -15,56 +15,164 @@
*/
package com.android.launcher3.taskbar;
-import android.content.ContextWrapper;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
+import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR;
+
+import android.app.ActivityOptions;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.Process;
+import android.os.SystemProperties;
+import android.util.Log;
+import android.view.ContextThemeWrapper;
+import android.view.Display;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.WindowManager;
+import android.widget.Toast;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
+import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.R;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton;
+import com.android.launcher3.touch.ItemClickHandler;
+import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.Themes;
+import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.views.ActivityContext;
-import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.WindowManagerWrapper;
/**
* The {@link ActivityContext} with which we inflate Taskbar-related Views. This allows UI elements
* that are used by both Launcher and Taskbar (such as Folder) to reference a generic
* ActivityContext and BaseDragLayer instead of the Launcher activity and its DragLayer.
*/
-public class TaskbarActivityContext extends ContextWrapper implements ActivityContext {
+public class TaskbarActivityContext extends ContextThemeWrapper implements ActivityContext {
+
+ private static final boolean ENABLE_THREE_BUTTON_TASKBAR =
+ SystemProperties.getBoolean("persist.debug.taskbar_three_button", false);
+ private static final String TAG = "TaskbarActivityContext";
+
+ private static final String WINDOW_TITLE = "Taskbar";
private final DeviceProfile mDeviceProfile;
private final LayoutInflater mLayoutInflater;
- private final TaskbarContainerView mTaskbarContainerView;
+ private final TaskbarDragLayer mDragLayer;
+ private final TaskbarIconController mIconController;
private final MyDragController mDragController;
- public TaskbarActivityContext(BaseQuickstepLauncher launcher) {
- super(launcher);
- mDeviceProfile = launcher.getDeviceProfile().copy(this);
+ private final WindowManager mWindowManager;
+ private WindowManager.LayoutParams mWindowLayoutParams;
+
+ private final SysUINavigationMode.Mode mNavMode;
+ private final TaskbarNavButtonController mNavButtonController;
+
+ private final boolean mIsSafeModeEnabled;
+
+ @NonNull
+ private TaskbarUIController mUIController = TaskbarUIController.DEFAULT;
+
+ private final View.OnClickListener mOnTaskbarIconClickListener;
+ private final View.OnLongClickListener mOnTaskbarIconLongClickListener;
+
+ public TaskbarActivityContext(Context windowContext, DeviceProfile dp,
+ TaskbarNavButtonController buttonController) {
+ super(windowContext, Themes.getActivityThemeRes(windowContext));
+ mDeviceProfile = dp;
+ mNavButtonController = buttonController;
+ mNavMode = SysUINavigationMode.getMode(windowContext);
+ mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
+ () -> getPackageManager().isSafeMode());
+
+ mOnTaskbarIconLongClickListener =
+ new TaskbarDragController(this)::startSystemDragOnLongClick;
+ mOnTaskbarIconClickListener = this::onTaskbarIconClicked;
+
float taskbarIconSize = getResources().getDimension(R.dimen.taskbar_icon_size);
float iconScale = taskbarIconSize / mDeviceProfile.iconSizePx;
mDeviceProfile.updateIconSize(iconScale, getResources());
mLayoutInflater = LayoutInflater.from(this).cloneInContext(this);
-
- mTaskbarContainerView = (TaskbarContainerView) mLayoutInflater
+ mDragLayer = (TaskbarDragLayer) mLayoutInflater
.inflate(R.layout.taskbar, null, false);
+ mIconController = new TaskbarIconController(this, mDragLayer);
mDragController = new MyDragController(this);
+
+ Display display = windowContext.getDisplay();
+ Context c = display.getDisplayId() == Display.DEFAULT_DISPLAY
+ ? windowContext.getApplicationContext()
+ : windowContext.getApplicationContext().createDisplayContext(display);
+ mWindowManager = c.getSystemService(WindowManager.class);
}
- public TaskbarContainerView getTaskbarContainerView() {
- return mTaskbarContainerView;
+ public void init() {
+ mWindowLayoutParams = new WindowManager.LayoutParams(
+ MATCH_PARENT,
+ mDeviceProfile.taskbarSize,
+ TYPE_APPLICATION_OVERLAY,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSLUCENT);
+ mWindowLayoutParams.setTitle(WINDOW_TITLE);
+ mWindowLayoutParams.packageName = getPackageName();
+ mWindowLayoutParams.gravity = Gravity.BOTTOM;
+ mWindowLayoutParams.setFitInsetsTypes(0);
+ mWindowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
+ mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ mWindowLayoutParams.setSystemApplicationOverlay(true);
+
+ WindowManagerWrapper wmWrapper = WindowManagerWrapper.getInstance();
+ wmWrapper.setProvidesInsetsTypes(
+ mWindowLayoutParams,
+ new int[] { ITYPE_EXTRA_NAVIGATION_BAR, ITYPE_BOTTOM_TAPPABLE_ELEMENT }
+ );
+
+ mIconController.init(mOnTaskbarIconClickListener, mOnTaskbarIconLongClickListener);
+ mWindowManager.addView(mDragLayer, 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(mDragLayer, mWindowLayoutParams);
+ }
+
+ public boolean canShowNavButtons() {
+ return ENABLE_THREE_BUTTON_TASKBAR && mNavMode == Mode.THREE_BUTTONS;
}
@Override
@@ -73,8 +181,8 @@
}
@Override
- public BaseDragLayer<TaskbarActivityContext> getDragLayer() {
- return mTaskbarContainerView;
+ public TaskbarDragLayer getDragLayer() {
+ return mDragLayer;
}
@Override
@@ -84,7 +192,7 @@
@Override
public Rect getFolderBoundingBox() {
- return mTaskbarContainerView.getFolderBoundingBox();
+ return mDragLayer.getFolderBoundingBox();
}
@Override
@@ -92,6 +200,116 @@
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(mDragLayer);
+ }
+
+ void onNavigationButtonClick(@TaskbarButton int buttonType) {
+ mNavButtonController.onButtonClick(buttonType);
+ }
+
+ /**
+ * Should be called when the IME visibility changes, so we can hide/show Taskbar accordingly.
+ */
+ public void setImeIsVisible(boolean isImeVisible) {
+ mIconController.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
+ */
+ public void updateImeStatus(int displayId, int vis, boolean showImeSwitcher) {
+ mIconController.updateImeStatus(displayId, vis, showImeSwitcher);
+ }
+
+ /**
+ * Updates the TaskbarContainer to MATCH_PARENT vs original Taskbar size.
+ */
+ protected void setTaskbarWindowFullscreen(boolean fullscreen) {
+ setTaskbarWindowHeight(fullscreen ? MATCH_PARENT : getDeviceProfile().taskbarSize);
+ }
+
+ protected void onTaskbarIconClicked(View view) {
+ Object tag = view.getTag();
+ if (tag instanceof Task) {
+ Task task = (Task) tag;
+ ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key,
+ ActivityOptions.makeBasic());
+ } else if (tag instanceof FolderInfo) {
+ FolderIcon folderIcon = (FolderIcon) view;
+ Folder folder = folderIcon.getFolder();
+ setTaskbarWindowFullscreen(true);
+
+ getDragLayer().post(() -> {
+ folder.animateOpen();
+
+ folder.iterateOverItems((itemInfo, itemView) -> {
+ itemView.setOnClickListener(mOnTaskbarIconClickListener);
+ itemView.setOnLongClickListener(mOnTaskbarIconLongClickListener);
+ // To play haptic when dragging, like other Taskbar items do.
+ itemView.setHapticFeedbackEnabled(true);
+ return false;
+ });
+ });
+ } else if (tag instanceof WorkspaceItemInfo) {
+ WorkspaceItemInfo info = (WorkspaceItemInfo) tag;
+ if (info.isDisabled()) {
+ ItemClickHandler.handleDisabledItemClicked(info, this);
+ } else {
+ Intent intent = new Intent(info.getIntent())
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
+ Toast.makeText(this, R.string.safemode_shortcut_error,
+ Toast.LENGTH_SHORT).show();
+ } else if (info.isPromise()) {
+ intent = new PackageManagerHelper(this)
+ .getMarketIntent(info.getTargetPackage())
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+
+ } else if (info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ String id = info.getDeepShortcutId();
+ String packageName = intent.getPackage();
+ getSystemService(LauncherApps.class)
+ .startShortcut(packageName, id, null, null, info.user);
+ } else if (info.user.equals(Process.myUserHandle())) {
+ startActivity(intent);
+ } else {
+ getSystemService(LauncherApps.class).startMainActivity(
+ intent.getComponent(), info.user, intent.getSourceBounds(), null);
+ }
+ } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
+ Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT)
+ .show();
+ Log.e(TAG, "Unable to launch. tag=" + info + " intent=" + intent, e);
+ }
+ }
+ } else {
+ Log.e(TAG, "Unknown type clicked: " + tag);
+ }
+
+ AbstractFloatingView.closeAllOpenViews(this);
+ }
+
private static class MyDragController extends DragController<TaskbarActivityContext> {
MyDragController(TaskbarActivityContext activity) {
super(activity);
@@ -106,7 +324,8 @@
}
@Override
- protected void exitDrag() { }
+ protected void exitDrag() {
+ }
@Override
protected DropTarget getDefaultDropTarget(int[] dropCoordinates) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java
index 29f6935..e20ddf8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java
@@ -21,6 +21,7 @@
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.Utilities;
+import com.android.launcher3.taskbar.LauncherTaskbarUIController.TaskbarAnimationControllerCallbacks;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.SystemUiProxy;
import com.android.systemui.shared.system.QuickStepContract;
@@ -34,7 +35,7 @@
private static final long IME_VISIBILITY_ALPHA_DURATION = 120;
private final BaseQuickstepLauncher mLauncher;
- private final TaskbarController.TaskbarAnimationControllerCallbacks mTaskbarCallbacks;
+ private final TaskbarAnimationControllerCallbacks mTaskbarCallbacks;
// Background alpha.
private final AnimatedFloat mTaskbarBackgroundAlpha = new AnimatedFloat(
@@ -55,7 +56,7 @@
this::updateTranslationY);
public TaskbarAnimationController(BaseQuickstepLauncher launcher,
- TaskbarController.TaskbarAnimationControllerCallbacks taskbarCallbacks) {
+ TaskbarAnimationControllerCallbacks taskbarCallbacks) {
mLauncher = launcher;
mTaskbarCallbacks = taskbarCallbacks;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java
deleted file mode 100644
index 621bba7..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java
+++ /dev/null
@@ -1,180 +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 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;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.R;
-import com.android.launcher3.util.TouchController;
-import com.android.launcher3.views.BaseDragLayer;
-import com.android.systemui.shared.system.ViewTreeObserverWrapper;
-
-/**
- * Top-level ViewGroup that hosts the TaskbarView as well as Views created by it such as Folder.
- */
-public class TaskbarContainerView extends BaseDragLayer<TaskbarActivityContext> {
-
- private final int[] mTempLoc = new int[2];
- private final int mFolderMargin;
- private final Paint mTaskbarBackgroundPaint;
-
- // Initialized in TaskbarController constructor.
- private TaskbarController.TaskbarContainerViewCallbacks mControllerCallbacks;
-
- // Initialized in init.
- private TaskbarView mTaskbarView;
- private ViewTreeObserverWrapper.OnComputeInsetsListener mTaskbarInsetsComputer;
-
- public TaskbarContainerView(@NonNull Context context) {
- this(context, null);
- }
-
- public TaskbarContainerView(@NonNull Context context, @Nullable AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public TaskbarContainerView(@NonNull Context context, @Nullable AttributeSet attrs,
- int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public TaskbarContainerView(@NonNull Context context, @Nullable AttributeSet attrs,
- int defStyleAttr, int defStyleRes) {
- super(context, attrs, 1 /* alphaChannelCount */);
- 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();
- }
-
- @Override
- public void recreateControllers() {
- mControllers = new TouchController[0];
- }
-
- private ViewTreeObserverWrapper.OnComputeInsetsListener createTaskbarInsetsComputer() {
- return insetsInfo -> {
- if (mControllerCallbacks.isTaskbarTouchable()) {
- // Accept touches anywhere in our bounds.
- insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME);
- } else {
- // Let touches pass through us.
- insetsInfo.touchableRegion.setEmpty();
- insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
- }
-
- // TaskbarContainerView provides insets to other apps based on contentInsets. These
- // insets should stay consistent even if we expand TaskbarContainerView's bounds, e.g.
- // to show a floating view like Folder. Thus, we set the contentInsets to be where
- // mTaskbarView is, since its position never changes and insets rather than overlays.
- int[] loc = mTempLoc;
- float scale = mTaskbarView.getScaleX();
- float translationY = mTaskbarView.getTranslationY();
- mTaskbarView.setScaleX(1);
- mTaskbarView.setScaleY(1);
- mTaskbarView.setTranslationY(0);
- mTaskbarView.getLocationInWindow(loc);
- mTaskbarView.setScaleX(scale);
- mTaskbarView.setScaleY(scale);
- mTaskbarView.setTranslationY(translationY);
- insetsInfo.contentInsets.left = loc[0];
- insetsInfo.contentInsets.top = loc[1];
- insetsInfo.contentInsets.right = getWidth() - (loc[0] + mTaskbarView.getWidth());
- insetsInfo.contentInsets.bottom = getHeight() - (loc[1] + mTaskbarView.getHeight());
- };
- }
-
- protected void cleanup() {
- ViewTreeObserverWrapper.removeOnComputeInsetsListener(mTaskbarInsetsComputer);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- ViewTreeObserverWrapper.addOnComputeInsetsListener(getViewTreeObserver(),
- mTaskbarInsetsComputer);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
-
- cleanup();
- }
-
- @Override
- protected boolean canFindActiveController() {
- // Unlike super class, we want to be able to find controllers when touches occur in the
- // gesture area. For example, this allows Folder to close itself when touching the Taskbar.
- return true;
- }
-
- @Override
- public void onViewRemoved(View child) {
- super.onViewRemoved(child);
- mControllerCallbacks.onViewRemoved();
- }
-
- /**
- * @return Bounds (in our coordinates) where an opened Folder can display.
- */
- protected Rect getFolderBoundingBox() {
- Rect boundingBox = new Rect(0, 0, getWidth(), getHeight() - mTaskbarView.getHeight());
- boundingBox.inset(mFolderMargin, mFolderMargin);
- 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.
- * @param alpha 0 is fully transparent, 1 is fully opaque.
- */
- protected void setTaskbarBackgroundAlpha(float alpha) {
- mTaskbarBackgroundPaint.setAlpha((int) (alpha * 255));
- invalidate();
- }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
deleted file mode 100644
index 6084e10..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
+++ /dev/null
@@ -1,596 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.taskbar;
-
-import static android.view.View.GONE;
-import static android.view.View.INVISIBLE;
-import static android.view.View.VISIBLE;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
-import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.app.ActivityOptions;
-import android.graphics.PixelFormat;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.inputmethodservice.InputMethodService;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.WindowManager;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseQuickstepLauncher;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.QuickstepTransitionManager;
-import com.android.launcher3.R;
-import com.android.launcher3.anim.AlphaUpdateListener;
-import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.folder.Folder;
-import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.model.data.FolderInfo;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton;
-import com.android.launcher3.touch.ItemClickHandler;
-import com.android.launcher3.views.ActivityContext;
-import com.android.quickstep.AnimatedFloat;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.TouchInteractionService.TaskbarOverviewProxyDelegate;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.WindowManagerWrapper;
-
-/**
- * Interfaces with Launcher/WindowManager/SystemUI to determine what to show in TaskbarView.
- */
-public class TaskbarController implements TaskbarOverviewProxyDelegate {
-
- private static final String WINDOW_TITLE = "Taskbar";
-
- private final TaskbarContainerView mTaskbarContainerView;
- private final TaskbarView mTaskbarViewInApp;
- private final TaskbarView mTaskbarViewOnHome;
- private final ImeBarView mImeBarView;
-
- private final BaseQuickstepLauncher mLauncher;
- private final WindowManager mWindowManager;
- // Layout width and height of the Taskbar in the default state.
- private final Point mTaskbarSize;
- private final TaskbarStateHandler mTaskbarStateHandler;
- private final TaskbarAnimationController mTaskbarAnimationController;
- private final TaskbarHotseatController mHotseatController;
- private final TaskbarDragController mDragController;
- private final TaskbarNavButtonController mNavButtonController;
-
- // Initialized in init().
- private WindowManager.LayoutParams mWindowLayoutParams;
- private SysUINavigationMode.Mode mNavMode = SysUINavigationMode.Mode.NO_BUTTON;
- private final SysUINavigationMode.NavigationModeChangeListener mNavigationModeChangeListener =
- this::onNavModeChanged;
-
- private @Nullable Animator mAnimator;
- private boolean mIsAnimatingToLauncher;
-
- public TaskbarController(BaseQuickstepLauncher launcher,
- TaskbarContainerView taskbarContainerView, TaskbarView taskbarViewOnHome) {
- mLauncher = launcher;
- mTaskbarContainerView = taskbarContainerView;
- mTaskbarContainerView.construct(createTaskbarContainerViewCallbacks());
- ButtonProvider buttonProvider = new ButtonProvider(launcher);
- mTaskbarViewInApp = mTaskbarContainerView.findViewById(R.id.taskbar_view);
- mTaskbarViewInApp.construct(createTaskbarViewCallbacks(), buttonProvider);
- mTaskbarViewOnHome = taskbarViewOnHome;
- mTaskbarViewOnHome.construct(createTaskbarViewCallbacks(), buttonProvider);
- mImeBarView = mTaskbarContainerView.findViewById(R.id.ime_bar_view);
- mImeBarView.construct(buttonProvider);
- mNavButtonController = new TaskbarNavButtonController(launcher);
- mWindowManager = mLauncher.getWindowManager();
- mTaskbarSize = new Point(MATCH_PARENT, mLauncher.getDeviceProfile().taskbarSize);
- mTaskbarStateHandler = mLauncher.getTaskbarStateHandler();
- mTaskbarAnimationController = new TaskbarAnimationController(mLauncher,
- createTaskbarAnimationControllerCallbacks());
- mHotseatController = new TaskbarHotseatController(mLauncher,
- createTaskbarHotseatControllerCallbacks());
- mDragController = new TaskbarDragController(mLauncher);
- }
-
- private TaskbarAnimationControllerCallbacks createTaskbarAnimationControllerCallbacks() {
- return new TaskbarAnimationControllerCallbacks() {
- @Override
- public void updateTaskbarBackgroundAlpha(float alpha) {
- mTaskbarContainerView.setTaskbarBackgroundAlpha(alpha);
- }
-
- @Override
- public void updateTaskbarVisibilityAlpha(float alpha) {
- mTaskbarViewInApp.setAlpha(alpha);
- mTaskbarViewOnHome.setAlpha(alpha);
- }
-
- @Override
- public void updateImeBarVisibilityAlpha(float alpha) {
- if (mNavMode != SysUINavigationMode.Mode.THREE_BUTTONS) {
- // TODO Remove sysui IME bar for gesture nav as well
- return;
- }
- mImeBarView.setAlpha(alpha);
- mImeBarView.setVisibility(alpha == 0 ? GONE : VISIBLE);
- }
-
- @Override
- public void updateTaskbarScale(float scale) {
- mTaskbarViewInApp.setScaleX(scale);
- mTaskbarViewInApp.setScaleY(scale);
- }
-
- @Override
- public void updateTaskbarTranslationY(float translationY) {
- if (translationY < 0) {
- // Resize to accommodate the max translation we'll reach.
- setTaskbarWindowHeight(mTaskbarSize.y
- + mLauncher.getHotseat().getTaskbarOffsetY());
- } else {
- setTaskbarWindowHeight(mTaskbarSize.y);
- }
- mTaskbarViewInApp.setTranslationY(translationY);
- }
- };
- }
-
- private TaskbarContainerViewCallbacks createTaskbarContainerViewCallbacks() {
- return new TaskbarContainerViewCallbacks() {
- @Override
- public void onViewRemoved() {
- // Ensure no other children present (like Folders, etc)
- for (int i = 0; i < mTaskbarContainerView.getChildCount(); i++) {
- View v = mTaskbarContainerView.getChildAt(i);
- if (!((v instanceof TaskbarView) || (v instanceof ImeBarView))){
- return;
- }
- }
- setTaskbarWindowFullscreen(false);
- }
-
- @Override
- public boolean isTaskbarTouchable() {
- return mTaskbarContainerView.getAlpha() > AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD
- && (mTaskbarViewInApp.getVisibility() == VISIBLE
- || mImeBarView.getVisibility() == VISIBLE)
- && !mIsAnimatingToLauncher;
- }
- };
- }
-
- private TaskbarViewCallbacks createTaskbarViewCallbacks() {
- return new TaskbarViewCallbacks() {
- @Override
- public View.OnClickListener getItemOnClickListener() {
- return view -> {
- Object tag = view.getTag();
- if (tag instanceof Task) {
- Task task = (Task) tag;
- ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key,
- ActivityOptions.makeBasic());
- } else if (tag instanceof FolderInfo) {
- FolderIcon folderIcon = (FolderIcon) view;
- Folder folder = folderIcon.getFolder();
-
- setTaskbarWindowFullscreen(true);
-
- mTaskbarContainerView.post(() -> {
- folder.animateOpen();
-
- folder.iterateOverItems((itemInfo, itemView) -> {
- itemView.setOnClickListener(getItemOnClickListener());
- itemView.setOnLongClickListener(getItemOnLongClickListener());
- // To play haptic when dragging, like other Taskbar items do.
- itemView.setHapticFeedbackEnabled(true);
- return false;
- });
- });
- } else {
- ItemClickHandler.INSTANCE.onClick(view);
- }
-
- AbstractFloatingView.closeAllOpenViews(
- mTaskbarContainerView.getTaskbarActivityContext());
- };
- }
-
- @Override
- public View.OnLongClickListener getItemOnLongClickListener() {
- return mDragController::startSystemDragOnLongClick;
- }
-
- @Override
- public int getEmptyHotseatViewVisibility(TaskbarView taskbarView) {
- // When on the home screen, we want the empty hotseat views to take up their full
- // space so that the others line up with the home screen hotseat.
- boolean isOnHomeScreen = taskbarView == mTaskbarViewOnHome
- || mLauncher.hasBeenResumed() || mIsAnimatingToLauncher;
- return isOnHomeScreen ? INVISIBLE : GONE;
- }
-
- @Override
- public float getNonIconScale(TaskbarView taskbarView) {
- return taskbarView == mTaskbarViewOnHome ? getTaskbarScaleOnHome() : 1f;
- }
-
- @Override
- public void onItemPositionsChanged(TaskbarView taskbarView) {
- if (taskbarView == mTaskbarViewOnHome) {
- alignRealHotseatWithTaskbar();
- }
- }
-
- @Override
- public void onNavigationButtonClick(@TaskbarButton int buttonType) {
- mNavButtonController.onButtonClick(buttonType);
- }
- };
- }
-
- private TaskbarHotseatControllerCallbacks createTaskbarHotseatControllerCallbacks() {
- return new TaskbarHotseatControllerCallbacks() {
- @Override
- public void updateHotseatItems(ItemInfo[] hotseatItemInfos) {
- mTaskbarViewInApp.updateHotseatItems(hotseatItemInfos);
- }
- };
- }
-
- /**
- * Initializes the Taskbar, including adding it to the screen.
- */
- public void init() {
- mNavMode = SysUINavigationMode.INSTANCE.get(mLauncher)
- .addModeChangeListener(mNavigationModeChangeListener);
- mTaskbarViewInApp.init(mHotseatController.getNumHotseatIcons(), mNavMode);
- mTaskbarViewOnHome.init(mHotseatController.getNumHotseatIcons(), mNavMode);
- mTaskbarContainerView.init(mTaskbarViewInApp);
- mImeBarView.init(createTaskbarViewCallbacks());
- addToWindowManager();
- mTaskbarStateHandler.setTaskbarCallbacks(createTaskbarStateHandlerCallbacks());
- mTaskbarAnimationController.init();
- mHotseatController.init();
-
- setWhichTaskbarViewIsVisible(mLauncher.hasBeenResumed()
- ? mTaskbarViewOnHome
- : mTaskbarViewInApp);
- }
-
- private TaskbarStateHandlerCallbacks createTaskbarStateHandlerCallbacks() {
- return new TaskbarStateHandlerCallbacks() {
- @Override
- public AnimatedFloat getAlphaTarget() {
- return mTaskbarAnimationController.getTaskbarVisibilityForLauncherState();
- }
-
- @Override
- public AnimatedFloat getScaleTarget() {
- return mTaskbarAnimationController.getTaskbarScaleForLauncherState();
- }
-
- @Override
- public AnimatedFloat getTranslationYTarget() {
- return mTaskbarAnimationController.getTaskbarTranslationYForLauncherState();
- }
- };
- }
-
- /**
- * Removes the Taskbar from the screen, and removes any obsolete listeners etc.
- */
- public void cleanup() {
- if (mAnimator != null) {
- // End this first, in case it relies on properties that are about to be cleaned up.
- mAnimator.end();
- }
-
- mTaskbarViewInApp.cleanup();
- mTaskbarViewOnHome.cleanup();
- mTaskbarContainerView.cleanup();
- mImeBarView.cleanup();
- removeFromWindowManager();
- mTaskbarStateHandler.setTaskbarCallbacks(null);
- mTaskbarAnimationController.cleanup();
- mHotseatController.cleanup();
-
- setWhichTaskbarViewIsVisible(null);
- SysUINavigationMode.INSTANCE.get(mLauncher)
- .removeModeChangeListener(mNavigationModeChangeListener);
- }
-
- private void removeFromWindowManager() {
- mWindowManager.removeViewImmediate(mTaskbarContainerView);
- }
-
- private void addToWindowManager() {
- final int gravity = Gravity.BOTTOM;
-
- mWindowLayoutParams = new WindowManager.LayoutParams(
- mTaskbarSize.x,
- mTaskbarSize.y,
- TYPE_APPLICATION_OVERLAY,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
- PixelFormat.TRANSLUCENT);
- mWindowLayoutParams.setTitle(WINDOW_TITLE);
- mWindowLayoutParams.packageName = mLauncher.getPackageName();
- mWindowLayoutParams.gravity = gravity;
- mWindowLayoutParams.setFitInsetsTypes(0);
- mWindowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
- mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- mWindowLayoutParams.setSystemApplicationOverlay(true);
-
- WindowManagerWrapper wmWrapper = WindowManagerWrapper.getInstance();
- wmWrapper.setProvidesInsetsTypes(
- mWindowLayoutParams,
- new int[] { ITYPE_EXTRA_NAVIGATION_BAR, ITYPE_BOTTOM_TAPPABLE_ELEMENT }
- );
-
- TaskbarContainerView.LayoutParams taskbarLayoutParams =
- new TaskbarContainerView.LayoutParams(mTaskbarSize.x, mTaskbarSize.y);
- taskbarLayoutParams.gravity = gravity;
- mTaskbarViewInApp.setLayoutParams(taskbarLayoutParams);
-
- mWindowManager.addView(mTaskbarContainerView, mWindowLayoutParams);
- }
-
- private void onNavModeChanged(SysUINavigationMode.Mode newMode) {
- mNavMode = newMode;
- cleanup();
- init();
- }
-
- /**
- * Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
- */
- public void onLauncherResumedOrPaused(boolean isResumed) {
- long duration = QuickstepTransitionManager.CONTENT_ALPHA_DURATION;
- if (mAnimator != null) {
- mAnimator.cancel();
- }
- if (isResumed) {
- mAnimator = createAnimToLauncher(null, duration);
- } else {
- mAnimator = createAnimToApp(duration);
- }
- mAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mAnimator = null;
- }
- });
- mAnimator.start();
- }
-
- /**
- * Create Taskbar animation when going from an app to Launcher.
- * @param toState If known, the state we will end up in when reaching Launcher.
- */
- public Animator createAnimToLauncher(@Nullable LauncherState toState, long duration) {
- PendingAnimation anim = new PendingAnimation(duration);
- anim.add(mTaskbarAnimationController.createAnimToBackgroundAlpha(0, duration));
- if (toState != null) {
- mTaskbarStateHandler.setStateWithAnimation(toState, new StateAnimationConfig(), anim);
- }
-
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mIsAnimatingToLauncher = true;
- mTaskbarViewInApp.updateHotseatItemsVisibility();
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mIsAnimatingToLauncher = false;
- setWhichTaskbarViewIsVisible(mTaskbarViewOnHome);
- }
- });
-
- return anim.buildAnim();
- }
-
- private Animator createAnimToApp(long duration) {
- PendingAnimation anim = new PendingAnimation(duration);
- anim.add(mTaskbarAnimationController.createAnimToBackgroundAlpha(1, duration));
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mTaskbarViewInApp.updateHotseatItemsVisibility();
- setWhichTaskbarViewIsVisible(mTaskbarViewInApp);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- }
- });
- return anim.buildAnim();
- }
-
- /**
- * Should be called when the IME visibility changes, so we can hide/show Taskbar accordingly.
- */
- public void setIsImeVisible(boolean isImeVisible) {
- mTaskbarAnimationController.animateToVisibilityForIme(isImeVisible ? 0 : 1);
- blockTaskbarTouchesForIme(isImeVisible);
- }
-
- /**
- * When in 3 button nav, the above doesn't get called since we prevent sysui nav bar from
- * instantiating at all, which is what's responsible for sending sysui state flags over.
- *
- * @param vis IME visibility flag
- * @param backDisposition Used to determine back button behavior for software keyboard
- * See BACK_DISPOSITION_* constants in {@link InputMethodService}
- */
- public void updateImeStatus(int displayId, int vis, int backDisposition,
- boolean showImeSwitcher) {
- if (displayId != mTaskbarContainerView.getContext().getDisplayId() ||
- mNavMode != SysUINavigationMode.Mode.THREE_BUTTONS) {
- return;
- }
-
- boolean imeVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
- mTaskbarAnimationController.animateToVisibilityForIme(imeVisible ? 0 : 1);
- mImeBarView.setImeSwitcherVisibility(showImeSwitcher);
- blockTaskbarTouchesForIme(imeVisible);
- }
-
- /**
- * Should be called when one or more items in the Hotseat have changed.
- */
- public void onHotseatUpdated() {
- mHotseatController.onHotseatUpdated();
- }
-
- /**
- * @param ev MotionEvent in screen coordinates.
- * @return Whether any Taskbar item could handle the given MotionEvent if given the chance.
- */
- public boolean isEventOverAnyTaskbarItem(MotionEvent ev) {
- return mTaskbarViewInApp.isEventOverAnyItem(ev);
- }
-
- public boolean isDraggingItem() {
- return mTaskbarViewInApp.isDraggingItem() || mTaskbarViewOnHome.isDraggingItem();
- }
-
- /**
- * @return Whether the given View is in the same window as Taskbar.
- */
- public boolean isViewInTaskbar(View v) {
- return mTaskbarContainerView.isAttachedToWindow()
- && mTaskbarContainerView.getWindowId().equals(v.getWindowId());
- }
-
- /**
- * Pads the Hotseat to line up exactly with Taskbar's copy of the Hotseat.
- */
- public void alignRealHotseatWithTaskbar() {
- Rect hotseatBounds = new Rect();
- DeviceProfile grid = mLauncher.getDeviceProfile();
- int hotseatHeight = grid.workspacePadding.bottom + grid.taskbarSize;
- int taskbarOffset = mLauncher.getHotseat().getTaskbarOffsetY();
- int hotseatTopDiff = hotseatHeight - grid.taskbarSize - taskbarOffset;
- int hotseatBottomDiff = taskbarOffset;
-
- mTaskbarViewOnHome.getHotseatBounds().roundOut(hotseatBounds);
- mLauncher.getHotseat().setPadding(hotseatBounds.left,
- hotseatBounds.top + hotseatTopDiff,
- mTaskbarViewOnHome.getWidth() - hotseatBounds.right,
- mTaskbarViewOnHome.getHeight() - hotseatBounds.bottom + hotseatBottomDiff);
- }
-
- private void setWhichTaskbarViewIsVisible(@Nullable TaskbarView visibleTaskbar) {
- mTaskbarViewInApp.setVisibility(visibleTaskbar == mTaskbarViewInApp
- ? VISIBLE : INVISIBLE);
- mTaskbarViewOnHome.setVisibility(visibleTaskbar == mTaskbarViewOnHome
- ? VISIBLE : INVISIBLE);
- mLauncher.getHotseat().setIconsAlpha(visibleTaskbar != mTaskbarViewInApp ? 1f : 0f);
- }
-
- private void blockTaskbarTouchesForIme(boolean block) {
- mTaskbarViewOnHome.setTouchesEnabled(!block);
- mTaskbarViewInApp.setTouchesEnabled(!block);
- }
-
- /**
- * Returns the ratio of the taskbar icon size on home vs in an app.
- */
- public float getTaskbarScaleOnHome() {
- DeviceProfile inAppDp = mTaskbarContainerView.getTaskbarActivityContext()
- .getDeviceProfile();
- DeviceProfile onHomeDp = ActivityContext.lookupContext(mTaskbarViewOnHome.getContext())
- .getDeviceProfile();
- return (float) onHomeDp.cellWidthPx / inAppDp.cellWidthPx;
- }
-
- /**
- * Updates the TaskbarContainer to MATCH_PARENT vs original Taskbar size.
- */
- private void setTaskbarWindowFullscreen(boolean fullscreen) {
- setTaskbarWindowHeight(fullscreen ? MATCH_PARENT : mTaskbarSize.y);
- }
-
- /**
- * Updates the TaskbarContainer height (pass mTaskbarSize.y to reset).
- */
- private void setTaskbarWindowHeight(int height) {
- mWindowLayoutParams.width = mTaskbarSize.x;
- mWindowLayoutParams.height = height;
- mWindowManager.updateViewLayout(mTaskbarContainerView, mWindowLayoutParams);
- }
-
- /**
- * Contains methods that TaskbarStateHandler can call to interface with TaskbarController.
- */
- protected interface TaskbarStateHandlerCallbacks {
- AnimatedFloat getAlphaTarget();
- AnimatedFloat getScaleTarget();
- AnimatedFloat getTranslationYTarget();
- }
-
- /**
- * Contains methods that TaskbarAnimationController can call to interface with
- * TaskbarController.
- */
- protected interface TaskbarAnimationControllerCallbacks {
- void updateTaskbarBackgroundAlpha(float alpha);
- void updateTaskbarVisibilityAlpha(float alpha);
- void updateImeBarVisibilityAlpha(float alpha);
- void updateTaskbarScale(float scale);
- void updateTaskbarTranslationY(float translationY);
- }
-
- /**
- * Contains methods that TaskbarContainerView can call to interface with TaskbarController.
- */
- protected interface TaskbarContainerViewCallbacks {
- void onViewRemoved();
- boolean isTaskbarTouchable();
- }
-
- /**
- * Contains methods that TaskbarView can call to interface with TaskbarController.
- */
- protected interface TaskbarViewCallbacks {
- View.OnClickListener getItemOnClickListener();
- View.OnLongClickListener getItemOnLongClickListener();
- int getEmptyHotseatViewVisibility(TaskbarView taskbarView);
- /** Returns how much to scale non-icon elements such as spacing and dividers. */
- float getNonIconScale(TaskbarView taskbarView);
- void onItemPositionsChanged(TaskbarView taskbarView);
- void onNavigationButtonClick(@TaskbarButton int buttonType);
- }
-
- /**
- * Contains methods that TaskbarHotseatController can call to interface with TaskbarController.
- */
- protected interface TaskbarHotseatControllerCallbacks {
- void updateHotseatItems(ItemInfo[] hotseatItemInfos);
- }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index 5eb34cb..ee44927 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -20,6 +20,7 @@
import android.content.ClipData;
import android.content.ClipDescription;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.res.Resources;
@@ -29,7 +30,6 @@
import android.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 @@
*/
public class TaskbarDragController {
- private final BaseQuickstepLauncher mLauncher;
+ private final Context mContext;
private final int mDragIconSize;
- public TaskbarDragController(BaseQuickstepLauncher launcher) {
- mLauncher = launcher;
- Resources resources = mLauncher.getResources();
+ public TaskbarDragController(Context context) {
+ mContext = context;
+ Resources resources = mContext.getResources();
mDragIconSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_drag_icon_size);
}
@@ -63,7 +63,6 @@
}
BubbleTextView btv = (BubbleTextView) view;
-
View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view) {
@Override
public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
@@ -87,7 +86,7 @@
Intent intent = null;
if (tag instanceof WorkspaceItemInfo) {
WorkspaceItemInfo item = (WorkspaceItemInfo) tag;
- LauncherApps launcherApps = mLauncher.getSystemService(LauncherApps.class);
+ LauncherApps launcherApps = mContext.getSystemService(LauncherApps.class);
clipDescription = new ClipDescription(item.title,
new String[] {
item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
new file mode 100644
index 0000000..45ec911
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -0,0 +1,150 @@
+/*
+ * 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.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+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 TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> {
+
+ private final int mFolderMargin;
+ private final Paint mTaskbarBackgroundPaint;
+
+ private TaskbarIconController.Callbacks mControllerCallbacks;
+ private TaskbarView mTaskbarView;
+
+ private final OnComputeInsetsListener mTaskbarInsetsComputer = this::onComputeTaskbarInsets;
+
+ public TaskbarDragLayer(@NonNull Context context) {
+ this(context, null);
+ }
+
+ public TaskbarDragLayer(@NonNull Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TaskbarDragLayer(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public TaskbarDragLayer(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, 1 /* alphaChannelCount */);
+ mFolderMargin = getResources().getDimensionPixelSize(R.dimen.taskbar_folder_margin);
+ mTaskbarBackgroundPaint = new Paint();
+ mTaskbarBackgroundPaint.setColor(getResources().getColor(R.color.taskbar_background));
+ recreateControllers();
+ }
+
+ @Override
+ public void recreateControllers() {
+ mControllers = new TouchController[0];
+ }
+
+ public void init(TaskbarIconController.Callbacks callbacks, TaskbarView taskbarView) {
+ mControllerCallbacks = callbacks;
+ mTaskbarView = taskbarView;
+ }
+
+ private void onComputeTaskbarInsets(InsetsInfo insetsInfo) {
+ if (mControllerCallbacks != null) {
+ mControllerCallbacks.updateInsetsTouchability(insetsInfo);
+ }
+ }
+
+ protected void onDestroy() {
+ ViewTreeObserverWrapper.removeOnComputeInsetsListener(mTaskbarInsetsComputer);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ ViewTreeObserverWrapper.addOnComputeInsetsListener(getViewTreeObserver(),
+ mTaskbarInsetsComputer);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ onDestroy();
+ }
+
+ @Override
+ protected boolean canFindActiveController() {
+ // Unlike super class, we want to be able to find controllers when touches occur in the
+ // gesture area. For example, this allows Folder to close itself when touching the Taskbar.
+ 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.onDragLayerViewRemoved();
+ }
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ canvas.drawRect(0, canvas.getHeight() - mTaskbarView.getHeight(), canvas.getWidth(),
+ canvas.getHeight(), mTaskbarBackgroundPaint);
+ super.dispatchDraw(canvas);
+ }
+
+ /**
+ * @return Bounds (in our coordinates) where an opened Folder can display.
+ */
+ protected Rect getFolderBoundingBox() {
+ Rect boundingBox = new Rect(0, 0, getWidth(), getHeight() - mTaskbarView.getHeight());
+ boundingBox.inset(mFolderMargin, mFolderMargin);
+ return boundingBox;
+ }
+
+
+ /**
+ * Sets the alpha of the background color behind all the Taskbar contents.
+ * @param alpha 0 is fully transparent, 1 is fully opaque.
+ */
+ protected void setTaskbarBackgroundAlpha(float alpha) {
+ mTaskbarBackgroundPaint.setAlpha((int) (alpha * 255));
+ invalidate();
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java
index 68829cd..91cf7ef 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java
@@ -26,6 +26,8 @@
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.model.data.ItemInfo;
+import java.util.function.Consumer;
+
/**
* Works with TaskbarController to update the TaskbarView's Hotseat items.
*/
@@ -33,13 +35,12 @@
private final BaseQuickstepLauncher mLauncher;
private final Hotseat mHotseat;
- private final TaskbarController.TaskbarHotseatControllerCallbacks mTaskbarCallbacks;
+ private final Consumer<ItemInfo[]> mTaskbarCallbacks;
private final int mNumHotseatIcons;
private final DragController.DragListener mDragListener = new DragController.DragListener() {
@Override
- public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
- }
+ public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { }
@Override
public void onDragEnd() {
@@ -47,8 +48,8 @@
}
};
- public TaskbarHotseatController(BaseQuickstepLauncher launcher,
- TaskbarController.TaskbarHotseatControllerCallbacks taskbarCallbacks) {
+ public TaskbarHotseatController(
+ BaseQuickstepLauncher launcher, Consumer<ItemInfo[]> taskbarCallbacks) {
mLauncher = launcher;
mHotseat = mLauncher.getHotseat();
mTaskbarCallbacks = taskbarCallbacks;
@@ -85,10 +86,6 @@
}
}
- mTaskbarCallbacks.updateHotseatItems(hotseatItemInfos);
- }
-
- protected int getNumHotseatIcons() {
- return mNumHotseatIcons;
+ mTaskbarCallbacks.accept(hotseatItemInfos);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarIconController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarIconController.java
new file mode 100644
index 0000000..683a5b9
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarIconController.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar;
+
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+
+import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_FRAME;
+import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION;
+
+import android.graphics.Rect;
+import android.inputmethodservice.InputMethodService;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.R;
+import com.android.launcher3.anim.AlphaUpdateListener;
+import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo;
+
+/**
+ * Controller for taskbar icon UI
+ */
+public class TaskbarIconController {
+
+ private final Rect mTempRect = new Rect();
+
+ private final TaskbarActivityContext mActivity;
+ private final TaskbarDragLayer mDragLayer;
+
+ private final TaskbarView mTaskbarView;
+ private final ImeBarView mImeBarView;
+
+ @NonNull
+ private TaskbarUIController mUIController = TaskbarUIController.DEFAULT;
+
+ TaskbarIconController(TaskbarActivityContext activity, TaskbarDragLayer dragLayer) {
+ mActivity = activity;
+ mDragLayer = dragLayer;
+ mTaskbarView = mDragLayer.findViewById(R.id.taskbar_view);
+ mImeBarView = mDragLayer.findViewById(R.id.ime_bar_view);
+ }
+
+ public void init(OnClickListener clickListener, OnLongClickListener longClickListener) {
+ mDragLayer.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;
+
+ mDragLayer.init(new Callbacks(), mTaskbarView);
+ }
+
+ public void onDestroy() {
+ mDragLayer.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(mDragLayer, isImeVisible);
+ }
+
+ /**
+ * Callbacks for {@link TaskbarDragLayer} to interact with the icon controller
+ */
+ public class Callbacks {
+
+ /**
+ * Called to update the touchable insets
+ */
+ public void updateInsetsTouchability(InsetsInfo insetsInfo) {
+ insetsInfo.touchableRegion.setEmpty();
+ if (mDragLayer.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) {
+ mDragLayer.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 = mDragLayer.getWidth() - mTaskbarView.getRight();
+ insetsInfo.contentInsets.bottom = mDragLayer.getHeight() - mTaskbarView.getBottom();
+ }
+
+ public void onDragLayerViewRemoved() {
+ int count = mDragLayer.getChildCount();
+ // Ensure no other children present (like Folders, etc)
+ for (int i = 0; i < count; i++) {
+ View v = mDragLayer.getChildAt(i);
+ if (!((v instanceof TaskbarView) || (v instanceof ImeBarView))) {
+ return;
+ }
+ }
+ mActivity.setTaskbarWindowFullscreen(false);
+ }
+
+ public void updateImeBarVisibilityAlpha(float alpha) {
+ if (!mActivity.canShowNavButtons()) {
+ // TODO Remove sysui IME bar for gesture nav as well
+ return;
+ }
+ mImeBarView.setAlpha(alpha);
+ mImeBarView.setVisibility(alpha == 0 ? GONE : VISIBLE);
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
new file mode 100644
index 0000000..d026bfb
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
+import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
+import static com.android.launcher3.util.DisplayController.CHANGE_SUPPORTED_BOUNDS;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.inputmethodservice.InputMethodService;
+import android.view.Display;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.Info;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.TouchInteractionService;
+
+/**
+ * Class to manager taskbar lifecycle
+ */
+public class TaskbarManager implements DisplayController.DisplayInfoChangeListener,
+ SysUINavigationMode.NavigationModeChangeListener {
+
+ private final Context mContext;
+ private final DisplayController mDisplayController;
+ private final SysUINavigationMode mSysUINavigationMode;
+ private final TaskbarNavButtonController mNavButtonController;
+
+ private TaskbarActivityContext mTaskbarActivityContext;
+ private BaseQuickstepLauncher mLauncher;
+
+ private static final int CHANGE_FLAGS =
+ CHANGE_ACTIVE_SCREEN | CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS;
+
+ private boolean mUserUnlocked = false;
+
+ public TaskbarManager(TouchInteractionService service) {
+ mDisplayController = DisplayController.INSTANCE.get(service);
+ mSysUINavigationMode = SysUINavigationMode.INSTANCE.get(service);
+ Display display =
+ service.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY);
+ mContext = service.createWindowContext(display, TYPE_APPLICATION_OVERLAY, null);
+ mNavButtonController = new TaskbarNavButtonController(service);
+
+ mDisplayController.addChangeListener(this);
+ mSysUINavigationMode.addModeChangeListener(this);
+ recreateTaskbar();
+ }
+
+ @Override
+ public void onNavigationModeChanged(Mode newMode) {
+ recreateTaskbar();
+ }
+
+ @Override
+ public void onDisplayInfoChanged(Context context, Info info, int flags) {
+ if ((flags & CHANGE_FLAGS) != 0) {
+ recreateTaskbar();
+ }
+ }
+
+ private void destroyExistingTaskbar() {
+ if (mTaskbarActivityContext != null) {
+ mTaskbarActivityContext.onDestroy();
+ mTaskbarActivityContext = null;
+ }
+ }
+
+ /**
+ * Called when the user is unlocked
+ */
+ public void onUserUnlocked() {
+ mUserUnlocked = true;
+ recreateTaskbar();
+ }
+
+ /**
+ * Sets or clears a launcher to act as taskbar callback
+ */
+ public void setLauncher(@Nullable BaseQuickstepLauncher launcher) {
+ mLauncher = launcher;
+ if (mTaskbarActivityContext != null) {
+ mTaskbarActivityContext.setUIController(mLauncher == null
+ ? TaskbarUIController.DEFAULT
+ : new LauncherTaskbarUIController(launcher, mTaskbarActivityContext));
+ }
+ }
+
+ private void recreateTaskbar() {
+ destroyExistingTaskbar();
+ if (!FeatureFlags.ENABLE_TASKBAR.get()) {
+ return;
+ }
+ if (!mUserUnlocked) {
+ return;
+ }
+ DeviceProfile dp = LauncherAppState.getIDP(mContext).getDeviceProfile(mContext);
+ if (!dp.isTaskbarPresent) {
+ return;
+ }
+ mTaskbarActivityContext = new TaskbarActivityContext(
+ mContext, dp.copy(mContext), mNavButtonController);
+ mTaskbarActivityContext.init();
+ if (mLauncher != null) {
+ mTaskbarActivityContext.setUIController(
+ new LauncherTaskbarUIController(mLauncher, mTaskbarActivityContext));
+ }
+ }
+
+ /**
+ * See {@link com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags}
+ * @param systemUiStateFlags The latest SystemUiStateFlags
+ */
+ public void onSystemUiFlagsChanged(int systemUiStateFlags) {
+ boolean isImeVisible = (systemUiStateFlags & SYSUI_STATE_IME_SHOWING) != 0;
+ if (mTaskbarActivityContext != null) {
+ mTaskbarActivityContext.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.updateImeStatus(displayId, vis, showImeSwitcher);
+ }
+ }
+
+ /**
+ * Called when the manager is no longer needed
+ */
+ public void destroy() {
+ destroyExistingTaskbar();
+ mDisplayController.removeChangeListener(this);
+ mSysUINavigationMode.removeModeChangeListener(this);
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index 54e1610..3b5afad 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -16,7 +16,8 @@
package com.android.launcher3.taskbar;
-import android.content.Context;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import android.content.Intent;
import android.view.inputmethod.InputMethodManager;
@@ -53,11 +54,10 @@
static final int BUTTON_RECENTS = BUTTON_HOME << 1;
static final int BUTTON_IME_SWITCH = BUTTON_RECENTS << 1;
+ private final TouchInteractionService mService;
- private final Context mContext;
-
- public TaskbarNavButtonController(Context context) {
- mContext = context;
+ public TaskbarNavButtonController(TouchInteractionService service) {
+ mService = service;
}
public void onButtonClick(@TaskbarButton int buttonType) {
@@ -78,13 +78,13 @@
}
private void navigateHome() {
- mContext.startActivity(new Intent(Intent.ACTION_MAIN)
+ mService.startActivity(new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
private void navigateToOverview() {
- TouchInteractionService.getInstance().getOverviewCommandHelper()
+ mService.getOverviewCommandHelper()
.addCommand(OverviewCommandHelper.TYPE_SHOW);
}
@@ -93,8 +93,8 @@
}
private void showIMESwitcher() {
- mContext.getSystemService(InputMethodManager.class).showInputMethodPickerFromSystem(
- true /* showAuxiliarySubtypes */, mContext.getDisplayId());
+ mService.getSystemService(InputMethodManager.class)
+ .showInputMethodPickerFromSystem(true /* showAuxiliarySubtypes */,
+ DEFAULT_DISPLAY);
}
-
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
index 6ea51fa..a701aae 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
@@ -24,59 +24,52 @@
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.quickstep.AnimatedFloat;
/**
* StateHandler to animate Taskbar according to Launcher's state machine. Does nothing if Taskbar
- * isn't present (i.e. {@link #setTaskbarCallbacks} is never called).
+ * isn't present (i.e. {@link #setAnimationController} is never called).
*/
public class TaskbarStateHandler implements StateManager.StateHandler<LauncherState> {
private final BaseQuickstepLauncher mLauncher;
// Contains Taskbar-related methods and fields we should aniamte. If null, don't do anything.
- private @Nullable TaskbarController.TaskbarStateHandlerCallbacks mTaskbarCallbacks = null;
+ private @Nullable TaskbarAnimationController mAnimationController = null;
public TaskbarStateHandler(BaseQuickstepLauncher launcher) {
mLauncher = launcher;
}
- public void setTaskbarCallbacks(TaskbarController.TaskbarStateHandlerCallbacks callbacks) {
- mTaskbarCallbacks = callbacks;
+ public void setAnimationController(TaskbarAnimationController callbacks) {
+ mAnimationController = callbacks;
}
@Override
public void setState(LauncherState state) {
- if (mTaskbarCallbacks == null) {
- return;
- }
-
- AnimatedFloat alphaTarget = mTaskbarCallbacks.getAlphaTarget();
- AnimatedFloat scaleTarget = mTaskbarCallbacks.getScaleTarget();
- AnimatedFloat translationYTarget = mTaskbarCallbacks.getTranslationYTarget();
- boolean isTaskbarVisible = (state.getVisibleElements(mLauncher) & TASKBAR) != 0;
- alphaTarget.updateValue(isTaskbarVisible ? 1f : 0f);
- scaleTarget.updateValue(state.getTaskbarScale(mLauncher));
- translationYTarget.updateValue(state.getTaskbarTranslationY(mLauncher));
+ setState(state, PropertySetter.NO_ANIM_PROPERTY_SETTER);
}
@Override
public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
PendingAnimation animation) {
- if (mTaskbarCallbacks == null) {
+ setState(toState, animation);
+ }
+
+ private void setState(LauncherState toState, PropertySetter setter) {
+ if (mAnimationController == null) {
return;
}
- AnimatedFloat alphaTarget = mTaskbarCallbacks.getAlphaTarget();
- AnimatedFloat scaleTarget = mTaskbarCallbacks.getScaleTarget();
- AnimatedFloat translationYTarget = mTaskbarCallbacks.getTranslationYTarget();
boolean isTaskbarVisible = (toState.getVisibleElements(mLauncher) & TASKBAR) != 0;
- animation.setFloat(alphaTarget, AnimatedFloat.VALUE, isTaskbarVisible ? 1f : 0f, LINEAR);
- animation.setFloat(scaleTarget, AnimatedFloat.VALUE, toState.getTaskbarScale(mLauncher),
- LINEAR);
- animation.setFloat(translationYTarget, AnimatedFloat.VALUE,
- toState.getTaskbarTranslationY(mLauncher), ACCEL_DEACCEL);
+ setter.setFloat(mAnimationController.getTaskbarVisibilityForLauncherState(),
+ AnimatedFloat.VALUE, isTaskbarVisible ? 1f : 0f, LINEAR);
+ setter.setFloat(mAnimationController.getTaskbarScaleForLauncherState(),
+ AnimatedFloat.VALUE, toState.getTaskbarScale(mLauncher), LINEAR);
+ setter.setFloat(mAnimationController.getTaskbarTranslationYForLauncherState(),
+ AnimatedFloat.VALUE, toState.getTaskbarTranslationY(mLauncher), ACCEL_DEACCEL);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
new file mode 100644
index 0000000..50adead
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar;
+
+/**
+ * Base class for providing different taskbar UI
+ */
+public class TaskbarUIController {
+
+ public static final TaskbarUIController DEFAULT = new TaskbarUIController();
+
+ /**
+ * Pads the Hotseat to line up exactly with Taskbar's copy of the Hotseat.
+ */
+ public void alignRealHotseatWithTaskbar() { }
+
+ protected void onCreate() { }
+
+ protected void onDestroy() { }
+
+ protected boolean isTaskbarTouchable() {
+ return true;
+ }
+
+ protected void onImeVisible(TaskbarDragLayer container, boolean isVisible) {
+ container.updateImeBarVisibilityAlpha(isVisible ? 1 : 0);
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 9e8013e..c6573a6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -15,20 +15,14 @@
*/
package com.android.launcher3.taskbar;
-import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
-import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
-import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.makeMeasureSpec;
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.LayoutTransition;
-import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.os.SystemProperties;
import android.util.AttributeSet;
import android.view.DragEvent;
import android.view.Gravity;
@@ -51,17 +45,12 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.views.ActivityContext;
-import com.android.quickstep.SysUINavigationMode;
/**
* Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
*/
public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconParent, Insettable {
-
- private static final boolean ENABLE_THREE_BUTTON_TASKBAR =
- SystemProperties.getBoolean("persist.debug.taskbar_three_button", false);
-
private final int mIconTouchSize;
private final boolean mIsRtl;
private final int mTouchSlop;
@@ -69,17 +58,16 @@
private final RectF mDelegateSlopBounds = new RectF();
private final int[] mTempOutLocation = new int[2];
- // Initialized in TaskbarController constructor.
- private TaskbarController.TaskbarViewCallbacks mControllerCallbacks;
- // Scale on elements that aren't icons.
- private float mNonIconScale;
- private int mItemMarginLeftRight;
+ private final int mItemMarginLeftRight;
- // Initialized in init().
- private LayoutTransition mLayoutTransition;
- private int mHotseatStartIndex;
- private int mHotseatEndIndex;
- private LinearLayout mButtonRegion;
+ private final TaskbarActivityContext mActivityContext;
+
+ // Initialized in TaskbarController constructor.
+ private View.OnClickListener mIconClickListener;
+ private View.OnLongClickListener mIconLongClickListener;
+
+ LinearLayout mSystemButtonContainer;
+ LinearLayout mHotseatIconsContainer;
// Delegate touches to the closest view if within mIconTouchSize.
private boolean mDelegateTargeted;
@@ -91,10 +79,12 @@
// Only non-null when the corresponding Folder is open.
private @Nullable FolderIcon mLeaveBehindFolderIcon;
- private int mNavButtonStartIndex;
/** Provider of buttons added to taskbar in 3 button nav */
private ButtonProvider mButtonProvider;
+ private boolean mDisableRelayout;
+ private boolean mAreHolesAllowed;
+
public TaskbarView(@NonNull Context context) {
this(context, null);
}
@@ -111,80 +101,58 @@
public TaskbarView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mActivityContext = ActivityContext.lookupContext(context);
Resources resources = getResources();
mIconTouchSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_touch_size);
+ mItemMarginLeftRight = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
+
mIsRtl = Utilities.isRtl(resources);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
- protected void construct(TaskbarController.TaskbarViewCallbacks taskbarViewCallbacks,
- ButtonProvider buttonProvider) {
- mControllerCallbacks = taskbarViewCallbacks;
- mNonIconScale = mControllerCallbacks.getNonIconScale(this);
- mItemMarginLeftRight = getResources().getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
- mItemMarginLeftRight = Math.round(mItemMarginLeftRight * mNonIconScale);
- mButtonProvider = buttonProvider;
- mButtonProvider.setMarginLeftRight(mItemMarginLeftRight);
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mSystemButtonContainer = findViewById(R.id.system_button_layout);
+ mHotseatIconsContainer = findViewById(R.id.hotseat_icons_layout);
}
- protected void init(int numHotseatIcons, SysUINavigationMode.Mode newMode) {
- // TODO: check if buttons on left
- if (newMode == SysUINavigationMode.Mode.THREE_BUTTONS && ENABLE_THREE_BUTTON_TASKBAR) {
- // 3 button
- mNavButtonStartIndex = 0;
+ protected void construct(OnClickListener clickListener, OnLongClickListener longClickListener,
+ ButtonProvider buttonProvider) {
+ mIconClickListener = clickListener;
+ mIconLongClickListener = longClickListener;
+ mButtonProvider = buttonProvider;
+
+ if (mActivityContext.canShowNavButtons()) {
createNavButtons();
} else {
- mNavButtonStartIndex = -1;
- removeNavButtons();
+ mSystemButtonContainer.setVisibility(GONE);
}
- mHotseatStartIndex = mNavButtonStartIndex + 1;
- mHotseatEndIndex = mHotseatStartIndex + numHotseatIcons - 1;
+ int numHotseatIcons = mActivityContext.getDeviceProfile().numShownHotseatIcons;
updateHotseatItems(new ItemInfo[numHotseatIcons]);
-
- mLayoutTransition = new LayoutTransition();
- addUpdateListenerForAllLayoutTransitions(() -> {
- if (getLayoutTransition() == mLayoutTransition) {
- mControllerCallbacks.onItemPositionsChanged(this);
- }
- });
- setLayoutTransition(mLayoutTransition);
}
- private void addUpdateListenerForAllLayoutTransitions(Runnable onUpdate) {
- addUpdateListenerForLayoutTransition(LayoutTransition.CHANGE_APPEARING, onUpdate);
- addUpdateListenerForLayoutTransition(LayoutTransition.CHANGE_DISAPPEARING, onUpdate);
- addUpdateListenerForLayoutTransition(LayoutTransition.CHANGING, onUpdate);
- addUpdateListenerForLayoutTransition(LayoutTransition.APPEARING, onUpdate);
- addUpdateListenerForLayoutTransition(LayoutTransition.DISAPPEARING, onUpdate);
- }
-
- private void addUpdateListenerForLayoutTransition(int transitionType, Runnable onUpdate) {
- Animator anim = mLayoutTransition.getAnimator(transitionType);
- if (anim instanceof ValueAnimator) {
- ((ValueAnimator) anim).addUpdateListener(valueAnimator -> onUpdate.run());
- } else {
- AnimatorSet animSet = new AnimatorSet();
- ValueAnimator updateAnim = ValueAnimator.ofFloat(0, 1);
- updateAnim.addUpdateListener(valueAnimator -> onUpdate.run());
- animSet.playTogether(anim, updateAnim);
- mLayoutTransition.setAnimator(transitionType, animSet);
+ /**
+ * Enables/disables empty icons in taskbar so that the layout matches with Launcher
+ */
+ public void setHolesAllowedInLayout(boolean areHolesAllowed) {
+ if (mAreHolesAllowed != areHolesAllowed) {
+ mAreHolesAllowed = areHolesAllowed;
+ updateHotseatItemsVisibility();
+ // TODO: Add animation
}
}
- protected void cleanup() {
- endAllLayoutTransitionAnimators();
- setLayoutTransition(null);
- removeAllViews();
- }
-
- private void endAllLayoutTransitionAnimators() {
- mLayoutTransition.getAnimator(LayoutTransition.CHANGE_APPEARING).end();
- mLayoutTransition.getAnimator(LayoutTransition.CHANGE_DISAPPEARING).end();
- mLayoutTransition.getAnimator(LayoutTransition.CHANGING).end();
- mLayoutTransition.getAnimator(LayoutTransition.APPEARING).end();
- mLayoutTransition.getAnimator(LayoutTransition.DISAPPEARING).end();
+ private void setHolesAllowedInLayoutNoAnimation(boolean areHolesAllowed) {
+ if (mAreHolesAllowed != areHolesAllowed) {
+ mAreHolesAllowed = areHolesAllowed;
+ updateHotseatItemsVisibility();
+ onMeasure(makeMeasureSpec(getMeasuredWidth(), EXACTLY),
+ makeMeasureSpec(getMeasuredHeight(), EXACTLY));
+ onLayout(false, getLeft(), getTop(), getRight(), getBottom());
+ }
}
/**
@@ -192,10 +160,9 @@
*/
protected void updateHotseatItems(ItemInfo[] hotseatItemInfos) {
for (int i = 0; i < hotseatItemInfos.length; i++) {
- ItemInfo hotseatItemInfo = hotseatItemInfos[!mIsRtl ? i
- : hotseatItemInfos.length - i - 1];
- int hotseatIndex = mHotseatStartIndex + i;
- View hotseatView = getChildAt(hotseatIndex);
+ ItemInfo hotseatItemInfo = hotseatItemInfos[
+ !mIsRtl ? i : hotseatItemInfos.length - i - 1];
+ View hotseatView = mHotseatIconsContainer.getChildAt(i);
// Replace any Hotseat views with the appropriate type if it's not already that type.
final int expectedLayoutResId;
@@ -213,23 +180,23 @@
} else {
expectedLayoutResId = R.layout.taskbar_app_icon;
}
- if (hotseatView == null || hotseatView.getSourceLayoutResId() != expectedLayoutResId
+ if (hotseatView == null
+ || hotseatView.getSourceLayoutResId() != expectedLayoutResId
|| needsReinflate) {
- removeView(hotseatView);
- ActivityContext activityContext = getActivityContext();
+ mHotseatIconsContainer.removeView(hotseatView);
if (isFolder) {
FolderInfo folderInfo = (FolderInfo) hotseatItemInfo;
FolderIcon folderIcon = FolderIcon.inflateFolderAndIcon(expectedLayoutResId,
- getActivityContext(), this, folderInfo);
+ mActivityContext, this, folderInfo);
folderIcon.setTextVisible(false);
hotseatView = folderIcon;
} else {
hotseatView = inflate(expectedLayoutResId);
}
- int iconSize = activityContext.getDeviceProfile().iconSizePx;
+ int iconSize = mActivityContext.getDeviceProfile().iconSizePx;
LayoutParams lp = new LayoutParams(iconSize, iconSize);
lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0);
- addView(hotseatView, hotseatIndex, lp);
+ mHotseatIconsContainer.addView(hotseatView, i, lp);
}
// Apply the Hotseat ItemInfos, or hide the view if there is none for a given index.
@@ -237,13 +204,11 @@
&& hotseatItemInfo instanceof WorkspaceItemInfo) {
((BubbleTextView) hotseatView).applyFromWorkspaceItem(
(WorkspaceItemInfo) hotseatItemInfo);
- hotseatView.setOnClickListener(mControllerCallbacks.getItemOnClickListener());
- hotseatView.setOnLongClickListener(
- mControllerCallbacks.getItemOnLongClickListener());
+ hotseatView.setOnClickListener(mIconClickListener);
+ hotseatView.setOnLongClickListener(mIconLongClickListener);
} else if (isFolder) {
- hotseatView.setOnClickListener(mControllerCallbacks.getItemOnClickListener());
- hotseatView.setOnLongClickListener(
- mControllerCallbacks.getItemOnLongClickListener());
+ hotseatView.setOnClickListener(mIconClickListener);
+ hotseatView.setOnLongClickListener(mIconLongClickListener);
} else {
hotseatView.setOnClickListener(null);
hotseatView.setOnLongClickListener(null);
@@ -254,24 +219,14 @@
}
protected void updateHotseatItemsVisibility() {
- for (int i = mHotseatStartIndex; i <= mHotseatEndIndex; i++) {
- updateHotseatItemVisibility(getChildAt(i));
+ for (int i = mHotseatIconsContainer.getChildCount() - 1; i >= 0; i--) {
+ updateHotseatItemVisibility(mHotseatIconsContainer.getChildAt(i));
}
}
private void updateHotseatItemVisibility(View hotseatView) {
- if (hotseatView.getTag() != null) {
- hotseatView.setVisibility(VISIBLE);
- } else {
- int oldVisibility = hotseatView.getVisibility();
- int newVisibility = mControllerCallbacks.getEmptyHotseatViewVisibility(this);
- hotseatView.setVisibility(newVisibility);
- if (oldVisibility == GONE && newVisibility != GONE) {
- // By default, the layout transition only runs when going to VISIBLE,
- // but we want it to run when going to GONE to INVISIBLE as well.
- getLayoutTransition().showChild(this, hotseatView, oldVisibility);
- }
- }
+ hotseatView.setVisibility(
+ hotseatView.getTag() != null ? VISIBLE : (mAreHolesAllowed ? INVISIBLE : GONE));
}
@Override
@@ -378,49 +333,20 @@
return findDelegateView(xInOurCoordinates, yInOurCoorindates) != null;
}
- private void removeNavButtons() {
- if (mButtonRegion != null) {
- mButtonRegion.removeAllViews();
- removeView(mButtonRegion);
- } // else We've never been in 3 button. Woah Scoob!
- }
-
/**
* Add back/home/recents buttons into a single ViewGroup that will be inserted at
* {@param navButtonStartIndex}
*/
private void createNavButtons() {
- ActivityContext context = getActivityContext();
- if (mButtonRegion == null) {
- mButtonRegion = new LinearLayout(getContext());
- } else {
- mButtonRegion.removeAllViews();
- }
- mButtonRegion.setVisibility(VISIBLE);
-
LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams(
- context.getDeviceProfile().iconSizePx,
- context.getDeviceProfile().iconSizePx
+ mActivityContext.getDeviceProfile().iconSizePx,
+ mActivityContext.getDeviceProfile().iconSizePx
);
buttonParams.gravity = Gravity.CENTER;
- View backButton = mButtonProvider.getBack();
- backButton.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
- BUTTON_BACK));
- mButtonRegion.addView(backButton, buttonParams);
-
- // Home button
- View homeButton = mButtonProvider.getHome();
- homeButton.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
- BUTTON_HOME));
- mButtonRegion.addView(homeButton, buttonParams);
-
- View recentsButton = mButtonProvider.getRecents();
- recentsButton.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
- BUTTON_RECENTS));
- mButtonRegion.addView(recentsButton, buttonParams);
-
- addView(mButtonRegion, mNavButtonStartIndex);
+ mSystemButtonContainer.addView(mButtonProvider.getBack(), buttonParams);
+ mSystemButtonContainer.addView(mButtonProvider.getHome(), buttonParams);
+ mSystemButtonContainer.addView(mButtonProvider.getRecents(), buttonParams);
}
@Override
@@ -428,7 +354,7 @@
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
mIsDraggingItem = true;
- AbstractFloatingView.closeAllOpenViews(getActivityContext());
+ AbstractFloatingView.closeAllOpenViews(mActivityContext);
return true;
case DragEvent.ACTION_DRAG_ENDED:
mIsDraggingItem = false;
@@ -445,26 +371,26 @@
* @return The bounding box of where the hotseat elements are relative to this TaskbarView.
*/
protected RectF getHotseatBounds() {
- View firstHotseatView = null, lastHotseatView = null;
- for (int i = mHotseatStartIndex; i <= mHotseatEndIndex; i++) {
- View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- if (firstHotseatView == null) {
- firstHotseatView = child;
- }
- lastHotseatView = child;
- }
+ RectF result;
+ mDisableRelayout = true;
+ boolean wereHolesAllowed = mAreHolesAllowed;
+ setHolesAllowedInLayoutNoAnimation(true);
+ result = new RectF(
+ mHotseatIconsContainer.getLeft(),
+ mHotseatIconsContainer.getTop(),
+ mHotseatIconsContainer.getRight(),
+ mHotseatIconsContainer.getBottom());
+ setHolesAllowedInLayoutNoAnimation(wereHolesAllowed);
+ mDisableRelayout = false;
+
+ return result;
+ }
+
+ @Override
+ public void requestLayout() {
+ if (!mDisableRelayout) {
+ super.requestLayout();
}
- if (firstHotseatView == null || lastHotseatView == null) {
- return new RectF();
- }
- View leftmostHotseatView = !mIsRtl ? firstHotseatView : lastHotseatView;
- View rightmostHotseatView = !mIsRtl ? lastHotseatView : firstHotseatView;
- return new RectF(
- leftmostHotseatView.getLeft() - mItemMarginLeftRight,
- leftmostHotseatView.getTop(),
- rightmostHotseatView.getRight() + mItemMarginLeftRight,
- rightmostHotseatView.getBottom());
}
// FolderIconParent implemented methods.
@@ -495,7 +421,7 @@
}
private View inflate(@LayoutRes int layoutResId) {
- return getActivityContext().getLayoutInflater().inflate(layoutResId, this, false);
+ return mActivityContext.getLayoutInflater().inflate(layoutResId, this, false);
}
@Override
@@ -503,7 +429,11 @@
// Ignore, we just implement Insettable to draw behind system insets.
}
- private <T extends Context & ActivityContext> T getActivityContext() {
- return ActivityContext.lookupContext(getContext());
+ public void setIconsVisibility(boolean isVisible) {
+ mHotseatIconsContainer.setVisibility(isVisible ? VISIBLE : INVISIBLE);
+ }
+
+ public boolean areIconsVisible() {
+ return mHotseatIconsContainer.getVisibility() == VISIBLE;
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 45bb521..f0b02b3 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -165,7 +165,7 @@
@Override
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
// Only pause is taskbar controller is not present
- mHotseatPredictionController.setPauseUIUpdate(getTaskbarController() == null);
+ mHotseatPredictionController.setPauseUIUpdate(getTaskbarUIController() == null);
return super.startActivitySafely(v, intent, item);
}
@@ -233,9 +233,9 @@
@Override
public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) {
super.bindWorkspaceItemsChanged(updated);
- if (getTaskbarController() != null && updated.stream()
+ if (getTaskbarUIController() != null && updated.stream()
.filter(w -> w.container == CONTAINER_HOTSEAT).findFirst().isPresent()) {
- getTaskbarController().onHotseatUpdated();
+ getTaskbarUIController().onHotseatUpdated();
}
}
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index f5ddd0e..86bf119 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -52,7 +52,6 @@
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.launcher3.taskbar.TaskbarController;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.views.ScrimView;
@@ -123,11 +122,6 @@
return null;
}
- @Nullable
- public TaskbarController getTaskbarController() {
- return null;
- }
-
public final boolean isResumed() {
ACTIVITY_TYPE activity = getCreatedActivity();
return activity != null && activity.hasBeenResumed();
@@ -368,13 +362,6 @@
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.
*/
public abstract STATE_TYPE stateFromGestureEndTarget(GestureState.GestureEndTarget endTarget);
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 5217c3b..9014774 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -22,7 +22,6 @@
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -42,7 +41,7 @@
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statehandlers.DepthController.ClampedDepthProperty;
import com.android.launcher3.statemanager.StateManager;
-import com.android.launcher3.taskbar.TaskbarController;
+import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.SysUINavigationMode.Mode;
@@ -164,13 +163,12 @@
}
@Nullable
- @Override
- public TaskbarController getTaskbarController() {
+ private LauncherTaskbarUIController getTaskbarController() {
BaseQuickstepLauncher launcher = getCreatedActivity();
if (launcher == null) {
return null;
}
- return launcher.getTaskbarController();
+ return launcher.getTaskbarUIController();
}
@Nullable
@@ -278,13 +276,13 @@
@Override
public @Nullable Animator getParallelAnimationToLauncher(GestureEndTarget endTarget,
long duration) {
- TaskbarController taskbarController = getTaskbarController();
+ LauncherTaskbarUIController uiController = getTaskbarController();
Animator superAnimator = super.getParallelAnimationToLauncher(endTarget, duration);
- if (taskbarController == null) {
+ if (uiController == null) {
return superAnimator;
}
LauncherState toState = stateFromGestureEndTarget(endTarget);
- Animator taskbarAnimator = taskbarController.createAnimToLauncher(toState, duration);
+ Animator taskbarAnimator = uiController.createAnimToLauncher(toState, duration);
if (superAnimator == null) {
return taskbarAnimator;
} else {
@@ -301,31 +299,21 @@
}
@Override
- public void onSystemUiFlagsChanged(int systemUiStateFlags) {
- TaskbarController taskbarController = getTaskbarController();
- if (taskbarController == null) {
- return;
- }
- boolean isImeVisible = (systemUiStateFlags & SYSUI_STATE_IME_SHOWING) != 0;
- taskbarController.setIsImeVisible(isImeVisible);
- }
-
- @Override
public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
- TaskbarController taskbarController = getTaskbarController();
- if (taskbarController == null) {
+ LauncherTaskbarUIController uiController = getTaskbarController();
+ if (uiController == null) {
return super.deferStartingActivity(deviceState, ev);
}
- return taskbarController.isEventOverAnyTaskbarItem(ev);
+ return uiController.isEventOverAnyTaskbarItem(ev);
}
@Override
public boolean shouldCancelCurrentGesture() {
- TaskbarController taskbarController = getTaskbarController();
- if (taskbarController == null) {
+ LauncherTaskbarUIController uiController = getTaskbarController();
+ if (uiController == null) {
return super.shouldCancelCurrentGesture();
}
- return taskbarController.isDraggingItem();
+ return uiController.isDraggingItem();
}
@Override
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index edc7a3c..7956fcc 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -52,7 +52,6 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
-import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.Log;
@@ -75,6 +74,7 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.taskbar.TaskbarManager;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.tracing.LauncherTraceProto;
@@ -147,21 +147,9 @@
private OverscrollPlugin mOverscrollPlugin;
/**
- * Extension of OverviewProxy aidl interface without needing to modify the actual interface.
- * This is for methods that need only need local access and not intended to make IPC calls.
+ * Local IOverviewProxy implementation with some methods for local components
*/
- public abstract static class TISBinder extends IOverviewProxy.Stub {
- public abstract void setTaskbarOverviewProxyDelegate(
- @Nullable TaskbarOverviewProxyDelegate i);
- }
-
-
- private final TISBinder mMyBinder = new TISBinder() {
-
- public void setTaskbarOverviewProxyDelegate(
- @Nullable TaskbarOverviewProxyDelegate delegate) {
- mTaskbarOverviewProxyDelegate = delegate;
- }
+ public class TISBinder extends IOverviewProxy.Stub {
@BinderThread
public void onInitialize(Bundle bundle) {
@@ -274,40 +262,24 @@
@Override
public void onImeWindowStatusChanged(int displayId, IBinder token, int vis,
- int backDisposition, boolean showImeSwitcher) throws RemoteException {
- if (mTaskbarOverviewProxyDelegate == null) {
- return;
- }
- MAIN_EXECUTOR.execute(() -> {
- if (mTaskbarOverviewProxyDelegate == null) {
- return;
- }
- mTaskbarOverviewProxyDelegate
- .updateImeStatus(displayId, vis, backDisposition, showImeSwitcher);
- });
+ int backDisposition, boolean showImeSwitcher) {
+ MAIN_EXECUTOR.execute(() -> mTaskbarManager.updateImeStatus(
+ displayId, vis, backDisposition, showImeSwitcher));
}
- };
- public interface TaskbarOverviewProxyDelegate {
- void updateImeStatus(int displayId, int vis, int backDisposition,
- boolean showImeSwitcher);
+ public TaskbarManager getTaskbarManager() {
+ return mTaskbarManager;
+ }
}
private static boolean sConnected = false;
- private static TouchInteractionService sInstance;
private static boolean sIsInitialized = false;
private RotationTouchHelper mRotationTouchHelper;
- @Nullable
- private TaskbarOverviewProxyDelegate mTaskbarOverviewProxyDelegate;
public static boolean isConnected() {
return sConnected;
}
- @Nullable
- public static TouchInteractionService getInstance() {
- return sInstance;
- }
public static boolean isInitialized() {
return sIsInitialized;
@@ -336,9 +308,7 @@
private DisplayManager mDisplayManager;
- public TouchInteractionService() {
- sInstance = this;
- }
+ private TaskbarManager mTaskbarManager;
@Override
public void onCreate() {
@@ -348,13 +318,15 @@
mMainChoreographer = Choreographer.getInstance();
mAM = ActivityManagerWrapper.getInstance();
mDeviceState = new RecentsAnimationDeviceState(this, true);
+ mDisplayManager = getSystemService(DisplayManager.class);
+ mTaskbarManager = new TaskbarManager(this);
+
mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
mDeviceState.addOneHandedModeChangedCallback(this::onOneHandedModeOverlayChanged);
mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
+ mDeviceState.runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
ProtoTracer.INSTANCE.get(this).add(this);
- mDisplayManager = getSystemService(DisplayManager.class);
-
sConnected = true;
}
@@ -468,8 +440,7 @@
int systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
mOverviewComponentObserver.onSystemUiStateChanged();
- mOverviewComponentObserver.getActivityInterface().onSystemUiFlagsChanged(
- systemUiStateFlags);
+ mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags);
if ((lastSysUIFlags & SYSUI_STATE_TRACING_ENABLED) !=
(systemUiStateFlags & SYSUI_STATE_TRACING_ENABLED)) {
@@ -512,6 +483,7 @@
getSystemService(AccessibilityManager.class)
.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
+ mTaskbarManager.destroy();
sConnected = false;
super.onDestroy();
}
@@ -519,7 +491,7 @@
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "Touch service connected: user=" + getUserId());
- return mMyBinder;
+ return new TISBinder();
}
private void onInputEvent(InputEvent ev) {
diff --git a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index d430028..49aec93 100644
--- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -116,7 +116,7 @@
addStaggeredAnimationForView(child, lp.cellY + 1, totalRows);
}
} else {
- final int hotseatRow, qsbRow, taskbarRow;
+ final int hotseatRow, qsbRow;
if (grid.isTaskbarPresent) {
qsbRow = grid.inv.numRows + 1;
hotseatRow = grid.inv.numRows + 2;
@@ -124,16 +124,12 @@
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() {
diff --git a/res/layout/taskbar_view.xml b/res/layout/taskbar_view.xml
deleted file mode 100644
index 96ae43d..0000000
--- a/res/layout/taskbar_view.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2021 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<Space
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:visibility="gone" />
\ No newline at end of file
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
index 6b5678c..e1214ff 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
@@ -82,7 +82,7 @@
mTestProfile.numColumns = 5;
mUserHandle = Process.myUserHandle();
mAdapter = new WidgetsListAdapter(mContext, mMockLayoutInflater, mMockWidgetCache,
- mIconCache, null, null, null);
+ mIconCache, null, null);
mAdapter.registerAdapterDataObserver(mListener);
doAnswer(invocation -> ((ComponentWithLabel) invocation.getArgument(0))
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
index 12a092d..84a03d5 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
@@ -105,8 +105,7 @@
mWidgetPreviewLoader,
mIconCache,
/* iconClickListener= */ view -> {},
- /* iconLongClickListener= */ view -> false,
- /* searchBarUIHelper= */ null);
+ /* iconLongClickListener= */ view -> false);
mViewHolderBinder = new WidgetsListHeaderViewHolderBinder(
LayoutInflater.from(mTestActivity), mOnHeaderClickListener, widgetsListAdapter);
}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
index e090341..075c58d 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
@@ -105,8 +105,7 @@
mWidgetPreviewLoader,
mIconCache,
/* iconClickListener= */ view -> {},
- /* iconLongClickListener= */ view -> false,
- /* searchBarUIHelper= */ null);
+ /* iconLongClickListener= */ view -> false);
mViewHolderBinder = new WidgetsListSearchHeaderViewHolderBinder(
LayoutInflater.from(mTestActivity), mOnHeaderClickListener, widgetsListAdapter);
}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
index 0935d1c..0c6e717 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
@@ -111,8 +111,7 @@
mWidgetPreviewLoader,
mIconCache,
/* iconClickListener= */ view -> {},
- /* iconLongClickListener= */ view -> false,
- /* searchBarUIHelper= */ null);
+ /* iconLongClickListener= */ view -> false);
mViewHolderBinder = new WidgetsListTableViewHolderBinder(
mContext,
LayoutInflater.from(mTestActivity),
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 3d044d6..322c6ee 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -447,10 +447,6 @@
* @param canvas The canvas to draw to.
*/
protected void drawDotIfNecessary(Canvas canvas) {
- if (mActivity instanceof Launcher && ((Launcher) mActivity).isViewInTaskbar(this)) {
- // TODO: support notification dots in Taskbar
- return;
- }
if (!mForceHideDot && (hasDot() || mDotParams.scale > 0)) {
getIconBounds(mDotParams.iconBounds);
Utilities.scaleRectAboutCenter(mDotParams.iconBounds,
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 1df9df6..bfa1769 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -1080,9 +1080,10 @@
cellToRect(targetCell[0], targetCell[1], spanX, spanY, mTempRect);
// Now get the rect in drag layer coordinates.
- getBoundsForViewInDragLayer(launcher.getDragLayer(), workspace, mTempRect, false,
+ getBoundsForViewInDragLayer(launcher.getDragLayer(), this, mTempRect, true,
mTmpFloatArray, mTempRectF);
Utilities.setRect(mTempRectF, mTempRect);
+
((LauncherAppWidgetHostView) view).handleDrag(mTempRect, pageId);
}
}
@@ -2594,7 +2595,9 @@
final int cellWidth = mCellWidth;
final int cellHeight = mCellHeight;
- final int hStartPadding = getPaddingLeft();
+ // We observe a shift of 1 pixel on the x coordinate compared to the actual cell coordinates
+ final int hStartPadding = getPaddingLeft()
+ + (int) Math.ceil(getUnusedHorizontalSpace() / 2f);
final int vStartPadding = getPaddingTop();
int x = hStartPadding + (cellX * mBorderSpacing) + (cellX * cellWidth);
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index b2a9e75..ff380ce 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -49,7 +49,6 @@
private final View mQsb;
private final int mQsbHeight;
- private final View mTaskbarView;
private final int mTaskbarViewHeight;
public Hotseat(Context context) {
@@ -67,10 +66,7 @@
mQsbHeight = mQsb.getLayoutParams().height;
addView(mQsb);
- mTaskbarView = LayoutInflater.from(context).inflate(R.layout.taskbar_view, this, false);
- mTaskbarViewHeight = mTaskbarView.getLayoutParams().height;
- // We want taskbar in the back so its background applies to Hotseat as well.
- addView(mTaskbarView, 0);
+ mTaskbarViewHeight = context.getResources().getDimensionPixelSize(R.dimen.taskbar_size);
}
/**
@@ -187,8 +183,6 @@
int width = getShortcutsAndWidgets().getMeasuredWidth();
mQsb.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(mQsbHeight, MeasureSpec.EXACTLY));
- mTaskbarView.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(mTaskbarViewHeight, MeasureSpec.EXACTLY));
}
@Override
@@ -202,13 +196,6 @@
int bottom = b - t - getQsbOffsetY();
int top = bottom - mQsbHeight;
mQsb.layout(left, top, right, bottom);
-
- int taskbarWidth = mTaskbarView.getMeasuredWidth();
- left = (r - l - taskbarWidth) / 2;
- right = left + taskbarWidth;
- bottom = b - t - getTaskbarOffsetY();
- top = bottom - mTaskbarViewHeight;
- mTaskbarView.layout(left, top, right, bottom);
}
/**
@@ -244,10 +231,4 @@
return mQsb;
}
- /**
- * Returns the Taskbar inside hotseat
- */
- public View getTaskbarView() {
- return mTaskbarView;
- }
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index cf90216..8889e60 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1942,13 +1942,6 @@
@Override
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
- if (isViewInTaskbar(v)) {
- // Start the activity without the hacky workarounds below, which assume the View was
- // clicked when Launcher was resumed and will be hidden until Launcher is re-resumed
- // (this isn't the case for Taskbar).
- return super.startActivitySafely(v, intent, item);
- }
-
if (!hasBeenResumed()) {
// Workaround an issue where the WM launch animation is clobbered when finishing the
// recents animation into launcher. Defer launching the activity until Launcher is
@@ -2860,13 +2853,6 @@
.start();
}
- /**
- * @return Whether the View is in the same window as the Taskbar window.
- */
- public boolean isViewInTaskbar(View v) {
- return false;
- }
-
public boolean supportsAdaptiveIconAnimation(View clickedView) {
return false;
}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index b6cc6d6..dabbdd3 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -81,6 +81,8 @@
public LauncherAppState(Context context) {
this(context, LauncherFiles.APP_ICONS_DB);
+ Log.v(Launcher.TAG, "LauncherAppState initiated");
+ Preconditions.assertUIThread();
mInvariantDeviceProfile.addOnChangeListener(idp -> refreshAndReloadLauncher());
@@ -132,8 +134,6 @@
}
public LauncherAppState(Context context, @Nullable String iconCacheFileName) {
- Log.v(Launcher.TAG, "LauncherAppState initiated");
- Preconditions.assertUIThread();
mContext = context;
mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context);
@@ -142,6 +142,7 @@
iconCacheFileName, mIconProvider);
mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext));
+ mOnTerminateCallback.add(mIconCache::close);
}
private void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 7ae729a..2884fba 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -324,15 +324,17 @@
}
public static void scaleRectFAboutCenter(RectF r, float scale) {
+ scaleRectFAboutPivot(r, scale, r.centerX(), r.centerY());
+ }
+
+ public static void scaleRectFAboutPivot(RectF r, float scale, float px, float py) {
if (scale != 1.0f) {
- float cx = r.centerX();
- float cy = r.centerY();
- r.offset(-cx, -cy);
+ r.offset(-px, -py);
r.left = r.left * scale;
r.top = r.top * scale ;
r.right = r.right * scale;
r.bottom = r.bottom * scale;
- r.offset(cx, cy);
+ r.offset(px, py);
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index 355ccad..2443b83 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -17,6 +17,7 @@
import static com.android.launcher3.model.data.AppInfo.COMPONENT_KEY_COMPARATOR;
import static com.android.launcher3.model.data.AppInfo.EMPTY_ARRAY;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK;
import android.view.View;
import android.view.ViewGroup;
@@ -157,11 +158,17 @@
* If this app is installed and supports incremental downloads, the progress bar will be updated
* the app's total download progress. Otherwise, the progress bar will be updated to the app's
* installation progress.
+ *
+ * If this app is fully downloaded, the app icon will be reapplied.
*/
public void updateProgressBar(AppInfo app) {
updateAllIcons((child) -> {
if (child.getTag() == app) {
- child.applyProgressLevel();
+ if ((app.runtimeStatusFlags & FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) == 0) {
+ child.applyFromApplicationInfo(app);
+ } else {
+ child.applyProgressLevel();
+ }
}
});
}
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index af7896a..a0c598a 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -298,7 +298,7 @@
@Override
protected void dispatchDraw(Canvas canvas) {
- if (mHeaderCollapsed && mTabLayout.getVisibility() == VISIBLE
+ if (mHeaderCollapsed && !mCollapsed && mTabLayout.getVisibility() == VISIBLE
&& mHeaderColor != Color.TRANSPARENT) {
mBGPaint.setColor(mHeaderColor);
mBGPaint.setAlpha((int) (255 * mHeaderAnimator.getAnimatedFraction()));
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index a03e48d..8ca157b 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -4,6 +4,7 @@
import static com.android.launcher3.util.Themes.KEY_THEMED_ICONS;
import static com.android.launcher3.util.Themes.isThemedIconEnabled;
+import android.annotation.TargetApi;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.pm.PackageManager;
@@ -12,14 +13,24 @@
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.Xml;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.InvariantDeviceProfile.GridOption;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.Executors;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -65,6 +76,11 @@
private static final String ICON_THEMED = "/icon_themed";
private static final String BOOLEAN_VALUE = "boolean_value";
+ private static final String KEY_SURFACE_PACKAGE = "surface_package";
+ private static final String KEY_CALLBACK = "callback";
+
+ private final ArrayMap<IBinder, PreviewLifecycleObserver> mActivePreviews = new ArrayMap<>();
+
@Override
public boolean onCreate() {
return true;
@@ -177,10 +193,75 @@
return null;
}
- if (!METHOD_GET_PREVIEW.equals(method)) {
+ if (!Utilities.ATLEAST_R || !METHOD_GET_PREVIEW.equals(method)) {
return null;
}
+ return getPreview(extras);
+ }
- return new PreviewSurfaceRenderer(getContext(), extras).render();
+ @TargetApi(Build.VERSION_CODES.R)
+ private synchronized Bundle getPreview(Bundle request) {
+ PreviewLifecycleObserver observer = null;
+ try {
+ PreviewSurfaceRenderer renderer = new PreviewSurfaceRenderer(getContext(), request);
+
+ // Destroy previous
+ destroyObserver(mActivePreviews.get(renderer.getHostToken()));
+
+ observer = new PreviewLifecycleObserver(renderer);
+ mActivePreviews.put(renderer.getHostToken(), observer);
+
+ renderer.loadAsync();
+ renderer.getHostToken().linkToDeath(observer, 0);
+
+ Bundle result = new Bundle();
+ result.putParcelable(KEY_SURFACE_PACKAGE, renderer.getSurfacePackage());
+
+ Messenger messenger = new Messenger(new Handler(Looper.getMainLooper(), observer));
+ Message msg = Message.obtain();
+ msg.replyTo = messenger;
+ result.putParcelable(KEY_CALLBACK, msg);
+ return result;
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to generate preview", e);
+ if (observer != null) {
+ destroyObserver(observer);
+ }
+ return null;
+ }
+ }
+
+ private synchronized void destroyObserver(PreviewLifecycleObserver observer) {
+ if (observer == null || observer.destroyed) {
+ return;
+ }
+ observer.destroyed = true;
+ observer.renderer.getHostToken().unlinkToDeath(observer, 0);
+ Executors.MAIN_EXECUTOR.execute(observer.renderer::destroy);
+ PreviewLifecycleObserver cached = mActivePreviews.get(observer.renderer.getHostToken());
+ if (cached == observer) {
+ mActivePreviews.remove(observer.renderer.getHostToken());
+ }
+ }
+
+ private class PreviewLifecycleObserver implements Handler.Callback, DeathRecipient {
+
+ public final PreviewSurfaceRenderer renderer;
+ public boolean destroyed = false;
+
+ PreviewLifecycleObserver(PreviewSurfaceRenderer renderer) {
+ this.renderer = renderer;
+ }
+
+ @Override
+ public boolean handleMessage(Message message) {
+ destroyObserver(this);
+ return true;
+ }
+
+ @Override
+ public void binderDied() {
+ destroyObserver(this);
+ }
}
}
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index f5b6890..2a1aec8 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -23,7 +23,6 @@
import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
import static com.android.launcher3.model.ModelUtils.getMissingHotseatRanks;
import static com.android.launcher3.model.ModelUtils.sortWorkspaceItemsSpatially;
-import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.annotation.TargetApi;
import android.app.Fragment;
@@ -32,7 +31,6 @@
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
-import android.content.pm.ShortcutInfo;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Rect;
@@ -43,11 +41,12 @@
import android.os.Looper;
import android.os.Process;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowInsets;
+import android.view.WindowManager;
import android.widget.TextClock;
import com.android.launcher3.BubbleTextView;
@@ -57,23 +56,17 @@
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel;
-import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.WorkspaceLayoutManager;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.LauncherIcons;
-import com.android.launcher3.model.AllAppsList;
import com.android.launcher3.model.BgDataModel;
-import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
-import com.android.launcher3.model.LoaderResults;
-import com.android.launcher3.model.LoaderTask;
-import com.android.launcher3.model.ModelDelegate;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.model.data.FolderInfo;
@@ -100,13 +93,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
/**
* Utility class for generating the preview of Launcher for a given InvariantDeviceProfile.
@@ -120,8 +107,6 @@
public class LauncherPreviewRenderer extends ContextWrapper
implements ActivityContext, WorkspaceLayoutManager, LayoutInflater.Factory2 {
- private static final String TAG = "LauncherPreviewRenderer";
-
/**
* Context used just for preview. It also provides a few objects (e.g. UserCache) just for
* preview purposes.
@@ -138,9 +123,15 @@
private final ConcurrentLinkedQueue<LauncherIconsForPreview> mIconPool =
new ConcurrentLinkedQueue<>();
+ private boolean mDestroyed = false;
+
public PreviewContext(Context base, InvariantDeviceProfile idp) {
super(base);
mIdp = idp;
+ mObjectMap.put(InvariantDeviceProfile.INSTANCE, idp);
+ mObjectMap.put(LauncherAppState.INSTANCE,
+ new LauncherAppState(this, null /* iconCacheFileName */));
+
}
@Override
@@ -149,11 +140,9 @@
}
public void onDestroy() {
- CustomWidgetManager customWidgetManager = (CustomWidgetManager) mObjectMap.get(
- CustomWidgetManager.INSTANCE);
- if (customWidgetManager != null) {
- customWidgetManager.onDestroy();
- }
+ CustomWidgetManager.INSTANCE.get(this).onDestroy();
+ LauncherAppState.INSTANCE.get(this).onTerminate();
+ mDestroyed = true;
}
/**
@@ -162,17 +151,12 @@
*/
public <T> T getObject(MainThreadInitializedObject<T> mainThreadInitializedObject,
MainThreadInitializedObject.ObjectProvider<T> provider) {
+ if (FeatureFlags.IS_STUDIO_BUILD && mDestroyed) {
+ throw new RuntimeException("Context already destroyed");
+ }
if (!mAllowedObjects.contains(mainThreadInitializedObject)) {
throw new IllegalStateException("Leaking unknown objects");
}
- if (mainThreadInitializedObject == LauncherAppState.INSTANCE) {
- throw new IllegalStateException(
- "Should not use MainThreadInitializedObject to initialize this with "
- + "PreviewContext");
- }
- if (mainThreadInitializedObject == InvariantDeviceProfile.INSTANCE) {
- return (T) mIdp;
- }
if (mObjectMap.containsKey(mainThreadInitializedObject)) {
return (T) mObjectMap.get(mainThreadInitializedObject);
}
@@ -210,7 +194,6 @@
private final Context mContext;
private final InvariantDeviceProfile mIdp;
private final DeviceProfile mDp;
- private final boolean mMigrated;
private final Rect mInsets;
private final WorkspaceItemInfo mWorkspaceItemInfo;
private final LayoutInflater mHomeElementInflater;
@@ -218,18 +201,26 @@
private final Hotseat mHotseat;
private final CellLayout mWorkspace;
- public LauncherPreviewRenderer(Context context, InvariantDeviceProfile idp, boolean migrated) {
+ public LauncherPreviewRenderer(Context context, InvariantDeviceProfile idp) {
super(context);
mUiHandler = new Handler(Looper.getMainLooper());
mContext = context;
mIdp = idp;
mDp = idp.getDeviceProfile(context).copy(context);
- mMigrated = migrated;
- // TODO: get correct insets once display cutout API is available.
- mInsets = new Rect();
- mInsets.left = mInsets.right = (mDp.widthPx - mDp.availableWidthPx) / 2;
- mInsets.top = mInsets.bottom = (mDp.heightPx - mDp.availableHeightPx) / 2;
+ if (Utilities.ATLEAST_R) {
+ WindowInsets currentWindowInsets = context.getSystemService(WindowManager.class)
+ .getCurrentWindowMetrics().getWindowInsets();
+ mInsets = new Rect(
+ currentWindowInsets.getSystemWindowInsetLeft(),
+ currentWindowInsets.getSystemWindowInsetTop(),
+ currentWindowInsets.getSystemWindowInsetRight(),
+ currentWindowInsets.getSystemWindowInsetBottom());
+ } else {
+ mInsets = new Rect();
+ mInsets.left = mInsets.right = (mDp.widthPx - mDp.availableWidthPx) / 2;
+ mInsets.top = mInsets.bottom = (mDp.heightPx - mDp.availableHeightPx) / 2;
+ }
mDp.updateInsets(mInsets);
BaseIconFactory iconFactory =
@@ -265,8 +256,9 @@
}
/** Populate preview and render it. */
- public View getRenderedView() {
- populate();
+ public View getRenderedView(BgDataModel dataModel,
+ Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
+ populate(dataModel, widgetProviderInfoMap);
return mRootView;
}
@@ -392,38 +384,17 @@
}
}
- private void populate() {
- WorkspaceFetcher fetcher;
- PreviewContext previewContext = null;
- if (mMigrated) {
- previewContext = new PreviewContext(mContext, mIdp);
- LauncherAppState appForPreview = new LauncherAppState(
- previewContext, null /* iconCacheFileName */);
- fetcher = new WorkspaceItemsInfoFromPreviewFetcher(appForPreview);
- MODEL_EXECUTOR.execute(fetcher);
- } else {
- fetcher = new WorkspaceItemsInfoFetcher();
- LauncherAppState.getInstance(mContext).getModel().enqueueModelUpdateTask(
- (LauncherModel.ModelUpdateTask) fetcher);
- }
- WorkspaceResult workspaceResult = fetcher.get();
- if (previewContext != null) {
- previewContext.onDestroy();
- }
-
- if (workspaceResult == null) {
- return;
- }
-
+ private void populate(BgDataModel dataModel,
+ Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
// Separate the items that are on the current screen, and the other remaining items.
ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>();
ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>();
ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>();
filterCurrentWorkspaceItems(0 /* currentScreenId */,
- workspaceResult.mWorkspaceItems, currentWorkspaceItems,
+ dataModel.workspaceItems, currentWorkspaceItems,
otherWorkspaceItems);
- filterCurrentWorkspaceItems(0 /* currentScreenId */, workspaceResult.mAppWidgets,
+ filterCurrentWorkspaceItems(0 /* currentScreenId */, dataModel.appWidgets,
currentAppWidgets, otherAppWidgets);
sortWorkspaceItemsSpatially(mIdp, currentWorkspaceItems);
for (ItemInfo itemInfo : currentWorkspaceItems) {
@@ -444,12 +415,12 @@
switch (itemInfo.itemType) {
case Favorites.ITEM_TYPE_APPWIDGET:
case Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
- if (mMigrated) {
- inflateAndAddWidgets((LauncherAppWidgetInfo) itemInfo,
- workspaceResult.mWidgetProvidersMap);
+ if (widgetProviderInfoMap != null) {
+ inflateAndAddWidgets(
+ (LauncherAppWidgetInfo) itemInfo, widgetProviderInfoMap);
} else {
inflateAndAddWidgets((LauncherAppWidgetInfo) itemInfo,
- workspaceResult.mWidgetsModel);
+ dataModel.widgetsModel);
}
break;
default:
@@ -458,8 +429,10 @@
}
IntArray ranks = getMissingHotseatRanks(currentWorkspaceItems,
mDp.numShownHotseatIcons);
- List<ItemInfo> predictions = workspaceResult.mHotseatPredictions == null
- ? Collections.emptyList() : workspaceResult.mHotseatPredictions.items;
+ FixedContainerItems hotseatpredictions =
+ dataModel.extraItems.get(CONTAINER_HOTSEAT_PREDICTION);
+ List<ItemInfo> predictions = hotseatpredictions == null
+ ? Collections.emptyList() : hotseatpredictions.items;
int count = Math.min(ranks.size(), predictions.size());
for (int i = 0; i < count; i++) {
int rank = ranks.get(i);
@@ -494,109 +467,4 @@
view.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
view.layout(0, 0, width, height);
}
-
- private static class WorkspaceItemsInfoFetcher implements LauncherModel.ModelUpdateTask,
- WorkspaceFetcher {
-
- private final FutureTask<WorkspaceResult> mTask = new FutureTask<>(this);
-
- private LauncherAppState mApp;
- private LauncherModel mModel;
- private BgDataModel mBgDataModel;
- private AllAppsList mAllAppsList;
-
- @Override
- public void init(LauncherAppState app, LauncherModel model, BgDataModel dataModel,
- AllAppsList allAppsList, Executor uiExecutor) {
- mApp = app;
- mModel = model;
- mBgDataModel = dataModel;
- mAllAppsList = allAppsList;
- }
-
- @Override
- public FutureTask<WorkspaceResult> getTask() {
- return mTask;
- }
-
- @Override
- public void run() {
- mTask.run();
- }
-
- @Override
- public WorkspaceResult call() throws Exception {
- if (!mModel.isModelLoaded()) {
- Log.d(TAG, "Workspace not loaded, loading now");
- mModel.startLoaderForResults(
- new LoaderResults(mApp, mBgDataModel, mAllAppsList, new Callbacks[0]));
- return null;
- }
-
- return new WorkspaceResult(mBgDataModel, mBgDataModel.widgetsModel, null);
- }
- }
-
- private static class WorkspaceItemsInfoFromPreviewFetcher extends LoaderTask implements
- WorkspaceFetcher {
-
- private final FutureTask<WorkspaceResult> mTask = new FutureTask<>(this);
-
- WorkspaceItemsInfoFromPreviewFetcher(LauncherAppState app) {
- super(app, null, new BgDataModel(), new ModelDelegate(), null);
- }
-
- @Override
- public FutureTask<WorkspaceResult> getTask() {
- return mTask;
- }
-
- @Override
- public void run() {
- mTask.run();
- }
-
- @Override
- public WorkspaceResult call() {
- List<ShortcutInfo> allShortcuts = new ArrayList<>();
- loadWorkspace(allShortcuts, LauncherSettings.Favorites.PREVIEW_CONTENT_URI,
- LauncherSettings.Favorites.SCREEN + " = 0 or "
- + LauncherSettings.Favorites.CONTAINER + " = "
- + LauncherSettings.Favorites.CONTAINER_HOTSEAT);
- return new WorkspaceResult(mBgDataModel, null, mWidgetProvidersMap);
- }
- }
-
- private interface WorkspaceFetcher extends Runnable, Callable<WorkspaceResult> {
- FutureTask<WorkspaceResult> getTask();
-
- default WorkspaceResult get() {
- try {
- return getTask().get(5, TimeUnit.SECONDS);
- } catch (InterruptedException | ExecutionException | TimeoutException e) {
- Log.d(TAG, "Error fetching workspace items info", e);
- return null;
- }
- }
- }
-
- private static class WorkspaceResult {
- private final ArrayList<ItemInfo> mWorkspaceItems;
- private final ArrayList<LauncherAppWidgetInfo> mAppWidgets;
- private final FixedContainerItems mHotseatPredictions;
- private final WidgetsModel mWidgetsModel;
- private final Map<ComponentKey, AppWidgetProviderInfo> mWidgetProvidersMap;
-
- private WorkspaceResult(BgDataModel dataModel,
- WidgetsModel widgetsModel,
- Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
- synchronized (dataModel) {
- mWorkspaceItems = dataModel.workspaceItems;
- mAppWidgets = dataModel.appWidgets;
- mHotseatPredictions = dataModel.extraItems.get(CONTAINER_HOTSEAT_PREDICTION);
- mWidgetsModel = widgetsModel;
- mWidgetProvidersMap = widgetProviderInfoMap;
- }
- }
- }
}
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index 6193570..a8c3d15 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -21,32 +21,49 @@
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.app.WallpaperColors;
+import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.os.Bundle;
-import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
+import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.Display;
import android.view.SurfaceControlViewHost;
+import android.view.SurfaceControlViewHost.SurfacePackage;
import android.view.View;
+import android.view.WindowManager.LayoutParams;
import android.view.animation.AccelerateDecelerateInterpolator;
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+
import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewContext;
+import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.GridSizeMigrationTask;
import com.android.launcher3.model.GridSizeMigrationTaskV2;
+import com.android.launcher3.model.LoaderTask;
+import com.android.launcher3.model.ModelDelegate;
+import com.android.launcher3.model.ModelPreload;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.Themes;
import com.android.launcher3.widget.LocalColorExtractor;
+import java.util.ArrayList;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
/** Render preview using surface view. */
@SuppressWarnings("NewApi")
-public class PreviewSurfaceRenderer implements IBinder.DeathRecipient {
+public class PreviewSurfaceRenderer {
+
+ private static final String TAG = "PreviewSurfaceRenderer";
private static final int FADE_IN_ANIMATION_DURATION = 200;
@@ -54,8 +71,6 @@
private static final String KEY_VIEW_WIDTH = "width";
private static final String KEY_VIEW_HEIGHT = "height";
private static final String KEY_DISPLAY_ID = "display_id";
- private static final String KEY_SURFACE_PACKAGE = "surface_package";
- private static final String KEY_CALLBACK = "callback";
private static final String KEY_COLORS = "wallpaper_colors";
private final Context mContext;
@@ -65,10 +80,13 @@
private final int mHeight;
private final Display mDisplay;
private final WallpaperColors mWallpaperColors;
+ private final RunnableList mOnDestroyCallbacks = new RunnableList();
- private SurfaceControlViewHost mSurfaceControlViewHost;
+ private final SurfaceControlViewHost mSurfaceControlViewHost;
- PreviewSurfaceRenderer(Context context, Bundle bundle) {
+ private boolean mDestroyed = false;
+
+ public PreviewSurfaceRenderer(Context context, Bundle bundle) throws Exception {
mContext = context;
String gridName = bundle.getString("name");
@@ -77,106 +95,101 @@
gridName = InvariantDeviceProfile.getCurrentGridName(context);
}
mWallpaperColors = bundle.getParcelable(KEY_COLORS);
-
mIdp = new InvariantDeviceProfile(context, gridName);
mHostToken = bundle.getBinder(KEY_HOST_TOKEN);
mWidth = bundle.getInt(KEY_VIEW_WIDTH);
mHeight = bundle.getInt(KEY_VIEW_HEIGHT);
+ mDisplay = context.getSystemService(DisplayManager.class)
+ .getDisplay(bundle.getInt(KEY_DISPLAY_ID));
- final DisplayManager displayManager = (DisplayManager) context.getSystemService(
- Context.DISPLAY_SERVICE);
- mDisplay = displayManager.getDisplay(bundle.getInt(KEY_DISPLAY_ID));
+ mSurfaceControlViewHost = MAIN_EXECUTOR
+ .submit(() -> new SurfaceControlViewHost(mContext, mDisplay, mHostToken))
+ .get(5, TimeUnit.SECONDS);
+ mOnDestroyCallbacks.add(mSurfaceControlViewHost::release);
}
- /** Handle a received surface view request. */
- Bundle render() {
- if (mSurfaceControlViewHost != null) {
- binderDied();
- }
+ public IBinder getHostToken() {
+ return mHostToken;
+ }
- SurfaceControlViewHost.SurfacePackage surfacePackage;
- try {
- mSurfaceControlViewHost = MAIN_EXECUTOR
- .submit(() -> new SurfaceControlViewHost(mContext, mDisplay, mHostToken))
- .get(5, TimeUnit.SECONDS);
- surfacePackage = mSurfaceControlViewHost.getSurfacePackage();
- mHostToken.linkToDeath(this, 0);
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
+ public SurfacePackage getSurfacePackage() {
+ return mSurfaceControlViewHost.getSurfacePackage();
+ }
- MODEL_EXECUTOR.post(() -> {
- final boolean success = doGridMigrationIfNecessary();
+ /**
+ * Destroys the preview and all associated data
+ */
+ @UiThread
+ public void destroy() {
+ mDestroyed = true;
+ mOnDestroyCallbacks.executeAllAndDestroy();
+ }
- final Context inflationContext;
- if (mWallpaperColors != null) {
- // Workaround to create a themed context
- Context context = mContext.createDisplayContext(mDisplay);
- LocalColorExtractor.newInstance(mContext)
- .applyColorsOverride(context, mWallpaperColors);
+ /**
+ * Generates the preview in background
+ */
+ public void loadAsync() {
+ MODEL_EXECUTOR.execute(this::loadModelData);
+ }
- inflationContext = new ContextThemeWrapper(context,
- Themes.getActivityThemeRes(context, mWallpaperColors.getColorHints()));
- } else {
- inflationContext = new ContextThemeWrapper(mContext, R.style.AppTheme);
+ @WorkerThread
+ private void loadModelData() {
+ final boolean migrated = doGridMigrationIfNecessary();
+
+ final Context inflationContext;
+ if (mWallpaperColors != null) {
+ // Create a themed context, without affecting the main application context
+ Context context = mContext.createDisplayContext(mDisplay);
+ if (Utilities.ATLEAST_R) {
+ context = context.createWindowContext(
+ LayoutParams.TYPE_APPLICATION_OVERLAY, null);
}
-
- MAIN_EXECUTOR.post(() -> {
- // If mSurfaceControlViewHost is null due to any reason (e.g. binder died,
- // happening when user leaves the preview screen before preview rendering finishes),
- // we should return here.
- SurfaceControlViewHost host = mSurfaceControlViewHost;
- if (host == null) {
- return;
- }
-
- View view = new LauncherPreviewRenderer(inflationContext, mIdp, success)
- .getRenderedView();
- // This aspect scales the view to fit in the surface and centers it
- final float scale = Math.min(mWidth / (float) view.getMeasuredWidth(),
- mHeight / (float) view.getMeasuredHeight());
- view.setScaleX(scale);
- view.setScaleY(scale);
- view.setPivotX(0);
- view.setPivotY(0);
- view.setTranslationX((mWidth - scale * view.getWidth()) / 2);
- view.setTranslationY((mHeight - scale * view.getHeight()) / 2);
- view.setAlpha(0);
- view.animate().alpha(1)
- .setInterpolator(new AccelerateDecelerateInterpolator())
- .setDuration(FADE_IN_ANIMATION_DURATION)
- .start();
- host.setView(view, view.getMeasuredWidth(), view.getMeasuredHeight());
- });
- });
-
- Bundle result = new Bundle();
- result.putParcelable(KEY_SURFACE_PACKAGE, surfacePackage);
-
- Handler handler = new Handler(Looper.getMainLooper(), message -> {
- binderDied();
- return true;
- });
- Messenger messenger = new Messenger(handler);
- Message msg = Message.obtain();
- msg.replyTo = messenger;
- result.putParcelable(KEY_CALLBACK, msg);
- return result;
- }
-
- @Override
- public void binderDied() {
- if (mSurfaceControlViewHost != null) {
- MAIN_EXECUTOR.execute(() -> {
- mSurfaceControlViewHost.release();
- mSurfaceControlViewHost = null;
- });
+ LocalColorExtractor.newInstance(mContext)
+ .applyColorsOverride(context, mWallpaperColors);
+ inflationContext = new ContextThemeWrapper(context,
+ Themes.getActivityThemeRes(context, mWallpaperColors.getColorHints()));
+ } else {
+ inflationContext = new ContextThemeWrapper(mContext, R.style.AppTheme);
}
- mHostToken.unlinkToDeath(this, 0);
+
+ if (migrated) {
+ PreviewContext previewContext = new PreviewContext(inflationContext, mIdp);
+ new LoaderTask(
+ LauncherAppState.getInstance(previewContext),
+ null,
+ new BgDataModel(),
+ new ModelDelegate(), null) {
+
+ @Override
+ public void run() {
+ loadWorkspace(new ArrayList<>(), LauncherSettings.Favorites.PREVIEW_CONTENT_URI,
+ LauncherSettings.Favorites.SCREEN + " = 0 or "
+ + LauncherSettings.Favorites.CONTAINER + " = "
+ + LauncherSettings.Favorites.CONTAINER_HOTSEAT);
+ MAIN_EXECUTOR.execute(() -> {
+ renderView(previewContext, mBgDataModel, mWidgetProvidersMap);
+ mOnDestroyCallbacks.add(previewContext::onDestroy);
+ });
+ }
+ }.run();
+ } else {
+ new ModelPreload() {
+
+ @Override
+ public void onComplete(boolean isSuccess) {
+ if (isSuccess) {
+ MAIN_EXECUTOR.execute(() ->
+ renderView(inflationContext, getBgDataModel(), null));
+ } else {
+ Log.e(TAG, "Model loading failed");
+ }
+ }
+ }.start(inflationContext);
+ }
}
+ @WorkerThread
private boolean doGridMigrationIfNecessary() {
boolean needsToMigrate =
MULTI_DB_GRID_MIRATION_ALGO.get()
@@ -189,4 +202,29 @@
? GridSizeMigrationTaskV2.migrateGridIfNeeded(mContext, mIdp)
: GridSizeMigrationTask.migrateGridIfNeeded(mContext, mIdp);
}
+
+ @UiThread
+ private void renderView(Context inflationContext, BgDataModel dataModel,
+ Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
+ if (mDestroyed) {
+ return;
+ }
+ View view = new LauncherPreviewRenderer(inflationContext, mIdp)
+ .getRenderedView(dataModel, widgetProviderInfoMap);
+ // This aspect scales the view to fit in the surface and centers it
+ final float scale = Math.min(mWidth / (float) view.getMeasuredWidth(),
+ mHeight / (float) view.getMeasuredHeight());
+ view.setScaleX(scale);
+ view.setScaleY(scale);
+ view.setPivotX(0);
+ view.setPivotY(0);
+ view.setTranslationX((mWidth - scale * view.getWidth()) / 2);
+ view.setTranslationY((mHeight - scale * view.getHeight()) / 2);
+ view.setAlpha(0);
+ view.animate().alpha(1)
+ .setInterpolator(new AccelerateDecelerateInterpolator())
+ .setDuration(FADE_IN_ANIMATION_DURATION)
+ .start();
+ mSurfaceControlViewHost.setView(view, view.getMeasuredWidth(), view.getMeasuredHeight());
+ }
}
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 297325a..8e0a388 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -131,6 +131,13 @@
}
/**
+ * Closes the cache DB. This will clear any in-memory cache.
+ */
+ public void close() {
+ mIconDb.close();
+ }
+
+ /**
* Fetches high-res icon for the provided ItemInfo and updates the caller when done.
*
* @return a request ID that can be used to cancel the request.
diff --git a/src/com/android/launcher3/model/ModelPreload.java b/src/com/android/launcher3/model/ModelPreload.java
index 713492b..756b7da 100644
--- a/src/com/android/launcher3/model/ModelPreload.java
+++ b/src/com/android/launcher3/model/ModelPreload.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.model;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
import android.content.Context;
import android.util.Log;
@@ -52,8 +54,14 @@
public final void run() {
mModel.startLoaderForResultsIfNotLoaded(
new LoaderResults(mApp, mBgDataModel, mAllAppsList, new Callbacks[0]));
- Log.d(TAG, "Preload completed : " + mModel.isModelLoaded());
- onComplete(mModel.isModelLoaded());
+ MODEL_EXECUTOR.post(() -> {
+ Log.d(TAG, "Preload completed : " + mModel.isModelLoaded());
+ onComplete(mModel.isModelLoaded());
+ });
+ }
+
+ public BgDataModel getBgDataModel() {
+ return mBgDataModel;
}
/**
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index ce7dc07..b5dcd3a 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -27,6 +27,7 @@
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,36 +195,43 @@
}
/**
+ * 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.
*
* @param v The view that was clicked. Must be a tagged with a {@link WorkspaceItemInfo}.
*/
public static void onClickAppShortcut(View v, WorkspaceItemInfo shortcut, Launcher launcher) {
if (shortcut.isDisabled()) {
- final int disabledFlags = shortcut.runtimeStatusFlags
- & WorkspaceItemInfo.FLAG_DISABLED_MASK;
- if ((disabledFlags &
- ~FLAG_DISABLED_SUSPENDED &
- ~FLAG_DISABLED_QUIET_USER) == 0) {
- // If the app is only disabled because of the above flags, launch activity anyway.
- // Framework will tell the user why the app is suspended.
- } else {
- if (!TextUtils.isEmpty(shortcut.disabledMessage)) {
- // Use a message specific to this shortcut, if it has one.
- Toast.makeText(launcher, shortcut.disabledMessage, Toast.LENGTH_SHORT).show();
- return;
- }
- // Otherwise just use a generic error message.
- int error = R.string.activity_not_available;
- if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_SAFEMODE) != 0) {
- error = R.string.safemode_shortcut_error;
- } else if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_BY_PUBLISHER) != 0 ||
- (shortcut.runtimeStatusFlags & FLAG_DISABLED_LOCKED_USER) != 0) {
- error = R.string.shortcut_not_available;
- }
- Toast.makeText(launcher, error, Toast.LENGTH_SHORT).show();
- return;
- }
+ handleDisabledItemClicked(shortcut, launcher);
+ return;
}
// Check for abandoned promise
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index 5deecd4..8685aae 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -319,11 +319,15 @@
}
mIsScrollable = checkScrollableRecursively(this);
-
if (!mIsInDragMode && getTag() instanceof LauncherAppWidgetInfo) {
+ mCurrentWidgetSize.left = left;
+ mCurrentWidgetSize.right = right;
+ mCurrentWidgetSize.top = top;
+ mCurrentWidgetSize.bottom = bottom;
+
LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
- getBoundsForViewInDragLayer(mLauncher.getDragLayer(), this, mCurrentWidgetSize, true,
- mTmpFloatArray, mTempRectF);
+ getBoundsForViewInDragLayer(mLauncher.getDragLayer(), (View) getParent(),
+ mCurrentWidgetSize, true, mTmpFloatArray, mTempRectF);
setRect(mTempRectF, mCurrentWidgetSize);
updateColorExtraction(mCurrentWidgetSize,
mWorkspace.getPageIndexForScreenId(info.screenId));
diff --git a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
index 6781824..6643779 100644
--- a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
+++ b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
@@ -21,6 +21,7 @@
import android.view.View;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.RelativeLayout;
+import android.widget.TextView;
import androidx.annotation.Nullable;
@@ -39,9 +40,11 @@
private final View mSearchAndRecommendationViewParent;
private final WidgetsRecyclerView mPrimaryRecyclerView;
private final WidgetsRecyclerView mSearchRecyclerView;
+ private final TextView mNoWidgetsView;
private final int mTabsHeight;
private final ValueAnimator mAnimator = ValueAnimator.ofInt(0, 0);
private final Point mTempOffset = new Point();
+ private int mBottomInset;
// The following are only non null if mHasWorkProfile is true.
@Nullable private final WidgetsRecyclerView mWorkRecyclerView;
@@ -81,7 +84,8 @@
@Nullable WidgetsRecyclerView workRecyclerView,
WidgetsRecyclerView searchRecyclerView,
@Nullable View personalWorkTabsView,
- @Nullable PersonalWorkPagedView primaryWorkViewPager) {
+ @Nullable PersonalWorkPagedView primaryWorkViewPager,
+ TextView noWidgetsView) {
mHasWorkProfile = hasWorkProfile;
mViewHolder = viewHolder;
mViewHolder.mContainer.setSearchAndRecommendationScrollController(this);
@@ -92,6 +96,7 @@
mPrimaryWorkTabsView = personalWorkTabsView;
mPrimaryWorkViewPager = primaryWorkViewPager;
mTabsHeight = tabsHeight;
+ mNoWidgetsView = noWidgetsView;
setCurrentRecyclerView(mPrimaryRecyclerView, /* animateReset= */ false);
}
@@ -114,6 +119,15 @@
}
/**
+ * Updates padding of {@link WidgetsFullSheet} contents to include {@code bottomInset} wherever
+ * necessary.
+ */
+ public boolean updateBottomInset(int bottomInset) {
+ mBottomInset = bottomInset;
+ return updateMarginAndPadding();
+ }
+
+ /**
* Updates the margin and padding of {@link WidgetsFullSheet} to accumulate collapsible views.
*
* @return {@code true} if margins or/and padding of views in the search and recommendations
@@ -129,6 +143,8 @@
+ measureHeightWithVerticalMargins(mViewHolder.mRecommendedWidgetsTable);
int topContainerHeight = measureHeightWithVerticalMargins(mViewHolder.mContainer);
+ int noWidgetsViewHeight = topContainerHeight - mBottomInset;
+
if (mHasWorkProfile) {
mCollapsibleHeightForTabs = measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle)
+ measureHeightWithVerticalMargins(mViewHolder.mRecommendedWidgetsTable);
@@ -180,6 +196,10 @@
int topOffsetAfterAllViewsCollapsed =
topContainerHeight + mTabsHeight - mCollapsibleHeightForTabs;
+ if (mPrimaryWorkTabsView.getVisibility() == View.VISIBLE) {
+ noWidgetsViewHeight += mTabsHeight;
+ }
+
RelativeLayout.LayoutParams viewPagerLayoutParams =
(RelativeLayout.LayoutParams) mPrimaryWorkViewPager.getLayoutParams();
if (viewPagerLayoutParams.topMargin != topOffsetAfterAllViewsCollapsed) {
@@ -222,6 +242,14 @@
mSearchRecyclerView.getPaddingBottom());
hasMarginOrPaddingUpdated = true;
}
+ if (mNoWidgetsView.getPaddingTop() != noWidgetsViewHeight) {
+ mNoWidgetsView.setPadding(
+ mNoWidgetsView.getPaddingLeft(),
+ noWidgetsViewHeight,
+ mNoWidgetsView.getPaddingRight(),
+ mNoWidgetsView.getPaddingBottom());
+ hasMarginOrPaddingUpdated = true;
+ }
return hasMarginOrPaddingUpdated;
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 801ecc2..039cad8 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -61,7 +61,6 @@
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
import com.android.launcher3.widget.picker.search.SearchModeListener;
import com.android.launcher3.widget.picker.search.WidgetsSearchBar;
-import com.android.launcher3.widget.picker.search.WidgetsSearchBarUIHelper;
import com.android.launcher3.widget.util.WidgetsTableUtils;
import com.android.launcher3.workprofile.PersonalWorkPagedView;
import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
@@ -76,8 +75,7 @@
*/
public class WidgetsFullSheet extends BaseWidgetSheet
implements Insettable, ProviderChangedListener, OnActivePageChangedListener,
- WidgetsRecyclerView.HeaderViewDimensionsProvider, SearchModeListener,
- WidgetsSearchBarUIHelper {
+ WidgetsRecyclerView.HeaderViewDimensionsProvider, SearchModeListener {
private static final String TAG = WidgetsFullSheet.class.getSimpleName();
private static final long DEFAULT_OPEN_DURATION = 267;
@@ -94,8 +92,8 @@
private final boolean mHasWorkProfile;
private final SparseArray<AdapterHolder> mAdapters = new SparseArray();
private final UserHandle mCurrentUser = Process.myUserHandle();
- private final Predicate<WidgetsListBaseEntry> mPrimaryWidgetsFilter = entry ->
- mCurrentUser.equals(entry.mPkgItem.user);
+ private final Predicate<WidgetsListBaseEntry> mPrimaryWidgetsFilter =
+ entry -> mCurrentUser.equals(entry.mPkgItem.user);
private final Predicate<WidgetsListBaseEntry> mWorkWidgetsFilter =
mPrimaryWidgetsFilter.negate();
private final OnLayoutChangeListener mLayoutChangeListenerToShowTips =
@@ -133,6 +131,7 @@
@Nullable private WidgetsRecyclerView mCurrentWidgetsRecyclerView;
@Nullable private PersonalWorkPagedView mViewPager;
private boolean mIsInSearchMode;
+ private boolean mIsNoWidgetsViewNeeded;
private int mMaxSpansPerRow = 4;
private View mTabsView;
private TextView mNoWidgetsView;
@@ -189,6 +188,7 @@
layoutInflater.inflate(R.layout.widgets_full_sheet_search_and_recommendations, springLayout,
true);
+ mNoWidgetsView = findViewById(R.id.no_widgets_text);
mSearchAndRecommendationViewHolder = new SearchAndRecommendationViewHolder(
findViewById(R.id.search_and_recommendations_container));
mSearchAndRecommendationsScrollController = new SearchAndRecommendationsScrollController(
@@ -199,10 +199,10 @@
mHasWorkProfile ? findViewById(R.id.work_widgets_list_view) : null,
findViewById(R.id.search_widgets_list_view),
mTabsView,
- mViewPager);
+ mViewPager,
+ mNoWidgetsView);
fastScroller.setOnFastScrollChangeListener(mSearchAndRecommendationsScrollController);
- mNoWidgetsView = findViewById(R.id.no_widgets_text);
onRecommendedWidgetsBound();
onWidgetsBound();
@@ -297,6 +297,7 @@
if (mHasWorkProfile) {
setBottomPadding(mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView, insets.bottom);
}
+ mSearchAndRecommendationsScrollController.updateBottomInset(insets.bottom);
if (insets.bottom > 0) {
setupNavBarColor();
} else {
@@ -412,6 +413,16 @@
} else {
updateRecyclerViewVisibility(primaryUserAdapterHolder);
}
+ // Update recommended widgets section so that it occupies appropriate space on screen to
+ // leave enough space for presence/absence of mNoWidgetsView.
+ boolean isNoWidgetsViewNeeded =
+ mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.getItemCount() == 0
+ || (mHasWorkProfile && mAdapters.get(AdapterHolder.WORK)
+ .mWidgetsListAdapter.getItemCount() == 0);
+ if (mIsNoWidgetsViewNeeded != isNoWidgetsViewNeeded) {
+ mIsNoWidgetsViewNeeded = isNoWidgetsViewNeeded;
+ onRecommendedWidgetsBound();
+ }
}
@Override
@@ -478,9 +489,19 @@
WidgetsRecommendationTableLayout table =
mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable;
if (recommendedWidgets.size() > 0) {
- float maxTableHeight =
- (mActivityContext.getDeviceProfile().availableHeightPx - mTabsHeight
- - getHeaderViewHeight()) * RECOMMENDATION_TABLE_HEIGHT_RATIO;
+ float noWidgetsViewHeight = 0;
+ if (mIsNoWidgetsViewNeeded) {
+ // Make sure recommended section leaves enough space for noWidgetsView.
+ Rect noWidgetsViewTextBounds = new Rect();
+ mNoWidgetsView.getPaint()
+ .getTextBounds(mNoWidgetsView.getText().toString(), /* start= */ 0,
+ mNoWidgetsView.getText().length(), noWidgetsViewTextBounds);
+ noWidgetsViewHeight = noWidgetsViewTextBounds.height();
+ }
+ float maxTableHeight = (mActivityContext.getDeviceProfile().availableHeightPx
+ - mTabsHeight - getHeaderViewHeight() - noWidgetsViewHeight)
+ * RECOMMENDATION_TABLE_HEIGHT_RATIO;
+
List<ArrayList<WidgetItem>> recommendedWidgetsInTable =
WidgetsTableUtils.groupWidgetItemsIntoTable(recommendedWidgets,
mMaxSpansPerRow);
@@ -540,6 +561,12 @@
} else if (getPopupContainer().isEventOverView(mContent, ev)) {
mNoIntercept = !getRecyclerView().shouldContainerScroll(ev, getPopupContainer());
}
+
+ if (mSearchAndRecommendationViewHolder.mSearchBar.isSearchBarFocused()
+ && !getPopupContainer().isEventOverView(
+ mSearchAndRecommendationViewHolder.mSearchBarContainer, ev)) {
+ mSearchAndRecommendationViewHolder.mSearchBar.clearSearchBarFocus();
+ }
}
return super.onControllerInterceptTouchEvent(ev);
}
@@ -614,11 +641,6 @@
getWindowInsetsController().hide(WindowInsets.Type.ime());
}
- @Override
- public void clearSearchBarFocus() {
- mSearchAndRecommendationViewHolder.mSearchBar.clearSearchBarFocus();
- }
-
private void showEducationTipOnView(View view) {
mActivityContext.getSharedPrefs().edit()
.putBoolean(WIDGETS_EDUCATION_TIP_SEEN, true).apply();
@@ -686,9 +708,7 @@
apps.getWidgetCache(),
apps.getIconCache(),
/* iconClickListener= */ WidgetsFullSheet.this,
- /* iconLongClickListener= */ WidgetsFullSheet.this,
- /* WidgetsSearchBarUIHelper= */
- mAdapterType == SEARCH ? WidgetsFullSheet.this : null);
+ /* iconLongClickListener= */ WidgetsFullSheet.this);
mWidgetsListAdapter.setHasStableIds(true);
switch (mAdapterType) {
case PRIMARY:
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
index 8345a0e..7963431 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
@@ -42,7 +42,6 @@
import com.android.launcher3.widget.model.WidgetsListContentEntry;
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
-import com.android.launcher3.widget.picker.search.WidgetsSearchBarUIHelper;
import java.util.ArrayList;
import java.util.Arrays;
@@ -72,7 +71,6 @@
private static final int VIEW_TYPE_WIDGETS_HEADER = R.id.view_type_widgets_header;
private static final int VIEW_TYPE_WIDGETS_SEARCH_HEADER = R.id.view_type_widgets_search_header;
- @Nullable private final WidgetsSearchBarUIHelper mSearchBarUIHelper;
private final WidgetsDiffReporter mDiffReporter;
private final SparseArray<ViewHolderBinder> mViewHolderBinders = new SparseArray<>();
private final WidgetsListTableViewHolderBinder mWidgetsListTableViewHolderBinder;
@@ -92,9 +90,7 @@
public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
WidgetPreviewLoader widgetPreviewLoader, IconCache iconCache,
- OnClickListener iconClickListener, OnLongClickListener iconLongClickListener,
- @Nullable WidgetsSearchBarUIHelper searchBarUIHelper) {
- mSearchBarUIHelper = searchBarUIHelper;
+ OnClickListener iconClickListener, OnLongClickListener iconLongClickListener) {
mDiffReporter = new WidgetsDiffReporter(iconCache, this);
mWidgetsListTableViewHolderBinder = new WidgetsListTableViewHolderBinder(context,
layoutInflater, iconClickListener, iconLongClickListener,
@@ -248,9 +244,6 @@
@Override
public void onHeaderClicked(boolean showWidgets, PackageUserKey packageUserKey) {
- if (mSearchBarUIHelper != null) {
- mSearchBarUIHelper.clearSearchBarFocus();
- }
if (showWidgets) {
mWidgetsContentVisiblePackageUserKey = packageUserKey;
updateVisibleEntries();
diff --git a/src/com/android/launcher3/widget/picker/search/LauncherWidgetsSearchBar.java b/src/com/android/launcher3/widget/picker/search/LauncherWidgetsSearchBar.java
index 42f1bb2..65937b6 100644
--- a/src/com/android/launcher3/widget/picker/search/LauncherWidgetsSearchBar.java
+++ b/src/com/android/launcher3/widget/picker/search/LauncherWidgetsSearchBar.java
@@ -76,6 +76,11 @@
}
@Override
+ public boolean isSearchBarFocused() {
+ return mEditText.isFocused();
+ }
+
+ @Override
public void clearSearchBarFocus() {
mController.clearFocus();
}
diff --git a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBar.java b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBar.java
index 0ac47ce..44a5e80 100644
--- a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBar.java
+++ b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBar.java
@@ -32,6 +32,9 @@
*/
void reset();
+ /** Returns {@code true} if the search bar is in focus. */
+ boolean isSearchBarFocused();
+
/**
* Clears focus from search bar.
*/
diff --git a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarUIHelper.java b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarUIHelper.java
deleted file mode 100644
index edfdc65..0000000
--- a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarUIHelper.java
+++ /dev/null
@@ -1,27 +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.widget.picker.search;
-
-/**
- * UI helper for {@link WidgetsSearchBar}.
- */
-public interface WidgetsSearchBarUIHelper {
- /**
- * Clears focus from the search bar.
- */
- void clearSearchBarFocus();
-}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 57f40db..4cf52f0 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -496,13 +496,14 @@
private void fail(String message) {
checkForAnomaly();
Assert.fail(formatSystemHealthMessage(formatErrorWithEvents(
- "http://go/tapl test failure:\nOverview: " + getContextDescription()
+ "http://go/tapl test failure:\nSummary: " + getContextDescription()
+ " - visible state is " + getVisibleStateMessage()
+ ";\nDetails: " + message, true)));
}
private String getContextDescription() {
- return mDiagnosticContext.isEmpty() ? "" : String.join(", ", mDiagnosticContext);
+ return mDiagnosticContext.isEmpty()
+ ? "(no context)" : String.join(", ", mDiagnosticContext);
}
void assertTrue(String message, boolean condition) {