1c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk/*
2c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk * Copyright (C) 2015 The Android Open Source Project
3c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk *
4dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk * Licensed under the Apache License, Version 2.0 (the "License");
5dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk * you may not use this file except in compliance with the License.
6dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk * You may obtain a copy of the License at
7c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk *
8c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk *      http://www.apache.org/licenses/LICENSE-2.0
9c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk *
10dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk * Unless required by applicable law or agreed to in writing, software
11dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk * distributed under the License is distributed on an "AS IS" BASIS,
12dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk * See the License for the specific language governing permissions and
14dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk * limitations under the License.
15c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk */
16c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monkpackage com.android.systemui.qs;
17c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk
18c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monkimport android.content.Context;
19dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monkimport android.content.res.TypedArray;
20dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monkimport android.graphics.drawable.Drawable;
21dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monkimport android.graphics.drawable.RippleDrawable;
22c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monkimport android.os.Handler;
23c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monkimport android.os.Looper;
24c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monkimport android.os.Message;
2520aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynoldsimport android.text.TextUtils;
26c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monkimport android.view.View;
2720aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynoldsimport android.view.accessibility.AccessibilityEvent;
2820aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynoldsimport android.view.accessibility.AccessibilityNodeInfo;
29dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monkimport android.widget.LinearLayout;
3020aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynoldsimport android.widget.Switch;
31c0d7058b14c24cd07912f5629c26b39b7b4673d5Winson
32dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monkimport com.android.systemui.R;
33c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk
34dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monkpublic class QSTileBaseView extends LinearLayout {
35c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk
36c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk    private final H mHandler = new H();
37dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    private QSIconView mIcon;
38dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    private RippleDrawable mRipple;
39dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    private Drawable mTileBackground;
4020aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds    private String mAccessibilityClass;
4120aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds    private boolean mTileState;
4220aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds    private boolean mCollapsedView;
43c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk
44dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    public QSTileBaseView(Context context, QSIconView icon) {
4520aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds        this(context, icon, false);
4620aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds    }
4720aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds
4820aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds    public QSTileBaseView(Context context, QSIconView icon, boolean collapsedView) {
49c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk        super(context);
50dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        mIcon = icon;
51dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        addView(mIcon);
52dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk
53dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        mTileBackground = newTileBackground();
54dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        if (mTileBackground instanceof RippleDrawable) {
55dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk            setRipple((RippleDrawable) mTileBackground);
56dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        }
57dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
58dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        setBackground(mTileBackground);
59dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk
60dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        // Default to Quick Tile padding, and QSTileView will specify its own padding.
61dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        int padding = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_padding);
62ec87a878d340500be05be3ff622b06f66eae8a96Jason Monk        setPadding(0, padding, 0, padding);
63162011ebe8015f44d4d9ced519297a7cfb087af2Jason Monk        setClipChildren(false);
64162011ebe8015f44d4d9ced519297a7cfb087af2Jason Monk        setClipToPadding(false);
6520aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds        mCollapsedView = collapsedView;
66c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk    }
67c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk
68dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    private Drawable newTileBackground() {
69dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        final int[] attrs = new int[] { android.R.attr.selectableItemBackgroundBorderless };
70dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        final TypedArray ta = mContext.obtainStyledAttributes(attrs);
71dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        final Drawable d = ta.getDrawable(0);
72dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        ta.recycle();
73dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        return d;
74c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk    }
75c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk
76dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    private void setRipple(RippleDrawable tileBackground) {
77dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        mRipple = tileBackground;
78dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        if (getWidth() != 0) {
79dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk            updateRippleSize(getWidth(), getHeight());
80dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        }
81dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    }
82c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk
83dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    private void updateRippleSize(int width, int height) {
84dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        // center the touch feedback on the center of the icon, and dial it down a bit
85dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        final int cx = width / 2;
86dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        final int cy = height / 2;
87dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        final int rad = (int)(mIcon.getHeight() * .85f);
88dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        mRipple.setHotspotBounds(cx - rad, cy - rad, cx + rad, cy + rad);
89c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk    }
90c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk
91dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    public void init(OnClickListener click, OnLongClickListener longClick) {
92dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        setClickable(true);
93dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        setOnClickListener(click);
9476c67aa361f65dfb2f5e03d06cc1ccebce9cecd9Jason Monk        setOnLongClickListener(longClick);
95dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    }
96dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk
97dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    @Override
98dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    protected void onLayout(boolean changed, int l, int t, int r, int b) {
99dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        super.onLayout(changed, l, t, r, b);
100dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        final int w = getMeasuredWidth();
101dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        final int h = getMeasuredHeight();
102dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk
103dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        if (mRipple != null) {
104dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk            updateRippleSize(w, h);
105dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        }
106dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    }
107c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk
108c3081179d8961a291c47c05f52b63c134284cd4dJorim Jaggi    @Override
109c3081179d8961a291c47c05f52b63c134284cd4dJorim Jaggi    public boolean hasOverlappingRendering() {
110c3081179d8961a291c47c05f52b63c134284cd4dJorim Jaggi        // Avoid layers for this layout - we don't need them.
111c3081179d8961a291c47c05f52b63c134284cd4dJorim Jaggi        return false;
112c3081179d8961a291c47c05f52b63c134284cd4dJorim Jaggi    }
113c3081179d8961a291c47c05f52b63c134284cd4dJorim Jaggi
114dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    /**
115dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk     * Update the accessibility order for this view.
116dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk     *
117dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk     * @param previousView the view which should be before this one
118dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk     * @return the last view in this view which is accessible
119dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk     */
120dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    public View updateAccessibilityOrder(View previousView) {
121dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        setAccessibilityTraversalAfter(previousView.getId());
122dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        return this;
123dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    }
124c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk
125dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    public void onStateChanged(QSTile.State state) {
126dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        mHandler.obtainMessage(H.STATE_CHANGED, state).sendToTarget();
127c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk    }
128c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk
129dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk    protected void handleStateChanged(QSTile.State state) {
130dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk        mIcon.setIcon(state);
13120aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds        if (mCollapsedView && !TextUtils.isEmpty(state.minimalContentDescription)) {
13220aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds            setContentDescription(state.minimalContentDescription);
13320aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds        } else {
13420aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds            setContentDescription(state.contentDescription);
13520aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds        }
13620aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds        if (mCollapsedView) {
13720aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds            mAccessibilityClass = state.minimalAccessibilityClassName;
13820aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds        } else {
13920aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds            mAccessibilityClass = state.expandedAccessibilityClassName;
14020aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds        }
14120aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds        if (state instanceof QSTile.BooleanState) {
14220aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds            mTileState = ((QSTile.BooleanState) state).value;
14320aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds        }
144c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk    }
145c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk
1461aec93f1819369bda9e6cb0bd282419808c304c3Jason Monk    public QSIconView getIcon() {
147162011ebe8015f44d4d9ced519297a7cfb087af2Jason Monk        return mIcon;
148162011ebe8015f44d4d9ced519297a7cfb087af2Jason Monk    }
149162011ebe8015f44d4d9ced519297a7cfb087af2Jason Monk
15020aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds    @Override
15120aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
15220aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds        super.onInitializeAccessibilityEvent(event);
15320aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds        if (!TextUtils.isEmpty(mAccessibilityClass)) {
15420aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds            event.setClassName(mAccessibilityClass);
15520aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds            if (Switch.class.getName().equals(mAccessibilityClass)) {
15620aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds                String label = getResources()
157d8834773ebbc139df0052484af8129c240e1b3acJulia Reynolds                        .getString(!mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
15820aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds                event.setContentDescription(label);
159d8834773ebbc139df0052484af8129c240e1b3acJulia Reynolds                event.setChecked(!mTileState);
16020aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds            }
16120aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds        }
16220aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds    }
16320aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds
16420aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds    @Override
16520aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
16620aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds        super.onInitializeAccessibilityNodeInfo(info);
16720aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds        if (!TextUtils.isEmpty(mAccessibilityClass)) {
16820aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds            info.setClassName(mAccessibilityClass);
16920aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds            if (Switch.class.getName().equals(mAccessibilityClass)) {
17020aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds                String label = getResources()
17120aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds                        .getString(mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
17220aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds                info.setText(label);
17320aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds                info.setChecked(mTileState);
17420aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds                info.setCheckable(true);
17520aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds            }
17620aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds        }
17720aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds    }
17820aef8a2914478a85aa679ec31bf739c7818eb3cJulia Reynolds
179c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk    private class H extends Handler {
180c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk        private static final int STATE_CHANGED = 1;
181c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk        public H() {
182c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk            super(Looper.getMainLooper());
183c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk        }
184c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk
185c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk        @Override
186c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk        public void handleMessage(Message msg) {
187c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk            if (msg.what == STATE_CHANGED) {
188c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk                handleStateChanged((QSTile.State) msg.obj);
189c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk            }
190c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk        }
191c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk    }
192c133d26b3897811e9f567d787bff8eafb364f3e3Jason Monk}
193