Home | History | Annotate | Download | only in shortcutdemo
      1 /*
      2  * Copyright (C) 2016 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 package com.example.android.pm.shortcutdemo;
     17 
     18 import android.app.Activity;
     19 import android.app.Notification;
     20 import android.app.Notification.Action;
     21 import android.app.Notification.Builder;
     22 import android.app.NotificationManager;
     23 import android.app.PendingIntent;
     24 import android.app.RemoteInput;
     25 import android.content.ComponentName;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.content.pm.ShortcutInfo;
     29 import android.content.pm.ShortcutManager;
     30 import android.graphics.BitmapFactory;
     31 import android.graphics.drawable.Icon;
     32 import android.net.Uri;
     33 import android.os.Bundle;
     34 import android.support.v4.content.pm.ShortcutInfoCompat;
     35 import android.support.v4.content.pm.ShortcutManagerCompat;
     36 import android.support.v4.graphics.drawable.IconCompat;
     37 import android.text.format.Time;
     38 import android.util.ArrayMap;
     39 import android.util.Log;
     40 import android.util.Pair;
     41 import android.view.View;
     42 import android.widget.ListView;
     43 import android.widget.Toast;
     44 
     45 import java.util.ArrayList;
     46 import java.util.Arrays;
     47 import java.util.Collections;
     48 import java.util.Comparator;
     49 import java.util.List;
     50 import java.util.Map;
     51 import java.util.Objects;
     52 import java.util.Random;
     53 import java.util.concurrent.atomic.AtomicInteger;
     54 import java.util.function.BooleanSupplier;
     55 
     56 public class ShortcutPublisher extends Activity {
     57     public static final String TAG = "ShortcutDemo";
     58 
     59     private static final String SETUP_SHORTCUT_ID = "setup";
     60 
     61     private ShortcutManager mShortcutManager;
     62 
     63     private ListView mList;
     64     private MyAdapter mAdapter;
     65 
     66     private static final Random sRandom = new Random();
     67 
     68     private static final AtomicInteger sSequenceNumber = new AtomicInteger();
     69 
     70     private ComponentName mMyActivity;
     71 
     72     @Override
     73     public void onCreate(Bundle savedInstanceState) {
     74         super.onCreate(savedInstanceState);
     75 
     76         setContentView(R.layout.main);
     77 
     78         mShortcutManager = getSystemService(ShortcutManager.class);
     79 
     80         mMyActivity = getIntent().getComponent();
     81         if (mMyActivity == null) {
     82             mMyActivity = new ComponentName(this, ShortcutPublisher.class);
     83         }
     84 
     85         mList = (ListView) findViewById(android.R.id.list);
     86         mAdapter = new MyAdapter(this);
     87         mList.setAdapter(mAdapter);
     88 
     89         Log.d(TAG, "intent=" + getIntent());
     90         Log.d(TAG, "extras=" + getIntent().getExtras());
     91     }
     92 
     93     @Override
     94     protected void onResume() {
     95         super.onResume();
     96 
     97         refreshList();
     98     }
     99 
    100     @Override
    101     protected void onDestroy() {
    102         super.onDestroy();
    103     }
    104 
    105     private List<ShortcutInfo> getAllShortcuts() {
    106         final Map<String, ShortcutInfo> map = new ArrayMap<>();
    107         for (ShortcutInfo si : mShortcutManager.getManifestShortcuts()) {
    108             if (!Objects.equals(si.getActivity(), mMyActivity)) continue;
    109             if (!map.containsKey(si.getId())) {
    110                 map.put(si.getId(), si);
    111             }
    112         }
    113         for (ShortcutInfo si : mShortcutManager.getDynamicShortcuts()) {
    114             if (!Objects.equals(si.getActivity(), mMyActivity)) continue;
    115             if (!map.containsKey(si.getId())) {
    116                 map.put(si.getId(), si);
    117             }
    118         }
    119         for (ShortcutInfo si : mShortcutManager.getPinnedShortcuts()) {
    120             if (!Objects.equals(si.getActivity(), mMyActivity)) continue;
    121             if (!map.containsKey(si.getId())) {
    122                 map.put(si.getId(), si);
    123             }
    124         }
    125         return new ArrayList<>(map.values());
    126     }
    127 
    128     private void refreshList() {
    129         final List<ShortcutInfo> list = getAllShortcuts();
    130         Collections.sort(list, mShortcutComparator);
    131         mAdapter.setShortcuts(list);
    132     }
    133 
    134     private final Comparator<ShortcutInfo> mShortcutComparator =
    135             (ShortcutInfo s1, ShortcutInfo s2) -> {
    136                 int ret = 0;
    137                 ret = (s1.isDeclaredInManifest() ? 0 : 1) - (s2.isDeclaredInManifest() ? 0 : 1);
    138                 if (ret != 0) return ret;
    139 
    140                 ret = (s1.isDynamic() ? 0 : 1) - (s2.isDynamic() ? 0 : 1);
    141                 if (ret != 0) return ret;
    142 
    143                 ret = s1.getId().compareTo(s2.getId());
    144                 if (ret != 0) return ret;
    145 
    146                 return 0;
    147             };
    148 
    149     private void dumpCurrentShortcuts() {
    150         Log.d(TAG, "Dynamic shortcuts:");
    151         for (ShortcutInfo si : mShortcutManager.getDynamicShortcuts()) {
    152             Log.d(TAG, "  " + si.toString());
    153         }
    154         Log.d(TAG, "Pinned shortcuts:");
    155         for (ShortcutInfo si : mShortcutManager.getPinnedShortcuts()) {
    156             Log.d(TAG, "  " + si.toString());
    157         }
    158     }
    159 
    160     public static void showToast(Context context, String message) {
    161         Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
    162     }
    163 
    164     private static void showThrottledToast(Context context) {
    165         showToast(context,
    166                 "Throttled, use \"adb shell cmd shortcut reset-throttling\" to reset counters");
    167     }
    168 
    169     public static void callApi(Context context, BooleanSupplier call) {
    170         try {
    171             if (!call.getAsBoolean()) {
    172                 showThrottledToast(context);
    173             }
    174         } catch (RuntimeException r) {
    175             Log.w(TAG, r.getMessage(), r);
    176             showToast(context, r.getMessage());
    177         }
    178     }
    179 
    180     private static List<Pair<String, String>> sIntentList = Arrays.asList(
    181             Pair.create("Google Search", "http://www.google.com"),
    182             Pair.create("Google Mail", "http://mail.google.com"),
    183             Pair.create("Google Maps", "http://maps.google.com"),
    184             Pair.create("Google Drive", "http://drive.google.com"),
    185             Pair.create("Google Photos", "http://photos.google.com"),
    186             Pair.create("Google Hangouts", "http://hangouts.google.com"),
    187             Pair.create("Google+", "http://plus.google.com")
    188     );
    189 
    190     public static ShortcutInfo.Builder addRandomIntents(Context context, ShortcutInfo.Builder b) {
    191         final int i = sRandom.nextInt(sIntentList.size());
    192         b.setShortLabel(sIntentList.get(i).first);
    193         b.setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse(sIntentList.get(i).second)));
    194 
    195         if (sRandom.nextBoolean()) {
    196             b.setIcon(Icon.createWithResource(context, R.drawable.icon2));
    197         } else {
    198             b.setIcon(Icon.createWithBitmap(BitmapFactory.decodeResource(context.getResources(),
    199                     R.drawable.icon_large_2)));
    200         }
    201         return b;
    202     }
    203 
    204     public static ShortcutInfoCompat.Builder addRandomIntents(Context context,
    205             ShortcutInfoCompat.Builder b) {
    206         final int i = sRandom.nextInt(sIntentList.size());
    207         b.setShortLabel(sIntentList.get(i).first);
    208         b.setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse(sIntentList.get(i).second)));
    209 
    210         if (sRandom.nextBoolean()) {
    211             b.setIcon(IconCompat.createWithResource(context, R.drawable.icon2));
    212         } else {
    213             b.setIcon(IconCompat.createWithBitmap(
    214                     BitmapFactory.decodeResource(context.getResources(), R.drawable.icon_large_2)));
    215         }
    216         return b;
    217     }
    218 
    219     public void onPublishPressed(View view) {
    220         dumpCurrentShortcuts();
    221         final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(getResources(),
    222                 R.drawable.icon_large_2));
    223         final Icon icon3 = Icon.createWithResource(this, R.drawable.icon_large_3);
    224 
    225         final Intent intent2 = new Intent(Intent.ACTION_VIEW);
    226         intent2.setClass(this, ShortcutPublisher.class);
    227 
    228         final Intent intent3 = new Intent(Intent.ACTION_VIEW);
    229         intent3.setClass(this, ShortcutPublisher.class);
    230         intent3.putExtra("str", "str-value");
    231         intent3.putExtra("nest", new Bundle());
    232         intent3.getBundleExtra("nest").putInt("int", 123);
    233 
    234         final ShortcutInfo si1 = addRandomIntents(this, new ShortcutInfo.Builder(this, "shortcut1"))
    235                 .setActivity(mMyActivity)
    236                 .build();
    237 
    238         final ShortcutInfo si2 = new ShortcutInfo.Builder(this, SETUP_SHORTCUT_ID)
    239                 .setActivity(mMyActivity)
    240                 .setShortLabel("Shortcut Demo Main")
    241                 .setIcon(icon2)
    242                 .setIntent(intent2)
    243                 .build();
    244 
    245         final ShortcutInfo si3 = new ShortcutInfo.Builder(this, "shortcut3")
    246                 .setActivity(mMyActivity)
    247                 .setShortLabel("Shortcut Demo Main with extras")
    248                 .setIcon(icon3)
    249                 .setIntent(intent3)
    250                 .build();
    251 
    252         callApi(this, () -> mShortcutManager.setDynamicShortcuts(Arrays.asList(si1, si2, si3)));
    253         refreshList();
    254     }
    255 
    256     public void onDeleteAllPressed(View view) {
    257         callApi(this, () -> {
    258             mShortcutManager.removeAllDynamicShortcuts();
    259             return true;
    260         });
    261         refreshList();
    262     }
    263 
    264     static String formatTime(long time) {
    265         Time tobj = new Time();
    266         tobj.set(time);
    267         return tobj.format("%Y-%m-%d %H:%M:%S");
    268     }
    269 
    270     public void onAddPressed(View view) {
    271         final ShortcutInfo si = addRandomIntents(this, new ShortcutInfo.Builder(this,
    272                     "shortcut-" + formatTime(System.currentTimeMillis()) + "-"
    273                         + sSequenceNumber.getAndIncrement()))
    274                 .setActivity(mMyActivity)
    275                 .build();
    276         callApi(this, () -> mShortcutManager.addDynamicShortcuts(Arrays.asList(si)));
    277         refreshList();
    278     }
    279 
    280     public void onUpdatePressed(View view) {
    281         final List updateList = new ArrayList<>();
    282 
    283         for (ShortcutInfo si : getAllShortcuts()) {
    284             if (SETUP_SHORTCUT_ID.equals(si.getId())) continue;
    285             if (si.isImmutable()) continue;
    286             if (!Objects.equals(si.getActivity(), mMyActivity)) continue;
    287             updateList.add(addRandomIntents(this, new ShortcutInfo.Builder(this, si.getId()))
    288                     .build());
    289         }
    290         callApi(this, () -> mShortcutManager.updateShortcuts(updateList));
    291         refreshList();
    292     }
    293 
    294     void launch(ShortcutInfo si) {
    295         startActivity(si.getIntent());
    296     }
    297 
    298     void deleteDynamic(ShortcutInfo si) {
    299         mShortcutManager.removeDynamicShortcuts(Arrays.asList(si.getId()));
    300         refreshList();
    301     }
    302 
    303     public void onShowNotificationPressed(View v) {
    304         final PendingIntent receiverIntent =
    305                 PendingIntent.getBroadcast(this, 0,
    306                         new Intent().setComponent(new ComponentName(this, ShortcutReceiver.class)),
    307                         PendingIntent.FLAG_UPDATE_CURRENT);
    308         final RemoteInput ri = new RemoteInput.Builder("result").setLabel("Remote input").build();
    309 
    310         final Notification.Builder nb = new Builder(this)
    311                 .setContentText("Test")
    312                 .setContentTitle(getPackageName())
    313                 .setSmallIcon(R.drawable.icon_large_2)
    314                 .addAction(new Action.Builder(0, "Remote input", receiverIntent)
    315                         .addRemoteInput(ri)
    316                         .build());
    317         getSystemService(NotificationManager.class).notify(0, nb.build());
    318     }
    319 
    320     // event handler
    321     public void onRequestPinPressed(View v) {
    322         requestPinShortcut(this);
    323     }
    324 
    325     public static void requestPinShortcut(Context context) {
    326         if (!ShortcutManagerCompat.isRequestPinShortcutSupported(context)) {
    327             showToast(context, "requestPinShortcut() is not supported by launcher");
    328             return;
    329         }
    330         final ShortcutInfoCompat si = addRandomIntents(
    331                 context, new ShortcutInfoCompat.Builder(context,
    332                         "shortcut-" + System.currentTimeMillis()))
    333                 .build();
    334         final PendingIntent resultIntent = PendingIntent.getBroadcast(context, 0,
    335                 new Intent(context, RequestPinShortcutResultReceiver.class),
    336                 PendingIntent.FLAG_UPDATE_CURRENT);
    337 
    338         ShortcutPublisher.callApi(context, () -> {
    339             ShortcutManagerCompat.requestPinShortcut(context,
    340                     si, resultIntent.getIntentSender());
    341             return true;
    342         });
    343     }
    344 
    345     class MyAdapter extends ShortcutAdapter {
    346         public MyAdapter(Context context) {
    347             super(context);
    348         }
    349 
    350         @Override
    351         protected int getLayoutId() {
    352             return R.layout.list_item;
    353         }
    354 
    355         @Override
    356         protected int getText1Id() {
    357             return R.id.line1;
    358         }
    359 
    360         @Override
    361         protected int getText2Id() {
    362             return R.id.line2;
    363         }
    364 
    365         @Override
    366         protected int getImageId() {
    367             return R.id.image;
    368         }
    369 
    370         @Override
    371         protected int getLaunchId() {
    372             return R.id.launch;
    373         }
    374 
    375         @Override
    376         protected int getAction2Id() {
    377             return R.id.action2;
    378         }
    379 
    380         @Override
    381         protected boolean showLaunch(ShortcutInfo si) {
    382             return true;
    383         }
    384 
    385         @Override
    386         protected boolean showAction2(ShortcutInfo si) {
    387             return si.isDynamic(); // TODO Need disable too.
    388         }
    389 
    390         @Override
    391         protected String getAction2Text(ShortcutInfo si) {
    392             return "Delete";
    393         }
    394 
    395         @Override
    396         protected void onLaunchClicked(ShortcutInfo si) {
    397             launch(si);
    398         }
    399 
    400         @Override
    401         protected void onAction2Clicked(ShortcutInfo si) {
    402             deleteDynamic(si);
    403         }
    404     }
    405 }
    406