In order to better understand sharing functionality, let’s define its use case. For example, if you’re developing a social media app, you might want to provide the ability to share text or images. In fact, there are many more applications for this functionality. Almost every messenger, social network, and even simple note-taking applications use this feature.
For a non-compose application there is a pretty straight forward guide in the official Android documentation, but I found out that this is not exactly the same for compose applications. So let’s go and learn how to do this step by step.
Tech stack
First, let’s discuss what technologies and libraries we will use. To keep this article simple, I’m assuming that most of you are familiar with Compose and all other modern tools. If not, I highly recommend checking them out.
- Jetpack Compose is our UI toolkit.
- Compose Navigation to navigate between composables while taking advantage of the Navigation component’s infrastructure and features.
- Hilt is our DI framework. Here is also important to know how to integrate Hilt with Jetpack Compose
In this list Hilt is an optional component, but it will make your life so much easier, so I highly recommend using it.
Photo by Myriam Jessier on Unsplash
Now it is time to write some code
First let’s update our manifest. In order to mark our application as a share target we need to specify <intent-filter>
section for our Activity.
<activity | |
android:name=".features.main.MainActivity" | |
android:theme="@style/Theme.AppSplash"> | |
<intent-filter> | |
<action android:name="android.intent.action.MAIN" /> | |
<category android:name="android.intent.category.LAUNCHER" /> | |
</intent-filter> | |
<intent-filter> | |
<action android:name="android.intent.action.SEND" /> | |
<category android:name="android.intent.category.DEFAULT" /> | |
<data android:mimeType="text/*" /> | |
</intent-filter> | |
<intent-filter> | |
<action android:name="android.intent.action.SEND" /> | |
<category android:name="android.intent.category.DEFAULT" /> | |
<data android:mimeType="image/*" /> | |
</intent-filter> | |
</activity> |
By specifying the intent filter section for our activity, we let the system know that we are ready to process the intent with the specified parameters.
So in our case those parameters will be:
<action android:name="android.intent.action.SEND" />
This is the default action that is used to share some data.
<data android:mimeType="text/*" />
Here we let the system know that we can process any text data.
<data android:mimeType="image/*" />
And this is serving for receiving an image.
For more in depth description please read the official guide.
If you are going to launch your application right now, and then share something — your app should appear in the android share sheet.
Navigation
Now it is time to update our navigation graph, as we probably want to show a specific screen that can process shared data.
Your navigation graph should look like this:
Pretty simple isn’t it? Just like for regular deep links, we specify the list of navDeepLink{}
. But instead of uriPattern
we are specifying action
and mimeType
that correspond to our intent filter in the manifest. The next question is how can i access shared data? Let’s look.
ViewModel
@HiltViewModel | |
class SharingTargetViewModel @Inject constructor( | |
savedStateHandle: SavedStateHandle, | |
... | |
) : ViewModel() { | |
private val sharedContentState = savedStateHandle.getStateFlow(NavController.KEY_DEEP_LINK_INTENT, Intent()) | |
.map { intent -> intent.parseSharedContent() } | |
.map { | |
// map to ui state | |
} | |
.stateIn( | |
scope = viewModelScope, | |
started = SharingStarted.WhileSubscribed(5_000), | |
initialValue = SharedContent.EMPTY | |
) | |
} |
Job Offers
And of course let’s inject our ViewModel into Composable:
Our Intent is already available to our ViewModel via the SavedStateHandle without any additional work. To access it, we need to use key NavController.KEY_DEEP_LINK_INTENT. Hilt will do all the magic for us.
Fetching shared data from intent
The last step is to get the data from the intent. So let’s implement our Intent.parseSharedContent()
function.
As you can see from this piece of code, you need to use the following method to access text data:
val textContent:String? = getStringExtra(Intent.EXTRA_TEXT)
And to access image data you need to use:
val imageContent: Uri? = getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as? Uri
And that is it!
This feature, along with many others, was implemented in my open source project. So make sure you check out the repository and thanks for reading.
This article was originally published on proandroiddev.com