blob: 2a2cc46cb98cc8c454e4e72691a8739aeca1d137 [file] [log] [blame]
John Spurlockbceed062014-08-10 18:04:16 -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.tiles;
18
19import android.app.PendingIntent;
20import android.content.BroadcastReceiver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.graphics.Bitmap;
25import android.graphics.BitmapFactory;
26import android.graphics.drawable.BitmapDrawable;
27import android.graphics.drawable.Drawable;
28import android.os.UserHandle;
29import android.text.TextUtils;
30import android.util.Log;
31
Chris Wren457a21c2015-05-06 17:50:34 -040032import com.android.internal.logging.MetricsLogger;
Chris Wrenf6e9228b2016-01-26 18:04:35 -050033import com.android.internal.logging.MetricsProto.MetricsEvent;
John Spurlockbceed062014-08-10 18:04:16 -040034import com.android.systemui.qs.QSTile;
35
John Spurlock2d695812014-10-30 13:25:21 -040036import java.util.Arrays;
37import java.util.Objects;
38
John Spurlockbceed062014-08-10 18:04:16 -040039public class IntentTile extends QSTile<QSTile.State> {
40 public static final String PREFIX = "intent(";
41
42 private PendingIntent mOnClick;
43 private String mOnClickUri;
John Spurlockc247b8f2014-11-06 23:06:25 -050044 private PendingIntent mOnLongClick;
45 private String mOnLongClickUri;
John Spurlockbceed062014-08-10 18:04:16 -040046 private int mCurrentUserId;
Chris Wren457a21c2015-05-06 17:50:34 -040047 private String mIntentPackage;
John Spurlockbceed062014-08-10 18:04:16 -040048
Jason Monk878c0932015-07-21 10:18:01 -040049 private Intent mLastIntent;
50
John Spurlockbceed062014-08-10 18:04:16 -040051 private IntentTile(Host host, String action) {
52 super(host);
53 mContext.registerReceiver(mReceiver, new IntentFilter(action));
54 }
55
56 @Override
57 protected void handleDestroy() {
58 super.handleDestroy();
59 mContext.unregisterReceiver(mReceiver);
60 }
61
62 public static QSTile<?> create(Host host, String spec) {
63 if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) {
64 throw new IllegalArgumentException("Bad intent tile spec: " + spec);
65 }
66 final String action = spec.substring(PREFIX.length(), spec.length() - 1);
67 if (action.isEmpty()) {
68 throw new IllegalArgumentException("Empty intent tile spec action");
69 }
70 return new IntentTile(host, action);
71 }
72
73 @Override
74 public void setListening(boolean listening) {
75 }
76
77 @Override
Jason Monk62b63a02016-02-02 15:15:31 -050078 public State newTileState() {
John Spurlockbceed062014-08-10 18:04:16 -040079 return new State();
80 }
81
82 @Override
83 protected void handleUserSwitch(int newUserId) {
84 super.handleUserSwitch(newUserId);
85 mCurrentUserId = newUserId;
86 }
87
88 @Override
89 protected void handleClick() {
Chris Wren9e7283f2015-05-08 17:23:47 -040090 MetricsLogger.action(mContext, getMetricsCategory(), mIntentPackage);
John Spurlockc247b8f2014-11-06 23:06:25 -050091 sendIntent("click", mOnClick, mOnClickUri);
92 }
93
94 @Override
Jason Monk76c67aa2016-02-19 14:49:42 -050095 public Intent getLongClickIntent() {
96 return null;
97 }
98
99 @Override
John Spurlockc247b8f2014-11-06 23:06:25 -0500100 protected void handleLongClick() {
101 sendIntent("long-click", mOnLongClick, mOnLongClickUri);
102 }
103
104 private void sendIntent(String type, PendingIntent pi, String uri) {
John Spurlockbceed062014-08-10 18:04:16 -0400105 try {
John Spurlockc247b8f2014-11-06 23:06:25 -0500106 if (pi != null) {
Jason Monkee43cdf2015-06-19 14:20:46 -0400107 if (pi.isActivity()) {
Adrian Roos62692b22015-09-11 17:46:23 -0700108 getHost().startActivityDismissingKeyguard(pi);
Jason Monkee43cdf2015-06-19 14:20:46 -0400109 } else {
110 pi.send();
111 }
John Spurlockc247b8f2014-11-06 23:06:25 -0500112 } else if (uri != null) {
113 final Intent intent = Intent.parseUri(uri, Intent.URI_INTENT_SCHEME);
John Spurlockbceed062014-08-10 18:04:16 -0400114 mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
115 }
116 } catch (Throwable t) {
John Spurlockc247b8f2014-11-06 23:06:25 -0500117 Log.w(TAG, "Error sending " + type + " intent", t);
John Spurlockbceed062014-08-10 18:04:16 -0400118 }
119 }
120
121 @Override
Jason Monk39c98e62016-03-16 09:18:35 -0400122 public CharSequence getTileLabel() {
123 return getState().label;
124 }
125
126 @Override
John Spurlockbceed062014-08-10 18:04:16 -0400127 protected void handleUpdateState(State state, Object arg) {
Jason Monk878c0932015-07-21 10:18:01 -0400128 Intent intent = (Intent) arg;
129 if (intent == null) {
130 if (mLastIntent == null) {
131 return;
132 }
133 // No intent but need to refresh state, just use the last one.
134 intent = mLastIntent;
135 }
136 // Save the last one in case we need it later.
137 mLastIntent = intent;
John Spurlockbceed062014-08-10 18:04:16 -0400138 state.contentDescription = intent.getStringExtra("contentDescription");
139 state.label = intent.getStringExtra("label");
John Spurlockbceed062014-08-10 18:04:16 -0400140 state.icon = null;
141 final byte[] iconBitmap = intent.getByteArrayExtra("iconBitmap");
142 if (iconBitmap != null) {
143 try {
John Spurlock2d695812014-10-30 13:25:21 -0400144 state.icon = new BytesIcon(iconBitmap);
John Spurlockbceed062014-08-10 18:04:16 -0400145 } catch (Throwable t) {
146 Log.w(TAG, "Error loading icon bitmap, length " + iconBitmap.length, t);
147 }
148 } else {
149 final int iconId = intent.getIntExtra("iconId", 0);
150 if (iconId != 0) {
151 final String iconPackage = intent.getStringExtra("iconPackage");
152 if (!TextUtils.isEmpty(iconPackage)) {
John Spurlock2d695812014-10-30 13:25:21 -0400153 state.icon = new PackageDrawableIcon(iconPackage, iconId);
John Spurlockbceed062014-08-10 18:04:16 -0400154 } else {
John Spurlock2d695812014-10-30 13:25:21 -0400155 state.icon = ResourceIcon.get(iconId);
John Spurlockbceed062014-08-10 18:04:16 -0400156 }
157 }
158 }
159 mOnClick = intent.getParcelableExtra("onClick");
160 mOnClickUri = intent.getStringExtra("onClickUri");
John Spurlockc247b8f2014-11-06 23:06:25 -0500161 mOnLongClick = intent.getParcelableExtra("onLongClick");
162 mOnLongClickUri = intent.getStringExtra("onLongClickUri");
Chris Wren457a21c2015-05-06 17:50:34 -0400163 mIntentPackage = intent.getStringExtra("package");
Chris Wren9e7283f2015-05-08 17:23:47 -0400164 mIntentPackage = mIntentPackage == null ? "" : mIntentPackage;
Chris Wren457a21c2015-05-06 17:50:34 -0400165 }
166
167 @Override
168 public int getMetricsCategory() {
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500169 return MetricsEvent.QS_INTENT;
Chris Wren457a21c2015-05-06 17:50:34 -0400170 }
171
John Spurlockbceed062014-08-10 18:04:16 -0400172 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
173 @Override
174 public void onReceive(Context context, Intent intent) {
175 refreshState(intent);
176 }
177 };
John Spurlock2d695812014-10-30 13:25:21 -0400178
179 private static class BytesIcon extends Icon {
180 private final byte[] mBytes;
181
182 public BytesIcon(byte[] bytes) {
183 mBytes = bytes;
184 }
185
186 @Override
187 public Drawable getDrawable(Context context) {
188 final Bitmap b = BitmapFactory.decodeByteArray(mBytes, 0, mBytes.length);
189 return new BitmapDrawable(context.getResources(), b);
190 }
191
192 @Override
193 public boolean equals(Object o) {
194 return o instanceof BytesIcon && Arrays.equals(((BytesIcon) o).mBytes, mBytes);
195 }
196
197 @Override
198 public String toString() {
199 return String.format("BytesIcon[len=%s]", mBytes.length);
200 }
201 }
202
203 private class PackageDrawableIcon extends Icon {
204 private final String mPackage;
205 private final int mResId;
206
207 public PackageDrawableIcon(String pkg, int resId) {
208 mPackage = pkg;
209 mResId = resId;
210 }
211
212 @Override
213 public boolean equals(Object o) {
214 if (!(o instanceof PackageDrawableIcon)) return false;
215 final PackageDrawableIcon other = (PackageDrawableIcon) o;
216 return Objects.equals(other.mPackage, mPackage) && other.mResId == mResId;
217 }
218
219 @Override
220 public Drawable getDrawable(Context context) {
221 try {
222 return context.createPackageContext(mPackage, 0).getDrawable(mResId);
223 } catch (Throwable t) {
224 Log.w(TAG, "Error loading package drawable pkg=" + mPackage + " id=" + mResId, t);
225 return null;
226 }
227 }
228
229 @Override
230 public String toString() {
231 return String.format("PackageDrawableIcon[pkg=%s,id=0x%08x]", mPackage, mResId);
232 }
233 }
John Spurlockbceed062014-08-10 18:04:16 -0400234}