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 17 package com.android.systemui.qs.tiles; 18 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; 31 32 import com.android.systemui.qs.QSTile; 33 34 import java.util.Arrays; 35 import java.util.Objects; 36 37 public class IntentTile extends QSTile<QSTile.State> { 38 public static final String PREFIX = "intent("; 39 40 private PendingIntent mOnClick; 41 private String mOnClickUri; 42 private PendingIntent mOnLongClick; 43 private String mOnLongClickUri; 44 private int mCurrentUserId; 45 46 private IntentTile(Host host, String action) { 47 super(host); 48 mContext.registerReceiver(mReceiver, new IntentFilter(action)); 49 } 50 51 @Override 52 protected void handleDestroy() { 53 super.handleDestroy(); 54 mContext.unregisterReceiver(mReceiver); 55 } 56 57 public static QSTile<?> create(Host host, String spec) { 58 if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) { 59 throw new IllegalArgumentException("Bad intent tile spec: " + spec); 60 } 61 final String action = spec.substring(PREFIX.length(), spec.length() - 1); 62 if (action.isEmpty()) { 63 throw new IllegalArgumentException("Empty intent tile spec action"); 64 } 65 return new IntentTile(host, action); 66 } 67 68 @Override 69 public void setListening(boolean listening) { 70 } 71 72 @Override 73 protected State newTileState() { 74 return new State(); 75 } 76 77 @Override 78 protected void handleUserSwitch(int newUserId) { 79 super.handleUserSwitch(newUserId); 80 mCurrentUserId = newUserId; 81 } 82 83 @Override 84 protected void handleClick() { 85 sendIntent("click", mOnClick, mOnClickUri); 86 } 87 88 @Override 89 protected void handleLongClick() { 90 sendIntent("long-click", mOnLongClick, mOnLongClickUri); 91 } 92 93 private void sendIntent(String type, PendingIntent pi, String uri) { 94 try { 95 if (pi != null) { 96 pi.send(); 97 } else if (uri != null) { 98 final Intent intent = Intent.parseUri(uri, Intent.URI_INTENT_SCHEME); 99 mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId)); 100 } 101 } catch (Throwable t) { 102 Log.w(TAG, "Error sending " + type + " intent", t); 103 } 104 } 105 106 @Override 107 protected void handleUpdateState(State state, Object arg) { 108 if (!(arg instanceof Intent)) return; 109 final Intent intent = (Intent) arg; 110 state.visible = intent.getBooleanExtra("visible", true); 111 state.contentDescription = intent.getStringExtra("contentDescription"); 112 state.label = intent.getStringExtra("label"); 113 state.icon = null; 114 final byte[] iconBitmap = intent.getByteArrayExtra("iconBitmap"); 115 if (iconBitmap != null) { 116 try { 117 state.icon = new BytesIcon(iconBitmap); 118 } catch (Throwable t) { 119 Log.w(TAG, "Error loading icon bitmap, length " + iconBitmap.length, t); 120 } 121 } else { 122 final int iconId = intent.getIntExtra("iconId", 0); 123 if (iconId != 0) { 124 final String iconPackage = intent.getStringExtra("iconPackage"); 125 if (!TextUtils.isEmpty(iconPackage)) { 126 state.icon = new PackageDrawableIcon(iconPackage, iconId); 127 } else { 128 state.icon = ResourceIcon.get(iconId); 129 } 130 } 131 } 132 mOnClick = intent.getParcelableExtra("onClick"); 133 mOnClickUri = intent.getStringExtra("onClickUri"); 134 mOnLongClick = intent.getParcelableExtra("onLongClick"); 135 mOnLongClickUri = intent.getStringExtra("onLongClickUri"); 136 } 137 138 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 139 @Override 140 public void onReceive(Context context, Intent intent) { 141 refreshState(intent); 142 } 143 }; 144 145 private static class BytesIcon extends Icon { 146 private final byte[] mBytes; 147 148 public BytesIcon(byte[] bytes) { 149 mBytes = bytes; 150 } 151 152 @Override 153 public Drawable getDrawable(Context context) { 154 final Bitmap b = BitmapFactory.decodeByteArray(mBytes, 0, mBytes.length); 155 return new BitmapDrawable(context.getResources(), b); 156 } 157 158 @Override 159 public boolean equals(Object o) { 160 return o instanceof BytesIcon && Arrays.equals(((BytesIcon) o).mBytes, mBytes); 161 } 162 163 @Override 164 public String toString() { 165 return String.format("BytesIcon[len=%s]", mBytes.length); 166 } 167 } 168 169 private class PackageDrawableIcon extends Icon { 170 private final String mPackage; 171 private final int mResId; 172 173 public PackageDrawableIcon(String pkg, int resId) { 174 mPackage = pkg; 175 mResId = resId; 176 } 177 178 @Override 179 public boolean equals(Object o) { 180 if (!(o instanceof PackageDrawableIcon)) return false; 181 final PackageDrawableIcon other = (PackageDrawableIcon) o; 182 return Objects.equals(other.mPackage, mPackage) && other.mResId == mResId; 183 } 184 185 @Override 186 public Drawable getDrawable(Context context) { 187 try { 188 return context.createPackageContext(mPackage, 0).getDrawable(mResId); 189 } catch (Throwable t) { 190 Log.w(TAG, "Error loading package drawable pkg=" + mPackage + " id=" + mResId, t); 191 return null; 192 } 193 } 194 195 @Override 196 public String toString() { 197 return String.format("PackageDrawableIcon[pkg=%s,id=0x%08x]", mPackage, mResId); 198 } 199 } 200 } 201