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