Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4d2eeb5b authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Refactored the contextual group of buttons that appear on the nav bar"

parents de92184b 25593cc9
Loading
Loading
Loading
Loading
+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();
    }
}
+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);
        }
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -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) {
+44 −139
Original line number Diff line number Diff line
@@ -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
+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