
IntentShare is a light open-source Android library that improves the sharing experience on API 16 and above.
TL;DR
Nowadays, sharing content is part of our daily life. Unfortunately, the Android framework tools do not provide a sharing experience which reaches all of our expectations. We decided to implement our own tool, IntentShare, that might improve the user experience of a sharing action by:
- providing different extras according to the chosen target activity in order to take advantages of each target's specificities.
- providing an easy way to track the selected target activities in order to improve/adapt the extras for a specific target.
- providing a sorted target activity list based on the context of your app in order to take advantages of how people use it.
Summary
Gradle dependency
Promoted on Maven Central :
compile 'fr.tvbarthel.intentshare:library:0.0.1'
Simple usage
IntentShare.with(MainActivity.this) .chooserTitle("Select a sharing target : ") .text("Default text you would like to share.") .mailBody("Extended text you would like to share in mail body.") .mailSubject("Mail subject.") .facebookBody(Uri.parse("http://tvbarthel.fr")) .twitterBody("Tweet can only have 127 char!") .deliver();
Advanced usage, custom ExtraProvider and Listener documentation here.
Motivations
Implicit intents are a very powerful feature of Android. They allow a component to delegate an operation to any other components register as able to handle it. Every intents are composed of an action and optional extras. Unlike explicit intents, an implicit intent doesn’t know in advance which component is going to received it. Extras part must respect a contract shared by every emitting and target components. The responsibility of extracting the extras and adapting them correctly to provide the expecting behaviour is let to the target component.
If every developers can create their own actions and define the contract of the extras part, Android provides a range of actions/extras contracts covering most of the common operations we would like to delegate. One of these provided actions is Intent.ACTION_SEND. As described in the official documentation, ACTION_SEND sends simple data to other activities. It is widely used to share content such as articles, videos, musics, and pictures across applications.
As sharing is a very common operation in our daily life, Android framework provides tools in order to help developers emitting and receiving ACTION_SEND intent. For instance ShareCompat will help us to handle the extras part while Intent.createChooser will help us to provide more context by changing the label displayed when a target activity must be chosen by the user.
But are this tools enough to fulfil the nowadays usages and expectations?
Unfortunately not, at least not from our point of view. Let’s see the gap between current tools and nowadays sharing expectations which motivated us to implement IntentShare.
We want specific extras according to the target activity
Sharing content is a perfect example to understand the power of implicit intents: the emitting application knows nothing about the applications that can receive the intent, and the applications that will receive the intent knows nothing about the emitting application, and yet they exchange data. They both fulfil a contract and Android brings the magic. However, sharing intent highlight a limitation of implicit intents : for a given intent, extras are the same for every target activities.
Why same extras for every target applications could be a limitation for a sharing operation?
The way of consuming contents change according to the target applications.
As mentioned above, adapting extras to provide the expecting behaviour is let to the target component. For instance, Gmail will use Intent.EXTRA_TEXT to fill the mail body while Twitter will used the same text to compose a tweet. But the usage and the context of both apps are quite different. In fact the way users consumes content changes according to the context of the app.
Let’s take the example of a news app from which you could share contents. In a social app such as Twitter, severals contents are displayed at the same time inside a list. Shared news must catch users' attentions in order to invite them to read more. That’s why a short headline with a picture as well as some hashtags are commonly used to display a small preview of the news inside a tweet. On the contrary, a news shared by mail is directly send to a friend by a friend. Content is already targeted, no needs of additional attractive elements. However people are used to take advantage of the offline access feature of email app and may expect to read the full article directly from their mail client.
Therefore, you might want to provide different data for the same intent based on the target activity. For instance :
Twitter data :
sendIntent.putExtra( Intent.EXTRA_TEXT, "Random Musings on the N Developer Preview by @CommonsWare http://ift.tt/1R8d4AP #AndroidDev” );
Mail data :
sendIntent.putExtra( Intent.EXTRA_SUBJECT, "Random Musings on the N Developer Preview by CommonsWare” ); // the whole article sendIntent.putExtra( Intent.EXTRA_TEXT, “Each time Google releases a new developer preview, I rummage through the API differences report and the high-level overviews," + "to see if there are things that warrant more attention from developers, with an emphasis on mainstream features that any" + "developer might reasonably use..." + "...I’ll be starting to cover the N Developer Preview in the next book update, due out later this month.” );
Unfortunately, implicit intents cannot provide this behaviour since the extra part is the same for every target components.
Target applications could break the data contract.
In addition to the previous example, another use case can reinforce the need of providing different extras according to the target app : every target applications don’t respect the extras contract of the ACTION_SEND.
In fact, as explained above, every applications can declare themselves as able to handle ACTION_SEND. Unfortunately, even if they are registered as target components, they won’t necessarily respect the extras contract.
Facebook app is the most known target app for sharing that does not respect the extras contract of the ACTION_SEND. EXTRA_TEXT will be taken into account by the Facebook app only if it’s an html link. Therefore, when sharing a news on Facebook, only the link of the article could be used while the full text could be shared by mail and only a short preview (headline, picture and hashtags) could be posted on twitter.
Different extras might be used for a given target application that does not respect the common extras contract in order to not affect other target applications.
These two examples highlight the first motivation for designing a new tool for sharing action : having the same content for every target applications does not provide the best experience to the users anymore.
We want to track selected target activity
In order to provide the best sharing experience as possible, developers have to know how people are using the sharing feature in order to adapt and improve the shared content.
Let’s go back to our example of a news application. If you could identify that most of your users are sharing articles through mails, you might want to take the time to improve the mail sharing experience. For instance, as the mail body does not have a character limit, you could add some related articles headlines and some links at its end: people would have access to more contents linked to what they just read. On the contrary, if users prefer to share your contents on Twitter, you might want to provide twitter account to the redactors of your team in order to @ them directly inside the sharing tweet. Thus, users could be able to follow redactors and discover more articles through their twitter accounts.
These two examples illustrate the need of tracking the chosen target activities in order to improve the sharing experience. Unfortunately, with the current tools of the Android framework, this feature is only accessible from API 22 thanks to Intent.createChooser(CharSequence, IntentSender).
Since still 78.5% of devices are pre 22, tracking selected activities became our second motivation for designing a new sharing tool which provide a listener from API 16.
(Android dashboard, Data collected during a 7-day period ending on 7 March 2016).
We want to sort target activities
Sharing content is now part of our daily life. More and more apps register themselves to handle the ACTION_SEND. On an average phone, when an Intent with the ACTION_SEND is fired, a list of 10 or more target activities is shown to the user. In order to provide the best sharing experience, this list should be sorted from the most likely to be chosen by the user to the less likely to be selected.
How current tools sort the target activities
When we started to design this sharing tools, our first thought regarding the listing of target activities was to replicate the native behaviour. Unfortunately, what we discovered didn’t reach our expectation.
The native component used by Intent.createChooser to display the list of target activities is an activity called : ChooserActivity.java which extends ResolverActivity.java.
public class ChooserActivity extends ResolverActivity {...} /** * This activity is displayed when the system attempts to start an Intent for * which there is more than one matching activity, allowing the user to decide * which to go to. It is not normally used directly by application developers. */ public class ResolverActivity extends AlertActivity implements AdapterView.OnItemClickListener {...}
Pre API 21
Target activities are simply sorted in the ResolverActivity according to their name thanks to : ResolveInfo.DisplayNameComparator.
The list won’t be adapted to the user needs. For instance, people using twitter to share content will always have to scroll to the bottom of the list when they could have selected the first one if the most used app for sharing have been identified.
From API 21
Target activities start to be sorted in the ResolverActivity by their total time in foreground thanks to UsageStatsManager used by ResolverActivity.ResolverComparator.
As fallback, each target activities which don’t have any totalTimeInForeground value are sorted by their name such as pre 21.
This approach seduces us at the begging. In fact, people are more likely to shared content via the app they use the most. Unfortunately, the usage api requires the android.permission.PACKAGE_USAGE_STATS permission : a system-level permission as explain in the official documentation.
This API requires the permission android.permission.PACKAGE_USAGE_STATS, which is a system-level permission and will not be granted to third-party apps. However, declaring the permission implies intention to use the API and the user of the device can grant permission through the Settings application.
Therefore, we couldn’t consider using the usage API for our sharing tools.
Since API 23
The ResolverComparator starts to used additional data from the usage API in order to improve the accuracy of the sorting by taking into account recentness or launch count for instance. In fact, total time in foreground isn’t necesseraly the best way to identify target activities which are more likely to be chosen by the user.
Even if this new way to evaluate a score for each target activities seduced us even more, it marked definitively the end of our intention to reproduce the native behaviour since it was based on hidden attributes such as launchCount.
Therefore, we decided not to try to replicate the native behaviour but to provide instead what we thought to be a good alternative.
Our sorting
As providing the same listing for every emitting applications seems to be hard to tackle without system permission, we started to think about providing a specific list for each emitting apps and the more we thought about it the more we were confident about providing a decent user experience.
In fact, sharing an article from the androiddev subreddit isn’t the same thing as sharing a meme from 9GAG. The target activity for a sharing request from 9GAG might not be the same as the one chosen for a sharing request from the androiddev subreddit. If all of your university buddies are using What’s App, you might use it for sharing the last meme you just find on 9GAG while you will send more serious stuff from the androiddev subreddit to your colleagues on a Slack channel for instance.
The chosen target activity might be very different according to the content of the emitting one. That’s the reason why we decided to provide a specific sorted list for every emitting applications. Sorting target activities became our third motivation for designing IntentShare in order to provide what we thought to be a decent sharing experience. Basically, the target activities are going to be sorted by the recentness of their last usage and by their original position in the queryIntentActivities list as a fallback.
Conclusion
Nowadays, sharing content is part of our daily life. Unfortunately, the Android framework tools do not provide a sharing experience which reaches all of our expectations. We decided to implement our own tool, IntentShare, that might improve the user experience of a sharing action by:
- providing different extras according to the chosen target activity in order to take advantages of each target's specificities.
- providing an easy way to track the selected target activities in order to improve/adapt the extras for a specific target.
- providing a sorted target activity list based on the context of your app in order to take advantages of how people use it.
Share your point of view (=
comments powered by DisqusSpecial Thanks to ...
Vincent Brison http://vincentbrison.com/ , for his precious advice.
License
Copyright (C) 2016 tvbarthel Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.