With the release of Android Nougat 7.1(API level 25) a new features is added i.e. App Shortcuts. App Shortcuts allow the user to access primary actions in your app straight from the launcher, taking the user deep into your application by long pressing on your app icon. Users can also pin these shortcuts to the home screen for even quicker access to your app’s primary actions.
You can publish the following types of shortcuts for your app:
Note: Users can also create pinned shortcuts themselves by copying your app’s static and dynamic shortcuts onto the launcher.
So today we are going to learn how to implement above mentioned shortcuts in our app and improve user interaction with our app.
Static shortcuts provides links to some actions within your app and these actions should remain consistent over the lifetime of your app’s current version. Some examples for whom you can add static shortcuts in your app:
To create Static shortcuts follow the below steps:
1. Add the below meta-data to your Launcher activity in AndroidManifest.xml.
1 2 |
<meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts" /> |
After adding your AndroidManifest will look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android_app_shortcuts_demo"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <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> </manifest> |
2. After adding meta-data you will be getting error for @xml/shortcuts. So for that create a new resource file: res/xml/shortcuts.xml.
In this new resource file, add a <shortcuts> root element, which contains a list of <shortcut> elements. Each <shortcut> element, in turn, contains information about a static shortcut, including its icon, its description labels, and the intents that it launches within the app:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<?xml version="1.0" encoding="utf-8"?> <shortcuts xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <shortcut android:enabled="true" android:icon="@drawable/ic_message" android:shortcutDisabledMessage="@string/message_disabled_message" android:shortcutId="message" android:shortcutLongLabel="@string/message_shortcut_long_label" android:shortcutShortLabel="@string/message_shortcut_short_label" tools:targetApi="n_mr1"> <!-- i have declared main activity also to maintain back stack --> <intent android:action="android.intent.action.VIEW" android:targetClass="com.android_app_shortcuts_demo.MainActivity" android:targetPackage="com.android_app_shortcuts_demo" /> <!-- this activity will open when this shortcut clicked --> <intent android:action="android.intent.action.VIEW" android:targetClass="com.android_app_shortcuts_demo.MessageActivity" android:targetPackage="com.android_app_shortcuts_demo" /> <!-- 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> |
I have added two intent under one shortcut one after other to maintain navigation. The last intent declared in <shortcut> tag will open when clicked on shortcut.
Check this link for App Shortcuts design guidelines.
Dynamic shortcuts provides links to specific, context-sensitive actions within your app. These actions can change between uses of your app, and they can change even while your app is running.
Some examples for whom you can add static shortcuts in your app:
To implement Dynamic shortcuts we have to use ShortcutManager API. These API provide following methods:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//get Shortcut manager ShortcutManager shortcutManager = getSystemService(ShortcutManager.class); //Add Compose Email Shortcut ShortcutInfo composeEmail = new ShortcutInfo.Builder(this, "compose_email")//add unique id .setShortLabel("Compose Email")//set short label .setLongLabel("Compose email in single tap")//set long label .setDisabledMessage("Compose email is disabled")//disabled message shown when shortcut is disabled .setIcon(Icon.createWithResource(this, R.drawable.ic_email))//set icon .setIntent(new Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:[email protected]")))//set intent : here i am opening Mail app to send email to an Email ID .build(); //NOTE : You can add more shortcuts here according to your requirement if (shortcutManager != null) shortcutManager.setDynamicShortcuts(Arrays.asList(composeEmail));//finally add all the created shortcut |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//if shortcut id is compose_email update the shortcut ShortcutManager shortcutManager = getSystemService(ShortcutManager.class); ShortcutInfo composeEmail = new ShortcutInfo.Builder(MainActivity.this, getShortcutId)//pass the same shortcut id .setShortLabel("Compose Email") .setLongLabel("Compose email in single tap") .setIcon(Icon.createWithResource(MainActivity.this, R.drawable.ic_web))//i am only updating icon of the shortcut .setIntent(new Intent(Intent.ACTION_SENDTO, .build(); if (shortcutManager != null) { //finally update shortcuts and pass the created shortcut shortcutManager.updateShortcuts(Arrays.asList(composeEmail)); Toast.makeText(MainActivity.this, "Shortcut updated.", Toast.LENGTH_SHORT).show(); //NOTE : You can update multiple shortcuts also } |
1 2 3 4 5 6 7 8 9 10 11 |
ShortcutManager shortcutManager = getSystemService(ShortcutManager.class); if (shortcutManager != null) { //NOTE : remove multiple or single shortcuts then uncomment the below line //shortcutManager.removeDynamicShortcuts(Arrays.asList("compose_email","open_website"));//here you have to provide created shortcut IDs you want to remove //NOTE : if you want to remove all shortcuts then use the below code shortcutManager.removeAllDynamicShortcuts(); Toast.makeText(MainActivity.this, "Shortcut deleted.", Toast.LENGTH_SHORT).show(); } |
1 2 3 4 5 6 7 |
ShortcutManager shortcutManager = getSystemService(ShortcutManager.class); if (shortcutManager!=null) { //you can disable created shortcuts by passing ID of shortcut you want to disable shortcutManager.disableShortcuts(Arrays.asList("compose_email")); } |
1 2 3 4 5 6 7 |
ShortcutManager shortcutManager = getSystemService(ShortcutManager.class); if (shortcutManager!=null) { //you can enable created shortcuts by passing ID of shortcut you want to enable shortcutManager.enableShortcuts(Arrays.asList("compose_email")); } |
On Android 8.0 (API level 26) and higher, you can create pinned shortcuts. Unlike static and dynamic shortcuts, pinned shortcuts appear in supported launchers as separate icons.
To pin a shortcut to a supported launcher using your app, follow the below steps:
Note: If the user doesn’t allow the shortcut to be pinned to the launcher, your app doesn’t receive a callback.
You can also remove, update pinned shortcuts.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
ShortcutManager mShortcutManager = getSystemService(ShortcutManager.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //check if device supports Pin Shortcut or not if (mShortcutManager != null && mShortcutManager.isRequestPinShortcutSupported()) { // Assumes there's already a shortcut with the ID "open_website". // The shortcut must be enabled. ShortcutInfo pinShortcutInfo = new ShortcutInfo.Builder(MainActivity.this, "open_website").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(MainActivity.this, 0, pinnedShortcutCallbackIntent, 0); //finally ask user to add the shortcut to home screen mShortcutManager.requestPinShortcut(pinShortcutInfo, successCallback.getIntentSender()); } } |
1. Open string.xml and add the below strings to it. We will use this strings in app.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<resources> <string name="app_name">Android App Shortcuts Demo</string> <string name="message_shortcut_short_label">Open Messages</string> <string name="message_shortcut_long_label">See your all messages in one click</string> <string name="message_disabled_message">Messages are disabled for now</string> <string name="message_activity">Message Activity</string> <string name="update_dynamic_shortcuts">Update Dynamic Shortcuts</string> <string name="add_dynamic_shortcuts">Add Dynamic Shortcuts</string> <string name="delete_dynamic_shortcuts">Delete Dynamic Shortcuts</string> <string name="add_pinned_shortcuts">Add Pinned Shortcuts</string> <string name="dynamic_shortcut">Dynamic Shortcut</string> <string name="pinned_shortcut">Pinned Shortcut</string> </resources> |
2. Open MainActivity.java and add the below code to it. This class contains all three types of Shortcuts.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
package com.android_app_shortcuts_demo; import android.annotation.TargetApi; import android.app.PendingIntent; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Build; import android.support.annotation.RequiresApi; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.EditText; import android.widget.Toast; import java.util.Arrays; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void addDynamicShortcuts(View view) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { addDynamicShortcuts(); } } //Method to add dynamic shortcuts @TargetApi(Build.VERSION_CODES.N_MR1) @RequiresApi(api = Build.VERSION_CODES.M) private void addDynamicShortcuts() { //get Shortcut manager ShortcutManager shortcutManager = getSystemService(ShortcutManager.class); //Add Compose Email Shortcut ShortcutInfo composeEmail = new ShortcutInfo.Builder(this, "compose_email")//add unique id .setShortLabel("Compose Email")//set short label .setLongLabel("Compose email in single tap")//set long label .setDisabledMessage("Compose email is disabled")//disabled message shown when shortcut is disabled .setIcon(Icon.createWithResource(this, R.drawable.ic_email))//set icon .setIntent(new Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:[email protected]")))//set intent : here i am opening Mail app to send email to an Email ID .build(); ShortcutInfo openWebsite = new ShortcutInfo.Builder(this, "open_website")//add unique id .setShortLabel("Open Website")//set short label .setLongLabel("Click to see my blog")//set long label .setDisabledMessage("You cannot access the link as it is disabled")//disabled message shown when shortcut is disabled .setIcon(Icon.createWithResource(this, R.drawable.ic_web))//set icon .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.androhub.com")))//set intent : here i am opening browser app to open provided link .build(); //NOTE : You can add more shortcuts here according to your requirement if (shortcutManager != null) shortcutManager.setDynamicShortcuts(Arrays.asList(composeEmail, openWebsite));//finally add all the created shortcut } /** * method to update dynamic shortcuts * @param view of button * here in this method an alert dialog will be shown up and user have to enter shortcut id to update the shortcut if required */ @TargetApi(Build.VERSION_CODES.N_MR1) @RequiresApi(api = Build.VERSION_CODES.M) public void updateDynamicShortcuts(View view) { //create alert dialog AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Enter Shortcut Id to update");//set dialog title final EditText enterShortcutId = new EditText(this);//create edit text builder.setView(enterShortcutId);//add edit text to alert dialog //set positive button builder.setPositiveButton("Update", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { //get the entered shortcut id String getShortcutId = enterShortcutId.getText().toString().trim(); //check shortcut id should not be empty if (!getShortcutId.equals("")) { switch (getShortcutId) { case "compose_email": //if shortcut id is compose_email update the shortcut ShortcutManager shortcutManager = getSystemService(ShortcutManager.class); ShortcutInfo composeEmail = new ShortcutInfo.Builder(MainActivity.this, getShortcutId)//pass the same shortcut id .setShortLabel("Compose Email") .setLongLabel("Compose email in single tap") .setIcon(Icon.createWithResource(MainActivity.this, R.drawable.ic_web))//i am only updating icon of the shortcut .setIntent(new Intent(Intent.ACTION_SENDTO, .build(); if (shortcutManager != null) { //finally update shortcuts and pass the created shortcut shortcutManager.updateShortcuts(Arrays.asList(composeEmail)); Toast.makeText(MainActivity.this, "Shortcut updated.", Toast.LENGTH_SHORT).show(); //NOTE : You can update multiple shortcuts also } break; case "open_website": //if shortcut id is open_website update the shortcut ShortcutManager shortcutManager1 = getSystemService(ShortcutManager.class); ShortcutInfo openWebsite = new ShortcutInfo.Builder(MainActivity.this, getShortcutId) .setShortLabel("Open Website") .setLongLabel("Click to see my blog") .setIcon(Icon.createWithResource(MainActivity.this, R.drawable.ic_web)) .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("https://androhub.com")))//I am also changing link .build(); if (shortcutManager1 != null) { //finally update shortcuts and pass the created shortcut shortcutManager1.updateShortcuts(Arrays.asList(openWebsite)); Toast.makeText(MainActivity.this, "Shortcut updated.", Toast.LENGTH_SHORT).show(); //NOTE : You can update multiple shortcuts also } break; default: //if shortcut id doesn't match show the below toast Toast.makeText(MainActivity.this, "No Shortcut created with " + getShortcutId + " name.", Toast.LENGTH_SHORT).show(); break; } } } }); builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { } }); //finally show the alert dialog AlertDialog alertDialog = builder.create(); alertDialog.show(); } //method to delete/remove dynamic shortcuts @TargetApi(Build.VERSION_CODES.N_MR1) @RequiresApi(api = Build.VERSION_CODES.M) public void deleteDynamicShortcuts(View view) { ShortcutManager shortcutManager = getSystemService(ShortcutManager.class); if (shortcutManager != null) { //NOTE : remove multiple or single shortcuts then uncomment the below line //shortcutManager.removeDynamicShortcuts(Arrays.asList("compose_email","open_website"));//here you have to provide created shortcut IDs you want to remove //NOTE : if you want to remove all shortcuts then use the below code shortcutManager.removeAllDynamicShortcuts(); Toast.makeText(MainActivity.this, "Shortcut deleted.", Toast.LENGTH_SHORT).show(); } } /** * NOTE : Things to know about PINNED Shortcuts * 1. PINNED SHORTCUTS only work in Android 8.0 * 2. To add Pinned Shortcuts you should be having at least one static and dynamic shortcuts created and enabled or you have to create a new one * 3. Previously created static or dynamic shortcut ID will be required to create Pinned shortcuts */ /** * method to add PINNED SHORTCUTS for the above created shortcuts * @param view of button */ @TargetApi(Build.VERSION_CODES.N_MR1) @RequiresApi(api = Build.VERSION_CODES.M) public void addPinnedShortcuts(View view) { ShortcutManager mShortcutManager = getSystemService(ShortcutManager.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //check if device supports Pin Shortcut or not if (mShortcutManager != null && mShortcutManager.isRequestPinShortcutSupported()) { // Assumes there's already a shortcut with the ID "open_website". // The shortcut must be enabled. ShortcutInfo pinShortcutInfo = new ShortcutInfo.Builder(MainActivity.this, "open_website").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(MainActivity.this, 0, pinnedShortcutCallbackIntent, 0); //finally ask user to add the shortcut to home screen mShortcutManager.requestPinShortcut(pinShortcutInfo, successCallback.getIntentSender()); } } } /** * method to disable created shortcuts */ @TargetApi(Build.VERSION_CODES.N_MR1) @RequiresApi(api = Build.VERSION_CODES.M) public void disableShortcuts(){ ShortcutManager shortcutManager = getSystemService(ShortcutManager.class); if (shortcutManager!=null) { //you can disable created shortcuts by passing ID of shortcut you want to disable shortcutManager.disableShortcuts(Arrays.asList("compose_email")); } } /** * method to enable created shortcuts */ @TargetApi(Build.VERSION_CODES.N_MR1) @RequiresApi(api = Build.VERSION_CODES.M) public void enableShortcuts(){ ShortcutManager shortcutManager = getSystemService(ShortcutManager.class); if (shortcutManager!=null) { //you can enable created shortcuts by passing ID of shortcut you want to enable shortcutManager.enableShortcuts(Arrays.asList("compose_email")); } } } |
3. Open activity_main.xml and add the below layout to it. This layout contains some button to add, update and remove shortcuts.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="10dp" tools:context="com.android_app_shortcuts_demo.MainActivity"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/dynamic_shortcut" android:padding="10dp" android:textColor="@color/colorAccent" android:textSize="14sp" /> <Button android:id="@+id/add_dynamic_shortcuts" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="@android:color/white" android:background="@color/colorPrimary" android:textSize="15sp" android:onClick="addDynamicShortcuts" android:text="@string/add_dynamic_shortcuts" /> <Button android:id="@+id/update_dynamic_shortcuts" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="@android:color/white" android:background="@color/colorPrimary" android:layout_marginTop="10dp" android:textSize="15sp" android:onClick="updateDynamicShortcuts" android:text="@string/update_dynamic_shortcuts" /> <Button android:id="@+id/delete_dynamic_shortcuts" android:layout_width="match_parent" android:textSize="15sp" android:layout_height="wrap_content" android:textColor="@android:color/white" android:background="@color/colorPrimary" android:layout_marginTop="10dp" android:onClick="deleteDynamicShortcuts" android:text="@string/delete_dynamic_shortcuts" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/pinned_shortcut" android:padding="10dp" android:layout_marginTop="10dp" android:textColor="@color/colorAccent" android:textSize="14sp" /> <Button android:id="@+id/add_pinned_shortcuts" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="15sp" android:textColor="@android:color/white" android:background="@color/colorPrimary" android:onClick="addPinnedShortcuts" android:text="@string/add_pinned_shortcuts" /> </LinearLayout> |
4. Open AndroidManifest.xml and add the below code to it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android_app_shortcuts_demo"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <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> <activity android:name=".MessageActivity" android:label="@string/message_activity" android:parentActivityName=".MainActivity"> <!-- Parent activity meta-data to support 4.0 and lower --> <meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".MainActivity" /> </activity> </application> </manifest> |
5. Finally all done, now you can also implement App Shortcuts in your app.
Thanks.
Subscribe to us and get the latest news.