Android快捷方式-Shortcuts

奇舞移動發表於2018-11-09

就在前幾天,跟一同事車回家,他用的是iOS版高德,每次發車前,重力長按高德icon,彈出shortcuts,很方便就進入回家的導航,也就是iOS 3D Touch功能。如下面這張圖,截圖來自647 iPhone X 。

https://user-gold-cdn.xitu.io/2018/11/9/166f61d167871745?w=350&h=758&f=png&s=187636

今天得空研究了一下,Android 在Android 7.1(API 25) 新增了App快捷方式的新功能,由ShortcutManager類來管理,這樣開發者可以隨意定義快速進入到指定的Activity或開啟指定網頁。目前有很多App已經有了這個特性,接了個圖如下:

https://user-gold-cdn.xitu.io/2018/11/9/166f61d1677eba16?w=350&h=700&f=jpeg&s=28076

下面我們就詳細探討一下這個特性。

實現Shortcuts的兩種形式

靜態Shortcuts

所謂的靜態就是在工程中配置,利用xml寫死。在APK中包含一個資原始檔來描述Shortcut,目錄res/xml/shortcuts.xml。這種方法做不到熱更新,需要從新發布App才可。

<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
    <shortcut
        android:shortcutId="static shortcut"
        android:enabled="true"
        android:icon="@mipmap/ic_launcher"
        android:shortcutShortLabel="@string/shortcut_short_name"
        android:shortcutLongLabel="@string/shortcut_long_name"
        android:shortcutDisabledMessage="@string/shortcut_disable_msg">
        <intent
            android:action="android.intent.action.VIEW"
            android:targetPackage="d.shortcuts"
            android:targetClass="d.shortcuts.MainActivity" />
        <categories android:name="android.shortcut.conversation"/>
    </shortcut>
</shortcuts>
複製程式碼
<application ... 
             ...>
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
        <meta-data
            android:name="android.app.shortcuts"
            android:resource="@xml/shortcuts"/>
    </activity>
</application>
複製程式碼

這種方式適合百年不變的場景,不然真是不夠靈活。

動態Shortcuts

動態Shortcuts在執行時,通過ShortcutManager API來進行註冊。用這種方式可以在執行時,動態的釋出、更新和刪除shortcut。官方給出了幾個場景可以作為shortcut的例子,比如:

  • 在地圖類app中,指導使用者到特定的位置;
  • 在社交類app中,傳送訊息給一個朋友;
  • 在媒體類app中,播放視訊的下一片段;
  • 在遊戲類app中,下載最後儲存的要點;

動態安裝

給Activity頁面構建shortcut

private void setupShortcutsForActivity() {
    mShortcutManager = getSystemService(ShortcutManager.class);
    List<ShortcutInfo> infos = new ArrayList<>();
    for (int i = 0; i < mShortcutManager.getMaxShortcutCountPerActivity(); i++) {
        Intent intent = new Intent(this, Main2Activity.class);
        intent.setAction(Intent.ACTION_VIEW);
        intent.putExtra("info", "this is info!");
        ShortcutInfo info = new ShortcutInfo.Builder(this, "ID:" + i)
                .setShortLabel("short label")
                .setLongLabel("long label")
                .setIcon(Icon.createWithResource(this, R.mipmap.ic_launcher))
                .setIntent(intent)
                .build();
        infos.add(info);
    }
    mShortcutManager.setDynamicShortcuts(infos);
}
複製程式碼

使用URL構建shortcut,開啟預設瀏覽器,如下

private ShortcutInfo createShortcutForUrl(String urlAsString) {
      final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mContext, urlAsString);
      final Uri uri = Uri.parse(urlAsString);
      b.setIntent(new Intent(Intent.ACTION_VIEW, uri));
      setSiteInformation(b, uri)
      setExtras(b);
      return b.build();
}

private ShortcutInfo.Builder setSiteInformation(ShortcutInfo.Builder b, Uri uri) {
        b.setShortLabel(uri.getHost());
        b.setLongLabel(uri.toString());
        Bitmap bmp = fetchFavicon(uri);
        if (bmp != null) {
            b.setIcon(Icon.createWithBitmap(bmp));
        } else {
            b.setIcon(Icon.createWithResource(mContext, R.drawable.link));
        }
        return b;
 }

