Merge "Refactored the contextual group of buttons that appear on the nav bar"
This commit is contained in:
committed by
Android (Google) Code Review
commit
4d2eeb5bf9
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user