Add rotate suggestion button to gestural nav.
Fixes: 128942468 Test: 1. In fully gestural mode, rotation/back button is moved to the bottom left corner and keyboard button is moved to the bottom right. When the keyboard is up, rotation button does not show. The rotation button in this setting has an oval background that matches the home handle color. 2. In 2-button and 3-button mode, the rotation button stays the same. Screenshot: https://screenshot.googleplex.com/or2GbOzmZTX Change-Id: I32f2f11d33bf6881a67770679db2118917d2123b
This commit is contained in:
@@ -42,16 +42,10 @@
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="invisible"
|
||||
/>
|
||||
<com.android.systemui.statusbar.policy.KeyButtonView
|
||||
android:id="@+id/rotate_suggestion"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="0"
|
||||
android:scaleType="center"
|
||||
android:visibility="invisible"
|
||||
android:contentDescription="@string/accessibility_rotate_button"
|
||||
android:paddingStart="@dimen/navigation_key_padding"
|
||||
android:paddingEnd="@dimen/navigation_key_padding"
|
||||
<include layout="@layout/rotate_suggestion"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="invisible"
|
||||
/>
|
||||
<com.android.systemui.statusbar.policy.KeyButtonView
|
||||
android:id="@+id/accessibility_button"
|
||||
|
||||
29
packages/SystemUI/res/layout/rotate_suggestion.xml
Normal file
29
packages/SystemUI/res/layout/rotate_suggestion.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2019 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License
|
||||
-->
|
||||
|
||||
<com.android.systemui.statusbar.policy.KeyButtonView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/rotate_suggestion"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="0"
|
||||
android:scaleType="center"
|
||||
android:visibility="invisible"
|
||||
android:contentDescription="@string/accessibility_rotate_button"
|
||||
android:paddingStart="@dimen/navigation_key_padding"
|
||||
android:paddingEnd="@dimen/navigation_key_padding"
|
||||
/>
|
||||
36
packages/SystemUI/res/layout/start_contextual.xml
Normal file
36
packages/SystemUI/res/layout/start_contextual.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2018 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.
|
||||
-->
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:systemui="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/start_menu_container"
|
||||
android:layout_width="@dimen/navigation_key_width"
|
||||
android:layout_height="match_parent"
|
||||
android:importantForAccessibility="no"
|
||||
android:focusable="false"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
>
|
||||
<include layout="@layout/rotate_suggestion"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="invisible"
|
||||
/>
|
||||
<include layout="@layout/back"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="invisible"
|
||||
/>
|
||||
</FrameLayout>
|
||||
@@ -331,7 +331,7 @@
|
||||
<!-- Nav bar button default ordering/layout -->
|
||||
<string name="config_navBarLayout" translatable="false">left[.5W],back[1WC];home;recent[1WC],right[.5W]</string>
|
||||
<string name="config_navBarLayoutQuickstep" translatable="false">back[1.7WC];home;contextual[1.7WC]</string>
|
||||
<string name="config_navBarLayoutHandle" translatable="false">back[1.7WC];home_handle;ime_switcher[1.7WC]</string>
|
||||
<string name="config_navBarLayoutHandle" translatable="false">start_contextual[.1WC];home_handle;ime_switcher[.1WC]</string>
|
||||
|
||||
<bool name="quick_settings_show_full_alarm">false</bool>
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ public class ButtonDispatcher {
|
||||
private Boolean mLongClickable;
|
||||
private Float mAlpha;
|
||||
private Float mDarkIntensity;
|
||||
private Integer mVisibility = -1;
|
||||
private Integer mVisibility = View.VISIBLE;
|
||||
private Boolean mDelayTouchFeedback;
|
||||
private KeyButtonDrawable mImageDrawable;
|
||||
private View mCurrentView;
|
||||
@@ -86,7 +86,7 @@ public class ButtonDispatcher {
|
||||
if (mAlpha != null) {
|
||||
view.setAlpha(mAlpha);
|
||||
}
|
||||
if (mVisibility != null && mVisibility != -1) {
|
||||
if (mVisibility != null) {
|
||||
view.setVisibility(mVisibility);
|
||||
}
|
||||
if (mAccessibilityDelegate != null) {
|
||||
|
||||
@@ -51,7 +51,7 @@ public class ContextualButton extends ButtonDispatcher {
|
||||
* Reload the drawable from resource id, should reapply the previous dark intensity.
|
||||
*/
|
||||
public void updateIcon() {
|
||||
if (getCurrentView() == null || !getCurrentView().isAttachedToWindow()) {
|
||||
if (getCurrentView() == null || !getCurrentView().isAttachedToWindow() || mIconResId == 0) {
|
||||
return;
|
||||
}
|
||||
final KeyButtonDrawable currentDrawable = getImageDrawable();
|
||||
@@ -92,7 +92,7 @@ public class ContextualButton extends ButtonDispatcher {
|
||||
setVisibility(View.VISIBLE);
|
||||
return true;
|
||||
}
|
||||
return mGroup.setButtonVisiblity(getId(), true /* visible */) == View.VISIBLE;
|
||||
return mGroup.setButtonVisibility(getId(), true /* visible */) == View.VISIBLE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,7 +104,7 @@ public class ContextualButton extends ButtonDispatcher {
|
||||
setVisibility(View.INVISIBLE);
|
||||
return false;
|
||||
}
|
||||
return mGroup.setButtonVisiblity(getId(), false /* visible */) != View.VISIBLE;
|
||||
return mGroup.setButtonVisibility(getId(), false /* visible */) != View.VISIBLE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,7 +37,7 @@ public class ContextualButtonGroup extends ButtonDispatcher {
|
||||
/**
|
||||
* Add a contextual button to the group. The order of adding increases in its priority. The
|
||||
* priority is used to determine which button should be visible when setting multiple button's
|
||||
* visibility {@see setButtonVisiblity}.
|
||||
* visibility {@see setButtonVisibility}.
|
||||
* @param button the button added to the group
|
||||
*/
|
||||
public void addButton(@NonNull ContextualButton button) {
|
||||
@@ -71,7 +71,7 @@ public class ContextualButtonGroup extends ButtonDispatcher {
|
||||
* @return if the button is visible after operation
|
||||
* @throws RuntimeException if the input id does not match any of the ids in the group
|
||||
*/
|
||||
public int setButtonVisiblity(@IdRes int buttonResId, boolean visible) {
|
||||
public int setButtonVisibility(@IdRes int buttonResId, boolean visible) {
|
||||
final int index = getContextButtonIndex(buttonResId);
|
||||
if (index == INVALID_INDEX) {
|
||||
throw new RuntimeException("Cannot find the button id of " + buttonResId
|
||||
|
||||
@@ -71,6 +71,7 @@ public class NavigationBarInflaterView extends FrameLayout
|
||||
public static final String RIGHT = "right";
|
||||
public static final String CONTEXTUAL = "contextual";
|
||||
public static final String IME_SWITCHER = "ime_switcher";
|
||||
public static final String START_CONTEXTUAL = "start_contextual";
|
||||
|
||||
public static final String GRAVITY_SEPARATOR = ";";
|
||||
public static final String BUTTON_SEPARATOR = ",";
|
||||
@@ -419,6 +420,8 @@ public class NavigationBarInflaterView extends FrameLayout
|
||||
v = inflater.inflate(R.layout.home_handle, parent, false);
|
||||
} else if (IME_SWITCHER.equals(button)) {
|
||||
v = inflater.inflate(R.layout.ime_switcher, parent, false);
|
||||
} else if (START_CONTEXTUAL.equals(button)) {
|
||||
v = inflater.inflate(R.layout.start_contextual, parent, false);
|
||||
} else if (button.startsWith(KEY)) {
|
||||
String uri = extractImage(button);
|
||||
int code = extractKeycode(button);
|
||||
|
||||
@@ -49,6 +49,8 @@ import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver.InternalInsetsInfo;
|
||||
import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManagerGlobal;
|
||||
@@ -135,6 +137,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
|
||||
private boolean mImeVisible;
|
||||
|
||||
private final SparseArray<ButtonDispatcher> mButtonDispatchers = new SparseArray<>();
|
||||
private final ContextualButtonGroup mStartContextualButtonGroup;
|
||||
private final ContextualButtonGroup mContextualButtonGroup;
|
||||
private Configuration mConfiguration;
|
||||
private Configuration mTmpLastConfiguration;
|
||||
@@ -233,11 +236,36 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
|
||||
}
|
||||
};
|
||||
|
||||
private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener = info -> {
|
||||
// When the nav bar is in 2-button or 3-button mode, or when IME is visible in fully
|
||||
// gestural mode, the entire nav bar should be touchable.
|
||||
if (!QuickStepContract.isGesturalMode(mNavBarMode) || mImeVisible) {
|
||||
info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
|
||||
return;
|
||||
}
|
||||
info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
|
||||
RotationContextButton rotationContextButton = getRotateSuggestionButton();
|
||||
// If the rotate suggestion button is not visible in fully gestural mode, the entire nav bar
|
||||
// is not touchable so that the app underneath can be clicked through.
|
||||
if (rotationContextButton.getVisibility() != VISIBLE) {
|
||||
info.touchableRegion.setEmpty();
|
||||
} else {
|
||||
// Set the rotate suggestion button area to be touchable.
|
||||
rotationContextButton.getCurrentView().getLocationInWindow(mTmpPosition);
|
||||
Rect rect = new Rect(mTmpPosition[0], mTmpPosition[1],
|
||||
mTmpPosition[0] + mRotationButtonBounds.width(),
|
||||
mTmpPosition[1] + mRotationButtonBounds.height());
|
||||
info.touchableRegion.union(rect);
|
||||
}
|
||||
};
|
||||
|
||||
public NavigationBarView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
mIsVertical = false;
|
||||
mLongClickableAccessibilityButton = false;
|
||||
mNavBarMode = Dependency.get(NavigationModeController.class).addListener(this);
|
||||
boolean isGesturalMode = QuickStepContract.isGesturalMode(mNavBarMode);
|
||||
|
||||
// Set up the context group of buttons
|
||||
mContextualButtonGroup = new ContextualButtonGroup(R.id.menu_container);
|
||||
@@ -253,12 +281,21 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
|
||||
R.drawable.ic_sysbar_accessibility_button);
|
||||
mContextualButtonGroup.addButton(menuButton);
|
||||
mContextualButtonGroup.addButton(imeSwitcherButton);
|
||||
mContextualButtonGroup.addButton(rotateSuggestionButton);
|
||||
if (!isGesturalMode) {
|
||||
mContextualButtonGroup.addButton(rotateSuggestionButton);
|
||||
}
|
||||
mContextualButtonGroup.addButton(accessibilityButton);
|
||||
|
||||
mOverviewProxyService = Dependency.get(OverviewProxyService.class);
|
||||
mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
|
||||
|
||||
final ContextualButton backButton = new ContextualButton(R.id.back, 0);
|
||||
mStartContextualButtonGroup = new ContextualButtonGroup(R.id.start_menu_container);
|
||||
if (isGesturalMode) {
|
||||
mStartContextualButtonGroup.addButton(rotateSuggestionButton);
|
||||
}
|
||||
mStartContextualButtonGroup.addButton(backButton);
|
||||
|
||||
mConfiguration = new Configuration();
|
||||
mTmpLastConfiguration = new Configuration();
|
||||
mConfiguration.updateFrom(context.getResources().getConfiguration());
|
||||
@@ -266,7 +303,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
|
||||
mScreenPinningNotify = new ScreenPinningNotify(mContext);
|
||||
mBarTransitions = new NavigationBarTransitions(this);
|
||||
|
||||
mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back));
|
||||
mButtonDispatchers.put(R.id.back, backButton);
|
||||
mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home));
|
||||
mButtonDispatchers.put(R.id.home_handle, new ButtonDispatcher(R.id.home_handle));
|
||||
mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps));
|
||||
@@ -275,6 +312,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
|
||||
mButtonDispatchers.put(R.id.accessibility_button, accessibilityButton);
|
||||
mButtonDispatchers.put(R.id.rotate_suggestion, rotateSuggestionButton);
|
||||
mButtonDispatchers.put(R.id.menu_container, mContextualButtonGroup);
|
||||
mButtonDispatchers.put(R.id.start_menu_container, mStartContextualButtonGroup);
|
||||
mDeadZone = new DeadZone(this);
|
||||
|
||||
mEdgeBackGestureHandler = new EdgeBackGestureHandler(context, mOverviewProxyService);
|
||||
@@ -390,8 +428,11 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
|
||||
}
|
||||
|
||||
public RotationContextButton getRotateSuggestionButton() {
|
||||
return (RotationContextButton) mContextualButtonGroup
|
||||
.getContextButton(R.id.rotate_suggestion);
|
||||
return (RotationContextButton) mButtonDispatchers.get(R.id.rotate_suggestion);
|
||||
}
|
||||
|
||||
public ContextualButtonGroup getStartContextualButtonGroup() {
|
||||
return mStartContextualButtonGroup;
|
||||
}
|
||||
|
||||
public ButtonDispatcher getHomeHandle() {
|
||||
@@ -430,6 +471,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
|
||||
if (densityChange || dirChange) {
|
||||
mRecentIcon = getDrawable(R.drawable.ic_sysbar_recent);
|
||||
mContextualButtonGroup.updateIcons();
|
||||
mStartContextualButtonGroup.updateIcons();
|
||||
}
|
||||
if (orientationChange || densityChange || dirChange) {
|
||||
mBackIcon = getBackDrawable();
|
||||
@@ -437,12 +479,16 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
|
||||
}
|
||||
|
||||
public KeyButtonDrawable getBackDrawable() {
|
||||
KeyButtonDrawable drawable = chooseNavigationIconDrawable(R.drawable.ic_sysbar_back,
|
||||
R.drawable.ic_sysbar_back_quick_step);
|
||||
KeyButtonDrawable drawable = getDrawable(getBackDrawableRes());
|
||||
orientBackButton(drawable);
|
||||
return drawable;
|
||||
}
|
||||
|
||||
public @DrawableRes int getBackDrawableRes() {
|
||||
return chooseNavigationIconDrawableRes(R.drawable.ic_sysbar_back,
|
||||
R.drawable.ic_sysbar_back_quick_step);
|
||||
}
|
||||
|
||||
public KeyButtonDrawable getHomeDrawable() {
|
||||
final boolean quickStepEnabled = mOverviewProxyService.shouldShowSwipeUpUI();
|
||||
KeyButtonDrawable drawable = quickStepEnabled
|
||||
@@ -485,8 +531,13 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
|
||||
|
||||
private KeyButtonDrawable chooseNavigationIconDrawable(@DrawableRes int icon,
|
||||
@DrawableRes int quickStepIcon) {
|
||||
return getDrawable(chooseNavigationIconDrawableRes(icon, quickStepIcon));
|
||||
}
|
||||
|
||||
private @DrawableRes int chooseNavigationIconDrawableRes(@DrawableRes int icon,
|
||||
@DrawableRes int quickStepIcon) {
|
||||
final boolean quickStepEnabled = mOverviewProxyService.shouldShowSwipeUpUI();
|
||||
return quickStepEnabled ? getDrawable(quickStepIcon) : getDrawable(icon);
|
||||
return quickStepEnabled ? quickStepIcon : icon;
|
||||
}
|
||||
|
||||
private KeyButtonDrawable getDrawable(@DrawableRes int icon) {
|
||||
@@ -527,7 +578,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
|
||||
mTransitionListener.onBackAltCleared();
|
||||
}
|
||||
mImeVisible = visible;
|
||||
updateWindowTouchable();
|
||||
}
|
||||
|
||||
public void setDisabledFlags(int disabledFlags) {
|
||||
@@ -564,7 +614,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
|
||||
updateRecentsIcon();
|
||||
|
||||
// Update IME button visibility, a11y and rotate button always overrides the appearance
|
||||
mContextualButtonGroup.setButtonVisiblity(R.id.ime_switcher,
|
||||
mContextualButtonGroup.setButtonVisibility(R.id.ime_switcher,
|
||||
(mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0);
|
||||
|
||||
mBarTransitions.reapplyDarkIntensity();
|
||||
@@ -609,6 +659,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
|
||||
}
|
||||
|
||||
getBackButton().setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE);
|
||||
mStartContextualButtonGroup.setButtonVisibility(R.id.back, !disableBack);
|
||||
getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
|
||||
getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
|
||||
}
|
||||
@@ -714,11 +765,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
|
||||
setWindowFlag(WindowManager.LayoutParams.FLAG_SLIPPERY, slippery);
|
||||
}
|
||||
|
||||
public void updateWindowTouchable() {
|
||||
boolean touchable = mImeVisible || !QuickStepContract.isGesturalMode(mNavBarMode);
|
||||
setWindowFlag(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, !touchable);
|
||||
}
|
||||
|
||||
private void setWindowFlag(int flags, boolean enable) {
|
||||
final ViewGroup navbarView = ((ViewGroup) getParent());
|
||||
if (navbarView == null) {
|
||||
@@ -743,6 +789,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
|
||||
mBarTransitions.onNavigationModeChanged(mNavBarMode);
|
||||
mEdgeBackGestureHandler.onNavigationModeChanged(mNavBarMode);
|
||||
mRecentsOnboarding.onNavigationModeChanged(mNavBarMode);
|
||||
getRotateSuggestionButton().onNavigationModeChanged(mNavBarMode);
|
||||
|
||||
// Color adaption is tied with showing home handle, only avaliable if visible
|
||||
mTintController.onNavigationModeChanged(mNavBarMode);
|
||||
@@ -751,17 +798,16 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
|
||||
} else {
|
||||
mTintController.stop();
|
||||
}
|
||||
updateWindowTouchable();
|
||||
}
|
||||
|
||||
public void setMenuVisibility(final boolean show) {
|
||||
mContextualButtonGroup.setButtonVisiblity(R.id.menu, show);
|
||||
mContextualButtonGroup.setButtonVisibility(R.id.menu, show);
|
||||
}
|
||||
|
||||
public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) {
|
||||
mLongClickableAccessibilityButton = longClickable;
|
||||
getAccessibilityButton().setLongClickable(longClickable);
|
||||
mContextualButtonGroup.setButtonVisiblity(R.id.accessibility_button, visible);
|
||||
mContextualButtonGroup.setButtonVisibility(R.id.accessibility_button, visible);
|
||||
}
|
||||
|
||||
void hideRecentsOnboarding() {
|
||||
@@ -1044,12 +1090,11 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
|
||||
onPluginDisconnected(null); // Create default gesture helper
|
||||
Dependency.get(PluginManager.class).addPluginListener(this,
|
||||
NavGesture.class, false /* Only one */);
|
||||
int navBarMode = Dependency.get(NavigationModeController.class).addListener(this);
|
||||
onNavigationModeChanged(navBarMode);
|
||||
onNavigationModeChanged(mNavBarMode);
|
||||
setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());
|
||||
|
||||
mEdgeBackGestureHandler.onNavBarAttached();
|
||||
updateWindowTouchable();
|
||||
getViewTreeObserver().addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1065,6 +1110,8 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
|
||||
mButtonDispatchers.valueAt(i).onDestroy();
|
||||
}
|
||||
mEdgeBackGestureHandler.onNavBarDetached();
|
||||
getViewTreeObserver().removeOnComputeInternalInsetsListener(
|
||||
mOnComputeInternalInsetsListener);
|
||||
}
|
||||
|
||||
private void setUpSwipeUpOnboarding(boolean connectedToOverviewProxy) {
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.systemui.statusbar.phone;
|
||||
|
||||
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
|
||||
|
||||
import static com.android.internal.view.RotationPolicy.NATURAL_ROTATION;
|
||||
|
||||
import android.animation.Animator;
|
||||
@@ -45,6 +47,7 @@ import com.android.systemui.Dependency;
|
||||
import com.android.systemui.Interpolators;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.QuickStepContract;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListener;
|
||||
import com.android.systemui.statusbar.policy.KeyButtonDrawable;
|
||||
import com.android.systemui.statusbar.policy.RotationLockController;
|
||||
@@ -52,7 +55,9 @@ import com.android.systemui.statusbar.policy.RotationLockController;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class RotationContextButton extends ContextualButton {
|
||||
/** Containing logic for the rotation button in nav bar. */
|
||||
public class RotationContextButton extends ContextualButton implements
|
||||
NavigationModeController.ModeChangedListener {
|
||||
public static final boolean DEBUG_ROTATION = false;
|
||||
|
||||
private static final int BUTTON_FADE_IN_OUT_DURATION_MS = 100;
|
||||
@@ -76,6 +81,7 @@ public class RotationContextButton extends ContextualButton {
|
||||
() -> mPendingRotationSuggestion = false;
|
||||
private Animator mRotateHideAnimator;
|
||||
private boolean mAccessibilityFeedbackEnabled;
|
||||
private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
|
||||
|
||||
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
|
||||
private final ViewRippler mViewRippler = new ViewRippler();
|
||||
@@ -304,7 +310,8 @@ public class RotationContextButton extends ContextualButton {
|
||||
@Override
|
||||
protected KeyButtonDrawable getNewDrawable() {
|
||||
Context context = new ContextThemeWrapper(getContext().getApplicationContext(), mStyleRes);
|
||||
return KeyButtonDrawable.create(context, mIconResId, false /* shadow */);
|
||||
return KeyButtonDrawable.create(context, mIconResId, false /* shadow */,
|
||||
QuickStepContract.isGesturalMode(mNavBarMode));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -390,9 +397,9 @@ public class RotationContextButton extends ContextualButton {
|
||||
}
|
||||
|
||||
private int computeRotationProposalTimeout() {
|
||||
if (mAccessibilityFeedbackEnabled) return 20000;
|
||||
if (mHoveringRotationSuggestion) return 16000;
|
||||
return 10000;
|
||||
if (mAccessibilityFeedbackEnabled) return 10000;
|
||||
if (mHoveringRotationSuggestion) return 8000;
|
||||
return 5000;
|
||||
}
|
||||
|
||||
private boolean isRotateSuggestionIntroduced() {
|
||||
@@ -414,6 +421,11 @@ public class RotationContextButton extends ContextualButton {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNavigationModeChanged(int mode) {
|
||||
mNavBarMode = mode;
|
||||
}
|
||||
|
||||
private class TaskStackListenerImpl extends TaskStackChangeListener {
|
||||
// Invalidate any rotation suggestion on task change or activity orientation change
|
||||
// Note: all callbacks happen on main thread
|
||||
|
||||
@@ -30,9 +30,11 @@ import android.graphics.Color;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuff.Mode;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.AnimatedVectorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.FloatProperty;
|
||||
@@ -77,13 +79,14 @@ public class KeyButtonDrawable extends Drawable {
|
||||
|
||||
private final Paint mIconPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
|
||||
private final Paint mShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
|
||||
private final Paint mOvalBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
|
||||
private final ShadowDrawableState mState;
|
||||
private AnimatedVectorDrawable mAnimatedDrawable;
|
||||
|
||||
public KeyButtonDrawable(Drawable d, @ColorInt int lightColor, @ColorInt int darkColor,
|
||||
boolean horizontalFlip) {
|
||||
boolean horizontalFlip, boolean hasOvalBg) {
|
||||
this(d, new ShadowDrawableState(lightColor, darkColor,
|
||||
d instanceof AnimatedVectorDrawable, horizontalFlip));
|
||||
d instanceof AnimatedVectorDrawable, horizontalFlip, hasOvalBg));
|
||||
}
|
||||
|
||||
private KeyButtonDrawable(Drawable d, ShadowDrawableState state) {
|
||||
@@ -98,6 +101,7 @@ public class KeyButtonDrawable extends Drawable {
|
||||
mAnimatedDrawable = (AnimatedVectorDrawable) mState.mChildState.newDrawable().mutate();
|
||||
setDrawableBounds(mAnimatedDrawable);
|
||||
}
|
||||
mOvalBgPaint.setColor(mState.mDarkColor);
|
||||
}
|
||||
|
||||
public void setDarkIntensity(float intensity) {
|
||||
@@ -165,7 +169,12 @@ public class KeyButtonDrawable extends Drawable {
|
||||
public void setColorFilter(ColorFilter colorFilter) {
|
||||
mIconPaint.setColorFilter(colorFilter);
|
||||
if (mAnimatedDrawable != null) {
|
||||
mAnimatedDrawable.setColorFilter(colorFilter);
|
||||
if (mState.mHasOvalBg) {
|
||||
mAnimatedDrawable.setColorFilter(
|
||||
new PorterDuffColorFilter(mState.mLightColor, PorterDuff.Mode.SRC_IN));
|
||||
} else {
|
||||
mAnimatedDrawable.setColorFilter(colorFilter);
|
||||
}
|
||||
}
|
||||
invalidateSelf();
|
||||
}
|
||||
@@ -235,6 +244,10 @@ public class KeyButtonDrawable extends Drawable {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mState.mHasOvalBg) {
|
||||
canvas.drawOval(new RectF(bounds), mOvalBgPaint);
|
||||
}
|
||||
|
||||
if (mAnimatedDrawable != null) {
|
||||
mAnimatedDrawable.draw(canvas);
|
||||
} else {
|
||||
@@ -379,14 +392,16 @@ public class KeyButtonDrawable extends Drawable {
|
||||
final int mLightColor;
|
||||
final int mDarkColor;
|
||||
final boolean mSupportsAnimation;
|
||||
final boolean mHasOvalBg;
|
||||
|
||||
public ShadowDrawableState(@ColorInt int lightColor, @ColorInt int darkColor,
|
||||
boolean animated, boolean horizontalFlip) {
|
||||
boolean animated, boolean horizontalFlip, boolean hasOvalBg) {
|
||||
mLightColor = lightColor;
|
||||
mDarkColor = darkColor;
|
||||
mSupportsAnimation = animated;
|
||||
mAlpha = 255;
|
||||
mHorizontalFlip = horizontalFlip;
|
||||
mHasOvalBg = hasOvalBg;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -411,32 +426,51 @@ public class KeyButtonDrawable extends Drawable {
|
||||
* @param ctx Context to get the drawable and determine the dark and light theme
|
||||
* @param icon the icon resource id
|
||||
* @param hasShadow if a shadow will appear with the drawable
|
||||
* @param hasOvalBg if an oval bg will be drawn
|
||||
* @return KeyButtonDrawable
|
||||
*/
|
||||
public static KeyButtonDrawable create(@NonNull Context ctx, @DrawableRes int icon,
|
||||
boolean hasShadow) {
|
||||
boolean hasShadow, boolean hasOvalBg) {
|
||||
final int dualToneDarkTheme = Utils.getThemeAttr(ctx, R.attr.darkIconTheme);
|
||||
final int dualToneLightTheme = Utils.getThemeAttr(ctx, R.attr.lightIconTheme);
|
||||
Context lightContext = new ContextThemeWrapper(ctx, dualToneLightTheme);
|
||||
Context darkContext = new ContextThemeWrapper(ctx, dualToneDarkTheme);
|
||||
return KeyButtonDrawable.create(lightContext, darkContext, icon, hasShadow);
|
||||
return KeyButtonDrawable.create(lightContext, darkContext, icon, hasShadow, hasOvalBg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a KeyButtonDrawable with a shadow given its icon. For more information, see
|
||||
* {@link #create(Context, int, boolean, boolean)}.
|
||||
*/
|
||||
public static KeyButtonDrawable create(@NonNull Context ctx, @DrawableRes int icon,
|
||||
boolean hasShadow) {
|
||||
return create(ctx, icon, hasShadow, false /* hasOvalBg */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a KeyButtonDrawable with a shadow given its icon. For more information, see
|
||||
* {@link #create(Context, int, boolean, boolean)}.
|
||||
*/
|
||||
public static KeyButtonDrawable create(Context lightContext, Context darkContext,
|
||||
@DrawableRes int iconResId, boolean hasShadow) {
|
||||
@DrawableRes int iconResId, boolean hasShadow, boolean hasOvalBg) {
|
||||
return create(lightContext,
|
||||
Utils.getColorAttrDefaultColor(lightContext, R.attr.singleToneColor),
|
||||
Utils.getColorAttrDefaultColor(darkContext, R.attr.singleToneColor),
|
||||
iconResId, hasShadow);
|
||||
iconResId, hasShadow, hasOvalBg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a KeyButtonDrawable with a shadow given its icon. For more information, see
|
||||
* {@link #create(Context, int, boolean, boolean)}.
|
||||
*/
|
||||
public static KeyButtonDrawable create(Context context, @ColorInt int lightColor,
|
||||
@ColorInt int darkColor, @DrawableRes int iconResId, boolean hasShadow) {
|
||||
@ColorInt int darkColor, @DrawableRes int iconResId, boolean hasShadow,
|
||||
boolean hasOvalBg) {
|
||||
final Resources res = context.getResources();
|
||||
boolean isRtl = res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
|
||||
Drawable d = context.getDrawable(iconResId);
|
||||
final KeyButtonDrawable drawable = new KeyButtonDrawable(d, lightColor, darkColor,
|
||||
isRtl && d.isAutoMirrored());
|
||||
isRtl && d.isAutoMirrored(), hasOvalBg);
|
||||
if (hasShadow) {
|
||||
int offsetX = res.getDimensionPixelSize(R.dimen.nav_key_button_shadow_offset_x);
|
||||
int offsetY = res.getDimensionPixelSize(R.dimen.nav_key_button_shadow_offset_y);
|
||||
|
||||
@@ -80,7 +80,7 @@ public class NavigationBarContextTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void testSetButtonVisibility() throws Exception {
|
||||
assertFalse("By default the group should be invisible.", mGroup.isVisible());
|
||||
assertTrue("By default the group should be visible.", mGroup.isVisible());
|
||||
|
||||
// Set button 1 to be visible, make sure it is the only visible button
|
||||
showButton(mBtn1);
|
||||
@@ -89,7 +89,7 @@ public class NavigationBarContextTest extends SysuiTestCase {
|
||||
assertFalse(mBtn2.isVisible());
|
||||
|
||||
// Hide button 1 and make sure the group is also invisible
|
||||
assertNotEquals(mGroup.setButtonVisiblity(BUTTON_1_ID, false /* visible */), View.VISIBLE);
|
||||
assertNotEquals(mGroup.setButtonVisibility(BUTTON_1_ID, false /* visible */), View.VISIBLE);
|
||||
assertFalse("No buttons are visible, group should also be hidden", mGroup.isVisible());
|
||||
assertNull("No buttons should be visible", mGroup.getVisibleContextButton());
|
||||
}
|
||||
@@ -97,7 +97,7 @@ public class NavigationBarContextTest extends SysuiTestCase {
|
||||
@Test(expected = RuntimeException.class)
|
||||
public void testSetButtonVisibilityUnaddedButton() throws Exception {
|
||||
int id = mBtn2.getId() + 1;
|
||||
mGroup.setButtonVisiblity(id, true /* visible */);
|
||||
mGroup.setButtonVisibility(id, true /* visible */);
|
||||
fail("Did not throw when setting a button with an invalid id");
|
||||
}
|
||||
|
||||
@@ -120,17 +120,17 @@ public class NavigationBarContextTest extends SysuiTestCase {
|
||||
assertTrue(mGroup.isButtonVisibleWithinGroup(mBtn2.getId()));
|
||||
|
||||
// Hide button 2
|
||||
assertNotEquals(mGroup.setButtonVisiblity(BUTTON_2_ID, false /* visible */), View.VISIBLE);
|
||||
assertNotEquals(mGroup.setButtonVisibility(BUTTON_2_ID, false /* visible */), View.VISIBLE);
|
||||
assertEquals("Hiding button 2 should show button 1", mBtn1,
|
||||
mGroup.getVisibleContextButton());
|
||||
|
||||
// Hide button 1
|
||||
assertNotEquals(mGroup.setButtonVisiblity(BUTTON_1_ID, false /* visible */), View.VISIBLE);
|
||||
assertNotEquals(mGroup.setButtonVisibility(BUTTON_1_ID, false /* visible */), View.VISIBLE);
|
||||
assertEquals("Hiding button 1 should show button 0", mBtn0,
|
||||
mGroup.getVisibleContextButton());
|
||||
|
||||
// Hide button 0, all buttons are now invisible
|
||||
assertNotEquals(mGroup.setButtonVisiblity(BUTTON_0_ID, false /* visible */), View.VISIBLE);
|
||||
assertNotEquals(mGroup.setButtonVisibility(BUTTON_0_ID, false /* visible */), View.VISIBLE);
|
||||
assertFalse("No buttons are visible, group should also be invisible", mGroup.isVisible());
|
||||
assertNull(mGroup.getVisibleContextButton());
|
||||
assertFalse(mGroup.isButtonVisibleWithinGroup(mBtn0.getId()));
|
||||
@@ -144,7 +144,7 @@ public class NavigationBarContextTest extends SysuiTestCase {
|
||||
showButton(mBtn2);
|
||||
|
||||
// Show button 1
|
||||
assertNotEquals(mGroup.setButtonVisiblity(BUTTON_1_ID, true /* visible */), View.VISIBLE);
|
||||
assertNotEquals(mGroup.setButtonVisibility(BUTTON_1_ID, true /* visible */), View.VISIBLE);
|
||||
assertTrue("Showing button 1 lower priority should be hidden but visible underneath",
|
||||
mGroup.isButtonVisibleWithinGroup(BUTTON_1_ID));
|
||||
assertFalse(mBtn0.isVisible());
|
||||
@@ -152,7 +152,7 @@ public class NavigationBarContextTest extends SysuiTestCase {
|
||||
assertTrue(mBtn2.isVisible());
|
||||
|
||||
// Hide button 1
|
||||
assertNotEquals(mGroup.setButtonVisiblity(BUTTON_1_ID, false /* visible */), View.VISIBLE);
|
||||
assertNotEquals(mGroup.setButtonVisibility(BUTTON_1_ID, false /* visible */), View.VISIBLE);
|
||||
assertFalse("Hiding button 1 with lower priority hides itself underneath",
|
||||
mGroup.isButtonVisibleWithinGroup(BUTTON_1_ID));
|
||||
assertTrue("A button still visible, group should also be visible", mGroup.isVisible());
|
||||
@@ -180,9 +180,9 @@ public class NavigationBarContextTest extends SysuiTestCase {
|
||||
final Drawable d = mock(Drawable.class);
|
||||
final ContextualButton button = spy(mBtn0);
|
||||
final KeyButtonDrawable kbd1 = spy(new KeyButtonDrawable(d, unusedColor, unusedColor,
|
||||
false /* horizontalFlip */));
|
||||
false /* horizontalFlip */, false /* hasOvalBg */));
|
||||
final KeyButtonDrawable kbd2 = spy(new KeyButtonDrawable(d, unusedColor, unusedColor,
|
||||
false /* horizontalFlip */));
|
||||
false /* horizontalFlip */, false /* hasOvalBg */));
|
||||
kbd1.setDarkIntensity(TEST_DARK_INTENSITY);
|
||||
kbd2.setDarkIntensity(0f);
|
||||
|
||||
@@ -198,7 +198,7 @@ public class NavigationBarContextTest extends SysuiTestCase {
|
||||
}
|
||||
|
||||
private void showButton(ContextualButton button) {
|
||||
assertEquals(View.VISIBLE, mGroup.setButtonVisiblity(button.getId(), true /* visible */));
|
||||
assertEquals(View.VISIBLE, mGroup.setButtonVisibility(button.getId(), true /* visible */));
|
||||
assertTrue("After set a button visible, group should also be visible", mGroup.isVisible());
|
||||
assertEquals(button, mGroup.getVisibleContextButton());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user