Home | History | Annotate | Download | only in shortcutsample
      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.shortcutsample;
     17 
     18 import android.content.Context;
     19 import android.content.Intent;
     20 import android.content.pm.ShortcutInfo;
     21 import android.content.pm.ShortcutManager;
     22 import android.graphics.drawable.Icon;
     23 import android.net.Uri;
     24 import android.os.AsyncTask;
     25 import android.os.PersistableBundle;
     26 import android.util.Log;
     27 
     28 import java.util.ArrayList;
     29 import java.util.Arrays;
     30 import java.util.HashSet;
     31 import java.util.List;
     32 import java.util.function.BooleanSupplier;
     33 
     34 public class ShortcutHelper {
     35     private static final String TAG = Main.TAG;
     36 
     37     private static final String EXTRA_LAST_REFRESH =
     38             "com.example.android.shortcutsample.EXTRA_LAST_REFRESH";
     39 
     40     private static final long REFRESH_INTERVAL_MS = 60 * 60 * 1000;
     41 
     42     private final Context mContext;
     43 
     44     private final ShortcutManager mShortcutManager;
     45 
     46     public ShortcutHelper(Context context) {
     47         mContext = context;
     48         mShortcutManager = mContext.getSystemService(ShortcutManager.class);
     49     }
     50 
     51     public void maybeRestoreAllDynamicShortcuts() {
     52         if (mShortcutManager.getDynamicShortcuts().size() == 0) {
     53             // NOTE: If this application is always supposed to have dynamic shortcuts, then publish
     54             // them here.
     55             // Note when an application is "restored" on a new device, all dynamic shortcuts
     56             // will *not* be restored but the pinned shortcuts *will*.
     57         }
     58     }
     59 
     60     public void reportShortcutUsed(String id) {
     61         mShortcutManager.reportShortcutUsed(id);
     62     }
     63 
     64     /**
     65      * Use this when interacting with ShortcutManager to show consistent error messages.
     66      */
     67     private void callShortcutManager(BooleanSupplier r) {
     68         try {
     69             if (!r.getAsBoolean()) {
     70                 Utils.showToast(mContext, "Call to ShortcutManager is rate-limited");
     71             }
     72         } catch (Exception e) {
     73             Log.e(TAG, "Caught Exception", e);
     74             Utils.showToast(mContext, "Error while calling ShortcutManager: " + e.toString());
     75         }
     76     }
     77 
     78     /**
     79      * Return all mutable shortcuts from this app self.
     80      */
     81     public List<ShortcutInfo> getShortcuts() {
     82         // Load mutable dynamic shortcuts and pinned shortcuts and put them into a single list
     83         // removing duplicates.
     84 
     85         final List<ShortcutInfo> ret = new ArrayList<>();
     86         final HashSet<String> seenKeys = new HashSet<>();
     87 
     88         // Check existing shortcuts shortcuts
     89         for (ShortcutInfo shortcut : mShortcutManager.getDynamicShortcuts()) {
     90             if (!shortcut.isImmutable()) {
     91                 ret.add(shortcut);
     92                 seenKeys.add(shortcut.getId());
     93             }
     94         }
     95         for (ShortcutInfo shortcut : mShortcutManager.getPinnedShortcuts()) {
     96             if (!shortcut.isImmutable() && !seenKeys.contains(shortcut.getId())) {
     97                 ret.add(shortcut);
     98                 seenKeys.add(shortcut.getId());
     99             }
    100         }
    101         return ret;
    102     }
    103 
    104     /**
    105      * Called when the activity starts.  Looks for shortcuts that have been pushed and refreshes
    106      * them (but the refresh part isn't implemented yet...).
    107      */
    108     public void refreshShortcuts(boolean force) {
    109         new AsyncTask<Void, Void, Void>() {
    110             @Override
    111             protected Void doInBackground(Void... params) {
    112                 Log.i(TAG, "refreshingShortcuts...");
    113 
    114                 final long now = System.currentTimeMillis();
    115                 final long staleThreshold = force ? now : now - REFRESH_INTERVAL_MS;
    116 
    117                 // Check all existing dynamic and pinned shortcut, and if their last refresh
    118                 // time is older than a certain threshold, update them.
    119 
    120                 final List<ShortcutInfo> updateList = new ArrayList<>();
    121 
    122                 for (ShortcutInfo shortcut : getShortcuts()) {
    123                     if (shortcut.isImmutable()) {
    124                         continue;
    125                     }
    126 
    127                     final PersistableBundle extras = shortcut.getExtras();
    128                     if (extras != null && extras.getLong(EXTRA_LAST_REFRESH) >= staleThreshold) {
    129                         // Shortcut still fresh.
    130                         continue;
    131                     }
    132                     Log.i(TAG, "Refreshing shortcut: " + shortcut.getId());
    133 
    134                     final ShortcutInfo.Builder b = new ShortcutInfo.Builder(
    135                             mContext, shortcut.getId());
    136 
    137                     setSiteInformation(b, shortcut.getIntent().getData());
    138                     setExtras(b);
    139 
    140                     updateList.add(b.build());
    141                 }
    142                 // Call update.
    143                 if (updateList.size() > 0) {
    144                     callShortcutManager(() -> mShortcutManager.updateShortcuts(updateList));
    145                 }
    146 
    147                 return null;
    148             }
    149         }.execute();
    150     }
    151 
    152     private ShortcutInfo createShortcutForUrl(String urlAsString) {
    153         Log.i(TAG, "createShortcutForUrl: " + urlAsString);
    154 
    155         final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mContext, urlAsString);
    156 
    157         final Uri uri = Uri.parse(urlAsString);
    158         b.setIntent(new Intent(Intent.ACTION_VIEW, uri));
    159 
    160         setSiteInformation(b, uri);
    161         setExtras(b);
    162 
    163         return b.build();
    164     }
    165 
    166     private ShortcutInfo.Builder setSiteInformation(ShortcutInfo.Builder b, Uri uri) {
    167         // TODO Get the actual site <title> and use it.
    168         // TODO Set the current locale to accept-language to get localized title.
    169         b.setShortLabel(uri.getHost());
    170         b.setLongLabel(uri.toString());
    171 
    172         // TODO Fetch the favicon from the URI and sets to the icon.
    173         b.setIcon(Icon.createWithResource(mContext, R.drawable.link));
    174 
    175         return b;
    176     }
    177 
    178     private ShortcutInfo.Builder setExtras(ShortcutInfo.Builder b) {
    179         final PersistableBundle extras = new PersistableBundle();
    180         extras.putLong(EXTRA_LAST_REFRESH, System.currentTimeMillis());
    181         b.setExtras(extras);
    182         return b;
    183     }
    184 
    185     private String normalizeUrl(String urlAsString) {
    186         if (urlAsString.startsWith("http://") || urlAsString.startsWith("https://")) {
    187             return urlAsString;
    188         } else {
    189             return "http://" + urlAsString;
    190         }
    191     }
    192 
    193     public void addWebSiteShortcut(String urlAsString) {
    194         final String uriFinal = urlAsString;
    195         callShortcutManager(() -> {
    196             final ShortcutInfo shortcut = createShortcutForUrl(normalizeUrl(uriFinal));
    197             return mShortcutManager.addDynamicShortcuts(Arrays.asList(shortcut));
    198         });
    199     }
    200 
    201     public void removeShortcut(ShortcutInfo shortcut) {
    202         mShortcutManager.removeDynamicShortcuts(Arrays.asList(shortcut.getId()));
    203     }
    204 
    205     public void disableShortcut(ShortcutInfo shortcut) {
    206         mShortcutManager.disableShortcuts(Arrays.asList(shortcut.getId()));
    207     }
    208 
    209     public void enableShortcut(ShortcutInfo shortcut) {
    210         mShortcutManager.enableShortcuts(Arrays.asList(shortcut.getId()));
    211     }
    212 }
    213