Development, Exaud Insights
Do More With Android 7.1 App Shortcuts
Here is some more Android sweetness: App Shortcuts! This new feature introduced in Nougat 7.1 turns Android apps more useful as it allows users to launch actions of their favorite apps directly from the home screen.
How to Use App Shortcuts
To use App Shortcuts, instead of launching the app with a normal press, the user needs to do a long-press until a menu appears. Each shortcut can be open with a normal press or it can even be dragged out of shortcut menu and be placed on the homescreen to be easily accessible afterwards.
Moving to our example, and diving into a more technical view, we will go through a basic implementation of the App Shortcuts feature. We will use a Music Player as an example, with two shortcuts options to control the Music: Play and Stop buttons.
How to Build App Shortcuts
Most important of all, to use App Shortcuts we need to increase the compileSdkVersion to the latest SDK version:
Add this to your build.gradle:
compileSdkVersion 25
There are two different ways to build App Shortcuts: Static and Dynamic Shortcuts.
Static Shortcuts are a simpler way to implement and only relies on an xml definition of the shortcuts to show up on the shortcut menu.
Pros: Static shortcuts are supported by the App Launcher (Pixel Launcher, Nova Launcher, etc.) and don’t depend on an Android device version.
Cons: These shortcuts can only be updated by replacing the xml file, with a newer release.
Dynamic Shortcuts are a more powerful way to manage the available App Shortcuts. It allows to change the shortcuts list in Runtime, where you can add, remove and update all the shortcuts.
Pros: The list of shortcuts can be manipulated on Runtime
Cons: Can only be used on devices with Android versions, starting from 7.1
Adding Static Shortcuts
In order to add static shortcuts to our app, we need to add the name of an xml file (containing the shortcuts) into a property android:name="android.app.shortcuts"
in the Manifest file.
<manifest xmlns:android=“http://schemas.android.com/apk/res/android”
package=“com.exaud.musicplayer” >
<application
android:icon=“@mipmap/ic_launcher”
android:label=“@string/app_name”
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>
<intent–filter>
<action android:name=“com.exaud.musicplayer.ACTION_PLAY” />
<category android:name=“android.intent.category.DEFAULT” />
</intent–filter>
<intent–filter>
<action android:name=“com.exaud.musicplayer.ACTION_STOP” />
<category android:name=“android.intent.category.DEFAULT” />
</intent–filter>
</activity>
<meta–data android:name=“android.app.shortcuts”
android:resource=“@xml/shortcuts” />
</application>
</manifest>
The contents of the shortcuts xml file, should be a list of shortcuts, where each shortcut
should be composed of an android:shortcutId
to identify the id of the shortcut, an android:icon
, an android:shortcutShortLabel
, an android:shortcutDisabledMessage
to be used if this shortcut was disabled and an intent
(a shortcut allows to launch multiple Intents. When opening the shortcuts the user will see the last intent). The intent
should have the expected android:action
,the android:targetPackage
and the android:targetClass
.
In our example, we have two shortcuts to launch two different Intents: PLAY and STOP.
<shortcuts xmlns:android=“http://schemas.android.com/apk/res/android”>
<shortcut
android:shortcutId=“play”
android:icon=“@drawable/ic_play”
android:shortcutShortLabel=“@string/play_shortcut_short_label”
android:shortcutDisabledMessage=“@string/play_disabled_message”>
<intent
android:action=“com.exaud.musicplayer.PLAY”
android:targetPackage=“com.exaud.musicplayer”
android:targetClass=“com.exaud.musicplayer.MainActivity” />
</shortcut>
<shortcut
android:shortcutId=“stop”
android:icon=“@drawable/ic_stop”
android:shortcutShortLabel=“@string/stop_shortcut_short_label”
android:shortcutDisabledMessage=“@string/stop_disabled_message”>
<intent />
android:action=“com.exaud.musicplayer.STOP”
android:targetPackage=“com.exaud.musicplayer”
android:targetClass=“com.exaud.musicplayer.MainActivity” />
</shortcut>
</shortcuts>
About Dynamic Shortcuts
With Dynamic Shortcuts you can add new shortcuts, change the visibility and modify the contents of existing ones. In order to use the Dynamic Shortcuts we will have to access the ShortcutManager
service. By using the ShortcutManager
you can manage the following:
setDynamicShortcuts(List)
– replace the list of dynamic shortcuts
addDynamicShortcuts(List)
– add shortcuts to the current list
enableShortcuts(List)
– enable shortcuts with the id on list
disableShortcuts(List)
– disable shortcuts with id on list
updateShortcuts(List)
– update existing shortcuts with updated contents
The following code will add 2 Dynamic Shortcuts that will result in the same list of shortcuts generated with the Static Shortcuts:
ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
ShortcutInfo play = new ShortcutInfo.Builder(this, “play”)
.setShortLabel(“Play”)
.setDisabledMessage(“Play is not available”)
.setIcon(Icon.createWithResource(MainActivity.this, R.drawable.ic_play))
.setIntent(new Intent(“com.exaud.musicplayer.PLAY”))
.build();
ShortcutInfo stop = new ShortcutInfo.Builder(this, “stop”)
.setShortLabel(“Stop”)
.setDisabledMessage(“Stop is not available”)
.setIcon(Icon.createWithResource(MainActivity.this, R.drawable.ic_stop))
.setIntent(new Intent(“com.exaud.musicplayer.STOP”))
.build();
shortcutManager.addDynamicShortcuts(Arrays.asList(play, stop));
Music Player with Dynamic Shortcuts Depending on Context
Now for a better usage of the Dynamic Shortcuts, we will use PLAY as a toggleable button between PLAY and PAUSE and we will disable STOP button until PLAY is pressed. For that, we require an extra shortcut for PAUSE that will share the same shortcutId
as PLAY:
ShortcutInfo pause= new ShortcutInfo.Builder(this, “play”)
.setShortLabel(“Pause”)
.setDisabledMessage(“Pause is not available”)
.setIcon(Icon.createWithResource(MainActivity.this, R.drawable.ic_pause))
.setIntent(new Intent(“com.exaud.musicplayer.PLAY”))
.build();
In order to keep track of the current state of playback we will use a static variable for playing:private static boolean mPlaying;
We will also manage the shortcuts depending on the intent of the shortcut:
ACTION_MAIN
When starting the application we want to make sure that PLAY will be added to the list of dynamic shortcuts. shortcutManager.addDynamicShortcuts(Arrays.asList(play));
ACTION_STOP
When receiving the STOP intent, we will disable the STOP buttonshortcutManager.disableShortcuts(Arrays.asList("stop"));
. We will force the PLAY button to be displayed as PLAY, which will guarantee that PAUSE is reverted back to PLAY shortcutManager.updateShortcuts(Arrays.asList(play));
ACTION_PLAY
As for the PLAY intent, we need to handle both states of the playback. If we receive PLAY when we are not playing, we will initiate the playback and change the PLAY button to a PAUSE button: shortcutManager.updateShortcuts(Arrays.asList(pause));
If we receive a PLAY intent while playing, we will resume the playback and change the PAUSE button to a PLAY button: shortcutManager.updateShortcuts(Arrays.asList(play));
. On both situations, we want to make sure the STOP button is added to the dynamic shortcuts: shortcutManager.addDynamicShortcuts(Arrays.asList(stop));
Obs:updateShortcuts
replaces the sameshortcutId "play"
with a different shortcut.
Adding all up we can have a simple Activity with:
public class MainActivity extends AppCompatActivity {
private static boolean mPlaying;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Only manage App Shortcuts on 7.1 or higher
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
ShortcutInfo play = new ShortcutInfo.Builder(this, “play”)
.setShortLabel(“Play”)
.setDisabledMessage(“Play is not available”)
.setIcon(Icon.createWithResource(MainActivity.this, R.drawable.ic_play))
.setIntent(new Intent(“com.exaud.musicplayer.ACTION_PLAY”))
.build();
ShortcutInfo pause = new ShortcutInfo.Builder(this, “play”)
.setShortLabel(“Pause”)
.setDisabledMessage(“Pause is not available”)
.setIcon(Icon.createWithResource(MainActivity.this, R.drawable.ic_pause))
.setIntent(new Intent(“com.exaud.musicplayer.ACTION_PLAY”))
.build();
ShortcutInfo stop = new ShortcutInfo.Builder(this, “stop”)
.setShortLabel(“Stop”)
.setDisabledMessage(“Stop is not available”)
.setIcon(Icon.createWithResource(MainActivity.this, R.drawable.ic_stop))
.setIntent(new Intent(“com.exaud.musicplayer.ACTION_STOP”))
.build();
Intent intent = getIntent();
if (intent != null) {
String action = intent.getAction();
if (Intent.ACTION_MAIN.equals(action)) {
// Initialize playing as false
mPlaying = false;
// Add PLAY button
shortcutManager.addDynamicShortcuts(Arrays.asList(play));
} else if (“com.exaud.musicplayer.ACTION_PLAY”.equals(action)) {
if (!mPlaying) {
// Change PLAY to PAUSE button
shortcutManager.updateShortcuts(Arrays.asList(pause));
} else {
// Change PAUSE to PLAY button
shortcutManager.updateShortcuts(Arrays.asList(play));
}
// Toggle playing state
mPlaying = !mPlaying;
// Add STOP buttons
shortcutManager.addDynamicShortcuts(Arrays.asList(stop));
} else if (“com.exaud.musicplayer.ACTION_STOP”.equals(action)) {
// Disable STOP button
shortcutManager.disableShortcuts(Arrays.asList(“stop”));
// Enable PLAY button
shortcutManager.updateShortcuts(Arrays.asList(play));
}
}
}
}
}
Only missing a little cleanup when we close the app. We need to make sure that when the app is terminated, we disable STOP and revert PLAY button to PLAY state:
@Override
protected void onDestroy() {
super.onDestroy();
// Only manage App Shortcuts on 7.1 or higher
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
ShortcutInfo play = new ShortcutInfo.Builder(this, “play”)
.setShortLabel(“Play”)
.setDisabledMessage(“Play is not available”)
.setIcon(Icon.createWithResource(MainActivity.this, R.drawable.ic_play))
.setIntent(new Intent(“com.exaud.musicplayer.ACTION_PLAY”))
.build();
// Reset PLAY button to initial state
shortcutManager.updateShortcuts(Arrays.asList(play));
// Disable STOP button
shortcutManager.disableShortcuts(Arrays.asList(“stop”));
}
}
There are some more features and also best practices on how to use the App Shortcuts. Be sure to check the official guide to learn more. And for a more comprehensive look at all the potential of ShortcutManager, you can visit this page. Hope this were useful tips, to guide us all to a world with richer apps.
Leave a reply
You must be logged in to post a comment.