• 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         setFocusable(true);
67     }
68 
newTileBackground()69     private Drawable newTileBackground() {
70         final int[] attrs = new int[] { android.R.attr.selectableItemBackgroundBorderless };
71         final TypedArray ta = mContext.obtainStyledAttributes(attrs);
72         final Drawable d = ta.getDrawable(0);
73         ta.recycle();
74         return d;
75     }
76 
setRipple(RippleDrawable tileBackground)77     private void setRipple(RippleDrawable tileBackground) {
78         mRipple = tileBackground;
79         if (getWidth() != 0) {
80             updateRippleSize(getWidth(), getHeight());
81         }
82     }
83 
updateRippleSize(int width, int height)84     private void updateRippleSize(int width, int height) {
85         // center the touch feedback on the center of the icon, and dial it down a bit
86         final int cx = width / 2;
87         final int cy = height / 2;
88         final int rad = (int)(mIcon.getHeight() * .85f);
89         mRipple.setHotspotBounds(cx - rad, cy - rad, cx + rad, cy + rad);
90     }
91 
init(OnClickListener click, OnLongClickListener longClick)92     public void init(OnClickListener click, OnLongClickListener longClick) {
93         setClickable(true);
94         setOnClickListener(click);
95         setOnLongClickListener(longClick);
96     }
97 
98     @Override
onLayout(boolean changed, int l, int t, int r, int b)99     protected void onLayout(boolean changed, int l, int t, int r, int b) {
100         super.onLayout(changed, l, t, r, b);
101         final int w = getMeasuredWidth();
102         final int h = getMeasuredHeight();
103 
104         if (mRipple != null) {
105             updateRippleSize(w, h);
106         }
107     }
108 
109     @Override
hasOverlappingRendering()110     public boolean hasOverlappingRendering() {
111         // Avoid layers for this layout - we don't need them.
112         return false;
113     }
114 
115     /**
116      * Update the accessibility order for this view.
117      *
118      * @param previousView the view which should be before this one
119      * @return the last view in this view which is accessible
120      */
updateAccessibilityOrder(View previousView)121     public View updateAccessibilityOrder(View previousView) {
122         setAccessibilityTraversalAfter(previousView.getId());
123         return this;
124     }
125 
onStateChanged(QSTile.State state)126     public void onStateChanged(QSTile.State state) {
127         mHandler.obtainMessage(H.STATE_CHANGED, state).sendToTarget();
128     }
129 
handleStateChanged(QSTile.State state)130     protected void handleStateChanged(QSTile.State state) {
131         mIcon.setIcon(state);
132         if (mCollapsedView && !TextUtils.isEmpty(state.minimalContentDescription)) {
133             setContentDescription(state.minimalContentDescription);
134         } else {
135             setContentDescription(state.contentDescription);
136         }
137         if (mCollapsedView) {
138             mAccessibilityClass = state.minimalAccessibilityClassName;
139         } else {
140             mAccessibilityClass = state.expandedAccessibilityClassName;
141         }
142         if (state instanceof QSTile.BooleanState) {
143             mTileState = ((QSTile.BooleanState) state).value;
144         }
145     }
146 
getIcon()147     public QSIconView getIcon() {
148         return mIcon;
149     }
150 
151     @Override
onInitializeAccessibilityEvent(AccessibilityEvent event)152     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
153         super.onInitializeAccessibilityEvent(event);
154         if (!TextUtils.isEmpty(mAccessibilityClass)) {
155             event.setClassName(mAccessibilityClass);
156             if (Switch.class.getName().equals(mAccessibilityClass)) {
157                 String label = getResources()
158                         .getString(!mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
159                 event.setContentDescription(label);
160                 event.setChecked(!mTileState);
161             }
162         }
163     }
164 
165     @Override
onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)166     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
167         super.onInitializeAccessibilityNodeInfo(info);
168         if (!TextUtils.isEmpty(mAccessibilityClass)) {
169             info.setClassName(mAccessibilityClass);
170             if (Switch.class.getName().equals(mAccessibilityClass)) {
171                 String label = getResources()
172                         .getString(mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
173                 info.setText(label);
174                 info.setChecked(mTileState);
175                 info.setCheckable(true);
176             }
177         }
178     }
179 
180     private class H extends Handler {
181         private static final int STATE_CHANGED = 1;
H()182         public H() {
183             super(Looper.getMainLooper());
184         }
185 
186         @Override
handleMessage(Message msg)187         public void handleMessage(Message msg) {
188             if (msg.what == STATE_CHANGED) {
189                 handleStateChanged((QSTile.State) msg.obj);
190             }
191         }
192     }
193 }
194