IntentTile.java revision 76c67aa361f65dfb2f5e03d06cc1ccebce9cecd9
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.internal.logging.MetricsProto.MetricsEvent;
34import com.android.systemui.qs.QSTile;
35
36import java.util.Arrays;
37import java.util.Objects;
38
39public class IntentTile extends QSTile<QSTile.State> {
40    public static final String PREFIX = "intent(";
41
42    private PendingIntent mOnClick;
43    private String mOnClickUri;
44    private PendingIntent mOnLongClick;
45    private String mOnLongClickUri;
46    private int mCurrentUserId;
47    private String mIntentPackage;
48
49    private Intent mLastIntent;
50
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
78    public State newTileState() {
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() {
90        MetricsLogger.action(mContext, getMetricsCategory(), mIntentPackage);
91        sendIntent("click", mOnClick, mOnClickUri);
92    }
93
94    @Override
95    public Intent getLongClickIntent() {
96        return null;
97    }
98
99    @Override
100    protected void handleLongClick() {
101        sendIntent("long-click", mOnLongClick, mOnLongClickUri);
102    }
103
104    private void sendIntent(String type, PendingIntent pi, String uri) {
105        try {
106            if (pi != null) {
107                if (pi.isActivity()) {
108                    getHost().startActivityDismissingKeyguard(pi);
109                } else {
110                    pi.send();
111                }
112            } else if (uri != null) {
113                final Intent intent = Intent.parseUri(uri, Intent.URI_INTENT_SCHEME);
114                mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
115            }
116        } catch (Throwable t) {
117            Log.w(TAG, "Error sending " + type + " intent", t);
118        }
119    }
120
121    @Override
122    protected void handleUpdateState(State state, Object arg) {
123        Intent intent = (Intent) arg;
124        if (intent == null) {
125            if (mLastIntent == null) {
126                return;
127            }
128            // No intent but need to refresh state, just use the last one.
129            intent = mLastIntent;
130        }
131        // Save the last one in case we need it later.
132        mLastIntent = intent;
133        state.contentDescription = intent.getStringExtra("contentDescription");
134        state.label = intent.getStringExtra("label");
135        state.icon = null;
136        final byte[] iconBitmap = intent.getByteArrayExtra("iconBitmap");
137        if (iconBitmap != null) {
138            try {
139                state.icon = new BytesIcon(iconBitmap);
140            } catch (Throwable t) {
141                Log.w(TAG, "Error loading icon bitmap, length " + iconBitmap.length, t);
142            }
143        } else {
144            final int iconId = intent.getIntExtra("iconId", 0);
145            if (iconId != 0) {
146                final String iconPackage = intent.getStringExtra("iconPackage");
147                if (!TextUtils.isEmpty(iconPackage)) {
148                    state.icon = new PackageDrawableIcon(iconPackage, iconId);
149                } else {
150                    state.icon = ResourceIcon.get(iconId);
151                }
152            }
153        }
154        mOnClick = intent.getParcelableExtra("onClick");
155        mOnClickUri = intent.getStringExtra("onClickUri");
156        mOnLongClick = intent.getParcelableExtra("onLongClick");
157        mOnLongClickUri = intent.getStringExtra("onLongClickUri");
158        mIntentPackage = intent.getStringExtra("package");
159        mIntentPackage = mIntentPackage == null ? "" : mIntentPackage;
160    }
161
162    @Override
163    public int getMetricsCategory() {
164        return MetricsEvent.QS_INTENT;
165    }
166
167    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
168        @Override
169        public void onReceive(Context context, Intent intent) {
170            refreshState(intent);
171        }
172    };
173
174    private static class BytesIcon extends Icon {
175        private final byte[] mBytes;
176
177        public BytesIcon(byte[] bytes) {
178            mBytes = bytes;
179        }
180
181        @Override
182        public Drawable getDrawable(Context context) {
183            final Bitmap b = BitmapFactory.decodeByteArray(mBytes, 0, mBytes.length);
184            return new BitmapDrawable(context.getResources(), b);
185        }
186
187        @Override
188        public boolean equals(Object o) {
189            return o instanceof BytesIcon && Arrays.equals(((BytesIcon) o).mBytes, mBytes);
190        }
191
192        @Override
193        public String toString() {
194            return String.format("BytesIcon[len=%s]", mBytes.length);
195        }
196    }
197
198    private class PackageDrawableIcon extends Icon {
199        private final String mPackage;
200        private final int mResId;
201
202        public PackageDrawableIcon(String pkg, int resId) {
203            mPackage = pkg;
204            mResId = resId;
205        }
206
207        @Override
208        public boolean equals(Object o) {
209            if (!(o instanceof PackageDrawableIcon)) return false;
210            final PackageDrawableIcon other = (PackageDrawableIcon) o;
211            return Objects.equals(other.mPackage, mPackage) && other.mResId == mResId;
212        }
213
214        @Override
215        public Drawable getDrawable(Context context) {
216            try {
217                return context.createPackageContext(mPackage, 0).getDrawable(mResId);
218            } catch (Throwable t) {
219                Log.w(TAG, "Error loading package drawable pkg=" + mPackage + " id=" + mResId, t);
220                return null;
221            }
222        }
223
224        @Override
225        public String toString() {
226            return String.format("PackageDrawableIcon[pkg=%s,id=0x%08x]", mPackage, mResId);
227        }
228    }
229}
230