IntentTile.java revision 878c093336b324962f1b659aa29c79caed75ef2f
1/* 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 32import com.android.internal.logging.MetricsLogger; 33import com.android.systemui.qs.QSTile; 34 35import java.util.Arrays; 36import java.util.Objects; 37 38public class IntentTile extends QSTile<QSTile.State> { 39 public static final String PREFIX = "intent("; 40 41 private PendingIntent mOnClick; 42 private String mOnClickUri; 43 private PendingIntent mOnLongClick; 44 private String mOnLongClickUri; 45 private int mCurrentUserId; 46 private String mIntentPackage; 47 48 private Intent mLastIntent; 49 50 private IntentTile(Host host, String action) { 51 super(host); 52 mContext.registerReceiver(mReceiver, new IntentFilter(action)); 53 } 54 55 @Override 56 protected void handleDestroy() { 57 super.handleDestroy(); 58 mContext.unregisterReceiver(mReceiver); 59 } 60 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); 64 } 65 final String action = spec.substring(PREFIX.length(), spec.length() - 1); 66 if (action.isEmpty()) { 67 throw new IllegalArgumentException("Empty intent tile spec action"); 68 } 69 return new IntentTile(host, action); 70 } 71 72 @Override 73 public void setListening(boolean listening) { 74 } 75 76 @Override 77 protected State newTileState() { 78 return new State(); 79 } 80 81 @Override 82 protected void handleUserSwitch(int newUserId) { 83 super.handleUserSwitch(newUserId); 84 mCurrentUserId = newUserId; 85 } 86 87 @Override 88 protected void handleClick() { 89 MetricsLogger.action(mContext, getMetricsCategory(), mIntentPackage); 90 sendIntent("click", mOnClick, mOnClickUri); 91 } 92 93 @Override 94 protected void handleLongClick() { 95 sendIntent("long-click", mOnLongClick, mOnLongClickUri); 96 } 97 98 private void sendIntent(String type, PendingIntent pi, String uri) { 99 try { 100 if (pi != null) { 101 if (pi.isActivity()) { 102 getHost().startActivityDismissingKeyguard(pi.getIntent()); 103 } else { 104 pi.send(); 105 } 106 } else if (uri != null) { 107 final Intent intent = Intent.parseUri(uri, Intent.URI_INTENT_SCHEME); 108 mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId)); 109 } 110 } catch (Throwable t) { 111 Log.w(TAG, "Error sending " + type + " intent", t); 112 } 113 } 114 115 @Override 116 protected void handleUpdateState(State state, Object arg) { 117 Intent intent = (Intent) arg; 118 if (intent == null) { 119 if (mLastIntent == null) { 120 return; 121 } 122 // No intent but need to refresh state, just use the last one. 123 intent = mLastIntent; 124 } 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"); 130 state.icon = null; 131 final byte[] iconBitmap = intent.getByteArrayExtra("iconBitmap"); 132 if (iconBitmap != null) { 133 try { 134 state.icon = new BytesIcon(iconBitmap); 135 } catch (Throwable t) { 136 Log.w(TAG, "Error loading icon bitmap, length " + iconBitmap.length, t); 137 } 138 } else { 139 final int iconId = intent.getIntExtra("iconId", 0); 140 if (iconId != 0) { 141 final String iconPackage = intent.getStringExtra("iconPackage"); 142 if (!TextUtils.isEmpty(iconPackage)) { 143 state.icon = new PackageDrawableIcon(iconPackage, iconId); 144 } else { 145 state.icon = ResourceIcon.get(iconId); 146 } 147 } 148 } 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; 155 } 156 157 @Override 158 public int getMetricsCategory() { 159 return MetricsLogger.QS_INTENT; 160 } 161 162 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 163 @Override 164 public void onReceive(Context context, Intent intent) { 165 refreshState(intent); 166 } 167 }; 168 169 private static class BytesIcon extends Icon { 170 private final byte[] mBytes; 171 172 public BytesIcon(byte[] bytes) { 173 mBytes = bytes; 174 } 175 176 @Override 177 public Drawable getDrawable(Context context) { 178 final Bitmap b = BitmapFactory.decodeByteArray(mBytes, 0, mBytes.length); 179 return new BitmapDrawable(context.getResources(), b); 180 } 181 182 @Override 183 public boolean equals(Object o) { 184 return o instanceof BytesIcon && Arrays.equals(((BytesIcon) o).mBytes, mBytes); 185 } 186 187 @Override 188 public String toString() { 189 return String.format("BytesIcon[len=%s]", mBytes.length); 190 } 191 } 192 193 private class PackageDrawableIcon extends Icon { 194 private final String mPackage; 195 private final int mResId; 196 197 public PackageDrawableIcon(String pkg, int resId) { 198 mPackage = pkg; 199 mResId = resId; 200 } 201 202 @Override 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; 207 } 208 209 @Override 210 public Drawable getDrawable(Context context) { 211 try { 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); 215 return null; 216 } 217 } 218 219 @Override 220 public String toString() { 221 return String.format("PackageDrawableIcon[pkg=%s,id=0x%08x]", mPackage, mResId); 222 } 223 } 224} 225