summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java76
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java155
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java183
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java61
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java199
7 files changed, 555 insertions, 140 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java
new file mode 100644
index 000000000000..4f957bfcbbcc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java
@@ -0,0 +1,76 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.annotation.DrawableRes;
+import android.annotation.IdRes;
+import android.content.Context;
+import android.view.View;
+import com.android.systemui.statusbar.policy.KeyButtonDrawable;
+import com.android.systemui.statusbar.policy.KeyButtonView;
+
+/**
+ * Simple contextual button that is added to the {@link ContextualButtonGroup}. Extend if need extra
+ * functionality.
+ */
+public class ContextualButton extends ButtonDispatcher {
+
+ protected final @DrawableRes int mIconResId;
+
+ /**
+ * Create a contextual button that will use a {@link KeyButtonView} and
+ * {@link KeyButtonDrawable} get and show the button from xml to its icon drawable.
+ * @param buttonResId the button view from xml layout
+ * @param iconResId icon resource to be used
+ */
+ public ContextualButton(@IdRes int buttonResId, @DrawableRes int iconResId) {
+ super(buttonResId);
+ mIconResId = iconResId;
+ }
+
+ /**
+ * Reload the drawable from resource id, should reapply the previous dark intensity.
+ */
+ public void updateIcon() {
+ final KeyButtonDrawable currentDrawable = getImageDrawable();
+ KeyButtonDrawable drawable = getNewDrawable();
+ if (currentDrawable != null) {
+ drawable.setDarkIntensity(currentDrawable.getDarkIntensity());
+ }
+ setImageDrawable(drawable);
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ super.setVisibility(visibility);
+
+ // Stop any active animations if hidden
+ final KeyButtonDrawable currentDrawable = getImageDrawable();
+ if (visibility != View.VISIBLE && currentDrawable != null && currentDrawable.canAnimate()) {
+ currentDrawable.clearAnimationCallbacks();
+ currentDrawable.resetAnimation();
+ }
+ }
+
+ protected KeyButtonDrawable getNewDrawable() {
+ return KeyButtonDrawable.create(getContext(), mIconResId, false /* shadow */);
+ }
+
+ protected Context getContext() {
+ return getCurrentView().getContext();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java
new file mode 100644
index 000000000000..1b039663ab73
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java
@@ -0,0 +1,155 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.annotation.IdRes;
+import android.annotation.NonNull;
+import android.view.View;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ContextualButtonGroup extends ButtonDispatcher {
+ private static final int INVALID_INDEX = -1;
+
+ // List of pairs that contains the button and if the button was visible within this group
+ private final List<ButtonData> mButtonData = new ArrayList<>();
+
+ public ContextualButtonGroup(@IdRes int containerId) {
+ super(containerId);
+ }
+
+ /**
+ * 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}.
+ * @param button the button added to the group
+ */
+ public void addButton(@NonNull ContextualButton button) {
+ mButtonData.add(new ButtonData(button));
+ }
+
+ public ContextualButton getContextButton(@IdRes int buttonResId) {
+ int index = getContextButtonIndex(buttonResId);
+ if (index != INVALID_INDEX) {
+ return mButtonData.get(index).button;
+ }
+ return null;
+ }
+
+ public ContextualButton getVisibleContextButton() {
+ for (int i = mButtonData.size() - 1; i >= 0; --i) {
+ if (mButtonData.get(i).markedVisible) {
+ return mButtonData.get(i).button;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Set the visibility of the button by {@param buttonResId} with {@param visible}. Only one
+ * button is shown at a time. The input button will only show up if it has higher priority than
+ * a previous button, otherwise it will be marked as visible and shown later if all higher
+ * priority buttons are invisible. Therefore hiding a button will show the next marked visible
+ * button. This group's view will be visible if at least one button is visible.
+ * @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) {
+ final int index = getContextButtonIndex(buttonResId);
+ if (index == INVALID_INDEX) {
+ throw new RuntimeException("Cannot find the button id of " + buttonResId
+ + " in context group");
+ }
+ setVisibility(View.INVISIBLE);
+ mButtonData.get(index).markedVisible = visible;
+
+ // Make all buttons invisible except the first markedVisible button
+ boolean alreadyFoundVisibleButton = false;
+ int i = mButtonData.size() - 1;
+ for (; i >= 0; --i) {
+ final ButtonData buttonData = mButtonData.get(i);
+ if (!alreadyFoundVisibleButton && buttonData.markedVisible) {
+ buttonData.setVisibility(View.VISIBLE);
+ setVisibility(View.VISIBLE);
+ alreadyFoundVisibleButton = true;
+ } else {
+ buttonData.setVisibility(View.INVISIBLE);
+ }
+ }
+ return mButtonData.get(index).button.getVisibility();
+ }
+
+ /**
+ * See if button is group visible. Group visible determines if a button can be visible when
+ * higher priority buttons go invisible.
+ * @param buttonResId the button to see if it is group visible
+ * @return true if button is group visible
+ */
+ public boolean isButtonVisibleWithinGroup(@IdRes int buttonResId) {
+ final int index = getContextButtonIndex(buttonResId);
+ return index != INVALID_INDEX && mButtonData.get(index).markedVisible;
+ }
+
+ /**
+ * Update all the icons that are attached to this group. This will get all the buttons to update
+ * their icons for their buttons.
+ */
+ public void updateIcons() {
+ for (ButtonData data : mButtonData) {
+ data.button.updateIcon();
+ }
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println("ContextualButtonGroup {");
+ pw.println(" getVisibleContextButton(): " + getVisibleContextButton());
+ pw.println(" isVisible(): " + isVisible());
+ pw.println(" mButtonData [ ");
+ for (int i = mButtonData.size() - 1; i >= 0; --i) {
+ final ButtonData data = mButtonData.get(i);
+ pw.println(" " + i + ": markedVisible=" + data.markedVisible
+ + " visible=" + data.button.getVisibility()
+ + " alpha=" + data.button.getAlpha());
+ }
+ pw.println(" ]");
+ pw.println(" }");
+ }
+
+ private int getContextButtonIndex(@IdRes int buttonResId) {
+ for (int i = 0; i < mButtonData.size(); ++i) {
+ if (mButtonData.get(i).button.getId() == buttonResId) {
+ return i;
+ }
+ }
+ return INVALID_INDEX;
+ }
+
+ private final static class ButtonData {
+ ContextualButton button;
+ boolean markedVisible;
+
+ ButtonData(ContextualButton button) {
+ this.button = button;
+ this.markedVisible = false;
+ }
+
+ void setVisibility(int visiblity) {
+ button.setVisibility(visiblity);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 66486cedcfb5..cbbb0e3dea96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -463,7 +463,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
style = rotationCCW ? R.style.RotateButtonCCWStart0 :
R.style.RotateButtonCWStart0;
}
- mNavigationBarView.updateRotateSuggestionButtonStyle(style, true);
+ mNavigationBarView.updateRotateSuggestionButtonStyle(style);
}
if (mNavigationBarWindowState != WINDOW_STATE_SHOWING) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 9d13ea22b281..aebcb9f27e24 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -36,8 +36,6 @@ import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.drawable.AnimatedVectorDrawable;
-import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -46,7 +44,6 @@ import androidx.annotation.ColorInt;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
-import android.view.ContextThemeWrapper;
import android.view.Display;
import android.view.MotionEvent;
import android.view.Surface;
@@ -59,7 +56,6 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
-import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.DockedStackExistsListener;
import com.android.systemui.Interpolators;
@@ -106,10 +102,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
boolean mVertical;
private int mCurrentRotation = -1;
- boolean mShowMenu;
- boolean mShowAccessibilityButton;
boolean mLongClickableAccessibilityButton;
- boolean mShowRotateButton;
int mDisabledFlags = 0;
int mNavigationIconHints = 0;
@@ -125,10 +118,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
private KeyButtonDrawable mHomeDefaultIcon;
private KeyButtonDrawable mRecentIcon;
private KeyButtonDrawable mDockedIcon;
- private KeyButtonDrawable mImeIcon;
- private KeyButtonDrawable mMenuIcon;
- private KeyButtonDrawable mAccessibilityIcon;
- private KeyButtonDrawable mRotateSuggestionIcon;
private GestureHelper mGestureHelper;
private final DeadZone mDeadZone;
@@ -151,6 +140,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
private boolean mDockedStackExists;
private final SparseArray<ButtonDispatcher> mButtonDispatchers = new SparseArray<>();
+ private final ContextualButtonGroup mContextualButtonGroup;
private Configuration mConfiguration;
private NavigationBarInflaterView mNavigationInflaterView;
@@ -159,8 +149,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
private RecentsOnboarding mRecentsOnboarding;
private NotificationPanelView mPanelView;
- private int mRotateBtnStyle = R.style.RotateButtonCCWStart90;
-
/**
* Helper that is responsible for showing the right toast when a disallowed activity operation
* occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in
@@ -279,17 +267,30 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
Context.WINDOW_SERVICE)).getDefaultDisplay();
mVertical = false;
- mShowMenu = false;
-
- mShowAccessibilityButton = false;
mLongClickableAccessibilityButton = false;
+ // Set up the context group of buttons
+ mContextualButtonGroup = new ContextualButtonGroup(R.id.menu_container);
+ final ContextualButton menuButton = new ContextualButton(R.id.menu,
+ R.drawable.ic_sysbar_menu);
+ final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher,
+ R.drawable.ic_ime_switcher_default);
+ final RotationContextButton rotateSuggestionButton = new RotationContextButton(
+ R.id.rotate_suggestion, R.drawable.ic_sysbar_rotate_button,
+ R.style.RotateButtonCCWStart90);
+ final ContextualButton accessibilityButton =
+ new ContextualButton(R.id.accessibility_button,
+ R.drawable.ic_sysbar_accessibility_button);
+ mContextualButtonGroup.addButton(menuButton);
+ mContextualButtonGroup.addButton(imeSwitcherButton);
+ mContextualButtonGroup.addButton(rotateSuggestionButton);
+ mContextualButtonGroup.addButton(accessibilityButton);
+
mOverviewProxyService = Dependency.get(OverviewProxyService.class);
mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
mConfiguration = new Configuration();
mConfiguration.updateFrom(context.getResources().getConfiguration());
- reloadNavIcons();
mScreenPinningNotify = new ScreenPinningNotify(mContext);
mBarTransitions = new NavigationBarTransitions(this);
@@ -297,14 +298,11 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back));
mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home));
mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps));
- mButtonDispatchers.put(R.id.menu, new ButtonDispatcher(R.id.menu));
- mButtonDispatchers.put(R.id.ime_switcher, new ButtonDispatcher(R.id.ime_switcher));
- mButtonDispatchers.put(R.id.accessibility_button,
- new ButtonDispatcher(R.id.accessibility_button));
- mButtonDispatchers.put(R.id.rotate_suggestion,
- new ButtonDispatcher(R.id.rotate_suggestion));
- mButtonDispatchers.put(R.id.menu_container,
- new ButtonDispatcher(R.id.menu_container));
+ mButtonDispatchers.put(R.id.menu, menuButton);
+ mButtonDispatchers.put(R.id.ime_switcher, imeSwitcherButton);
+ mButtonDispatchers.put(R.id.accessibility_button, accessibilityButton);
+ mButtonDispatchers.put(R.id.rotate_suggestion, rotateSuggestionButton);
+ mButtonDispatchers.put(R.id.menu_container, mContextualButtonGroup);
mDeadZone = new DeadZone(this);
}
@@ -432,10 +430,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
return mButtonDispatchers.get(R.id.rotate_suggestion);
}
- public ButtonDispatcher getMenuContainer() {
- return mButtonDispatchers.get(R.id.menu_container);
- }
-
public SparseArray<ButtonDispatcher> getButtonDispatchers() {
return mButtonDispatchers;
}
@@ -473,14 +467,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
}
if (densityChange || dirChange) {
mRecentIcon = getDrawable(R.drawable.ic_sysbar_recent);
- mMenuIcon = getDrawable(R.drawable.ic_sysbar_menu);
-
- mAccessibilityIcon = getDrawable(R.drawable.ic_sysbar_accessibility_button,
- false /* hasShadow */);
-
- mImeIcon = getDrawable(R.drawable.ic_ime_switcher_default, false /* hasShadow */);
-
- updateRotateSuggestionButtonStyle(mRotateBtnStyle, false);
+ mContextualButtonGroup.updateIcons();
}
if (orientationChange || densityChange || dirChange) {
mBackIcon = getBackDrawable();
@@ -538,19 +525,11 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
}
private KeyButtonDrawable getDrawable(@DrawableRes int icon) {
- return getDrawable(mContext, icon, true /* hasShadow */);
+ return KeyButtonDrawable.create(mContext, icon, true /* hasShadow */);
}
private KeyButtonDrawable getDrawable(@DrawableRes int icon, boolean hasShadow) {
- return getDrawable(mContext, icon, hasShadow);
- }
-
- private KeyButtonDrawable getDrawable(Context ctx, @DrawableRes int icon, boolean hasShadow) {
- 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(mContext, icon, hasShadow);
}
@Override
@@ -609,24 +588,8 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
updateRecentsIcon();
// Update IME button visibility, a11y and rotate button always overrides the appearance
- final boolean showImeButton =
- !mShowAccessibilityButton &&
- !mShowRotateButton &&
- ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0);
- getImeSwitchButton().setVisibility(showImeButton ? View.VISIBLE : View.INVISIBLE);
- getImeSwitchButton().setImageDrawable(mImeIcon);
- updateContextualContainerVisibility();
-
- // Update menu button, visibility logic in method
- setMenuVisibility(mShowMenu, true);
- getMenuButton().setImageDrawable(mMenuIcon);
-
- // Update rotate button, visibility altered by a11y button logic
- getRotateSuggestionButton().setImageDrawable(mRotateSuggestionIcon);
-
- // Update a11y button, visibility logic in state method
- setAccessibilityButtonState(mShowAccessibilityButton, mLongClickableAccessibilityButton);
- getAccessibilityButton().setImageDrawable(mAccessibilityIcon);
+ mContextualButtonGroup.setButtonVisiblity(R.id.ime_switcher,
+ (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0);
mBarTransitions.reapplyDarkIntensity();
@@ -782,88 +745,28 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
}
public void setMenuVisibility(final boolean show) {
- setMenuVisibility(show, false);
+ mContextualButtonGroup.setButtonVisiblity(R.id.menu, show);
}
- public void setMenuVisibility(final boolean show, final boolean force) {
- if (!force && mShowMenu == show) return;
-
- mShowMenu = show;
-
- // Only show Menu if IME switcher, rotate and Accessibility buttons are not shown.
- final boolean shouldShow = mShowMenu &&
- !mShowAccessibilityButton &&
- !mShowRotateButton &&
- ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) == 0);
-
- getMenuButton().setVisibility(shouldShow ? View.VISIBLE : View.INVISIBLE);
- updateContextualContainerVisibility();
+ public void updateRotateSuggestionButtonStyle(@StyleRes int style) {
+ RotationContextButton button = (RotationContextButton) mContextualButtonGroup
+ .getContextButton(R.id.rotate_suggestion);
+ button.setStyle(style);
+ button.updateIcon();
}
public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) {
- mShowAccessibilityButton = visible;
mLongClickableAccessibilityButton = longClickable;
- if (visible) {
- // Accessibility button overrides Menu, IME switcher and rotate buttons.
- setMenuVisibility(false, true);
- getImeSwitchButton().setVisibility(View.INVISIBLE);
- setRotateButtonVisibility(false);
- }
-
- getAccessibilityButton().setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
getAccessibilityButton().setLongClickable(longClickable);
- updateContextualContainerVisibility();
- }
-
- public void updateRotateSuggestionButtonStyle(@StyleRes int style, boolean setIcon) {
- mRotateBtnStyle = style;
- final Context ctx = getContext();
-
- // Use the supplied style to set the icon's rotation parameters
- Context rotateContext = new ContextThemeWrapper(ctx, style);
-
- // Recreate the icon and set it if needed
- float previousIntensity = mRotateSuggestionIcon != null
- ? mRotateSuggestionIcon.getDarkIntensity() : 0;
- mRotateSuggestionIcon = getDrawable(rotateContext, R.drawable.ic_sysbar_rotate_button,
- false /* hasShadow */);
- mRotateSuggestionIcon.setDarkIntensity(previousIntensity);
-
- if (setIcon) getRotateSuggestionButton().setImageDrawable(mRotateSuggestionIcon);
+ mContextualButtonGroup.setButtonVisiblity(R.id.accessibility_button, visible);
}
public int setRotateButtonVisibility(boolean visible) {
- // Never show if a11y is visible
- final boolean adjVisible = visible && !mShowAccessibilityButton;
- final int vis = adjVisible ? View.VISIBLE : View.INVISIBLE;
-
- // No need to do anything if the request matches the current state
- if (vis == getRotateSuggestionButton().getVisibility()) return vis;
-
- getRotateSuggestionButton().setVisibility(vis);
- mShowRotateButton = visible;
- updateContextualContainerVisibility();
-
- // Stop any active animations if hidden
- if (!visible && mRotateSuggestionIcon.canAnimate()) {
- mRotateSuggestionIcon.clearAnimationCallbacks();
- mRotateSuggestionIcon.resetAnimation();
- }
-
- // Hide/restore other button visibility, if necessary
- updateNavButtonIcons();
-
- // Return applied visibility
- return vis;
+ return mContextualButtonGroup.setButtonVisiblity(R.id.rotate_suggestion, visible);
}
- public boolean isRotateButtonVisible() { return mShowRotateButton; }
-
- private void updateContextualContainerVisibility() {
- // Only show the menu container when one of its buttons are visible
- getMenuContainer().setVisibility((mShowAccessibilityButton || mShowRotateButton || mShowMenu
- || (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0)
- ? VISIBLE : INVISIBLE);
+ public boolean isRotateButtonVisible() {
+ return getRotateSuggestionButton().isVisible();
}
void hideRecentsOnboarding() {
@@ -897,6 +800,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
DockedStackExistsListener.register(mDockedListener);
updateRotatedViews();
+ reloadNavIcons();
}
public void onDarkIntensityChange(float intensity) {
@@ -998,7 +902,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
// force the low profile & disabled states into compliance
mBarTransitions.init();
- setMenuVisibility(mShowMenu, true /* force */);
if (DEBUG) {
Log.d(TAG, "reorient(): rot=" + mCurrentRotation);
@@ -1204,17 +1107,19 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
pw.println(String.format(" disabled=0x%08x vertical=%s menu=%s",
mDisabledFlags,
mVertical ? "true" : "false",
- mShowMenu ? "true" : "false"));
+ getMenuButton().isVisible() ? "true" : "false"));
dumpButton(pw, "back", getBackButton());
dumpButton(pw, "home", getHomeButton());
dumpButton(pw, "rcnt", getRecentsButton());
dumpButton(pw, "menu", getMenuButton());
+ dumpButton(pw, "rota", getRotateSuggestionButton());
dumpButton(pw, "a11y", getAccessibilityButton());
- mRecentsOnboarding.dump(pw);
-
pw.println(" }");
+
+ mContextualButtonGroup.dump(pw);
+ mRecentsOnboarding.dump(pw);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
new file mode 100644
index 000000000000..15e189cc8baf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
@@ -0,0 +1,61 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.annotation.DrawableRes;
+import android.annotation.IdRes;
+import android.annotation.NonNull;
+import android.annotation.StyleRes;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.view.ContextThemeWrapper;
+import android.view.View;
+import com.android.systemui.statusbar.policy.KeyButtonDrawable;
+import com.android.systemui.util.Utils;
+
+public class RotationContextButton extends ContextualButton {
+
+ private @StyleRes int mStyleRes;
+
+ public RotationContextButton(@IdRes int buttonResId, @DrawableRes int iconResId,
+ @StyleRes int style) {
+ super(buttonResId, iconResId);
+ mStyleRes = style;
+ }
+
+ public void setStyle(@StyleRes int styleRes) {
+ mStyleRes = styleRes;
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ super.setVisibility(visibility);
+
+ // Start the rotation animation once it becomes visible
+ final KeyButtonDrawable currentDrawable = getImageDrawable();
+ if (visibility == View.VISIBLE && currentDrawable != null) {
+ currentDrawable.resetAnimation();
+ currentDrawable.startAnimation();
+ }
+ }
+
+ @Override
+ protected KeyButtonDrawable getNewDrawable() {
+ Context context = new ContextThemeWrapper(getContext().getApplicationContext(), mStyleRes);
+ return KeyButtonDrawable.create(context, mIconResId, false /* shadow */);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
index 945d9b9ebf6b..2340786d81fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.policy;
import android.animation.ArgbEvaluator;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -35,6 +36,7 @@ import android.graphics.Rect;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.util.FloatProperty;
+import android.view.ContextThemeWrapper;
import com.android.settingslib.Utils;
import com.android.systemui.R;
@@ -388,6 +390,23 @@ public class KeyButtonDrawable extends Drawable {
}
}
+ /**
+ * Creates a KeyButtonDrawable with a shadow given its icon. The tint applied to the drawable
+ * is determined by the dark and light theme given by the context.
+ * @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
+ * @return KeyButtonDrawable
+ */
+ public static KeyButtonDrawable create(@NonNull Context ctx, @DrawableRes int icon,
+ boolean hasShadow) {
+ 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);
+ }
+
public static KeyButtonDrawable create(Context lightContext, Context darkContext,
@DrawableRes int iconResId, boolean hasShadow) {
return create(lightContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java
new file mode 100644
index 000000000000..c792459418f3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java
@@ -0,0 +1,199 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.doReturn;
+
+import android.graphics.drawable.Drawable;
+import android.support.test.filters.SmallTest;
+import android.view.View;
+import com.android.systemui.statusbar.policy.KeyButtonDrawable;
+import com.android.systemui.SysuiTestCase;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.support.test.runner.AndroidJUnit4;
+
+/** atest NavigationBarContextTest */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NavigationBarContextTest extends SysuiTestCase {
+ private static final int GROUP_ID = 1;
+ private static final int BUTTON_0_ID = GROUP_ID + 1;
+ private static final int BUTTON_1_ID = GROUP_ID + 2;
+ private static final int BUTTON_2_ID = GROUP_ID + 3;
+
+ private static final float TEST_DARK_INTENSITY = 0.6f;
+ private static final float DARK_INTENSITY_ERR = 0.0002f;
+ private static final int ICON_RES_ID = 1;
+
+ private ContextualButtonGroup mGroup;
+ private ContextualButton mBtn0;
+ private ContextualButton mBtn1;
+ private ContextualButton mBtn2;
+
+ @Before
+ public void setup() {
+ mGroup = new ContextualButtonGroup(GROUP_ID);
+ mBtn0 = new ContextualButton(BUTTON_0_ID, ICON_RES_ID);
+ mBtn1 = new ContextualButton(BUTTON_1_ID, ICON_RES_ID);
+ mBtn2 = new ContextualButton(BUTTON_2_ID, ICON_RES_ID);
+
+ // Order of adding buttons to group determines the priority, ascending priority order
+ mGroup.addButton(mBtn0);
+ mGroup.addButton(mBtn1);
+ mGroup.addButton(mBtn2);
+ }
+
+ @Test
+ public void testAddGetContextButtons() throws Exception {
+ assertEquals(mBtn0, mGroup.getContextButton(BUTTON_0_ID));
+ assertEquals(mBtn1, mGroup.getContextButton(BUTTON_1_ID));
+ assertEquals(mBtn2, mGroup.getContextButton(BUTTON_2_ID));
+ }
+
+ @Test
+ public void testSetButtonVisibility() throws Exception {
+ assertFalse("By default the group should be invisible.", mGroup.isVisible());
+
+ // Set button 1 to be visible, make sure it is the only visible button
+ showButton(mBtn1);
+ assertFalse(mBtn0.isVisible());
+ assertTrue(mBtn1.isVisible());
+ assertFalse(mBtn2.isVisible());
+
+ // Hide button 1 and make sure the group is also invisible
+ assertNotEquals(mGroup.setButtonVisiblity(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());
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testSetButtonVisibilityUnaddedButton() throws Exception {
+ int id = mBtn2.getId() + 1;
+ mGroup.setButtonVisiblity(id, true /* visible */);
+ fail("Did not throw when setting a button with an invalid id");
+ }
+
+ @Test
+ public void testSetHigherPriorityButton() throws Exception {
+ // Show button 0
+ showButton(mBtn0);
+
+ // Show button 1
+ showButton(mBtn1);
+ assertTrue("Button 0 should be visible behind",
+ mGroup.isButtonVisibleWithinGroup(mBtn0.getId()));
+
+ // Show button 2
+ showButton(mBtn2);
+ assertTrue("Button 1 should be visible behind",
+ mGroup.isButtonVisibleWithinGroup(mBtn1.getId()));
+ assertTrue(mGroup.isButtonVisibleWithinGroup(mBtn0.getId()));
+ assertTrue(mGroup.isButtonVisibleWithinGroup(mBtn1.getId()));
+ assertTrue(mGroup.isButtonVisibleWithinGroup(mBtn2.getId()));
+
+ // Hide button 2
+ assertNotEquals(mGroup.setButtonVisiblity(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);
+ 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);
+ assertFalse("No buttons are visible, group should also be invisible", mGroup.isVisible());
+ assertNull(mGroup.getVisibleContextButton());
+ assertFalse(mGroup.isButtonVisibleWithinGroup(mBtn0.getId()));
+ assertFalse(mGroup.isButtonVisibleWithinGroup(mBtn1.getId()));
+ assertFalse(mGroup.isButtonVisibleWithinGroup(mBtn2.getId()));
+ }
+
+ @Test
+ public void testSetLowerPriorityButton() throws Exception {
+ // Show button 2
+ showButton(mBtn2);
+
+ // Show button 1
+ assertNotEquals(mGroup.setButtonVisiblity(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());
+ assertFalse(mBtn1.isVisible());
+ assertTrue(mBtn2.isVisible());
+
+ // Hide button 1
+ assertNotEquals(mGroup.setButtonVisiblity(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());
+ assertEquals(mBtn2, mGroup.getVisibleContextButton());
+ }
+
+ @Test
+ public void testSetSamePriorityButton() throws Exception {
+ // Show button 1
+ showButton(mBtn1);
+
+ // Show button 1 again
+ showButton(mBtn1);
+
+ // The original button should still be visible
+ assertEquals(mBtn1, mGroup.getVisibleContextButton());
+ assertFalse(mGroup.isButtonVisibleWithinGroup(mBtn0.getId()));
+ assertFalse(mGroup.isButtonVisibleWithinGroup(mBtn2.getId()));
+ }
+
+ @Test
+ public void testUpdateIconsDarkIntensity() throws Exception {
+ final int unusedColor = 0;
+ final Drawable d = mock(Drawable.class);
+ final ContextualButton button = spy(mBtn0);
+ final KeyButtonDrawable kbd1 = spy(new KeyButtonDrawable(d, unusedColor, unusedColor));
+ final KeyButtonDrawable kbd2 = spy(new KeyButtonDrawable(d, unusedColor, unusedColor));
+ kbd1.setDarkIntensity(TEST_DARK_INTENSITY);
+ kbd2.setDarkIntensity(0f);
+
+ // Update icon returns the drawable intensity to half
+ doReturn(kbd1).when(button).getNewDrawable();
+ button.updateIcon();
+ assertEquals(TEST_DARK_INTENSITY, kbd1.getDarkIntensity(), DARK_INTENSITY_ERR);
+
+ // Return old dark intensity on new drawable after update icon
+ doReturn(kbd2).when(button).getNewDrawable();
+ button.updateIcon();
+ assertEquals(TEST_DARK_INTENSITY, kbd2.getDarkIntensity(), DARK_INTENSITY_ERR);
+ }
+
+ private void showButton(ContextualButton button) {
+ assertEquals(View.VISIBLE, mGroup.setButtonVisiblity(button.getId(), true /* visible */));
+ assertTrue("After set a button visible, group should also be visible", mGroup.isVisible());
+ assertEquals(button, mGroup.getVisibleContextButton());
+ }
+}