2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.systemui.qs.tiles;
19 import android.app.PendingIntent;
20 import android.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.graphics.Bitmap;
25 import android.graphics.BitmapFactory;
26 import android.graphics.drawable.BitmapDrawable;
27 import android.graphics.drawable.Drawable;
28 import android.os.UserHandle;
29 import android.text.TextUtils;
30 import android.util.Log;
32 import com.android.internal.logging.MetricsLogger;
33 import com.android.systemui.qs.QSTile;
35 import java.util.Arrays;
36 import java.util.Objects;
38 public class IntentTile extends QSTile<QSTile.State> {
39 public static final String PREFIX = "intent(";
41 private PendingIntent mOnClick;
42 private String mOnClickUri;
43 private PendingIntent mOnLongClick;
44 private String mOnLongClickUri;
45 private int mCurrentUserId;
46 private String mIntentPackage;
48 private Intent mLastIntent;
50 private IntentTile(Host host, String action) {
52 mContext.registerReceiver(mReceiver, new IntentFilter(action));
56 protected void handleDestroy() {
57 super.handleDestroy();
58 mContext.unregisterReceiver(mReceiver);
61 public static QSTile<?> create(Host host, String spec) {
62 if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) {
63 throw new IllegalArgumentException("Bad intent tile spec: " + spec);
65 final String action = spec.substring(PREFIX.length(), spec.length() - 1);
66 if (action.isEmpty()) {
67 throw new IllegalArgumentException("Empty intent tile spec action");
69 return new IntentTile(host, action);
73 public void setListening(boolean listening) {
77 protected State newTileState() {
82 protected void handleUserSwitch(int newUserId) {
83 super.handleUserSwitch(newUserId);
84 mCurrentUserId = newUserId;
88 protected void handleClick() {
89 MetricsLogger.action(mContext, getMetricsCategory(), mIntentPackage);
90 sendIntent("click", mOnClick, mOnClickUri);
94 protected void handleLongClick() {
95 sendIntent("long-click", mOnLongClick, mOnLongClickUri);
98 private void sendIntent(String type, PendingIntent pi, String uri) {
101 if (pi.isActivity()) {
102 getHost().startActivityDismissingKeyguard(pi);
106 } else if (uri != null) {
107 final Intent intent = Intent.parseUri(uri, Intent.URI_INTENT_SCHEME);
108 mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
110 } catch (Throwable t) {
111 Log.w(TAG, "Error sending " + type + " intent", t);
116 protected void handleUpdateState(State state, Object arg) {
117 Intent intent = (Intent) arg;
118 if (intent == null) {
119 if (mLastIntent == null) {
122 // No intent but need to refresh state, just use the last one.
123 intent = mLastIntent;
125 // Save the last one in case we need it later.
126 mLastIntent = intent;
127 state.visible = intent.getBooleanExtra("visible", true);
128 state.contentDescription = intent.getStringExtra("contentDescription");
129 state.label = intent.getStringExtra("label");
131 final byte[] iconBitmap = intent.getByteArrayExtra("iconBitmap");
132 if (iconBitmap != null) {
134 state.icon = new BytesIcon(iconBitmap);
135 } catch (Throwable t) {
136 Log.w(TAG, "Error loading icon bitmap, length " + iconBitmap.length, t);
139 final int iconId = intent.getIntExtra("iconId", 0);
141 final String iconPackage = intent.getStringExtra("iconPackage");
142 if (!TextUtils.isEmpty(iconPackage)) {
143 state.icon = new PackageDrawableIcon(iconPackage, iconId);
145 state.icon = ResourceIcon.get(iconId);
149 mOnClick = intent.getParcelableExtra("onClick");
150 mOnClickUri = intent.getStringExtra("onClickUri");
151 mOnLongClick = intent.getParcelableExtra("onLongClick");
152 mOnLongClickUri = intent.getStringExtra("onLongClickUri");
153 mIntentPackage = intent.getStringExtra("package");
154 mIntentPackage = mIntentPackage == null ? "" : mIntentPackage;
158 public int getMetricsCategory() {
159 return MetricsLogger.QS_INTENT;
162 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
164 public void onReceive(Context context, Intent intent) {
165 refreshState(intent);
169 private static class BytesIcon extends Icon {
170 private final byte[] mBytes;
172 public BytesIcon(byte[] bytes) {
177 public Drawable getDrawable(Context context) {
178 final Bitmap b = BitmapFactory.decodeByteArray(mBytes, 0, mBytes.length);
179 return new BitmapDrawable(context.getResources(), b);
183 public boolean equals(Object o) {
184 return o instanceof BytesIcon && Arrays.equals(((BytesIcon) o).mBytes, mBytes);
188 public String toString() {
189 return String.format("BytesIcon[len=%s]", mBytes.length);
193 private class PackageDrawableIcon extends Icon {
194 private final String mPackage;
195 private final int mResId;
197 public PackageDrawableIcon(String pkg, int resId) {
203 public boolean equals(Object o) {
204 if (!(o instanceof PackageDrawableIcon)) return false;
205 final PackageDrawableIcon other = (PackageDrawableIcon) o;
206 return Objects.equals(other.mPackage, mPackage) && other.mResId == mResId;
210 public Drawable getDrawable(Context context) {
212 return context.createPackageContext(mPackage, 0).getDrawable(mResId);
213 } catch (Throwable t) {
214 Log.w(TAG, "Error loading package drawable pkg=" + mPackage + " id=" + mResId, t);
220 public String toString() {
221 return String.format("PackageDrawableIcon[pkg=%s,id=0x%08x]", mPackage, mResId);