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