• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.systemui.qs;
17 
18 import android.content.Context;
19 import android.content.res.TypedArray;
20 import android.graphics.drawable.Drawable;
21 import android.graphics.drawable.RippleDrawable;
22 import android.os.Handler;
23 import android.os.Looper;
24 import android.os.Message;
25 import android.text.TextUtils;
26 import android.view.View;
27 import android.view.accessibility.AccessibilityEvent;
28 import android.view.accessibility.AccessibilityNodeInfo;
29 import android.widget.LinearLayout;
30 import android.widget.Switch;
31 
32 import com.android.systemui.R;
33 
34 public class QSTileBaseView extends LinearLayout {
35 
36     private final H mHandler = new H();
37     private QSIconView mIcon;
38     private RippleDrawable mRipple;
39     private Drawable mTileBackground;
40     private String mAccessibilityClass;
41     private boolean mTileState;
42     private boolean mCollapsedView;
43 
QSTileBaseView(Context context, QSIconView icon)44     public QSTileBaseView(Context context, QSIconView icon) {
45         this(context, icon, false);
46     }
47 
QSTileBaseView(Context context, QSIconView icon, boolean collapsedView)48     public QSTileBaseView(Context context, QSIconView icon, boolean collapsedView) {
49         super(context);
50         mIcon = icon;
51         addView(mIcon);
52 
53         mTileBackground = newTileBackground();
54         if (mTileBackground instanceof RippleDrawable) {
55             setRipple((RippleDrawable) mTileBackground);
56         }
57         setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
58         setBackground(mTileBackground);
59 
60         // Default to Quick Tile padding, and QSTileView will specify its own padding.
61         int padding = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_padding);
62         setPadding(0, padding, 0, padding);
63         setClipChildren(false);
64         setClipToPadding(false);
65         mCollapsedView = collapsedView;
66     }
67 
newTileBackground()68     private Drawable newTileBackground() {
69         final int[] attrs = new int[] { android.R.attr.selectableItemBackgroundBorderless };
70         final TypedArray ta = mContext.obtainStyledAttributes(attrs);
71         final Drawable d = ta.getDrawable(0);
72         ta.recycle();
73         return d;
74     }
75 
setRipple(RippleDrawable tileBackground)76     private void setRipple(RippleDrawable tileBackground) {
77         mRipple = tileBackground;
78         if (getWidth() != 0) {
79             updateRippleSize(getWidth(), getHeight());
80         }
81     }
82 
updateRippleSize(int width, int height)83     private void updateRippleSize(int width, int height) {
84         // center the touch feedback on the center of the icon, and dial it down a bit
85         final int cx = width / 2;
86         final int cy = height / 2;
87         final int rad = (int)(mIcon.getHeight() * .85f);
88         mRipple.setHotspotBounds(cx - rad, cy - rad, cx + rad, cy + rad);
89     }
90 
init(OnClickListener click, OnLongClickListener longClick)91     public void init(OnClickListener click, OnLongClickListener longClick) {
92         setClickable(true);
93         setOnClickListener(click);
94         setOnLongClickListener(longClick);
95     }
96 
97     @Override
onLayout(boolean changed, int l, int t, int r, int b)98     protected void onLayout(boolean changed, int l, int t, int r, int b) {
99         super.onLayout(changed, l, t, r, b);
100         final int w = getMeasuredWidth();
101         final int h = getMeasuredHeight();
102 
103         if (mRipple != null) {
104             updateRippleSize(w, h);
105         }
106     }
107 
108     @Override
hasOverlappingRendering()109     public boolean hasOverlappingRendering() {
110         // Avoid layers for this layout - we don't need them.
111         return false;
112     }
113 
114     /**
115      * Update the accessibility order for this view.
116      *
117      * @param previousView the view which should be before this one
118      * @return the last view in this view which is accessible
119      */
updateAccessibilityOrder(View previousView)120     public View updateAccessibilityOrder(View previousView) {
121         setAccessibilityTraversalAfter(previousView.getId());
122         return this;
123     }
124 
onStateChanged(QSTile.State state)125     public void onStateChanged(QSTile.State state) {
126         mHandler.obtainMessage(H.STATE_CHANGED, state).sendToTarget();
127     }
128 
handleStateChanged(QSTile.State state)129     protected void handleStateChanged(QSTile.State state) {
130         mIcon.setIcon(state);
131         if (mCollapsedView && !TextUtils.isEmpty(state.minimalContentDescription)) {
132             setContentDescription(state.minimalContentDescription);
133         } else {
134             setContentDescription(state.contentDescription);
135         }
136         if (mCollapsedView) {
137             mAccessibilityClass = state.minimalAccessibilityClassName;
138         } else {
139             mAccessibilityClass = state.expandedAccessibilityClassName;
140         }
141         if (state instanceof QSTile.BooleanState) {
142             mTileState = ((QSTile.BooleanState) state).value;
143         }
144     }
145 
getIcon()146     public QSIconView getIcon() {
147         return mIcon;
148     }
149 
150     @Override
onInitializeAccessibilityEvent(AccessibilityEvent event)151     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
152         super.onInitializeAccessibilityEvent(event);
153         if (!TextUtils.isEmpty(mAccessibilityClass)) {
154             event.setClassName(mAccessibilityClass);
155             if (Switch.class.getName().equals(mAccessibilityClass)) {
156                 String label = getResources()
157                         .getString(!mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
158                 event.setContentDescription(label);
159                 event.setChecked(!mTileState);
160             }
161         }
162     }
163 
164     @Override
onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)165     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
166         super.onInitializeAccessibilityNodeInfo(info);
167         if (!TextUtils.isEmpty(mAccessibilityClass)) {
168             info.setClassName(mAccessibilityClass);
169             if (Switch.class.getName().equals(mAccessibilityClass)) {
170                 String label = getResources()
171                         .getString(mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
172                 info.setText(label);
173                 info.setChecked(mTileState);
174                 info.setCheckable(true);
175             }
176         }
177     }
178 
179     private class H extends Handler {
180         private static final int STATE_CHANGED = 1;
H()181         public H() {
182             super(Looper.getMainLooper());
183         }
184 
185         @Override
handleMessage(Message msg)186         public void handleMessage(Message msg) {
187             if (msg.what == STATE_CHANGED) {
188                 handleStateChanged((QSTile.State) msg.obj);
189             }
190         }
191     }
192 }
193