John Spurlock | bceed06 | 2014-08-10 18:04:16 -0400 | [diff] [blame] | 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 | |
Chris Wren | 457a21c | 2015-05-06 17:50:34 -0400 | [diff] [blame] | 32 | import com.android.internal.logging.MetricsLogger; |
Chris Wren | f6e9228b | 2016-01-26 18:04:35 -0500 | [diff] [blame] | 33 | import com.android.internal.logging.MetricsProto.MetricsEvent; |
John Spurlock | bceed06 | 2014-08-10 18:04:16 -0400 | [diff] [blame] | 34 | import com.android.systemui.qs.QSTile; |
| 35 | |
John Spurlock | 2d69581 | 2014-10-30 13:25:21 -0400 | [diff] [blame] | 36 | import java.util.Arrays; |
| 37 | import java.util.Objects; |
| 38 | |
John Spurlock | bceed06 | 2014-08-10 18:04:16 -0400 | [diff] [blame] | 39 | public class IntentTile extends QSTile<QSTile.State> { |
| 40 | public static final String PREFIX = "intent("; |
| 41 | |
| 42 | private PendingIntent mOnClick; |
| 43 | private String mOnClickUri; |
John Spurlock | c247b8f | 2014-11-06 23:06:25 -0500 | [diff] [blame] | 44 | private PendingIntent mOnLongClick; |
| 45 | private String mOnLongClickUri; |
John Spurlock | bceed06 | 2014-08-10 18:04:16 -0400 | [diff] [blame] | 46 | private int mCurrentUserId; |
Chris Wren | 457a21c | 2015-05-06 17:50:34 -0400 | [diff] [blame] | 47 | private String mIntentPackage; |
John Spurlock | bceed06 | 2014-08-10 18:04:16 -0400 | [diff] [blame] | 48 | |
Jason Monk | 878c093 | 2015-07-21 10:18:01 -0400 | [diff] [blame] | 49 | private Intent mLastIntent; |
| 50 | |
John Spurlock | bceed06 | 2014-08-10 18:04:16 -0400 | [diff] [blame] | 51 | 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 Monk | 62b63a0 | 2016-02-02 15:15:31 -0500 | [diff] [blame] | 78 | public State newTileState() { |
John Spurlock | bceed06 | 2014-08-10 18:04:16 -0400 | [diff] [blame] | 79 | 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 Wren | 9e7283f | 2015-05-08 17:23:47 -0400 | [diff] [blame] | 90 | MetricsLogger.action(mContext, getMetricsCategory(), mIntentPackage); |
John Spurlock | c247b8f | 2014-11-06 23:06:25 -0500 | [diff] [blame] | 91 | sendIntent("click", mOnClick, mOnClickUri); |
| 92 | } |
| 93 | |
| 94 | @Override |
Jason Monk | 76c67aa | 2016-02-19 14:49:42 -0500 | [diff] [blame] | 95 | public Intent getLongClickIntent() { |
| 96 | return null; |
| 97 | } |
| 98 | |
| 99 | @Override |
John Spurlock | c247b8f | 2014-11-06 23:06:25 -0500 | [diff] [blame] | 100 | protected void handleLongClick() { |
| 101 | sendIntent("long-click", mOnLongClick, mOnLongClickUri); |
| 102 | } |
| 103 | |
| 104 | private void sendIntent(String type, PendingIntent pi, String uri) { |
John Spurlock | bceed06 | 2014-08-10 18:04:16 -0400 | [diff] [blame] | 105 | try { |
John Spurlock | c247b8f | 2014-11-06 23:06:25 -0500 | [diff] [blame] | 106 | if (pi != null) { |
Jason Monk | ee43cdf | 2015-06-19 14:20:46 -0400 | [diff] [blame] | 107 | if (pi.isActivity()) { |
Adrian Roos | 62692b2 | 2015-09-11 17:46:23 -0700 | [diff] [blame] | 108 | getHost().startActivityDismissingKeyguard(pi); |
Jason Monk | ee43cdf | 2015-06-19 14:20:46 -0400 | [diff] [blame] | 109 | } else { |
| 110 | pi.send(); |
| 111 | } |
John Spurlock | c247b8f | 2014-11-06 23:06:25 -0500 | [diff] [blame] | 112 | } else if (uri != null) { |
| 113 | final Intent intent = Intent.parseUri(uri, Intent.URI_INTENT_SCHEME); |
John Spurlock | bceed06 | 2014-08-10 18:04:16 -0400 | [diff] [blame] | 114 | mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId)); |
| 115 | } |
| 116 | } catch (Throwable t) { |
John Spurlock | c247b8f | 2014-11-06 23:06:25 -0500 | [diff] [blame] | 117 | Log.w(TAG, "Error sending " + type + " intent", t); |
John Spurlock | bceed06 | 2014-08-10 18:04:16 -0400 | [diff] [blame] | 118 | } |
| 119 | } |
| 120 | |
| 121 | @Override |
Jason Monk | 39c98e6 | 2016-03-16 09:18:35 -0400 | [diff] [blame] | 122 | public CharSequence getTileLabel() { |
| 123 | return getState().label; |
| 124 | } |
| 125 | |
| 126 | @Override |
John Spurlock | bceed06 | 2014-08-10 18:04:16 -0400 | [diff] [blame] | 127 | protected void handleUpdateState(State state, Object arg) { |
Jason Monk | 878c093 | 2015-07-21 10:18:01 -0400 | [diff] [blame] | 128 | 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 Spurlock | bceed06 | 2014-08-10 18:04:16 -0400 | [diff] [blame] | 138 | state.contentDescription = intent.getStringExtra("contentDescription"); |
| 139 | state.label = intent.getStringExtra("label"); |
John Spurlock | bceed06 | 2014-08-10 18:04:16 -0400 | [diff] [blame] | 140 | state.icon = null; |
| 141 | final byte[] iconBitmap = intent.getByteArrayExtra("iconBitmap"); |
| 142 | if (iconBitmap != null) { |
| 143 | try { |
John Spurlock | 2d69581 | 2014-10-30 13:25:21 -0400 | [diff] [blame] | 144 | state.icon = new BytesIcon(iconBitmap); |
John Spurlock | bceed06 | 2014-08-10 18:04:16 -0400 | [diff] [blame] | 145 | } 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 Spurlock | 2d69581 | 2014-10-30 13:25:21 -0400 | [diff] [blame] | 153 | state.icon = new PackageDrawableIcon(iconPackage, iconId); |
John Spurlock | bceed06 | 2014-08-10 18:04:16 -0400 | [diff] [blame] | 154 | } else { |
John Spurlock | 2d69581 | 2014-10-30 13:25:21 -0400 | [diff] [blame] | 155 | state.icon = ResourceIcon.get(iconId); |
John Spurlock | bceed06 | 2014-08-10 18:04:16 -0400 | [diff] [blame] | 156 | } |
| 157 | } |
| 158 | } |
| 159 | mOnClick = intent.getParcelableExtra("onClick"); |
| 160 | mOnClickUri = intent.getStringExtra("onClickUri"); |
John Spurlock | c247b8f | 2014-11-06 23:06:25 -0500 | [diff] [blame] | 161 | mOnLongClick = intent.getParcelableExtra("onLongClick"); |
| 162 | mOnLongClickUri = intent.getStringExtra("onLongClickUri"); |
Chris Wren | 457a21c | 2015-05-06 17:50:34 -0400 | [diff] [blame] | 163 | mIntentPackage = intent.getStringExtra("package"); |
Chris Wren | 9e7283f | 2015-05-08 17:23:47 -0400 | [diff] [blame] | 164 | mIntentPackage = mIntentPackage == null ? "" : mIntentPackage; |
Chris Wren | 457a21c | 2015-05-06 17:50:34 -0400 | [diff] [blame] | 165 | } |
| 166 | |
| 167 | @Override |
| 168 | public int getMetricsCategory() { |
Chris Wren | f6e9228b | 2016-01-26 18:04:35 -0500 | [diff] [blame] | 169 | return MetricsEvent.QS_INTENT; |
Chris Wren | 457a21c | 2015-05-06 17:50:34 -0400 | [diff] [blame] | 170 | } |
| 171 | |
John Spurlock | bceed06 | 2014-08-10 18:04:16 -0400 | [diff] [blame] | 172 | private final BroadcastReceiver mReceiver = new BroadcastReceiver() { |
| 173 | @Override |
| 174 | public void onReceive(Context context, Intent intent) { |
| 175 | refreshState(intent); |
| 176 | } |
| 177 | }; |
John Spurlock | 2d69581 | 2014-10-30 13:25:21 -0400 | [diff] [blame] | 178 | |
| 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 Spurlock | bceed06 | 2014-08-10 18:04:16 -0400 | [diff] [blame] | 234 | } |