Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java 0 → 100644 +76 −0 Original line number Diff line number Diff line /* * 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(); } } packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java 0 → 100644 +155 −0 Original line number Diff line number Diff line /* * 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); } } } packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +1 −1 Original line number Diff line number Diff line Loading @@ -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) { Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +44 −139 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading Loading @@ -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); Loading @@ -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); } Loading Loading @@ -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; } Loading Loading @@ -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(); Loading Loading @@ -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 Loading Loading @@ -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(); Loading Loading @@ -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() { Loading Loading @@ -897,6 +800,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav DockedStackExistsListener.register(mDockedListener); updateRotatedViews(); reloadNavIcons(); } public void onDarkIntensityChange(float intensity) { Loading Loading @@ -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); Loading Loading @@ -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 Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java 0 → 100644 +61 −0 Original line number Diff line number Diff line /* * 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 */); } } Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java 0 → 100644 +76 −0 Original line number Diff line number Diff line /* * 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(); } }
packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java 0 → 100644 +155 −0 Original line number Diff line number Diff line /* * 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); } } }
packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +1 −1 Original line number Diff line number Diff line Loading @@ -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) { Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +44 −139 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading Loading @@ -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); Loading @@ -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); } Loading Loading @@ -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; } Loading Loading @@ -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(); Loading Loading @@ -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 Loading Loading @@ -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(); Loading Loading @@ -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() { Loading Loading @@ -897,6 +800,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav DockedStackExistsListener.register(mDockedListener); updateRotatedViews(); reloadNavIcons(); } public void onDarkIntensityChange(float intensity) { Loading Loading @@ -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); Loading Loading @@ -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 Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java 0 → 100644 +61 −0 Original line number Diff line number Diff line /* * 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 */); } }