private ShortcutInfo.Builder setExtras(ShortcutInfo.Builder b) {
        final PersistableBundle extras = new PersistableBundle();
        extras.putLong(EXTRA_LAST_REFRESH, System.currentTimeMillis());
        b.setExtras(extras);
        return b;
}

//注意要非同步Task執行
private Bitmap fetchFavicon(Uri uri) {
        final Uri iconUri = uri.buildUpon().path("favicon.ico").build();
        InputStream is = null;
        BufferedInputStream bis = null;
        try
        {
            URLConnection conn = new URL(iconUri.toString()).openConnection();
            conn.connect();
            is = conn.getInputStream();
            bis = new BufferedInputStream(is, 8192);
            return BitmapFactory.decodeStream(bis);
        } catch (IOException e) {
            return null;
        }
    }
複製程式碼

http://p0.qhimg.com/t01dbcef3351fa365be.jpg

動態刪除

public void removeShortcut(ShortcutInfo shortcut) { 
    mShortcutManager.removeDynamicShortcuts(Arrays.asList(shortcut.getId()));
}
複製程式碼

動態停用

public void disableShortcut(ShortcutInfo shortcut) {
    mShortcutManager.disableShortcuts(Arrays.asList(shortcut.getId()));
}
複製程式碼

動態開啟

public void enableShortcut(ShortcutInfo shortcut) {
    mShortcutManager.enableShortcuts(Arrays.asList(shortcut.getId()));
}
複製程式碼

Pinning Shortcut

在動態裡面還有一個Pinning Shortcuts概念,相當於app的另外一種快捷方式,只允許使用者新增與刪除它。

Android快捷方式-Shortcuts

用isRequestPinShortcutSupported() 判斷當前裝置是否支援PinShort 。在Android 8.0 (API level 26) 以及以上的版本上支援建立pinned shortcuts。

ShortcutManager mShortcutManager = context.getSystemService(ShortcutManager.class);
if (mShortcutManager.isRequestPinShortcutSupported()) {
    // Assumes there's already a shortcut with the ID "my-shortcut".
    // The shortcut must be enabled.
    ShortcutInfo pinShortcutInfo =
            new ShortcutInfo.Builder(context, "my-shortcut").build();
    // Create the PendingIntent object only if your app needs to be notified
    // that the user allowed the shortcut to be pinned. Note that, if the
    // pinning operation fails, your app isn't notified. We assume here that the
    // app has implemented a method called createShortcutResultIntent() that
    // returns a broadcast intent.
    Intent pinnedShortcutCallbackIntent =
            mShortcutManager.createShortcutResultIntent(pinShortcutInfo);

    // Configure the intent so that your app's broadcast receiver gets
    // the callback successfully.For details, see PendingIntent.getBroadcast().
    PendingIntent successCallback = PendingIntent.getBroadcast(context, /* request code */ 0,
            pinnedShortcutCallbackIntent, /* flags */ 0);

    mShortcutManager.requestPinShortcut(pinShortcutInfo,
            successCallback.getIntentSender());
}
複製程式碼

shortcuts 最佳實踐

1、不管是靜態形式還是動態形式,每個應用最多可以註冊4個Shortcuts。

2、 "short description" 限制在 10 個字元內

3、"long description" 限制在 25 個字元內

4、改變 dynamic and pinned shortcuts時,呼叫方法updateShortcuts()

5、重複建立shortcut時,

  • 動態的 shortcuts使用方法: addDynamicShortcuts()setDynamicShortcuts().
  • Pinned shortcuts使用方法: requestPinShortcut().

6、在每次啟動與需要重新發布動態shortcut時,推薦檢查方法getDynamicShortcuts() 返回的數量。

public class MainActivity extends Activity {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
        if (shortcutManager.getDynamicShortcuts().size() == 0) {
            // Application restored. Need to re-publish dynamic shortcuts.
            if (shortcutManager.getPinnedShortcuts().size() > 0) {
                // Pinned shortcuts have been restored. Use
                // updateShortcuts() to make sure they contain
                // up-to-date information.
            }
        }
    }
    // ...
}
複製程式碼

本文demo地址:
github.com/donald99/sh…

推薦Google demo
github.com/googlesampl…

關注微信公眾號,最新技術乾貨實時推送

Android快捷方式-Shortcuts

相關文章