Merge "Implement overlay window for Taskbar EDU and All Apps." into tm-qpr-dev

This commit is contained in:
TreeHugger Robot
2022-10-19 14:01:56 +00:00
committed by Android (Google) Code Review
18 changed files with 530 additions and 438 deletions

View File

@@ -276,13 +276,6 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
&& !mLauncher.getOnboardingPrefs().getBoolean(OnboardingPrefs.TASKBAR_EDU_SEEN);
}
/**
* Manually ends the taskbar education flow.
*/
public void hideEdu() {
mControllers.taskbarEduController.hideEdu();
}
@Override
public void onTaskbarIconLaunched(ItemInfo item) {
InstanceId instanceId = new InstanceIdSequence().newInstanceId();

View File

@@ -76,6 +76,7 @@ import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.touch.ItemClickHandler;
@@ -211,7 +212,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
new TaskbarAutohideSuspendController(this),
new TaskbarPopupController(this),
new TaskbarForceVisibleImmersiveController(this),
new TaskbarAllAppsController(this, dp),
new TaskbarOverlayController(this, dp),
new TaskbarAllAppsController(),
new TaskbarInsetsController(this),
new VoiceInteractionWindowController(this),
isDesktopMode
@@ -243,7 +245,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
/** Updates {@link DeviceProfile} instances for any Taskbar windows. */
public void updateDeviceProfile(DeviceProfile dp, NavigationMode navMode) {
mNavMode = navMode;
mControllers.taskbarAllAppsController.updateDeviceProfile(dp);
mControllers.taskbarOverlayController.updateDeviceProfile(dp);
mDeviceProfile = dp.copy(this);
updateIconSize(getResources());

View File

@@ -22,6 +22,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
import com.android.systemui.shared.rotation.RotationButtonController;
import java.io.PrintWriter;
@@ -54,6 +55,7 @@ public class TaskbarControllers {
public final TaskbarInsetsController taskbarInsetsController;
public final VoiceInteractionWindowController voiceInteractionWindowController;
public final TaskbarRecentAppsController taskbarRecentAppsController;
public final TaskbarOverlayController taskbarOverlayController;
@Nullable private LoggableTaskbarController[] mControllersToLog = null;
@@ -81,6 +83,7 @@ public class TaskbarControllers {
TaskbarAutohideSuspendController taskbarAutoHideSuspendController,
TaskbarPopupController taskbarPopupController,
TaskbarForceVisibleImmersiveController taskbarForceVisibleImmersiveController,
TaskbarOverlayController taskbarOverlayController,
TaskbarAllAppsController taskbarAllAppsController,
TaskbarInsetsController taskbarInsetsController,
VoiceInteractionWindowController voiceInteractionWindowController,
@@ -101,6 +104,7 @@ public class TaskbarControllers {
this.taskbarAutohideSuspendController = taskbarAutoHideSuspendController;
this.taskbarPopupController = taskbarPopupController;
this.taskbarForceVisibleImmersiveController = taskbarForceVisibleImmersiveController;
this.taskbarOverlayController = taskbarOverlayController;
this.taskbarAllAppsController = taskbarAllAppsController;
this.taskbarInsetsController = taskbarInsetsController;
this.voiceInteractionWindowController = voiceInteractionWindowController;
@@ -129,6 +133,7 @@ public class TaskbarControllers {
taskbarEduController.init(this);
taskbarPopupController.init(this);
taskbarForceVisibleImmersiveController.init(this);
taskbarOverlayController.init(this);
taskbarAllAppsController.init(this, sharedState.allAppsVisible);
navButtonController.init(this);
taskbarInsetsController.init(this);
@@ -179,7 +184,7 @@ public class TaskbarControllers {
taskbarAutohideSuspendController.onDestroy();
taskbarPopupController.onDestroy();
taskbarForceVisibleImmersiveController.onDestroy();
taskbarAllAppsController.onDestroy();
taskbarOverlayController.onDestroy();
navButtonController.onDestroy();
taskbarInsetsController.onDestroy();
voiceInteractionWindowController.onDestroy();

View File

@@ -35,6 +35,7 @@ import android.view.View;
import com.android.launcher3.R;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.uioverrides.PredictedAppIcon;
import java.io.PrintWriter;
@@ -87,8 +88,10 @@ public class TaskbarEduController implements TaskbarControllers.LoggableTaskbarC
void showEdu() {
mActivity.setTaskbarWindowFullscreen(true);
mActivity.getDragLayer().post(() -> {
mTaskbarEduView = (TaskbarEduView) mActivity.getLayoutInflater().inflate(
R.layout.taskbar_edu, mActivity.getDragLayer(), false);
TaskbarOverlayContext overlayContext =
mControllers.taskbarOverlayController.requestWindow();
mTaskbarEduView = (TaskbarEduView) overlayContext.getLayoutInflater().inflate(
R.layout.taskbar_edu, overlayContext.getDragLayer(), false);
mTaskbarEduView.init(new TaskbarEduCallbacks());
mControllers.navbarButtonsViewController.setSlideInViewVisible(true);
mTaskbarEduView.setOnCloseBeginListener(
@@ -99,12 +102,6 @@ public class TaskbarEduController implements TaskbarControllers.LoggableTaskbarC
});
}
void hideEdu() {
if (mTaskbarEduView != null) {
mTaskbarEduView.close(true /* animate */);
}
}
/**
* Starts the given animation, ending the previous animation first if it's still playing.
*/

View File

@@ -28,10 +28,11 @@ import android.widget.Button;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.views.AbstractSlideInView;
/** Education view about the Taskbar. */
public class TaskbarEduView extends AbstractSlideInView<TaskbarActivityContext>
public class TaskbarEduView extends AbstractSlideInView<TaskbarOverlayContext>
implements Insettable {
private static final int DEFAULT_OPEN_DURATION = 500;

View File

@@ -29,7 +29,7 @@ import android.view.WindowManager
import android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD
import android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION
import com.android.launcher3.AbstractFloatingView
import com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_ALL_APPS
import com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_OVERLAY_PROXY
import com.android.launcher3.DeviceProfile
import com.android.launcher3.anim.AlphaUpdateListener
import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
@@ -158,8 +158,11 @@ class TaskbarInsetsController(val context: TaskbarActivityContext): LoggableTask
} else if (controllers.taskbarDragController.isSystemDragInProgress) {
// Let touches pass through us.
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
} else if (AbstractFloatingView.hasOpenView(context, TYPE_TASKBAR_ALL_APPS)) {
// Let touches pass through us.
} else if (AbstractFloatingView.hasOpenView(context, TYPE_TASKBAR_OVERLAY_PROXY)) {
// Let touches pass through us if icons are hidden.
if (controllers.taskbarViewController.areIconsVisible()) {
insetsInfo.touchableRegion.set(touchableRegion)
}
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
} else if (controllers.taskbarViewController.areIconsVisible()
|| AbstractFloatingView.hasOpenView(context, AbstractFloatingView.TYPE_ALL)

View File

@@ -80,10 +80,10 @@ public class TaskbarUIController {
}
/**
* Manually closes the all apps window.
* Manually closes the overlay window.
*/
public void hideAllApps() {
mControllers.taskbarAllAppsController.hide();
public void hideOverlayWindow() {
mControllers.taskbarOverlayController.hideWindow();
}
/**

View File

@@ -24,10 +24,11 @@ import com.android.launcher3.allapps.AllAppsGridAdapter;
import com.android.launcher3.allapps.AlphabeticalAppsList;
import com.android.launcher3.allapps.BaseAdapterProvider;
import com.android.launcher3.allapps.BaseAllAppsAdapter;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
/** All apps container accessible from taskbar. */
public class TaskbarAllAppsContainerView extends
ActivityAllAppsContainerView<TaskbarAllAppsContext> {
ActivityAllAppsContainerView<TaskbarOverlayContext> {
public TaskbarAllAppsContainerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
@@ -44,8 +45,8 @@ public class TaskbarAllAppsContainerView extends
}
@Override
protected BaseAllAppsAdapter<TaskbarAllAppsContext> createAdapter(
AlphabeticalAppsList<TaskbarAllAppsContext> appsList,
protected BaseAllAppsAdapter<TaskbarOverlayContext> createAdapter(
AlphabeticalAppsList<TaskbarOverlayContext> appsList,
BaseAdapterProvider[] adapterProviders) {
return new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), appsList,
adapterProviders);

View File

@@ -1,248 +0,0 @@
/*
* Copyright (C) 2022 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.allapps;
import static android.view.KeyEvent.ACTION_UP;
import static android.view.KeyEvent.KEYCODE_BACK;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import android.content.Context;
import android.graphics.Insets;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
import android.view.WindowInsets;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider;
import com.android.launcher3.allapps.search.SearchAdapterProvider;
import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.taskbar.BaseTaskbarContext;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarControllers;
import com.android.launcher3.taskbar.TaskbarDragController;
import com.android.launcher3.taskbar.TaskbarStashController;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
/**
* Window context for the taskbar all apps overlay.
* <p>
* All apps has its own window and needs a window context. Some properties are delegated to the
* {@link TaskbarActivityContext} such as {@link DeviceProfile} and {@link PopupDataProvider}.
*/
class TaskbarAllAppsContext extends BaseTaskbarContext {
private final TaskbarActivityContext mTaskbarContext;
private final OnboardingPrefs<TaskbarAllAppsContext> mOnboardingPrefs;
private final TaskbarAllAppsController mWindowController;
private final TaskbarAllAppsViewController mAllAppsViewController;
private final TaskbarDragController mDragController;
private final TaskbarAllAppsDragLayer mDragLayer;
private final TaskbarAllAppsContainerView mAppsView;
// We automatically stash taskbar when all apps is opened in gesture navigation mode.
private final boolean mWillTaskbarBeVisuallyStashed;
private final int mStashedTaskbarHeight;
TaskbarAllAppsContext(
TaskbarActivityContext taskbarContext,
TaskbarAllAppsController windowController,
TaskbarControllers taskbarControllers) {
super(taskbarContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null));
mTaskbarContext = taskbarContext;
mWindowController = windowController;
mDragController = new TaskbarDragController(this);
mOnboardingPrefs = new OnboardingPrefs<>(this, Utilities.getPrefs(this));
mDragLayer = new TaskbarAllAppsDragLayer(this);
TaskbarAllAppsSlideInView slideInView = (TaskbarAllAppsSlideInView) mLayoutInflater.inflate(
R.layout.taskbar_all_apps, mDragLayer, false);
mAllAppsViewController = new TaskbarAllAppsViewController(
this,
slideInView,
windowController,
taskbarControllers);
mAppsView = slideInView.getAppsView();
TaskbarStashController taskbarStashController = taskbarControllers.taskbarStashController;
mWillTaskbarBeVisuallyStashed = taskbarStashController.supportsVisualStashing();
mStashedTaskbarHeight = taskbarStashController.getStashedHeight();
}
TaskbarAllAppsViewController getAllAppsViewController() {
return mAllAppsViewController;
}
@Override
public DeviceProfile getDeviceProfile() {
return mWindowController.getDeviceProfile();
}
@Override
public TaskbarDragController getDragController() {
return mDragController;
}
@Override
public TaskbarAllAppsDragLayer getDragLayer() {
return mDragLayer;
}
@Override
public TaskbarAllAppsContainerView getAppsView() {
return mAppsView;
}
@Override
public OnboardingPrefs<TaskbarAllAppsContext> getOnboardingPrefs() {
return mOnboardingPrefs;
}
@Override
public boolean isBindingItems() {
return mTaskbarContext.isBindingItems();
}
@Override
public View.OnClickListener getItemOnClickListener() {
return mTaskbarContext.getItemOnClickListener();
}
@Override
public PopupDataProvider getPopupDataProvider() {
return mTaskbarContext.getPopupDataProvider();
}
@Override
public DotInfo getDotInfoForItem(ItemInfo info) {
return mTaskbarContext.getDotInfoForItem(info);
}
@Override
public void onDragStart() {}
@Override
public void onDragEnd() {
mWindowController.maybeCloseWindow();
}
@Override
public void onPopupVisibilityChanged(boolean isVisible) {}
@Override
public SearchAdapterProvider<?> createSearchAdapterProvider(
ActivityAllAppsContainerView<?> appsView) {
return new DefaultSearchAdapterProvider(this);
}
/** Root drag layer for this context. */
private static class TaskbarAllAppsDragLayer extends
BaseDragLayer<TaskbarAllAppsContext> implements OnComputeInternalInsetsListener {
private TaskbarAllAppsDragLayer(Context context) {
super(context, null, 1);
setClipChildren(false);
recreateControllers();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
getViewTreeObserver().addOnComputeInternalInsetsListener(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
}
@Override
public void recreateControllers() {
mControllers = new TouchController[]{mActivity.mDragController};
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
return super.dispatchTouchEvent(ev);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK) {
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
if (topView != null && topView.onBackPressed()) {
return true;
}
}
return super.dispatchKeyEvent(event);
}
@Override
public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
if (mActivity.mDragController.isSystemDragInProgress()) {
inoutInfo.touchableRegion.setEmpty();
inoutInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
}
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
return updateInsetsDueToStashing(insets);
}
/**
* Taskbar automatically stashes when opening all apps, but we don't report the insets as
* changing to avoid moving the underlying app. But internally, the apps view should still
* layout according to the stashed insets rather than the unstashed insets. So this method
* does two things:
* 1) Sets navigationBars bottom inset to stashedHeight.
* 2) Sets tappableInsets bottom inset to 0.
*/
private WindowInsets updateInsetsDueToStashing(WindowInsets oldInsets) {
if (!mActivity.mWillTaskbarBeVisuallyStashed) {
return oldInsets;
}
WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
Insets oldNavInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
Insets newNavInsets = Insets.of(oldNavInsets.left, oldNavInsets.top, oldNavInsets.right,
mActivity.mStashedTaskbarHeight);
updatedInsetsBuilder.setInsets(WindowInsets.Type.navigationBars(), newNavInsets);
Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
Insets newTappableInsets = Insets.of(oldTappableInsets.left, oldTappableInsets.top,
oldTappableInsets.right, 0);
updatedInsetsBuilder.setInsets(WindowInsets.Type.tappableElement(), newTappableInsets);
return updatedInsetsBuilder.build();
}
}
}

View File

@@ -15,34 +15,17 @@
*/
package com.android.launcher3.taskbar.allapps;
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.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
import android.content.Context;
import android.graphics.PixelFormat;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.appprediction.PredictionRowView;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarControllers;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import java.util.List;
import java.util.Optional;
/**
* Handles the all apps overlay window initialization, updates, and its data.
@@ -57,36 +40,14 @@ import java.util.Optional;
*/
public final class TaskbarAllAppsController {
private static final String WINDOW_TITLE = "Taskbar All Apps";
private final TaskbarActivityContext mTaskbarContext;
private final TaskbarAllAppsProxyView mProxyView;
private final LayoutParams mLayoutParams;
private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
@Override
public void onTaskStackChanged() {
mProxyView.close(false);
}
};
private DeviceProfile mDeviceProfile;
private TaskbarControllers mControllers;
/** Window context for all apps if it is open. */
private @Nullable TaskbarAllAppsContext mAllAppsContext;
private @Nullable TaskbarAllAppsContainerView mAppsView;
// Application data models.
private AppInfo[] mApps;
private int mAppsModelFlags;
private List<ItemInfo> mPredictedApps;
public TaskbarAllAppsController(TaskbarActivityContext context, DeviceProfile dp) {
mDeviceProfile = dp;
mTaskbarContext = context;
mProxyView = new TaskbarAllAppsProxyView(mTaskbarContext);
mLayoutParams = createLayoutParams();
}
/** Initialize the controller. */
public void init(TaskbarControllers controllers, boolean allAppsVisible) {
if (!FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
@@ -111,8 +72,8 @@ public final class TaskbarAllAppsController {
mApps = apps;
mAppsModelFlags = flags;
if (mAllAppsContext != null) {
mAllAppsContext.getAppsView().getAppsStore().setApps(mApps, mAppsModelFlags);
if (mAppsView != null) {
mAppsView.getAppsStore().setApps(mApps, mAppsModelFlags);
}
}
@@ -123,8 +84,8 @@ public final class TaskbarAllAppsController {
}
mPredictedApps = predictedApps;
if (mAllAppsContext != null) {
mAllAppsContext.getAppsView().getFloatingHeaderView()
if (mAppsView != null) {
mAppsView.getFloatingHeaderView()
.findFixedRowByType(PredictionRowView.class)
.setPredictedApps(mPredictedApps);
}
@@ -136,120 +97,30 @@ public final class TaskbarAllAppsController {
}
private void show(boolean animate) {
if (mProxyView.isOpen()) {
if (mAppsView != null) {
return;
}
mProxyView.show();
// mControllers and getSharedState should never be null here. Do not handle null-pointer
// to catch invalid states.
mControllers.getSharedState().allAppsVisible = true;
mAllAppsContext = new TaskbarAllAppsContext(mTaskbarContext, this, mControllers);
mAllAppsContext.getDragController().init(mControllers);
TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
Optional.ofNullable(mAllAppsContext.getSystemService(WindowManager.class))
.ifPresent(m -> m.addView(mAllAppsContext.getDragLayer(), mLayoutParams));
TaskbarOverlayContext overlayContext =
mControllers.taskbarOverlayController.requestWindow();
TaskbarAllAppsSlideInView slideInView =
(TaskbarAllAppsSlideInView) overlayContext.getLayoutInflater().inflate(
R.layout.taskbar_all_apps, overlayContext.getDragLayer(), false);
slideInView.addOnCloseListener(() -> {
mControllers.getSharedState().allAppsVisible = false;
mAppsView = null;
});
TaskbarAllAppsViewController viewController = new TaskbarAllAppsViewController(
overlayContext, slideInView, mControllers);
mAllAppsContext.getAppsView().getAppsStore().setApps(mApps, mAppsModelFlags);
mAllAppsContext.getAppsView().getFloatingHeaderView()
viewController.show(animate);
mAppsView = overlayContext.getAppsView();
mAppsView.getAppsStore().setApps(mApps, mAppsModelFlags);
mAppsView.getFloatingHeaderView()
.findFixedRowByType(PredictionRowView.class)
.setPredictedApps(mPredictedApps);
mAllAppsContext.getAllAppsViewController().show(animate);
}
/** Closes the {@link TaskbarAllAppsContainerView}. */
public void hide() {
mProxyView.close(true);
}
/**
* Removes the all apps window from the hierarchy, if all floating views are closed and there is
* no system drag operation in progress.
* <p>
* This method should be called after an exit animation finishes, if applicable.
*/
void maybeCloseWindow() {
if (mAllAppsContext != null && (AbstractFloatingView.hasOpenView(mAllAppsContext, TYPE_ALL)
|| mAllAppsContext.getDragController().isSystemDragInProgress())) {
return;
}
mProxyView.close(false);
// mControllers and getSharedState should never be null here. Do not handle null-pointer
// to catch invalid states.
mControllers.getSharedState().allAppsVisible = false;
onDestroy();
}
/** Destroys the controller and any All Apps window if present. */
public void onDestroy() {
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
Optional.ofNullable(mAllAppsContext)
.map(c -> c.getSystemService(WindowManager.class))
.ifPresent(m -> m.removeViewImmediate(mAllAppsContext.getDragLayer()));
mAllAppsContext = null;
}
/** Updates {@link DeviceProfile} instance for Taskbar's All Apps window. */
public void updateDeviceProfile(DeviceProfile dp) {
mDeviceProfile = dp;
Optional.ofNullable(mAllAppsContext).ifPresent(c -> {
AbstractFloatingView.closeAllOpenViewsExcept(c, false, TYPE_REBIND_SAFE);
c.dispatchDeviceProfileChanged();
});
}
DeviceProfile getDeviceProfile() {
return mDeviceProfile;
}
private LayoutParams createLayoutParams() {
LayoutParams layoutParams = new LayoutParams(
TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT);
layoutParams.setTitle(WINDOW_TITLE);
layoutParams.gravity = Gravity.BOTTOM;
layoutParams.packageName = mTaskbarContext.getPackageName();
layoutParams.setFitInsetsTypes(0); // Handled by container view.
layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
layoutParams.setSystemApplicationOverlay(true);
return layoutParams;
}
/**
* Proxy view connecting taskbar drag layer to the all apps window.
* <p>
* The all apps view is in a separate window and has its own drag layer, but this proxy lets it
* behave as though its in the taskbar drag layer. For instance, when the taskbar closes all
* {@link AbstractFloatingView} instances, the all apps window will also close.
*/
private class TaskbarAllAppsProxyView extends AbstractFloatingView {
private TaskbarAllAppsProxyView(Context context) {
super(context, null);
}
private void show() {
mIsOpen = true;
mTaskbarContext.getDragLayer().addView(this);
}
@Override
protected void handleClose(boolean animate) {
mTaskbarContext.getDragLayer().removeView(this);
Optional.ofNullable(mAllAppsContext)
.map(TaskbarAllAppsContext::getAllAppsViewController)
.ifPresent(v -> v.close(animate));
}
@Override
protected boolean isOfType(int type) {
return (type & TYPE_TASKBAR_ALL_APPS) != 0;
}
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
return false;
}
}
}

View File

@@ -28,10 +28,11 @@ import android.view.animation.Interpolator;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.views.AbstractSlideInView;
/** Wrapper for taskbar all apps with slide-in behavior. */
public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarAllAppsContext>
public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarOverlayContext>
implements Insettable, DeviceProfile.OnDeviceProfileChangeListener {
private TaskbarAllAppsContainerView mAppsView;
private float mShiftRange;

View File

@@ -25,6 +25,7 @@ import com.android.launcher3.appprediction.PredictionRowView;
import com.android.launcher3.taskbar.NavbarButtonsViewController;
import com.android.launcher3.taskbar.TaskbarControllers;
import com.android.launcher3.taskbar.TaskbarStashController;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
/**
* Handles the {@link TaskbarAllAppsContainerView} behavior and synchronizes its transitions with
@@ -32,16 +33,15 @@ import com.android.launcher3.taskbar.TaskbarStashController;
*/
final class TaskbarAllAppsViewController {
private final TaskbarAllAppsContext mContext;
private final TaskbarOverlayContext mContext;
private final TaskbarAllAppsSlideInView mSlideInView;
private final TaskbarAllAppsContainerView mAppsView;
private final TaskbarStashController mTaskbarStashController;
private final NavbarButtonsViewController mNavbarButtonsViewController;
TaskbarAllAppsViewController(
TaskbarAllAppsContext context,
TaskbarOverlayContext context,
TaskbarAllAppsSlideInView slideInView,
TaskbarAllAppsController windowController,
TaskbarControllers taskbarControllers) {
mContext = context;
@@ -53,7 +53,6 @@ final class TaskbarAllAppsViewController {
setUpIconLongClick();
setUpAppDivider();
setUpTaskbarStashing();
mSlideInView.addOnCloseListener(windowController::maybeCloseWindow);
}
/** Starts the {@link TaskbarAllAppsSlideInView} enter transition. */

View File

@@ -0,0 +1,146 @@
/*
* Copyright (C) 2022 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.overlay;
import android.content.Context;
import android.view.View;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider;
import com.android.launcher3.allapps.search.SearchAdapterProvider;
import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.taskbar.BaseTaskbarContext;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarControllers;
import com.android.launcher3.taskbar.TaskbarDragController;
import com.android.launcher3.taskbar.TaskbarStashController;
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsContainerView;
import com.android.launcher3.util.OnboardingPrefs;
/**
* Window context for the taskbar overlays such as All Apps and EDU.
* <p>
* Overlays have their own window and need a window context. Some properties are delegated to the
* {@link TaskbarActivityContext} such as {@link PopupDataProvider}.
*/
public class TaskbarOverlayContext extends BaseTaskbarContext {
private final TaskbarActivityContext mTaskbarContext;
private final OnboardingPrefs<TaskbarOverlayContext> mOnboardingPrefs;
private final TaskbarOverlayController mOverlayController;
private final TaskbarDragController mDragController;
private final TaskbarOverlayDragLayer mDragLayer;
// We automatically stash taskbar when All Apps is opened in gesture navigation mode.
private final boolean mWillTaskbarBeVisuallyStashed;
private final int mStashedTaskbarHeight;
public TaskbarOverlayContext(
Context windowContext,
TaskbarActivityContext taskbarContext,
TaskbarControllers controllers) {
super(windowContext);
mTaskbarContext = taskbarContext;
mOverlayController = controllers.taskbarOverlayController;
mDragController = new TaskbarDragController(this);
mDragController.init(controllers);
mOnboardingPrefs = new OnboardingPrefs<>(this, Utilities.getPrefs(this));
mDragLayer = new TaskbarOverlayDragLayer(this);
TaskbarStashController taskbarStashController = controllers.taskbarStashController;
mWillTaskbarBeVisuallyStashed = taskbarStashController.supportsVisualStashing();
mStashedTaskbarHeight = taskbarStashController.getStashedHeight();
}
boolean willTaskbarBeVisuallyStashed() {
return mWillTaskbarBeVisuallyStashed;
}
int getStashedTaskbarHeight() {
return mStashedTaskbarHeight;
}
public TaskbarOverlayController getOverlayController() {
return mOverlayController;
}
@Override
public DeviceProfile getDeviceProfile() {
return mOverlayController.getDeviceProfile();
}
@Override
public TaskbarDragController getDragController() {
return mDragController;
}
@Override
public TaskbarOverlayDragLayer getDragLayer() {
return mDragLayer;
}
@Override
public TaskbarAllAppsContainerView getAppsView() {
return mDragLayer.findViewById(R.id.apps_view);
}
@Override
public OnboardingPrefs<TaskbarOverlayContext> getOnboardingPrefs() {
return mOnboardingPrefs;
}
@Override
public boolean isBindingItems() {
return mTaskbarContext.isBindingItems();
}
@Override
public View.OnClickListener getItemOnClickListener() {
return mTaskbarContext.getItemOnClickListener();
}
@Override
public PopupDataProvider getPopupDataProvider() {
return mTaskbarContext.getPopupDataProvider();
}
@Override
public DotInfo getDotInfoForItem(ItemInfo info) {
return mTaskbarContext.getDotInfoForItem(info);
}
@Override
public void onDragStart() {}
@Override
public void onDragEnd() {
mOverlayController.maybeCloseWindow();
}
@Override
public void onPopupVisibilityChanged(boolean isVisible) {}
@Override
public SearchAdapterProvider<?> createSearchAdapterProvider(
ActivityAllAppsContainerView<?> appsView) {
return new DefaultSearchAdapterProvider(this);
}
}

View File

@@ -0,0 +1,197 @@
/*
* Copyright (C) 2022 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.overlay;
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.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PixelFormat;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarControllers;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import java.util.Optional;
/**
* Handles the Taskbar overlay window lifecycle.
* <p>
* Overlays need to be inflated in a separate window so that have the correct hierarchy. For
* instance, they need to be below the notification tray. If there are multiple overlays open, the
* same window is used.
*/
public final class TaskbarOverlayController {
private static final String WINDOW_TITLE = "Taskbar Overlay";
private final TaskbarActivityContext mTaskbarContext;
private final Context mWindowContext;
private final TaskbarOverlayProxyView mProxyView;
private final LayoutParams mLayoutParams;
private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
@Override
public void onTaskStackChanged() {
mProxyView.close(false);
}
};
private DeviceProfile mDeviceProfile;
private @Nullable TaskbarOverlayContext mOverlayContext;
private TaskbarControllers mControllers; // Initialized in init.
public TaskbarOverlayController(
TaskbarActivityContext taskbarContext, DeviceProfile deviceProfile) {
mTaskbarContext = taskbarContext;
mWindowContext = mTaskbarContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null);
mProxyView = new TaskbarOverlayProxyView();
mLayoutParams = createLayoutParams();
mDeviceProfile = deviceProfile;
}
/** Initialize the controller. */
public void init(TaskbarControllers controllers) {
mControllers = controllers;
}
/**
* Creates a window for Taskbar overlays, if it does not already exist. Returns the window
* context for the current overlay window.
*/
public TaskbarOverlayContext requestWindow() {
if (mOverlayContext == null) {
mOverlayContext = new TaskbarOverlayContext(
mWindowContext, mTaskbarContext, mControllers);
}
if (!mProxyView.isOpen()) {
mProxyView.show();
Optional.ofNullable(mOverlayContext.getSystemService(WindowManager.class))
.ifPresent(m -> m.addView(mOverlayContext.getDragLayer(), mLayoutParams));
TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
}
return mOverlayContext;
}
/** Hides the current overlay window with animation. */
public void hideWindow() {
mProxyView.close(true);
}
/**
* Removes the overlay window from the hierarchy, if all floating views are closed and there is
* no system drag operation in progress.
* <p>
* This method should be called after an exit animation finishes, if applicable.
*/
@SuppressLint("WrongConstant")
void maybeCloseWindow() {
if (mOverlayContext != null && (AbstractFloatingView.hasOpenView(mOverlayContext, TYPE_ALL)
|| mOverlayContext.getDragController().isSystemDragInProgress())) {
return;
}
mProxyView.close(false);
onDestroy();
}
/** Destroys the controller and any overlay window if present. */
public void onDestroy() {
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
Optional.ofNullable(mOverlayContext)
.map(c -> c.getSystemService(WindowManager.class))
.ifPresent(m -> m.removeViewImmediate(mOverlayContext.getDragLayer()));
mOverlayContext = null;
}
/** The current device profile for the overlay window. */
public DeviceProfile getDeviceProfile() {
return mDeviceProfile;
}
/** Updates {@link DeviceProfile} instance for Taskbar's overlay window. */
public void updateDeviceProfile(DeviceProfile dp) {
mDeviceProfile = dp;
Optional.ofNullable(mOverlayContext).ifPresent(c -> {
AbstractFloatingView.closeAllOpenViewsExcept(c, false, TYPE_REBIND_SAFE);
c.dispatchDeviceProfileChanged();
});
}
@SuppressLint("WrongConstant")
private LayoutParams createLayoutParams() {
LayoutParams layoutParams = new LayoutParams(
TYPE_APPLICATION_OVERLAY,
LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT);
layoutParams.setTitle(WINDOW_TITLE);
layoutParams.gravity = Gravity.BOTTOM;
layoutParams.packageName = mTaskbarContext.getPackageName();
layoutParams.setFitInsetsTypes(0); // Handled by container view.
layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
layoutParams.setSystemApplicationOverlay(true);
return layoutParams;
}
/**
* Proxy view connecting taskbar drag layer to the overlay window.
*
* Overlays are in a separate window and has its own drag layer, but this proxy lets its views
* behave as though they are in the taskbar drag layer. For instance, when the taskbar closes
* all {@link AbstractFloatingView} instances, the overlay window will also close.
*/
private class TaskbarOverlayProxyView extends AbstractFloatingView {
private TaskbarOverlayProxyView() {
super(mTaskbarContext, null);
}
private void show() {
mIsOpen = true;
mTaskbarContext.getDragLayer().addView(this);
}
@Override
protected void handleClose(boolean animate) {
mTaskbarContext.getDragLayer().removeView(this);
Optional.ofNullable(mOverlayContext).ifPresent(c -> closeAllOpenViews(c, animate));
}
@Override
protected boolean isOfType(int type) {
return (type & TYPE_TASKBAR_OVERLAY_PROXY) != 0;
}
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
return false;
}
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) 2022 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.overlay;
import static android.view.KeyEvent.ACTION_UP;
import static android.view.KeyEvent.KEYCODE_BACK;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
import android.content.Context;
import android.graphics.Insets;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
/** Root drag layer for the Taskbar overlay window. */
public class TaskbarOverlayDragLayer extends
BaseDragLayer<TaskbarOverlayContext> implements
ViewTreeObserver.OnComputeInternalInsetsListener {
TaskbarOverlayDragLayer(Context context) {
super(context, null, 1);
setClipChildren(false);
recreateControllers();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
getViewTreeObserver().addOnComputeInternalInsetsListener(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
}
@Override
public void recreateControllers() {
mControllers = new TouchController[]{mActivity.getDragController()};
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
return super.dispatchTouchEvent(ev);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK) {
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
if (topView != null && topView.onBackPressed()) {
return true;
}
}
return super.dispatchKeyEvent(event);
}
@Override
public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
if (mActivity.getDragController().isSystemDragInProgress()) {
inoutInfo.touchableRegion.setEmpty();
inoutInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
}
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
return updateInsetsDueToStashing(insets);
}
@Override
public void onViewRemoved(View child) {
super.onViewRemoved(child);
mActivity.getOverlayController().maybeCloseWindow();
}
/**
* Taskbar automatically stashes when opening all apps, but we don't report the insets as
* changing to avoid moving the underlying app. But internally, the apps view should still
* layout according to the stashed insets rather than the unstashed insets. So this method
* does two things:
* 1) Sets navigationBars bottom inset to stashedHeight.
* 2) Sets tappableInsets bottom inset to 0.
*/
private WindowInsets updateInsetsDueToStashing(WindowInsets oldInsets) {
if (!mActivity.willTaskbarBeVisuallyStashed()) {
return oldInsets;
}
WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
Insets oldNavInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
Insets newNavInsets = Insets.of(oldNavInsets.left, oldNavInsets.top, oldNavInsets.right,
mActivity.getStashedTaskbarHeight());
updatedInsetsBuilder.setInsets(WindowInsets.Type.navigationBars(), newNavInsets);
Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
Insets newTappableInsets = Insets.of(oldTappableInsets.left, oldTappableInsets.top,
oldTappableInsets.right, 0);
updatedInsetsBuilder.setInsets(WindowInsets.Type.tappableElement(), newTappableInsets);
return updatedInsetsBuilder.build();
}
}

View File

@@ -188,7 +188,8 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
* Closes any overlays.
*/
public void closeOverlay() {
Optional.ofNullable(getTaskbarController()).ifPresent(TaskbarUIController::hideAllApps);
Optional.ofNullable(getTaskbarController()).ifPresent(
TaskbarUIController::hideOverlayWindow);
}
public void switchRunningTaskViewToScreenshot(HashMap<Integer, ThumbnailData> thumbnailDatas,

View File

@@ -290,10 +290,6 @@ public final class LauncherActivityInterface extends
} else {
om.hideOverlay(150);
}
LauncherTaskbarUIController taskbarController = getTaskbarController();
if (taskbarController != null) {
taskbarController.hideEdu();
}
}
@Override

View File

@@ -94,6 +94,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
public static final int TYPE_TASKBAR_EDUCATION_DIALOG = 1 << 16;
public static final int TYPE_TASKBAR_ALL_APPS = 1 << 17;
public static final int TYPE_ADD_TO_HOME_CONFIRMATION = 1 << 18;
public static final int TYPE_TASKBAR_OVERLAY_PROXY = 1 << 19;
public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET