Skip to content

Instantly share code, notes, and snippets.

@Zimins
Last active April 17, 2018 02:56
Show Gist options
  • Save Zimins/d2cd87c19f675cffaa5a2d74227cd1fd to your computer and use it in GitHub Desktop.
Save Zimins/d2cd87c19f675cffaa5a2d74227cd1fd to your computer and use it in GitHub Desktop.
appshortcut.md

App shortcuts

안드로이드 7.1 (25) 이상에서, 앱에 대한 바로가기 를 설정 가능합니다. 사용하는 런처에서 표시됩니다. 자주 사용하는 작업이나 권장되는 작업을 빨리 접근 할 수 있게 해줍니다. (Works 대응되어 있습니다.)

꾹 눌러서 표시 가능한데, 기존의 UX 와 겹쳐서 사람들이 많이 쓰는데는 시간이 걸릴 듯 합니다.

각 바로가기는 한개 또는 여러개의 intent 를 가지고 있습니다. 해당 intent 로 어떤 작업을 하게 할지 정할 수 있습니다.

  • 지도에서 특정 위치로 바로 이동
  • 친구에게 메시지 보내기
  • 비디오 앱에서 다음 에피소드를 표시하기
  • 게임에서 "불러오기" 같은 기능

Static Shortcut

바로가기 메뉴를 띄우면 항상 존재합니다.

예 ) 메시지앱에서 "새 메시지", 크롬 앱에서 "북마크" 등 일반적으로 바로 사용가능한 기능들

런처에서 알아야 하기 때문에 manifest 를 설정해주어야 합니다.

런처에서 처음 띄우게 설정된 <activity>metadata 를 추가하면 됩니다. 그리고 android:resource 태그에 바로가기를 설정한 shortcut xml 을 명시해줍니다.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.myapplication">
  <application ... >
    <activity android:name="Main">
      <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>
</manifest>
    

해당 xml 에서는 <shortcut> 의 목록을 만들어주며 해당 바로가기당 어떤 intent를 들고 있을 지 명시해주면 됩니다.

<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
  <shortcut
    android:shortcutId="compose"
    android:enabled="true"
    android:icon="@drawable/compose_icon"
    android:shortcutShortLabel="@string/compose_shortcut_short_label1"
    android:shortcutLongLabel="@string/compose_shortcut_long_label1"
    android:shortcutDisabledMessage="@string/compose_disabled_message1">
    <intent
      android:action="android.intent.action.VIEW"
      android:targetPackage="com.example.myapplication"
      android:targetClass="com.example.myapplication.ComposeActivity" />
    <!-- If your shortcut is associated with multiple intents, include them
         here. The last intent in the list determines what the user sees when
         they launch this shortcut. -->
    <categories android:name="android.shortcut.conversation" />
  </shortcut>
  <!-- Specify more shortcuts here. -->
</shortcuts>

하나의 바로가기에 여러개의 intent 를 설정하면 시스템은 가장 마지막 intent 부터 보이게 만듭니다. 즉 명시한 순서대로 backstack 을 만듭니다. 뒤로가기 버튼 등으로 모든 stack 이 제거됬을 때 런처로 이동하게 됩니다.

Dynamic Shortcut

사용자가 앱을 사용함에 따라 변경 가능한 바로가기 메뉴입니다. 예를 들면 즐겨찾기한 사람에게 연락하기, 특정 위치로 바로 지도앱을 켜기 등이 있습니다.

ShortcutManager api가 Dynamic Shortcut 을 만들 수 있게 해줍니다.

  • Publish : setDynamicShortcuts() 로 바로가기 목록을 전체 다 재정의 하거나, addDynamicShortcuts() 로 이미 있는 리스트에 추가할 수 있습니다.
  • Update: updateShortcuts()
  • Remove: 전부 지우려면 removeAllDynamicShortcuts(), 특정 바로가기만 지우려면 removeDynamicShortcuts(List<String> shortcutIds)

예시:

ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);

ShortcutInfo shortcut = new ShortcutInfo.Builder(this, "id1")
    .setShortLabel("Web site")
    .setLongLabel("Open the web site")
    .setIcon(Icon.createWithResource(context, R.drawable.icon_website))
    .setIntent(new Intent(Intent.ACTION_VIEW,
                   Uri.parse("https://www.mysite.example.com/")))
    .build();

shortcutManager.setDynamicShortcuts(Arrays.asList(shortcut));

동적으로 intent 를 설정할 때 setintents() 로 여러개의 intent 를 설정 할 수 있고 static short처럼 해당 이벤트가 생길때 back stack을 만들게 됩니다.

Pinned Shortcut

https://sc02.alicdn.com/kf/HTB18RifOFXXXXaFXpXXq6xXFXXXD/for-tecno-S9-fancy-book-cover-removable.jpg_350x350.jpg

8.0 이상에서 사용 가능한 기능입니다.

앱을 길게 누를 때 나오는 바로가기 각각의 아이콘을 제공해줍니다.

