blob: 597bb937b956854a9f01636566fbcdb8dc57befd [file] [log] [blame]
John Spurlockaf8d6c42014-05-07 17:49:08 -04001/*
2 * Copyright (C) 2014 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
17package com.android.systemui.qs;
18
19import android.content.Context;
20import android.content.res.Resources;
21import android.content.res.TypedArray;
22import android.graphics.Typeface;
23import android.graphics.drawable.Drawable;
John Spurlock4bf31982014-05-21 13:04:22 -040024import android.graphics.drawable.RippleDrawable;
John Spurlockaf8d6c42014-05-07 17:49:08 -040025import android.os.Handler;
26import android.os.Looper;
27import android.os.Message;
28import android.util.TypedValue;
29import android.view.Gravity;
30import android.view.View;
31import android.view.ViewGroup;
John Spurlock83e28482014-05-18 17:20:05 -040032import android.widget.ImageView;
John Spurlockaf8d6c42014-05-07 17:49:08 -040033import android.widget.ImageView.ScaleType;
34import android.widget.TextView;
35
36import com.android.systemui.R;
37import com.android.systemui.qs.QSTile.State;
38
39/** View that represents a standard quick settings tile. **/
40public class QSTileView extends ViewGroup {
41 private static final Typeface CONDENSED = Typeface.create("sans-serif-condensed",
42 Typeface.NORMAL);
John Spurlockaf8d6c42014-05-07 17:49:08 -040043
44 protected final Context mContext;
45 private final View mIcon;
46 private final View mDivider;
John Spurlockaf8d6c42014-05-07 17:49:08 -040047 private final H mHandler = new H();
John Spurlock4bf31982014-05-21 13:04:22 -040048 private final int mIconSizePx;
John Spurlock92d9b192014-06-29 12:54:24 -040049 private final int mTileSpacingPx;
50 private final int mTilePaddingTopPx;
51 private final int mTilePaddingBelowIconPx;
John Spurlock39076ed2014-06-30 20:47:20 -040052 private final int mDualTileVerticalPaddingPx;
John Spurlockaf8d6c42014-05-07 17:49:08 -040053
John Spurlockd47a3f32014-05-18 19:14:14 -040054 private TextView mLabel;
John Spurlock92d9b192014-06-29 12:54:24 -040055 private QSDualTileLabel mDualLabel;
John Spurlockaf8d6c42014-05-07 17:49:08 -040056 private boolean mDual;
57 private OnClickListener mClickPrimary;
58 private OnClickListener mClickSecondary;
John Spurlock4bf31982014-05-21 13:04:22 -040059 private RippleDrawable mRipple;
John Spurlockaf8d6c42014-05-07 17:49:08 -040060
61 public QSTileView(Context context) {
62 super(context);
63
64 mContext = context;
65 final Resources res = context.getResources();
John Spurlock4bf31982014-05-21 13:04:22 -040066 mIconSizePx = res.getDimensionPixelSize(R.dimen.qs_tile_icon_size);
John Spurlock92d9b192014-06-29 12:54:24 -040067 mTileSpacingPx = res.getDimensionPixelSize(R.dimen.qs_tile_spacing);
68 mTilePaddingTopPx = res.getDimensionPixelSize(R.dimen.qs_tile_padding_top);
69 mTilePaddingBelowIconPx = res.getDimensionPixelSize(R.dimen.qs_tile_padding_below_icon);
John Spurlock39076ed2014-06-30 20:47:20 -040070 mDualTileVerticalPaddingPx =
71 res.getDimensionPixelSize(R.dimen.qs_dual_tile_padding_vertical);
John Spurlockd47a3f32014-05-18 19:14:14 -040072 recreateLabel();
John Spurlockaf8d6c42014-05-07 17:49:08 -040073 setClipChildren(false);
74
75 mIcon = createIcon();
76 addView(mIcon);
77
78 mDivider = new View(mContext);
John Spurlock4bf31982014-05-21 13:04:22 -040079 mDivider.setBackgroundColor(res.getColor(R.color.qs_tile_divider));
80 final int dh = res.getDimensionPixelSize(R.dimen.qs_tile_divider_height);
John Spurlockaf8d6c42014-05-07 17:49:08 -040081 mDivider.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, dh));
82 addView(mDivider);
83
84 setClickable(true);
John Spurlock4bf31982014-05-21 13:04:22 -040085 setBackground(getTileBackground());
John Spurlockaf8d6c42014-05-07 17:49:08 -040086 }
87
John Spurlockd47a3f32014-05-18 19:14:14 -040088 private void recreateLabel() {
89 CharSequence labelText = null;
90 if (mLabel != null) {
91 labelText = mLabel.getText();
92 removeView(mLabel);
John Spurlock92d9b192014-06-29 12:54:24 -040093 mLabel = null;
94 }
95 if (mDualLabel != null) {
96 labelText = mDualLabel.getText();
97 removeView(mDualLabel);
98 mDualLabel = null;
John Spurlockd47a3f32014-05-18 19:14:14 -040099 }
100 final Resources res = mContext.getResources();
John Spurlock92d9b192014-06-29 12:54:24 -0400101 if (mDual) {
John Spurlock39076ed2014-06-30 20:47:20 -0400102 mDualLabel = new QSDualTileLabel(mContext);
John Spurlock92d9b192014-06-29 12:54:24 -0400103 mDualLabel.setId(android.R.id.title);
John Spurlock39076ed2014-06-30 20:47:20 -0400104 mDualLabel.setBackgroundResource(R.drawable.btn_borderless_rect);
105 mDualLabel.setFirstLineCaret(res.getDrawable(R.drawable.qs_dual_tile_caret));
John Spurlock92d9b192014-06-29 12:54:24 -0400106 mDualLabel.setTextColor(res.getColor(R.color.qs_tile_text));
John Spurlock39076ed2014-06-30 20:47:20 -0400107 mDualLabel.setPadding(0, mDualTileVerticalPaddingPx, 0, mDualTileVerticalPaddingPx);
John Spurlock92d9b192014-06-29 12:54:24 -0400108 mDualLabel.setTypeface(CONDENSED);
109 mDualLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX,
110 res.getDimensionPixelSize(R.dimen.qs_tile_text_size));
111 mDualLabel.setClickable(true);
112 mDualLabel.setOnClickListener(mClickSecondary);
113 if (labelText != null) {
114 mDualLabel.setText(labelText);
115 }
116 addView(mDualLabel);
117 } else {
118 mLabel = new TextView(mContext);
119 mLabel.setId(android.R.id.title);
120 mLabel.setTextColor(res.getColor(R.color.qs_tile_text));
121 mLabel.setGravity(Gravity.CENTER_HORIZONTAL);
122 mLabel.setMinLines(2);
123 mLabel.setPadding(0, 0, 0, 0);
124 mLabel.setTypeface(CONDENSED);
125 mLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX,
126 res.getDimensionPixelSize(R.dimen.qs_tile_text_size));
127 mLabel.setClickable(false);
128 if (labelText != null) {
129 mLabel.setText(labelText);
130 }
131 addView(mLabel);
John Spurlockd47a3f32014-05-18 19:14:14 -0400132 }
John Spurlockd47a3f32014-05-18 19:14:14 -0400133 }
134
John Spurlockaf8d6c42014-05-07 17:49:08 -0400135 public void setDual(boolean dual) {
John Spurlockd47a3f32014-05-18 19:14:14 -0400136 final boolean changed = dual != mDual;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400137 mDual = dual;
John Spurlockd47a3f32014-05-18 19:14:14 -0400138 if (changed) {
139 recreateLabel();
140 }
John Spurlock92d9b192014-06-29 12:54:24 -0400141 setOnClickListener(mClickPrimary);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400142 mDivider.setVisibility(dual ? VISIBLE : GONE);
143 postInvalidate();
144 }
145
146 public void init(OnClickListener clickPrimary, OnClickListener clickSecondary) {
147 mClickPrimary = clickPrimary;
148 mClickSecondary = clickSecondary;
149 }
150
151 protected View createIcon() {
John Spurlock83e28482014-05-18 17:20:05 -0400152 final ImageView icon = new ImageView(mContext);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400153 icon.setId(android.R.id.icon);
154 icon.setScaleType(ScaleType.CENTER_INSIDE);
155 return icon;
156 }
157
John Spurlock4bf31982014-05-21 13:04:22 -0400158 private Drawable getTileBackground() {
John Spurlock856edeb2014-06-01 20:36:47 -0400159 final int[] attrs = new int[] { android.R.attr.selectableItemBackgroundBorderless };
John Spurlockaf8d6c42014-05-07 17:49:08 -0400160 final TypedArray ta = mContext.obtainStyledAttributes(attrs);
161 final Drawable d = ta.getDrawable(0);
162 ta.recycle();
John Spurlock4bf31982014-05-21 13:04:22 -0400163 if (d instanceof RippleDrawable) {
164 mRipple = (RippleDrawable) d;
165 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400166 return d;
167 }
168
John Spurlock92d9b192014-06-29 12:54:24 -0400169 private View labelView() {
170 return mDual ? mDualLabel : mLabel;
171 }
172
John Spurlockaf8d6c42014-05-07 17:49:08 -0400173 @Override
174 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
175 final int w = MeasureSpec.getSize(widthMeasureSpec);
176 final int h = MeasureSpec.getSize(heightMeasureSpec);
John Spurlock4bf31982014-05-21 13:04:22 -0400177 final int iconSpec = exactly(mIconSizePx);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400178 mIcon.measure(iconSpec, iconSpec);
John Spurlock92d9b192014-06-29 12:54:24 -0400179 labelView().measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(h, MeasureSpec.AT_MOST));
John Spurlockaf8d6c42014-05-07 17:49:08 -0400180 if (mDual) {
181 mDivider.measure(widthMeasureSpec, exactly(mDivider.getLayoutParams().height));
182 }
183 setMeasuredDimension(w, h);
184 }
185
186 private static int exactly(int size) {
187 return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
188 }
189
190 @Override
191 protected void onLayout(boolean changed, int l, int t, int r, int b) {
192 final int w = getMeasuredWidth();
John Spurlock529aa732014-07-10 22:49:16 -0400193 final int h = getMeasuredHeight();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400194
John Spurlock92d9b192014-06-29 12:54:24 -0400195 int top = 0;
196 top += mTileSpacingPx;
197 top += mTilePaddingTopPx;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400198 final int iconLeft = (w - mIcon.getMeasuredWidth()) / 2;
199 layout(mIcon, iconLeft, top);
John Spurlock4bf31982014-05-21 13:04:22 -0400200 if (mRipple != null) {
201 // center the touch feedback on the center of the icon, and dial it down a bit
202 final int cx = w / 2;
John Spurlock529aa732014-07-10 22:49:16 -0400203 final int cy = mDual ? mIcon.getTop() + mIcon.getHeight() / 2 : h / 2;
204 final int rad = (int)(mIcon.getHeight() * 1.25);
John Spurlock4bf31982014-05-21 13:04:22 -0400205 mRipple.setHotspotBounds(cx - rad, cy - rad, cx + rad, cy + rad);
206 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400207 top = mIcon.getBottom();
John Spurlock92d9b192014-06-29 12:54:24 -0400208 top += mTilePaddingBelowIconPx;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400209 if (mDual) {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400210 layout(mDivider, 0, top);
211 top = mDivider.getBottom();
212 }
John Spurlock92d9b192014-06-29 12:54:24 -0400213 layout(labelView(), 0, top);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400214 }
215
216 private static void layout(View child, int left, int top) {
217 child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
218 }
219
220 protected void handleStateChanged(QSTile.State state) {
John Spurlock83e28482014-05-18 17:20:05 -0400221 if (mIcon instanceof ImageView) {
222 ImageView iv = (ImageView) mIcon;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400223 if (state.icon != null) {
John Spurlock83e28482014-05-18 17:20:05 -0400224 iv.setImageDrawable(state.icon);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400225 } else if (state.iconId > 0) {
John Spurlock83e28482014-05-18 17:20:05 -0400226 iv.setImageResource(state.iconId);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400227 }
228 }
John Spurlock92d9b192014-06-29 12:54:24 -0400229 if (mDual) {
230 mDualLabel.setText(state.label);
231 } else {
232 mLabel.setText(state.label);
233 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400234 setContentDescription(state.contentDescription);
235 }
236
237 public void onStateChanged(QSTile.State state) {
238 mHandler.obtainMessage(H.STATE_CHANGED, state).sendToTarget();
239 }
240
241 private class H extends Handler {
242 private static final int STATE_CHANGED = 1;
243 public H() {
244 super(Looper.getMainLooper());
245 }
246 @Override
247 public void handleMessage(Message msg) {
248 if (msg.what == STATE_CHANGED) {
249 handleStateChanged((State) msg.obj);
250 }
251 }
252 }
253}