1dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk/*
2702e2ebf9fbfb715c1d9b716683c9e4d182b575eJason Monk * Copyright (C) 2017 The Android Open Source Project
3dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk *
4702e2ebf9fbfb715c1d9b716683c9e4d182b575eJason Monk * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5702e2ebf9fbfb715c1d9b716683c9e4d182b575eJason Monk * except in compliance with the License. You may obtain a copy of the License at
6dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk *
7dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk *      http://www.apache.org/licenses/LICENSE-2.0
8dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk *
9702e2ebf9fbfb715c1d9b716683c9e4d182b575eJason Monk * Unless required by applicable law or agreed to in writing, software distributed under the
10702e2ebf9fbfb715c1d9b716683c9e4d182b575eJason Monk * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11702e2ebf9fbfb715c1d9b716683c9e4d182b575eJason Monk * KIND, either express or implied. See the License for the specific language governing
12702e2ebf9fbfb715c1d9b716683c9e4d182b575eJason Monk * permissions and limitations under the License.
13dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk */
14dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk
15702e2ebf9fbfb715c1d9b716683c9e4d182b575eJason Monkpackage com.android.systemui.qs.tileimpl;
16dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk
17702e2ebf9fbfb715c1d9b716683c9e4d182b575eJason Monkimport static com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState;
183250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk
193250885c9feb9b42d60058a823c57a027c0dcf58Jason Monkimport android.animation.ValueAnimator;
20dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monkimport android.content.Context;
213250885c9feb9b42d60058a823c57a027c0dcf58Jason Monkimport android.content.res.ColorStateList;
22dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monkimport android.content.res.Resources;
233250885c9feb9b42d60058a823c57a027c0dcf58Jason Monkimport android.graphics.Color;
24e645aee45a7784d772e4bdf846dd2e77335f30f2Jason Monkimport android.graphics.drawable.Animatable2;
25e645aee45a7784d772e4bdf846dd2e77335f30f2Jason Monkimport android.graphics.drawable.Animatable2.AnimationCallback;
26dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monkimport android.graphics.drawable.Drawable;
27dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monkimport android.view.View;
28dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monkimport android.widget.ImageView;
29eae7c318864bcfd814c8be697315f829952ccc69Jason Monkimport android.widget.ImageView.ScaleType;
303250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk
31dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monkimport com.android.systemui.R;
32702e2ebf9fbfb715c1d9b716683c9e4d182b575eJason Monkimport com.android.systemui.plugins.qs.QSIconView;
33702e2ebf9fbfb715c1d9b716683c9e4d182b575eJason Monkimport com.android.systemui.plugins.qs.QSTile;
34e5b770e47d44a40d412c7d42010b2cf67920d9e3Jason Monkimport com.android.systemui.plugins.qs.QSTile.State;
35dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk
366026b057e7e81a8e54e1986007360b2f97bf454fEvan Lairdimport com.android.systemui.qs.AlphaControlledSignalTileView.AlphaControlledSlashImageView;
37dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monkimport java.util.Objects;
38dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk
39702e2ebf9fbfb715c1d9b716683c9e4d182b575eJason Monkpublic class QSIconViewImpl extends QSIconView {
40dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk
418d2e540854ce6d6660a83637b918660f6922f402Evan Laird    public static final long QS_ANIM_LENGTH = 350;
428d2e540854ce6d6660a83637b918660f6922f402Evan Laird
432f3551b04d5a5d05ba1eb7401f19c034708a9d00Xiaohui Chen    protected final View mIcon;
442f3551b04d5a5d05ba1eb7401f19c034708a9d00Xiaohui Chen    protected final int mIconSizePx;
452f3551b04d5a5d05ba1eb7401f19c034708a9d00Xiaohui Chen    protected final int mTilePaddingBelowIconPx;
461aec93f1819369bda9e6cb0bd282419808c304c3Jason Monk    private boolean mAnimationEnabled = true;
473250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk    private int mState = -1;
483250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk    private int mTint;
49dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk
50702e2ebf9fbfb715c1d9b716683c9e4d182b575eJason Monk    public QSIconViewImpl(Context context) {
51dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        super(context);
52dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk
53dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        final Resources res = context.getResources();
54dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        mIconSizePx = res.getDimensionPixelSize(R.dimen.qs_tile_icon_size);
55dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        mTilePaddingBelowIconPx =  res.getDimensionPixelSize(R.dimen.qs_tile_padding_below_icon);
56dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk
57dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        mIcon = createIcon();
58dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        addView(mIcon);
59dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    }
60dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk
611aec93f1819369bda9e6cb0bd282419808c304c3Jason Monk    public void disableAnimation() {
621aec93f1819369bda9e6cb0bd282419808c304c3Jason Monk        mAnimationEnabled = false;
631aec93f1819369bda9e6cb0bd282419808c304c3Jason Monk    }
641aec93f1819369bda9e6cb0bd282419808c304c3Jason Monk
652f3551b04d5a5d05ba1eb7401f19c034708a9d00Xiaohui Chen    public View getIconView() {
662f3551b04d5a5d05ba1eb7401f19c034708a9d00Xiaohui Chen        return mIcon;
672f3551b04d5a5d05ba1eb7401f19c034708a9d00Xiaohui Chen    }
682f3551b04d5a5d05ba1eb7401f19c034708a9d00Xiaohui Chen
69dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    @Override
70dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
71dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        final int w = MeasureSpec.getSize(widthMeasureSpec);
72dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        final int iconSpec = exactly(mIconSizePx);
73dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        mIcon.measure(MeasureSpec.makeMeasureSpec(w, getIconMeasureMode()), iconSpec);
74dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        setMeasuredDimension(w, mIcon.getMeasuredHeight() + mTilePaddingBelowIconPx);
75dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    }
76dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk
77dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    @Override
78dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    protected void onLayout(boolean changed, int l, int t, int r, int b) {
79dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        final int w = getMeasuredWidth();
80dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        int top = 0;
81dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        final int iconLeft = (w - mIcon.getMeasuredWidth()) / 2;
82dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        layout(mIcon, iconLeft, top);
83dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    }
84dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk
85dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    public void setIcon(QSTile.State state) {
86dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        setIcon((ImageView) mIcon, state);
87dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    }
88dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk
89e5b770e47d44a40d412c7d42010b2cf67920d9e3Jason Monk    protected void updateIcon(ImageView iv, State state) {
90b3daf2b88c86243db85dd8425df8d3a59d3b3d4dEvan Laird        final QSTile.Icon icon = state.iconSupplier != null ? state.iconSupplier.get() : state.icon;
91b3daf2b88c86243db85dd8425df8d3a59d3b3d4dEvan Laird        if (!Objects.equals(icon, iv.getTag(R.id.qs_icon_tag))
92395617fd35ef0e712945359a760a00eb1aaf7475Jason Monk                || !Objects.equals(state.slash, iv.getTag(R.id.qs_slash_tag))) {
93da9395e681bfaa21692bcb4a7a249b1d7ff2e2d3Jason Monk            boolean shouldAnimate = iv.isShown() && mAnimationEnabled
94da9395e681bfaa21692bcb4a7a249b1d7ff2e2d3Jason Monk                    && iv.getDrawable() != null;
95b3daf2b88c86243db85dd8425df8d3a59d3b3d4dEvan Laird            Drawable d = icon != null
96b3daf2b88c86243db85dd8425df8d3a59d3b3d4dEvan Laird                    ? shouldAnimate ? icon.getDrawable(mContext)
97b3daf2b88c86243db85dd8425df8d3a59d3b3d4dEvan Laird                    : icon.getInvisibleDrawable(mContext) : null;
98b3daf2b88c86243db85dd8425df8d3a59d3b3d4dEvan Laird            int padding = icon != null ? icon.getPadding() : 0;
99702e2ebf9fbfb715c1d9b716683c9e4d182b575eJason Monk            if (d != null) {
100d686e9c26e84e56810fcf87f6002ba8ba2b3f0c5Jason Monk                d.setAutoMirrored(false);
1013367c3353c11d5d9bd4ec8de21bdff24c6adb3b6Evan Laird                d.setLayoutDirection(getLayoutDirection());
102dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk            }
103929bd54c6a91e78de4634cb50b781a9616157f5cEvan Laird
104929bd54c6a91e78de4634cb50b781a9616157f5cEvan Laird            if (iv instanceof SlashImageView) {
105929bd54c6a91e78de4634cb50b781a9616157f5cEvan Laird                ((SlashImageView) iv).setAnimationEnabled(shouldAnimate);
106929bd54c6a91e78de4634cb50b781a9616157f5cEvan Laird                ((SlashImageView) iv).setState(state.slash, d);
107929bd54c6a91e78de4634cb50b781a9616157f5cEvan Laird            } else {
108929bd54c6a91e78de4634cb50b781a9616157f5cEvan Laird                iv.setImageDrawable(d);
109395617fd35ef0e712945359a760a00eb1aaf7475Jason Monk            }
110929bd54c6a91e78de4634cb50b781a9616157f5cEvan Laird
111b3daf2b88c86243db85dd8425df8d3a59d3b3d4dEvan Laird            iv.setTag(R.id.qs_icon_tag, icon);
112395617fd35ef0e712945359a760a00eb1aaf7475Jason Monk            iv.setTag(R.id.qs_slash_tag, state.slash);
113b53b6c566f8e4754406cd1c3fddfa16a27515a7eJason Monk            iv.setPadding(0, padding, 0, padding);
114e645aee45a7784d772e4bdf846dd2e77335f30f2Jason Monk            if (d instanceof Animatable2) {
115e645aee45a7784d772e4bdf846dd2e77335f30f2Jason Monk                Animatable2 a = (Animatable2) d;
11666239fbdfce1586555b339854b8a21f8187e805fJason Monk                a.start();
117e645aee45a7784d772e4bdf846dd2e77335f30f2Jason Monk                if (state.isTransient) {
118e645aee45a7784d772e4bdf846dd2e77335f30f2Jason Monk                    a.registerAnimationCallback(new AnimationCallback() {
119e645aee45a7784d772e4bdf846dd2e77335f30f2Jason Monk                        @Override
120e645aee45a7784d772e4bdf846dd2e77335f30f2Jason Monk                        public void onAnimationEnd(Drawable drawable) {
121e645aee45a7784d772e4bdf846dd2e77335f30f2Jason Monk                            a.start();
122e645aee45a7784d772e4bdf846dd2e77335f30f2Jason Monk                        }
123e645aee45a7784d772e4bdf846dd2e77335f30f2Jason Monk                    });
124dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk                }
125dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk            }
126dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        }
127e5b770e47d44a40d412c7d42010b2cf67920d9e3Jason Monk    }
128e5b770e47d44a40d412c7d42010b2cf67920d9e3Jason Monk
129e5b770e47d44a40d412c7d42010b2cf67920d9e3Jason Monk    protected void setIcon(ImageView iv, QSTile.State state) {
130e5b770e47d44a40d412c7d42010b2cf67920d9e3Jason Monk        updateIcon(iv, state);
1311c7cda8bbfb848961536a4adbf2c81c5ffddae16Sudheer Shanka        if (state.disabledByPolicy) {
1321c7cda8bbfb848961536a4adbf2c81c5ffddae16Sudheer Shanka            iv.setColorFilter(getContext().getColor(R.color.qs_tile_disabled_color));
1331c7cda8bbfb848961536a4adbf2c81c5ffddae16Sudheer Shanka        } else {
1341c7cda8bbfb848961536a4adbf2c81c5ffddae16Sudheer Shanka            iv.clearColorFilter();
1351c7cda8bbfb848961536a4adbf2c81c5ffddae16Sudheer Shanka        }
1363250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk        if (state.state != mState) {
137e5b770e47d44a40d412c7d42010b2cf67920d9e3Jason Monk            int color = getColor(state.state);
1383250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk            mState = state.state;
1399a82d124628350e64a835043baffced59ffcd143Jason Monk            if (iv.isShown() && mTint != 0) {
1403250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk                animateGrayScale(mTint, color, iv);
1413250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk                mTint = color;
1423250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk            } else {
1436026b057e7e81a8e54e1986007360b2f97bf454fEvan Laird                if (iv instanceof AlphaControlledSlashImageView) {
1446026b057e7e81a8e54e1986007360b2f97bf454fEvan Laird                    ((AlphaControlledSlashImageView)iv)
1456026b057e7e81a8e54e1986007360b2f97bf454fEvan Laird                            .setFinalImageTintList(ColorStateList.valueOf(color));
1466026b057e7e81a8e54e1986007360b2f97bf454fEvan Laird                } else {
1476026b057e7e81a8e54e1986007360b2f97bf454fEvan Laird                    setTint(iv, color);
1486026b057e7e81a8e54e1986007360b2f97bf454fEvan Laird                }
1493250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk                mTint = color;
1503250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk            }
1513250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk        }
1523250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk    }
1533250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk
154e5b770e47d44a40d412c7d42010b2cf67920d9e3Jason Monk    protected int getColor(int state) {
155e5b770e47d44a40d412c7d42010b2cf67920d9e3Jason Monk        return getColorForState(getContext(), state);
156e5b770e47d44a40d412c7d42010b2cf67920d9e3Jason Monk    }
157e5b770e47d44a40d412c7d42010b2cf67920d9e3Jason Monk
1583250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk    public static void animateGrayScale(int fromColor, int toColor, ImageView iv) {
1596026b057e7e81a8e54e1986007360b2f97bf454fEvan Laird        if (iv instanceof AlphaControlledSlashImageView) {
1606026b057e7e81a8e54e1986007360b2f97bf454fEvan Laird            ((AlphaControlledSlashImageView)iv)
1616026b057e7e81a8e54e1986007360b2f97bf454fEvan Laird                    .setFinalImageTintList(ColorStateList.valueOf(toColor));
1626026b057e7e81a8e54e1986007360b2f97bf454fEvan Laird        }
163bdcd1cc85bdeaeb3b025073c995107d02a97caf7Evan Laird        if (ValueAnimator.areAnimatorsEnabled()) {
164bdcd1cc85bdeaeb3b025073c995107d02a97caf7Evan Laird            final float fromAlpha = Color.alpha(fromColor);
165bdcd1cc85bdeaeb3b025073c995107d02a97caf7Evan Laird            final float toAlpha = Color.alpha(toColor);
166bdcd1cc85bdeaeb3b025073c995107d02a97caf7Evan Laird            final float fromChannel = Color.red(fromColor);
167bdcd1cc85bdeaeb3b025073c995107d02a97caf7Evan Laird            final float toChannel = Color.red(toColor);
168bdcd1cc85bdeaeb3b025073c995107d02a97caf7Evan Laird
169bdcd1cc85bdeaeb3b025073c995107d02a97caf7Evan Laird            ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
170bdcd1cc85bdeaeb3b025073c995107d02a97caf7Evan Laird            anim.setDuration(QS_ANIM_LENGTH);
171bdcd1cc85bdeaeb3b025073c995107d02a97caf7Evan Laird            anim.addUpdateListener(animation -> {
172bdcd1cc85bdeaeb3b025073c995107d02a97caf7Evan Laird                float fraction = animation.getAnimatedFraction();
173bdcd1cc85bdeaeb3b025073c995107d02a97caf7Evan Laird                int alpha = (int) (fromAlpha + (toAlpha - fromAlpha) * fraction);
174bdcd1cc85bdeaeb3b025073c995107d02a97caf7Evan Laird                int channel = (int) (fromChannel + (toChannel - fromChannel) * fraction);
175bdcd1cc85bdeaeb3b025073c995107d02a97caf7Evan Laird
176bdcd1cc85bdeaeb3b025073c995107d02a97caf7Evan Laird                setTint(iv, Color.argb(alpha, channel, channel, channel));
177bdcd1cc85bdeaeb3b025073c995107d02a97caf7Evan Laird            });
178bdcd1cc85bdeaeb3b025073c995107d02a97caf7Evan Laird
179bdcd1cc85bdeaeb3b025073c995107d02a97caf7Evan Laird            anim.start();
180bdcd1cc85bdeaeb3b025073c995107d02a97caf7Evan Laird        } else {
181bdcd1cc85bdeaeb3b025073c995107d02a97caf7Evan Laird            setTint(iv, toColor);
182bdcd1cc85bdeaeb3b025073c995107d02a97caf7Evan Laird        }
183dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    }
184dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk
1853250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk    public static void setTint(ImageView iv, int color) {
1863250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk        iv.setImageTintList(ColorStateList.valueOf(color));
1873250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk    }
1883250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk
1893250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk
190dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    protected int getIconMeasureMode() {
191dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        return MeasureSpec.EXACTLY;
192dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    }
193dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk
194dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    protected View createIcon() {
195395617fd35ef0e712945359a760a00eb1aaf7475Jason Monk        final ImageView icon = new SlashImageView(mContext);
196dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        icon.setId(android.R.id.icon);
197eae7c318864bcfd814c8be697315f829952ccc69Jason Monk        icon.setScaleType(ScaleType.FIT_CENTER);
198dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        return icon;
199dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    }
200dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk
2012f3551b04d5a5d05ba1eb7401f19c034708a9d00Xiaohui Chen    protected final int exactly(int size) {
202dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
203dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    }
204dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk
2052f3551b04d5a5d05ba1eb7401f19c034708a9d00Xiaohui Chen    protected final void layout(View child, int left, int top) {
206dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
207dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    }
208dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk}
209