권한이 필요한 기능입니다. 이기능을 쓰면 런처에서 사용자에게 권한을 요청하는데, 만약 거부한다면 이 기능을 쓸 수 없습니다. 근데 삼성 런처에서는 못봄...

  1. isRequestPinShortcutSupported() 로 기기의 기본 런처가 이 기능을 제공하는지 확인합니다.
  2. 바로가기의 존재 여부에 따라 ShortcutInfo 객체를 두가지 방법으로 만들기
    • 이미 있을 경우, 바로가기의 id 만 가지는 ShortcutInfo 객체 사용
    • 새로운 바로가기일 경우, id, intent, shortcut label 을 모두 가지는 ShortcutInfo 사용
  3. requestPinShortcut() 을 통해 런처에 요청. PendingIntent 를 통해 만든 앱에 바로가기가 성공적으로 만들어 졌는지 여부를 전달 가능

만약 런처가 바로가기를 지원하지 않는다면 앱은 콜백을 받지 못합니다.

예시 :

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.
    PendingIntent successCallback = PendingIntent.getBroadcast(context, 0,
            pinnedShortcutCallbackIntent, 0);

    mShortcutManager.requestPinShortcut(pinShortcutInfo,
            successCallback.getIntentSender());
}

Note: See also the support library APIs,isRequestPinShortcutSupported() andrequestPinShortcut(), which work on Android 7.1 (API level 25) and lower. The support library falls back to the deprecatedEXTRA_SHORTCUT_INTENT extra to attempt the pinning process.

Shortcut 사용 추적하기

앱에서 reportShortcutUsed() 를 호출해서 어떤 바로가기를 사용했는지에 대한 기록을 유지 가능합니다. (id 를 전달)

둘중 하나의 상황에서 보낼 수 있습니다.

  • 사용자가 해당 id 를 가지는 바로가기를 선택
  • 사용자가 앱을 직접 열어서 바로가기와 같은 작업을 수행 할 때

Disable Shortcuts

바로가기는 런처에서 유지되기 때문에 , 앱상에서는 더이상 존재하지 않는 기능이 보여질 수도 있습니다. 그래서 사용자가 더이상 선택하지 않기를 원할 때는 disableShortcuts(List<String> shortcutIds) 를 사용 가능합니다. void disableShortcuts (List<String> shortcutIds, CharSequence disabledMessage) 를 사용하면 직접 에러 메시지를 띄울 수도 있습니다.

만약 앱을 업데이트 시에 정적 바로가기를 없앴다면 , 시스템이 알아서 disable 해줍니다.

Rate Limiting

setDynamicShortcuts(), addDynamicShortcuts(), updateShortcuts() 사용시 , 백그라운드 앱에서는 특정 횟수만 호출 할 수 있습니다. (액티비티가 없는 앱이나, forground에서 실행되는 service).

Production 환경에서는 이 제한을 앱을 foreground 로 실행하게 해서 초기화 할 수 있습니다.

왜 그런가?

개발 환경에서는 개발자메뉴 > shortcutManager api 초기화 로 가능하고,

Adb 에서는 아래 방법으로 가능합니다.

$ adb shell cmd shortcut reset-throttling [ --user your-user-id ]

BackUp and Restore

기기를 바꿀 때도 바로가기를 그대로 유지하고 싶다면 manifest 에서 android:allowBackup="true" 를 설정하면 됩니다.

주의사항

  • Static shortcut 은 사용자가 앱을 설치 시 자동으로 적용
  • Dynamic shortcut 은 백업되지 않습니다. 그래서 따로 앱 안에 새 기기에서 설치될 시에 바로가기를 추가하는 로직을 추가해주어야 합니다.
  • Pinned Shortcut 은 런처에 저장됩니다. 그러나 해당 바로가기의 아이콘은 저장하지 않습니다. 그래서 앱 내에서 이미지를 저장해서 따로 복구해주어야 합니다. (원격 사용 등의 방법이 있을 듯 합니다)

pinned shortcut 복구 예시

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.
            }
        }
    }
    // ...
}

Best Practices(가이드라인 )

디자인 가이드라인을 따라서 만드세요.

App Shortcuts Design Guidelines. 통일성을 보장해줍니다.

4개만 제공하세요

5개 까지 제공하기는 하지만 보기 좋게 하기 위함

shortcut 설명의 길이를 제한

"shortDescription" = 10자

"longDescription" = 25자

바로가기와 액션 사용 기록 유지

reportShortcutUsed() 사용. 특정 바로가기의 기능을 다른 방법으로 접근하는 것을 고려?

바로가기의 의미가 유지될 때만 업데이트를 할 것

슈퍼마켓으로 가는 길을 안내하는 바로가기를 만들었는데, 만약 슈퍼마켓의 이름은 바뀌로 위치는 바뀌지 않았다면 이름만 업데이트 하는것이 적적하고, 다른 위치의 슈퍼마켓에서 쇼핑을 시작했다면 , 새로 바로가기를 만드는 것이 좋습니다.

Dynamic shortcuts 은 backup and restore 동안 복구되지 않습니다.

getDynamicShortcuts()를 통해 앱을 켤 때마다 확인해서 원하는 바로가기가 유지되고 있는지 봐야합니다. (없으면 re-publicsh)

예시

The Android AppShortcuts sample further demonstrates the use of the APIs covered on this page.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment