diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml index 8dab9150c4..d95cc01427 100644 --- a/quickstep/AndroidManifest.xml +++ b/quickstep/AndroidManifest.xml @@ -32,6 +32,7 @@ + + + + + + + + \ No newline at end of file diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml index 449fe108ef..3bc8ddc0a8 100644 --- a/quickstep/res/values/colors.xml +++ b/quickstep/res/values/colors.xml @@ -24,4 +24,7 @@ #61FFFFFF #3c000000 #3cffffff + + + #101010 \ No newline at end of file diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 551f7b0eb1..4272f50b56 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -119,4 +119,7 @@ 80dp + + + 48dp diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index 3643d6d7e3..2518f423ca 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -19,6 +19,7 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON; import static com.android.launcher3.LauncherState.FLAG_HIDE_BACK_BUTTON; import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.util.DisplayController.DisplayHolder.CHANGE_SIZE; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY; @@ -29,8 +30,11 @@ import android.content.Intent; import android.content.IntentSender; import android.os.Bundle; import android.os.CancellationSignal; +import android.view.LayoutInflater; import android.view.View; +import androidx.annotation.Nullable; + import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.WellbeingModel; import com.android.launcher3.popup.SystemShortcut; @@ -39,7 +43,10 @@ import com.android.launcher3.proxy.StartActivityParams; import com.android.launcher3.statehandlers.BackButtonAlphaHandler; import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.statemanager.StateManager.StateHandler; +import com.android.launcher3.taskbar.TaskbarContainerView; +import com.android.launcher3.taskbar.TaskbarController; import com.android.launcher3.uioverrides.RecentsViewStateController; +import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.UiThreadHelper; import com.android.quickstep.RecentsModel; import com.android.quickstep.SysUINavigationMode; @@ -53,7 +60,6 @@ import com.android.quickstep.views.OverviewActionsView; import com.android.quickstep.views.RecentsView; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.ActivityOptionsCompat; -import com.android.systemui.shared.system.InteractionJankMonitorWrapper; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import java.util.stream.Stream; @@ -75,6 +81,8 @@ public abstract class BaseQuickstepLauncher extends Launcher private OverviewActionsView mActionsView; + private @Nullable TaskbarController mTaskbarController; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -86,6 +94,11 @@ public abstract class BaseQuickstepLauncher extends Launcher @Override public void onDestroy() { SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this); + + if (mTaskbarController != null) { + mTaskbarController.cleanup(); + } + super.onDestroy(); } @@ -190,6 +203,29 @@ public abstract class BaseQuickstepLauncher extends Launcher mActionsView = findViewById(R.id.overview_actions_view); ((RecentsView) getOverviewPanel()).init(mActionsView); mActionsView.updateVerticalMargin(SysUINavigationMode.getMode(this)); + + addTaskbarIfNecessary(); + } + + @Override + public void onDisplayInfoChanged(DisplayController.Info info, int flags) { + super.onDisplayInfoChanged(info, flags); + if ((flags & CHANGE_SIZE) != 0) { + addTaskbarIfNecessary(); + } + } + + private void addTaskbarIfNecessary() { + if (mTaskbarController != null) { + mTaskbarController.cleanup(); + mTaskbarController = null; + } + if (FeatureFlags.ENABLE_TASKBAR.get() && mDeviceProfile.isTablet) { + TaskbarContainerView taskbarContainer = (TaskbarContainerView) LayoutInflater.from(this) + .inflate(R.layout.taskbar, null, false); + mTaskbarController = new TaskbarController(this, taskbarContainer); + mTaskbarController.init(); + } } public T getActionsView() { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java new file mode 100644 index 0000000000..0093e66859 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java @@ -0,0 +1,46 @@ +/* + * 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.util.AttributeSet; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +/** + * Top-level ViewGroup that hosts the TaskbarView as well as Views created by it such as Folder. + */ +public class TaskbarContainerView extends FrameLayout { + 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, defStyleAttr, defStyleRes); + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java new file mode 100644 index 0000000000..7be1b9275a --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java @@ -0,0 +1,111 @@ +/* + * 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.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.graphics.PixelFormat; +import android.graphics.Point; +import android.view.Gravity; +import android.view.WindowManager; + +import com.android.launcher3.BaseQuickstepLauncher; +import com.android.launcher3.R; +import com.android.systemui.shared.system.WindowManagerWrapper; + +/** + * Interfaces with Launcher/WindowManager/SystemUI to determine what to show in TaskbarView. + */ +public class TaskbarController { + + private static final String WINDOW_TITLE = "Taskbar"; + + private final TaskbarContainerView mTaskbarContainerView; + private final TaskbarView mTaskbarView; + private final BaseQuickstepLauncher mLauncher; + private final WindowManager mWindowManager; + // Layout width and height of the Taskbar in the default state. + private final Point mTaskbarSize; + + private WindowManager.LayoutParams mWindowLayoutParams; + + public TaskbarController(BaseQuickstepLauncher launcher, + TaskbarContainerView taskbarContainerView) { + mLauncher = launcher; + mTaskbarContainerView = taskbarContainerView; + mTaskbarView = mTaskbarContainerView.findViewById(R.id.taskbar_view); + mWindowManager = mLauncher.getWindowManager(); + mTaskbarSize = new Point(MATCH_PARENT, + mLauncher.getResources().getDimensionPixelSize(R.dimen.taskbar_size)); + } + + /** + * Initializes the Taskbar, including adding it to the screen. + */ + public void init() { + addToWindowManager(); + } + + /** + * Removes the Taskbar from the screen, and removes any obsolete listeners etc. + */ + public void cleanup() { + removeFromWindowManager(); + } + + private void removeFromWindowManager() { + if (mTaskbarContainerView.isAttachedToWindow()) { + mWindowManager.removeViewImmediate(mTaskbarContainerView); + } + } + + private void addToWindowManager() { + removeFromWindowManager(); + + 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; + + 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; + mTaskbarView.setLayoutParams(taskbarLayoutParams); + + mWindowManager.addView(mTaskbarContainerView, mWindowLayoutParams); + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java new file mode 100644 index 0000000000..5df8d5f9cc --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -0,0 +1,46 @@ +/* + * 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.util.AttributeSet; +import android.widget.LinearLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +/** + * Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps. + */ +public class TaskbarView extends LinearLayout { + public TaskbarView(@NonNull Context context) { + this(context, null); + } + + public TaskbarView(@NonNull Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public TaskbarView(@NonNull Context context, @Nullable AttributeSet attrs, + int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public TaskbarView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } +} diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 81174d814d..1c5c222084 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -214,6 +214,9 @@ public final class FeatureFlags { "ENABLE_APP_PREDICTIONS_WHILE_VISIBLE", true, "Allows app " + "predictions to be updated while they are visible to the user."); + public static final BooleanFlag ENABLE_TASKBAR = new DeviceFlag( + "ENABLE_TASKBAR", false, "Allows a system Taskbar to be shown on larger devices."); + public static void initialize(Context context) { synchronized (sDebugFlags) { for (DebugFlag flag : sDebugFlags) {