Merge changes from topics "taskbar3Button", "taskbarIME" into sc-dev

* changes:
  Add IME switcher/hide button bar when IME showing
  Initial commit of 3 button work
This commit is contained in:
Vinit Nayak
2021-05-06 20:47:41 +00:00
committed by Android (Google) Code Review
16 changed files with 698 additions and 26 deletions
@@ -0,0 +1,25 @@
<!--
~ 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
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M19,7h2v2h-2V7zM15,7h2v2h-2V7zM3,7h2v2H3V7zM7,7h2v2H7V7zM11,7h2v2h-2V7zM19,11h2v2h-2V11zM15,11h2v2h-2V11zM3,11h2v2H3V11zM7,11h2v2H7V11zM11,11h2v2h-2V11zM7,15h10v2H7V15z"
android:fillColor="@android:color/white" />
</vector>
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:autoMirrored="true"
android:viewportWidth="28"
android:viewportHeight="28">
<path
android:fillColor="@android:color/white"
android:pathData="M6.49,14.86c-0.66-0.39-0.66-1.34,0-1.73l6.02-3.53l5.89-3.46C19.11,5.73,20,6.26,20,7.1V14v6.9 c0,0.84-0.89,1.37-1.6,0.95l-5.89-3.46L6.49,14.86z" />
</vector>
+27
View File
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28"
android:viewportHeight="28">
<path
android:fillColor="@android:color/white"
android:pathData="M 14 7 C 17.8659932488 7 21 10.1340067512 21 14 C 21 17.8659932488 17.8659932488 21 14 21 C 10.1340067512 21 7 17.8659932488 7 14 C 7 10.1340067512 10.1340067512 7 14 7 Z" />
</vector>
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28"
android:viewportHeight="28">
<path
android:fillColor="@android:color/white"
android:pathData="M19.9,21.5H8.1c-0.88,0-1.6-0.72-1.6-1.6V8.1c0-0.88,0.72-1.6,1.6-1.6h11.8c0.88,0,1.6,0.72,1.6,1.6v11.8 C21.5,20.78,20.78,21.5,19.9,21.5z" />
</vector>
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 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.
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/taskbar_icon_selection_ripple">
<item android:id="@android:id/mask">
<shape android:shape="rectangle">
<solid android:color="@android:color/white" />
<corners android:radius="8dp" />
</shape>
</item>
</ripple>
+6
View File
@@ -26,4 +26,10 @@
android:layout_height="wrap_content"
android:gravity="center"/>
<com.android.launcher3.taskbar.ImeBarView
android:id="@+id/ime_bar_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"/>
</com.android.launcher3.taskbar.TaskbarContainerView>
+1
View File
@@ -27,4 +27,5 @@
<!-- Taskbar -->
<color name="taskbar_background">#101010</color>
<color name="taskbar_icon_selection_ripple">#E0E0E0</color>
</resources>
@@ -31,8 +31,11 @@ import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.ComponentName;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.IBinder;
import android.view.View;
import androidx.annotation.Nullable;
@@ -62,6 +65,7 @@ import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.TouchInteractionService;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.RemoteFadeOutAnimationListener;
import com.android.quickstep.util.SplitSelectStateController;
@@ -83,6 +87,8 @@ public abstract class BaseQuickstepLauncher extends Launcher
private DepthController mDepthController = new DepthController(this);
private QuickstepTransitionManager mAppTransitionManager;
private ServiceConnection mTisBinderConnection;
protected TouchInteractionService.TISBinder mTisBinder;
/**
* Reusable command for applying the back button alpha on the background thread.
@@ -104,6 +110,24 @@ public abstract class BaseQuickstepLauncher extends Launcher
super.onCreate(savedInstanceState);
SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this);
addMultiWindowModeChangedListener(mDepthController);
setupTouchInteractionServiceBinder();
}
private void setupTouchInteractionServiceBinder() {
Intent intent = new Intent(this, TouchInteractionService.class);
mTisBinderConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder binder) {
mTisBinder = ((TouchInteractionService.TISBinder) binder);
mTisBinder.setTaskbarOverviewProxyDelegate(mTaskbarController);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mTisBinder = null;
}
};
bindService(intent, mTisBinderConnection, 0);
}
@Override
@@ -114,6 +138,10 @@ public abstract class BaseQuickstepLauncher extends Launcher
if (mTaskbarController != null) {
mTaskbarController.cleanup();
mTaskbarController = null;
if (mTisBinder != null) {
mTisBinder.setTaskbarOverviewProxyDelegate(null);
unbindService(mTisBinderConnection);
}
}
super.onDestroy();
@@ -249,6 +277,9 @@ public abstract class BaseQuickstepLauncher extends Launcher
private void addTaskbarIfNecessary() {
if (mTaskbarController != null) {
mTaskbarController.cleanup();
if (mTisBinder != null) {
mTisBinder.setTaskbarOverviewProxyDelegate(null);
}
mTaskbarController = null;
}
if (mDeviceProfile.isTaskbarPresent) {
@@ -257,6 +288,9 @@ public abstract class BaseQuickstepLauncher extends Launcher
mTaskbarController = new TaskbarController(this,
taskbarActivityContext.getTaskbarContainerView(), taskbarViewOnHome);
mTaskbarController.init();
if (mTisBinder != null) {
mTisBinder.setTaskbarOverviewProxyDelegate(mTaskbarController);
}
}
}
@@ -0,0 +1,76 @@
/*
* Copyright 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.annotation.DrawableRes;
import android.content.Context;
import android.view.View;
import android.widget.ImageView;
import com.android.launcher3.R;
/**
* Creates Buttons for Taskbar for 3 button nav.
* Can add animations and state management for buttons in this class as things progress.
*/
public class ButtonProvider {
private int mMarginLeftRight;
private final Context mContext;
public ButtonProvider(Context context) {
mContext = context;
}
public void setMarginLeftRight(int margin) {
mMarginLeftRight = margin;
}
public View getBack() {
// Back button
return getButtonForDrawable(R.drawable.ic_sysbar_back);
}
public View getDown() {
// Ime down button
return getButtonForDrawable(R.drawable.ic_sysbar_back);
}
public View getHome() {
// Home button
return getButtonForDrawable(R.drawable.ic_sysbar_home);
}
public View getRecents() {
// Recents button
return getButtonForDrawable(R.drawable.ic_sysbar_recent);
}
public View getImeSwitcher() {
// IME Switcher Button
return getButtonForDrawable(R.drawable.ic_ime_switcher);
}
private View getButtonForDrawable(@DrawableRes int drawableId) {
ImageView buttonView = new ImageView(mContext);
buttonView.setImageResource(drawableId);
buttonView.setBackgroundResource(R.drawable.taskbar_icon_click_feedback_roundrect);
buttonView.setPadding(mMarginLeftRight, 0, mMarginLeftRight, 0);
return buttonView;
}
}
@@ -0,0 +1,92 @@
/*
* Copyright 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.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;
import android.widget.RelativeLayout;
import com.android.launcher3.views.ActivityContext;
public class ImeBarView extends RelativeLayout {
private ButtonProvider mButtonProvider;
private TaskbarController.TaskbarViewCallbacks mControllerCallbacks;
private View mImeView;
public ImeBarView(Context context) {
this(context, null);
}
public ImeBarView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ImeBarView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void construct(ButtonProvider buttonProvider) {
mButtonProvider = buttonProvider;
}
public void init(TaskbarController.TaskbarViewCallbacks taskbarCallbacks) {
mControllerCallbacks = taskbarCallbacks;
ActivityContext context = getActivityContext();
RelativeLayout.LayoutParams imeParams = new RelativeLayout.LayoutParams(
context.getDeviceProfile().iconSizePx,
context.getDeviceProfile().iconSizePx
);
RelativeLayout.LayoutParams downParams = new RelativeLayout.LayoutParams(imeParams);
imeParams.addRule(ALIGN_PARENT_END);
imeParams.setMarginEnd(context.getDeviceProfile().iconSizePx);
downParams.setMarginStart(context.getDeviceProfile().iconSizePx);
downParams.addRule(ALIGN_PARENT_START);
// 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);
}
private <T extends Context & ActivityContext> T getActivityContext() {
return ActivityContext.lookupContext(getContext());
}
}
@@ -44,7 +44,7 @@ public class TaskbarAnimationController {
private final AnimatedFloat mTaskbarVisibilityAlphaForLauncherState = new AnimatedFloat(
this::updateVisibilityAlpha);
private final AnimatedFloat mTaskbarVisibilityAlphaForIme = new AnimatedFloat(
this::updateVisibilityAlpha);
this::updateVisibilityAlphaForIme);
// Scale.
private final AnimatedFloat mTaskbarScaleForLauncherState = new AnimatedFloat(
@@ -110,16 +110,22 @@ public class TaskbarAnimationController {
// We use mTaskbarBackgroundAlpha as a proxy for whether Launcher is resumed/paused, the
// assumption being that Taskbar should always be visible regardless of the current
// LauncherState if Launcher is paused.
float alphaDueToIme = mTaskbarVisibilityAlphaForIme.value;
float alphaDueToLauncher = Math.max(mTaskbarBackgroundAlpha.value,
mTaskbarVisibilityAlphaForLauncherState.value);
float alphaDueToOther = mTaskbarVisibilityAlphaForIme.value;
float taskbarAlpha = alphaDueToLauncher * alphaDueToOther;
float taskbarAlpha = alphaDueToLauncher * alphaDueToIme;
mTaskbarCallbacks.updateTaskbarVisibilityAlpha(taskbarAlpha);
// Make the nav bar invisible if taskbar is visible.
setNavBarButtonAlpha(1f - taskbarAlpha);
}
private void updateVisibilityAlphaForIme() {
updateVisibilityAlpha();
float taskbarAlphaDueToIme = mTaskbarVisibilityAlphaForIme.value;
mTaskbarCallbacks.updateImeBarVisibilityAlpha(1f - taskbarAlphaDueToIme);
}
private void updateScale() {
// We use mTaskbarBackgroundAlpha as a proxy for whether Launcher is resumed/paused, the
// assumption being that Taskbar should always be at scale 1f regardless of the current
@@ -15,6 +15,9 @@
*/
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;
@@ -28,6 +31,7 @@ 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;
@@ -48,9 +52,12 @@ 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;
@@ -58,13 +65,15 @@ import com.android.systemui.shared.system.WindowManagerWrapper;
/**
* Interfaces with Launcher/WindowManager/SystemUI to determine what to show in TaskbarView.
*/
public class TaskbarController {
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.
@@ -73,9 +82,13 @@ public class TaskbarController {
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;
@@ -85,10 +98,14 @@ public class TaskbarController {
mLauncher = launcher;
mTaskbarContainerView = taskbarContainerView;
mTaskbarContainerView.construct(createTaskbarContainerViewCallbacks());
ButtonProvider buttonProvider = new ButtonProvider(launcher);
mTaskbarViewInApp = mTaskbarContainerView.findViewById(R.id.taskbar_view);
mTaskbarViewInApp.construct(createTaskbarViewCallbacks());
mTaskbarViewInApp.construct(createTaskbarViewCallbacks(), buttonProvider);
mTaskbarViewOnHome = taskbarViewOnHome;
mTaskbarViewOnHome.construct(createTaskbarViewCallbacks());
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();
@@ -108,10 +125,20 @@ public class TaskbarController {
@Override
public void updateTaskbarVisibilityAlpha(float alpha) {
mTaskbarContainerView.setAlpha(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);
@@ -136,16 +163,21 @@ public class TaskbarController {
return new TaskbarContainerViewCallbacks() {
@Override
public void onViewRemoved() {
if (mTaskbarContainerView.getChildCount() == 1) {
// Only TaskbarView remains.
setTaskbarWindowFullscreen(false);
// 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() == View.VISIBLE
&& (mTaskbarViewInApp.getVisibility() == VISIBLE
|| mImeBarView.getVisibility() == VISIBLE)
&& !mIsAnimatingToLauncher;
}
};
@@ -198,7 +230,7 @@ public class TaskbarController {
// space so that the others line up with the home screen hotseat.
boolean isOnHomeScreen = taskbarView == mTaskbarViewOnHome
|| mLauncher.hasBeenResumed() || mIsAnimatingToLauncher;
return isOnHomeScreen ? View.INVISIBLE : View.GONE;
return isOnHomeScreen ? INVISIBLE : GONE;
}
@Override
@@ -212,6 +244,11 @@ public class TaskbarController {
alignRealHotseatWithTaskbar();
}
}
@Override
public void onNavigationButtonClick(@TaskbarButton int buttonType) {
mNavButtonController.onButtonClick(buttonType);
}
};
}
@@ -228,9 +265,12 @@ public class TaskbarController {
* Initializes the Taskbar, including adding it to the screen.
*/
public void init() {
mTaskbarViewInApp.init(mHotseatController.getNumHotseatIcons());
mTaskbarViewOnHome.init(mHotseatController.getNumHotseatIcons());
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();
@@ -272,12 +312,15 @@ public class TaskbarController {
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() {
@@ -315,6 +358,12 @@ public class TaskbarController {
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.
*/
@@ -387,6 +436,28 @@ public class TaskbarController {
*/
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);
}
/**
@@ -436,12 +507,17 @@ public class TaskbarController {
private void setWhichTaskbarViewIsVisible(@Nullable TaskbarView visibleTaskbar) {
mTaskbarViewInApp.setVisibility(visibleTaskbar == mTaskbarViewInApp
? View.VISIBLE : View.INVISIBLE);
? VISIBLE : INVISIBLE);
mTaskbarViewOnHome.setVisibility(visibleTaskbar == mTaskbarViewOnHome
? View.VISIBLE : View.INVISIBLE);
? 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.
*/
@@ -485,6 +561,7 @@ public class TaskbarController {
protected interface TaskbarAnimationControllerCallbacks {
void updateTaskbarBackgroundAlpha(float alpha);
void updateTaskbarVisibilityAlpha(float alpha);
void updateImeBarVisibilityAlpha(float alpha);
void updateTaskbarScale(float scale);
void updateTaskbarTranslationY(float translationY);
}
@@ -507,6 +584,7 @@ public class TaskbarController {
/** 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);
}
/**
@@ -0,0 +1,100 @@
/*
* Copyright 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.content.Intent;
import android.view.inputmethod.InputMethodManager;
import androidx.annotation.IntDef;
import com.android.quickstep.OverviewCommandHelper;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TouchInteractionService;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Controller for 3 button mode in the taskbar.
* Handles all the functionality of the various buttons, making/routing the right calls into
* launcher or sysui/system.
*
* TODO: Create callbacks to hook into UI layer since state will change for more context buttons/
* assistant invocation.
*/
public class TaskbarNavButtonController {
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
BUTTON_BACK,
BUTTON_HOME,
BUTTON_RECENTS,
BUTTON_IME_SWITCH
})
public @interface TaskbarButton {}
static final int BUTTON_BACK = 1;
static final int BUTTON_HOME = BUTTON_BACK << 1;
static final int BUTTON_RECENTS = BUTTON_HOME << 1;
static final int BUTTON_IME_SWITCH = BUTTON_RECENTS << 1;
private final Context mContext;
public TaskbarNavButtonController(Context context) {
mContext = context;
}
public void onButtonClick(@TaskbarButton int buttonType) {
switch (buttonType) {
case BUTTON_BACK:
executeBack();
break;
case BUTTON_HOME:
navigateHome();
break;
case BUTTON_RECENTS:
navigateToOverview();;
break;
case BUTTON_IME_SWITCH:
showIMESwitcher();
break;
}
}
private void navigateHome() {
mContext.startActivity(new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
private void navigateToOverview() {
TouchInteractionService.getInstance().getOverviewCommandHelper()
.addCommand(OverviewCommandHelper.TYPE_SHOW);
}
private void executeBack() {
SystemUiProxy.INSTANCE.getNoCreate().onBackPressed();
}
private void showIMESwitcher() {
mContext.getSystemService(InputMethodManager.class).showInputMethodPickerFromSystem(
true /* showAuxiliarySubtypes */, mContext.getDisplayId());
}
}
@@ -15,6 +15,10 @@
*/
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 android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.LayoutTransition;
@@ -24,8 +28,10 @@ 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;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
@@ -45,12 +51,17 @@ import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.SysUINavigationMode;
/**
* Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
*/
public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconParent, Insettable {
private static final boolean ENABLE_THREE_BUTTON_TASKBAR =
SystemProperties.getBoolean("persist.debug.taskbar_three_button", false);
private final int mIconTouchSize;
private final boolean mIsRtl;
private final int mTouchSlop;
@@ -68,15 +79,22 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
private LayoutTransition mLayoutTransition;
private int mHotseatStartIndex;
private int mHotseatEndIndex;
private LinearLayout mButtonRegion;
// Delegate touches to the closest view if within mIconTouchSize.
private boolean mDelegateTargeted;
private View mDelegateView;
// Prevents dispatching touches to children if true
private boolean mTouchEnabled = true;
private boolean mIsDraggingItem;
// 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;
public TaskbarView(@NonNull Context context) {
this(context, null);
}
@@ -100,15 +118,28 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
protected void construct(TaskbarController.TaskbarViewCallbacks taskbarViewCallbacks) {
protected void construct(TaskbarController.TaskbarViewCallbacks taskbarViewCallbacks,
ButtonProvider buttonProvider) {
mControllerCallbacks = taskbarViewCallbacks;
mNonIconScale = mControllerCallbacks.getNonIconScale(this);
mItemMarginLeftRight = getResources().getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
mItemMarginLeftRight = Math.round(mItemMarginLeftRight * mNonIconScale);
mButtonProvider = buttonProvider;
mButtonProvider.setMarginLeftRight(mItemMarginLeftRight);
}
protected void init(int numHotseatIcons) {
mHotseatStartIndex = 0;
protected void init(int numHotseatIcons, SysUINavigationMode.Mode newMode) {
// TODO: check if buttons on left
if (newMode == SysUINavigationMode.Mode.THREE_BUTTONS && ENABLE_THREE_BUTTON_TASKBAR) {
// 3 button
mNavButtonStartIndex = 0;
createNavButtons();
} else {
mNavButtonStartIndex = -1;
removeNavButtons();
}
mHotseatStartIndex = mNavButtonStartIndex + 1;
mHotseatEndIndex = mHotseatStartIndex + numHotseatIcons - 1;
updateHotseatItems(new ItemInfo[numHotseatIcons]);
@@ -185,11 +216,11 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
if (hotseatView == null || hotseatView.getSourceLayoutResId() != expectedLayoutResId
|| needsReinflate) {
removeView(hotseatView);
ActivityContext activityContext = ActivityContext.lookupContext(getContext());
ActivityContext activityContext = getActivityContext();
if (isFolder) {
FolderInfo folderInfo = (FolderInfo) hotseatItemInfo;
FolderIcon folderIcon = FolderIcon.inflateFolderAndIcon(expectedLayoutResId,
ActivityContext.lookupContext(getContext()), this, folderInfo);
getActivityContext(), this, folderInfo);
folderIcon.setTextVisible(false);
hotseatView = folderIcon;
} else {
@@ -243,12 +274,24 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (!mTouchEnabled) {
return true;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean handled = delegateTouchIfNecessary(event);
return super.onTouchEvent(event) || handled;
}
public void setTouchesEnabled(boolean touchEnabled) {
this.mTouchEnabled = touchEnabled;
}
/**
* User touched the Taskbar background. Determine whether the touch is close enough to a view
* that we should forward the touches to it.
@@ -335,12 +378,57 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
return findDelegateView(xInOurCoordinates, yInOurCoorindates) != null;
}
private void removeNavButtons() {
if (mButtonRegion != null) {
mButtonRegion.removeAllViews();
removeView(mButtonRegion);
} // else We've never been in 3 button. Woah Scoob!
}
/**
* Add back/home/recents buttons into a single ViewGroup that will be inserted at
* {@param navButtonStartIndex}
*/
private void createNavButtons() {
ActivityContext context = getActivityContext();
if (mButtonRegion == null) {
mButtonRegion = new LinearLayout(getContext());
} else {
mButtonRegion.removeAllViews();
}
mButtonRegion.setVisibility(VISIBLE);
LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams(
context.getDeviceProfile().iconSizePx,
context.getDeviceProfile().iconSizePx
);
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);
}
@Override
public boolean onDragEvent(DragEvent event) {
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
mIsDraggingItem = true;
AbstractFloatingView.closeAllOpenViews(ActivityContext.lookupContext(getContext()));
AbstractFloatingView.closeAllOpenViews(getActivityContext());
return true;
case DragEvent.ACTION_DRAG_ENDED:
mIsDraggingItem = false;
@@ -407,12 +495,15 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
}
private View inflate(@LayoutRes int layoutResId) {
return ActivityContext.lookupContext(getContext()).getLayoutInflater()
.inflate(layoutResId, this, false);
return getActivityContext().getLayoutInflater().inflate(layoutResId, this, false);
}
@Override
public void setInsets(Rect insets) {
// Ignore, we just implement Insettable to draw behind system insets.
}
private <T extends Context & ActivityContext> T getActivityContext() {
return ActivityContext.lookupContext(getContext());
}
}
@@ -34,6 +34,7 @@ import com.android.launcher3.util.MainThreadInitializedObject;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Observer for the resource config that specifies the navigation bar mode.
@@ -75,7 +76,8 @@ public class SysUINavigationMode {
private int mNavBarGesturalHeight;
private int mNavBarLargerGesturalHeight;
private final List<NavigationModeChangeListener> mChangeListeners = new ArrayList<>();
private final List<NavigationModeChangeListener> mChangeListeners =
new CopyOnWriteArrayList<>();
private final List<OneHandedModeChangeListener> mOneHandedOverlayChangeListeners =
new ArrayList<>();
@@ -52,6 +52,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.Log;
@@ -145,7 +146,22 @@ public class TouchInteractionService extends Service implements PluginListener<O
@Nullable
private OverscrollPlugin mOverscrollPlugin;
private final IBinder mMyBinder = new IOverviewProxy.Stub() {
/**
* Extension of OverviewProxy aidl interface without needing to modify the actual interface.
* This is for methods that need only need local access and not intended to make IPC calls.
*/
public abstract static class TISBinder extends IOverviewProxy.Stub {
public abstract void setTaskbarOverviewProxyDelegate(
@Nullable TaskbarOverviewProxyDelegate i);
}
private final TISBinder mMyBinder = new TISBinder() {
public void setTaskbarOverviewProxyDelegate(
@Nullable TaskbarOverviewProxyDelegate delegate) {
mTaskbarOverviewProxyDelegate = delegate;
}
@BinderThread
public void onInitialize(Bundle bundle) {
@@ -252,20 +268,49 @@ public class TouchInteractionService extends Service implements PluginListener<O
MAIN_EXECUTOR.execute(() -> mDeviceState.setDeferredGestureRegion(region));
}
@Override
public void onSplitScreenSecondaryBoundsChanged(Rect bounds, Rect insets) {
WindowBounds wb = new WindowBounds(bounds, insets);
MAIN_EXECUTOR.execute(() -> SplitScreenBounds.INSTANCE.setSecondaryWindowBounds(wb));
}
@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);
});
}
};
public interface TaskbarOverviewProxyDelegate {
void updateImeStatus(int displayId, int vis, int backDisposition,
boolean showImeSwitcher);
}
private static boolean sConnected = false;
private static TouchInteractionService sInstance;
private static boolean sIsInitialized = false;
private RotationTouchHelper mRotationTouchHelper;
@Nullable
private TaskbarOverviewProxyDelegate mTaskbarOverviewProxyDelegate;
public static boolean isConnected() {
return sConnected;
}
@Nullable
public static TouchInteractionService getInstance() {
return sInstance;
}
public static boolean isInitialized() {
return sIsInitialized;
}
@@ -293,6 +338,10 @@ public class TouchInteractionService extends Service implements PluginListener<O
private DisplayManager mDisplayManager;
public TouchInteractionService() {
sInstance = this;
}
@Override
public void onCreate() {
super.onCreate();
@@ -389,6 +438,10 @@ public class TouchInteractionService extends Service implements PluginListener<O
onOverviewTargetChange(mOverviewComponentObserver.isHomeAndOverviewSame());
}
public OverviewCommandHelper getOverviewCommandHelper() {
return mOverviewCommandHelper;
}
private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
if (!mDeviceState.isUserUnlocked() || mDeviceState.isButtonNavMode()) {
// Skip if not yet unlocked (can't read user shared prefs) or if the current navigation