Home | History | Annotate | Download | only in tiles
      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.internal.logging.MetricsLogger;
     33 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
     34 import com.android.systemui.Dependency;
     35 import com.android.systemui.plugins.ActivityStarter;
     36 import com.android.systemui.qs.QSHost;
     37 import com.android.systemui.plugins.qs.QSTile;
     38 import com.android.systemui.plugins.qs.QSTile.State;
     39 import com.android.systemui.qs.tileimpl.QSTileImpl;
     40 
     41 import java.util.Arrays;
     42 import java.util.Objects;
     43 
     44 public class IntentTile extends QSTileImpl<State> {
     45     public static final String PREFIX = "intent(";
     46 
     47     private PendingIntent mOnClick;
     48     private String mOnClickUri;
     49     private PendingIntent mOnLongClick;
     50     private String mOnLongClickUri;
     51     private int mCurrentUserId;
     52     private String mIntentPackage;
     53 
     54     private Intent mLastIntent;
     55 
     56     private IntentTile(QSHost host, String action) {
     57         super(host);
     58         mContext.registerReceiver(mReceiver, new IntentFilter(action));
     59     }
     60 
     61     @Override
     62     protected void handleDestroy() {
     63         super.handleDestroy();
     64         mContext.unregisterReceiver(mReceiver);
     65     }
     66 
     67     public static QSTile create(QSHost host, String spec) {
     68         if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) {
     69             throw new IllegalArgumentException("Bad intent tile spec: " + spec);
     70         }
     71         final String action = spec.substring(PREFIX.length(), spec.length() - 1);
     72         if (action.isEmpty()) {
     73             throw new IllegalArgumentException("Empty intent tile spec action");
     74         }
     75         return new IntentTile(host, action);
     76     }
     77 
     78     @Override
     79     public void handleSetListening(boolean listening) {
     80     }
     81 
     82     @Override
     83     public State newTileState() {
     84         return new State();
     85     }
     86 
     87     @Override
     88     protected void handleUserSwitch(int newUserId) {
     89         super.handleUserSwitch(newUserId);
     90         mCurrentUserId = newUserId;
     91     }
     92 
     93     @Override
     94     protected void handleClick() {
     95         sendIntent("click", mOnClick, mOnClickUri);
     96     }
     97 
     98     @Override
     99     public Intent getLongClickIntent() {
    100         return null;
    101     }
    102 
    103     @Override
    104     protected void handleLongClick() {
    105         sendIntent("long-click", mOnLongClick, mOnLongClickUri);
    106     }
    107 
    108     private void sendIntent(String type, PendingIntent pi, String uri) {
    109         try {
    110             if (pi != null) {
    111                 if (pi.isActivity()) {
    112                     Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(pi);
    113                 } else {
    114                     pi.send();
    115                 }
    116             } else if (uri != null) {
    117                 final Intent intent = Intent.parseUri(uri, Intent.URI_INTENT_SCHEME);
    118                 mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
    119             }
    120         } catch (Throwable t) {
    121             Log.w(TAG, "Error sending " + type + " intent", t);
    122         }
    123     }
    124 
    125     @Override
    126     public CharSequence getTileLabel() {
    127         return getState().label;
    128     }
    129 
    130     @Override
    131     protected void handleUpdateState(State state, Object arg) {
    132         Intent intent = (Intent) arg;
    133         if (intent == null) {
    134             if (mLastIntent == null) {
    135                 return;
    136             }
    137             // No intent but need to refresh state, just use the last one.
    138             intent = mLastIntent;
    139         }
    140         // Save the last one in case we need it later.
    141         mLastIntent = intent;
    142         state.contentDescription = intent.getStringExtra("contentDescription");
    143         state.label = intent.getStringExtra("label");
    144         state.icon = null;
    145         final byte[] iconBitmap = intent.getByteArrayExtra("iconBitmap");
    146         if (iconBitmap != null) {
    147             try {
    148                 state.icon = new BytesIcon(iconBitmap);
    149             } catch (Throwable t) {
    150                 Log.w(TAG, "Error loading icon bitmap, length " + iconBitmap.length, t);
    151             }
    152         } else {
    153             final int iconId = intent.getIntExtra("iconId", 0);
    154             if (iconId != 0) {
    155                 final String iconPackage = intent.getStringExtra("iconPackage");
    156                 if (!TextUtils.isEmpty(iconPackage)) {
    157                     state.icon = new PackageDrawableIcon(iconPackage, iconId);
    158                 } else {
    159                     state.icon = ResourceIcon.get(iconId);
    160                 }
    161             }
    162         }
    163         mOnClick = intent.getParcelableExtra("onClick");
    164         mOnClickUri = intent.getStringExtra("onClickUri");
    165         mOnLongClick = intent.getParcelableExtra("onLongClick");
    166         mOnLongClickUri = intent.getStringExtra("onLongClickUri");
    167         mIntentPackage = intent.getStringExtra("package");
    168         mIntentPackage = mIntentPackage == null ? "" : mIntentPackage;
    169     }
    170 
    171     @Override
    172     public int getMetricsCategory() {
    173         return MetricsEvent.QS_INTENT;
    174     }
    175 
    176     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    177         @Override
    178         public void onReceive(Context context, Intent intent) {
    179             refreshState(intent);
    180         }
    181     };
    182 
    183     private static class BytesIcon extends Icon {
    184         private final byte[] mBytes;
    185 
    186         public BytesIcon(byte[] bytes) {
    187             mBytes = bytes;
    188         }
    189 
    190         @Override
    191         public Drawable getDrawable(Context context) {
    192             final Bitmap b = BitmapFactory.decodeByteArray(mBytes, 0, mBytes.length);
    193             return new BitmapDrawable(context.getResources(), b);
    194         }
    195 
    196         @Override
    197         public boolean equals(Object o) {
    198             return o instanceof BytesIcon && Arrays.equals(((BytesIcon) o).mBytes, mBytes);
    199         }
    200 
    201         @Override
    202         public String toString() {
    203             return String.format("BytesIcon[len=%s]", mBytes.length);
    204         }
    205     }
    206 
    207     private class PackageDrawableIcon extends Icon {
    208         private final String mPackage;
    209         private final int mResId;
    210 
    211         public PackageDrawableIcon(String pkg, int resId) {
    212             mPackage = pkg;
    213             mResId = resId;
    214         }
    215 
    216         @Override
    217         public boolean equals(Object o) {
    218             if (!(o instanceof PackageDrawableIcon)) return false;
    219             final PackageDrawableIcon other = (PackageDrawableIcon) o;
    220             return Objects.equals(other.mPackage, mPackage) && other.mResId == mResId;
    221         }
    222 
    223         @Override
    224         public Drawable getDrawable(Context context) {
    225             try {
    226                 return context.createPackageContext(mPackage, 0).getDrawable(mResId);
    227             } catch (Throwable t) {
    228                 Log.w(TAG, "Error loading package drawable pkg=" + mPackage + " id=" + mResId, t);
    229                 return null;
    230             }
    231         }
    232 
    233         @Override
    234         public String toString() {
    235             return String.format("PackageDrawableIcon[pkg=%s,id=0x%08x]", mPackage, mResId);
    236         }
    237     }
    238 }
    239