Great mobile apps and luxury cars are similar, in that a lot of attention is paid to even the smallest details. Since Android version 7.1 (API 25) there is a new feature that lets users get to a specific screen or an action right from the home screen — Shortcuts. Although the feature has been available for quite a long time (since Dec 2016) there are tons of apps which haven’t adopted it yet. Having Shortcuts will not only make your app more professional but will also significantly improve UX. Let’s take a deeper look!
There are three types of shortcuts:
- Static shortcuts — defined in a resource file which is a part of the app;
- Dynamic shortcuts — shortcuts that are created (also updated or removed) during runtime;
- Pinned shortcuts — also created during runtime which appear in launchers as separate app icons. Pinned shortcuts cannot be removed programmatically.
Let’s walk through creating shortcuts using a real example. Meet the STAmina app — an app used by athletes of all kinds of underwater sports to improve breath-holding skills.
Note: Before proceeding to shortcuts itself, we need to make sure that the targetSdkVersion (Android Project -> Options -> Android Application -> Target Android version in VS for Mac) is set to 25 or greater.
Static Shortcuts
A good candidate for a static shortcut is the Add Apnea Exercise page. It’s used quite often and is always available. First, we need to let the OS know that the app has static shortcuts. This is done by adding a new meta-data to the android manifest (in Xamarin by having an additional class attribute for the Main Activity):
[Activity (Label = "STAmina", MainLauncher = true, Icon = "@mipmap/ic_launcher", RoundIcon = "@mipmap/ic_launcher_round", Theme = "@style/Theme.Splash", NoHistory = true, ScreenOrientation = ScreenOrientation.Portrait)] [MetaData("android.app.shortcuts", Resource = "@xml/shortcuts")] public class SplashScreen : MvxSplashScreenActivity { }
Notice that we reference an @xml/shortcuts resource. Let’s create it by adding a new shortcuts.xml file under the Resources/xml/ folder.
<shortcuts xmlns:android="https://schemas.android.com/apk/res/android"> <shortcut android:shortcutId="add_new" android:enabled="true" android:icon="@drawable/shortcut_add" android:shortcutShortLabel="@string/shortcut_add_new_short" android:shortcutLongLabel="@string/shortcut_add_new_long" android:shortcutDisabledMessage="@string/shortcut_add_new_disabled"> <intent android:action="android.intent.action.VIEW" android:targetPackage="com.squarecrowdapps.stamina" android:targetClass="com.squarecrowdapps.stamina.MainView"> <extra android:name="shortcut" android:value="add_new" /> </intent> <categories android:name="android.shortcut.conversation" /> </shortcut> <!-- Specify more shortcuts here. --> </shortcuts>
It is recommended to limit the length of shortcutShortLabel to 10 characters and the length of shortcutLongLabel to 25 characters due to the limited space within the menu.
There is a design guidelines document on how to design shortcut icons. You can find it here.
The enabled attribute works in pair with the shortcutDisabledMessage attribute. If for some reason we will decide to disable the ‘Add Apnea Exercise’ shortcut in the future, we will set enabled to false and put an explanation of why it’s disabled into shortcutDisableMessage and publish a new version of the app. This will do the following:
- The ‘Add Apnea Exercise’ shortcut will be removed from the shortcut menu;
- If a user had pinned the ‘Add Apnea Exercise’ shortcut to the home screen, the shortcut will be greyed out and shortcutDisableMessage will be displayed if the user taps on it.
Next, we provide the intent to navigate users to the correct place within the app. Before referencing an activity, make sure that the Activity attribute of that activity has the Exported property set to true and specifies the type name in the Name property (by default, the type name of an activity is based on the MD5SUM of the assembly-qualified name of the type being exported, which is not quite user friendly).
[Activity(Label = "STAmina", Theme = "@style/StaminaAppTheme", ScreenOrientation = Android.Content.PM.ScreenOrientation.Portrait, Exported = true, Name = "com.squarecrowdapps.stamina.MainView")] public class MainView : MvxAppCompatActivity<MainViewModel>, View.IOnClickListener, Toolbar.IOnMenuItemClickListener { }
Note: Make sure that you have set these properties and the value of the targetClass attribute from the shortcuts.xml matches the type name, otherwise users will be getting an ‘App isn’t installed’ error when tapping on the shortcut.
And last we check extras within the ‘OnCreate’ method and do the navigation.
protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); ... if (Intent?.Extras?.ContainsKey("shortcut") == true) { // Check the value and navigate to the appropriate screen } ... }
That’s it with static shortcuts!
Dynamic Shortcuts
Now it’s time to create some dynamic shortcuts. From the UX point of view, it’s worth adding links to the most frequently used screens and actions. Let’s update the app to show shortcuts to recently used exercises (users tend to use 2–3 different kinds of exercises until they advance to more difficult ones), but before creating shortcuts, make sure that the API version is 25 or greater.
if (Build.VERSION.SdkInt < BuildVersionCodes.NMr1) { return; }
To create a dynamic shortcut we will use ShortcutManager.
var shortcutManager = (ShortcutManager)context.GetSystemService(Context.ShortcutService); var newIntent = new Intent(context, typeof(MainView)); newIntent.SetFlags(ActivityFlags.ReorderToFront | ActivityFlags.ClearTop); newIntent.PutExtra("shortcut", "table"); newIntent.PutExtra("tableId", tableId); newIntent.SetFlags(ActivityFlags.ClearTop | ActivityFlags.SingleTop); newIntent.AddCategory("android.intent.category.LAUNCHER"); newIntent.SetAction("android.intent.action.MAIN"); ShortcutInfo shortcut = new ShortcutInfo.Builder(context, "table-" + tableId) .SetShortLabel(tableName) .SetLongLabel(tableName) .SetIcon(Icon.CreateWithResource(globals.ApplicationContext, Resource.Drawable.shortcut_o2)) .SetIntent(newIntent) .Build();
Here we fill the same information as we did in the shortcuts.xml. The only difference is that since we’re creating a link to the user-generated content, we put an additional piece of information in the extras (that is exercise id). Also, notice that the shortcut ID is unique.
And finally, let’s create a shortcut.
shortcutManager.SetDynamicShortcuts(new List<ShortcutInfo> { shortcut });
SetDynamicShortcuts will replace all current shortcuts with new ones (the ones that are passed as the parameter merged with static shortcuts). Alternatively, we could use AddDynamicShortcuts which adds shortcuts to the existing list. It is recommended to have no more than 4 shortcuts.
Looks good, we’re almost there! Now let’s do some housekeeping. Imagine that the user deleted the ‘Easy — 10 m’ exercise. Or even had pinned the shortcut to the home screen and then deleted the exercise (remember, pinned shortcuts cannot be removed programmatically). What will happen with the shortcuts? Nothing. They’ll still be there and will take the user to a non-existing page (which probably will crash the app). To avoid that we need to remove (dynamic) and disable (pinned) shortcuts each time when an exercise is deleted.
var shortcutManager = (ShortcutManager)context.GetSystemService(Context.ShortcutService); shortcutManager.RemoveDynamicShortcuts(new List<string> { "table-" + tableId }); shortcutManager.DisableShortcuts(new List<string> { "table-" + tableId });
Notice that we reuse the same unique shortcut ID here.
App Shortcuts are an easy to implement feature that your app can benefit from. Well thought out navigation simplifies the use of the application and improves user satisfaction. Don’t miss this opportunity.