Once you’ve begin your audience, you charge to adhere assimilate them! Keep users affianced with your app, by arrive Android P’s new slices feature, appear at Google I/O 2018 as allotment of Android Jetpack.
The adamantine assignment isn’t over aloof because you’ve auspiciously arise your app and congenital up a user base. Once you’ve begin your audience, you charge to adhere assimilate them!
At this year’s I/O, Google arise Android slices, a new affection to advice accumulate users affianced with your application. Android slices arise in places area abounding Android users absorb a lot of time, including Google chase results, so they’re an able way to accumulate users advancing aback to your application.
By the end of this article, you’ll accept created two slices: a simple allotment that launches an Activity, and a activating allotment that lets users collaborate with your app, from alfresco of the appliance context.
Android Slices are snippets of your app’s agreeable displayed alfresco of your application. They will be debuting in Google search, and Google affairs to add allotment abutment to added applications and areas of the operating arrangement in the future.
Slices can affectation a ambit of content, including text, images, video, alive data, scrolling content, and abysmal links, as able-bodied as alternate controls such as toggles and sliders. Slices can additionally be dynamic, afterlight to reflect contest accident central your application.
Imagine you’ve installed an app for booking tickets for your bounded cinema. The abutting time you’re Googling the latest blockbuster, you’ll get the accepted chase results, and maybe that application’s “Book Now” slice. This lets you assets tickets to see this cine at your bounded cinema, after accepting to cross abroad from your chase results.
From the user’s perspective, this allotment has provided them with quick and accessible admission to the affection they bare at that exact moment. From the developer’s perspective, this allotment got their appliance in advanced of the user in a accordant context, and auspiciously re-engaged them.
Android Slices are additionally allotment of Android Jetpack, so they’re accurate on aggregate from Android 4.4 onwards. If you add slices to your project, according to Google the slices accept the abeyant to ability 95 percent of all Android users!
Slices can accomplish a ambit of actions, but let’s accumulate things simple for now and actualize a allotment that launches our application’s MainActivity.
Start by creating a new activity appliance the latest bare body of Android Studio 3.2, again accessible your project’s build.gradle book and add the androidx.slice dependencies. To accumulate things consistent, I’m additionally appliance the AndroidX namespace for the added dependencies.
At the time of writing, the activity of creating a allotment sometimes acquired Android Studio to automatically add alike slice-core and slice-builders dependencies. If you’re encountering aberrant absurdity messages, analysis your build.gradle book to accomplish abiding this hasn’t happened.
A allotment provider is the basal that lets you affectation slices alfresco of your application, including in Google chase results.
To actualize a allotment provider:
Every time a host appliance needs to affectation a slice, it’ll accelerate a bounden appeal to your allotment provider, with the Uniform Ability Identifier (URI) of the allotment it wants to display. The allotment provider will again alarm onCreateSliceProvider() and body the allotment by calling the onBindSlice() method. Finally, the onBindSlice() adjustment will acknowledgment the allotment and canyon it to the host application.
If you accessible your MySliceProvider class, the automatically generated cipher provides an overview of this process:
Since SliceProvider is a agreeable provider, it has to be declared in your project’s Manifest. Aback you actualize a allotment provider appliance Android Studio by activity to New… > Added > Allotment Provider, this acknowledgment gets added to your Manifest automatically:
If this Android allotment is activity to barrage our application’s MainActivity, we charge to accomplish some changes to the allotment provider:
You accomplish a allotment alternate by creating one or added allotment actions. A SliceAction can abide of a title, an icon, and a PendingIntent, which handles user alternation in your slices.
I’m activity to ascertain a distinct allotment activity to barrage our application’s MainActivity.
Then, I’m activity to mark this as the slice’s primary action, so it’ll activate whenever the user interacts with any allotment of the slice:
Although you can adapt your Android slices to a degree, ultimately they’re templated content. You cannot absolutely position a slice’s UI elements like aback defining an application’s blueprint via XML files.
To body a slice’s user interface, you charge to apparatus a ListBuilder, specify the blazon of rows you appetite to display, and ascertain the agreeable for anniversary row.
For now, let’s accumulate things simple and use a basal RowBuilder, which supports all of the afterward agreeable types:
To accumulate things simple, I’m activity to actualize a distinct row, consisting of a “Launch MainActivity” title.
This is all you charge to actualize a activity slice. However, aback slices are still an beginning feature, you’ll charge to jump through a few hoops afore you can acquaintance this allotment in action.
At the time of writing, you can alone analysis your Android slices appliance Google’s Allotment Viewer application, which emulates how slices will eventually arise in Google chase results.
To install Allotment Viewer:
cd /Users/jessicathornsby/Library/Android/sdk/platform-tools
./adb install -r -t slice-viewer.apk
Next, you’ll charge to actualize a allotment run configuration, and canyon it your slice’s different URI:
slice-content://com.jessicathornsby.launchslice/mainActivity
This app will now be installed on your Android device. Allotment Viewer will appeal permission to admission your app’s slices; tap Allow and your allotment should arise onscreen.
Give the slice’s “Launch MainActivity” button a click, and the allotment should acknowledge by ablution your application’s MainActivity.
Download the accomplished appliance from GitHub.
Let’s move assimilate article added agitative and actualize a activating slice, which allows users to collaborate with the accompanying appliance anon from the slice’s user interface.
This additional appliance is activity to affectation a amount the user can admission and decrease, either from the appliance itself, or from the slice. Regardless of whether the user changes the amount in the app or the slice, the new abstracts will be synced beyond both components, so they’ll consistently accept admission to the latest data.
To body this slice, either actualize a new project, or amend your absolute application. If you do adjudge to actualize a beginning project, again you’ll charge to echo the afterward setup:
Start by creating the application’s user interface.
Open your project’s activity_main.xml file, and actualize an “Increase” and a “Decrease” button, additional a TextView to eventually affectation the application’s activating value:
We additionally charge to actualize a cord ability that’ll affectation our activating value:
In the slice, I’m activity to affectation “Up” and “Down” arrows that change the application’s amount aback tapped:
Repeat the aloft steps, but this time baddest the ‘Arrow downward’ figure and accord it the name “ic_count_down.”
Every time the user increases or decreases the value, we charge to accomplish abiding our allotment knows about it!
To acquaint a allotment about changes, our app needs to alarm context.getResolver.notifyChange(Uri, null), which will activate the onBindSlice() adjustment and account the allotment to be rebuilt with the new content.
In our additional allotment provider, we charge to complete the accepted accomplish (such as implementing onCreateSliceProvider and onBindSlice), additional the following:
Here’s the accomplished MySliceProvider class:
Finally, we charge to actualize the advertisement receiver for retrieving anniversary new value, and allegorical the allotment provider whenever it needs to clean the slice:
To analysis this slice, you’ll charge to actualize a additional run agreement that passes this accurate slice’s different URI:
slice-content://com.jessicathornsby.dynamicslice/clickCount
Your allotment will now arise in the adversary or affiliated Android device.
To put this allotment to the test, tap its “Up” and “Down” arrows, and about-face to your application’s MainActivity. Tap either of the application’s “Increase” or “Decrease” buttons, and it should alpha counting from the amount you created in the slice, rather than from zero. If you about-face aback to the slice, you should acquisition the amount has adapted automatically.
Download the complete activity from GitHub.
Now you apperceive how to apparatus this new feature. Will you be appliance slices in your own Android projects? Let us apperceive in the comments below!
What are Android slices?
Create your aboriginal slice
Create your allotment provider
Making your Android slices interactive: Creating a Allotment Action
See Also: hack facebook messengerTesting Android slices with the Allotment Viewer
Creating a activating slice
Creating a multi-choice slice
Handling the slice’s intents
Put your activating allotment to the test
Wrapping up
dependencies { accomplishing fileTree(dir: 'libs', include: ['*.jar']) accomplishing 'androidx.appcompat:appcompat:1.0.0-alpha1' accomplishing 'androidx.constraintlayout:constraintlayout:1.1.0' accomplishing 'androidx.slice:slice-core:1.0.0-alpha2' accomplishing 'androidx.slice:slice-builders:1.0.0-alpha2' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.1.0-alpha1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-alpha1' }
import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.net.Uri; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.slice.Slice; import androidx.slice.SliceProvider; import androidx.slice.builders.ListBuilder; import androidx.slice.builders.ListBuilder.RowBuilder; //Create a chic that extends SliceProvider// public chic MySliceProvider extends SliceProvider { //Initialize your allotment provider, by calling onCreateSliceProvider// @Override accessible boolean onCreateSliceProvider() { acknowledgment true; } @Override @NonNull accessible Uri onMapIntentToUri(@Nullable Absorbed intent) { Uri.Builder uriBuilder = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT); if (intent == null) acknowledgment uriBuilder.build(); Uri abstracts = intent.getData(); if (data != absent && data.getPath() != null) { Cord aisle = data.getPath().replace("/", ""); uriBuilder = uriBuilder.path(path); } Ambience ambience = getContext(); if (context != null) { uriBuilder = uriBuilder.authority(context.getPackageName()); } acknowledgment uriBuilder.build(); } //Build the slice// accessible Allotment onBindSlice(Uri sliceUri) { Ambience ambience = getContext(); if (context == null) { acknowledgment null; } //Check the URI path// if (sliceUri.getPath().equals("/")) { //Create a ListBuilder, which you’ll use to add rows to your slice// acknowledgment new ListBuilder(getContext(), sliceUri) //Construct your rows appliance RowBuilder, and again add them to the list// .addRow(new RowBuilder(context, sliceUri).setTitle("URI found.")) //Build the list// .build(); } abroad { acknowledgment new ListBuilder(context, sliceUri) .addRow(new RowBuilder(context, sliceUri).setTitle("URI not found.")) .build(); } } @Override //Note that we don’t awning pinning a allotment in this article// accessible abandoned onSlicePinned(Uri sliceUri) { //Register any assemblage that charge to be notified of changes to the slice’s data// } @Override accessible abandoned onSliceUnpinned(Uri sliceUri) { //Don’t balloon to unregister any assemblage to abstain anamnesis leaks// } }
</activity> <provider android:name=".MySliceProvider" android:authorities="com.jessicathornsby.launchslice" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.app.slice.category.SLICE" /> <data android:host="jessicathornsby.com" android:pathPrefix="/" android:scheme="http" /> </intent-filter> </provider> </application> </manifest>
accessible SliceAction createActivityAction() { Absorbed absorbed = new Intent(getContext(), MainActivity.class); acknowledgment new SliceAction(PendingIntent.getActivity(getContext(), 0, intent, 0), IconCompat.createWithResource(getContext(), R.drawable.ic_home), "Launch MainActivity"); }
accessible Allotment createSlice(Uri sliceUri) { SliceAction activityAction = createActivityAction(); … … … .setPrimaryAction(activityAction);
import android.app.PendingIntent; import android.content.Intent; import android.net.Uri; import androidx.core.graphics.drawable.IconCompat; import androidx.slice.Slice; import androidx.slice.SliceProvider; import androidx.slice.builders.ListBuilder; import androidx.slice.builders.SliceAction; public chic MySliceProvider extends SliceProvider { @Override accessible boolean onCreateSliceProvider() { acknowledgment true; } @Override accessible Allotment onBindSlice(Uri sliceUri) { final Cord aisle = sliceUri.getPath(); about-face (path) { //Define the slice’s URI; I’m appliance ‘mainActivity’// case "/mainActivity": acknowledgment createSlice(sliceUri); } acknowledgment null; } accessible Allotment createSlice(Uri sliceUri) { SliceAction activityAction = createActivityAction(); //Create the ListBuilder// ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY); //Create the RowBuilder// ListBuilder.RowBuilder rowBuilder = new ListBuilder.RowBuilder(listBuilder) //Set the appellation text// .setTitle("Launch MainActivity.") //Set the row’s primary action// .setPrimaryAction(activityAction); //Add the row to the ListBuilder// listBuilder.addRow(rowBuilder); //Build the List// acknowledgment listBuilder.build(); } accessible SliceAction createActivityAction() { Absorbed absorbed = new Intent(getContext(), MainActivity.class); acknowledgment new SliceAction(PendingIntent.getActivity(getContext(), 0, intent, 0), IconCompat.createWithResource(getContext(), R.drawable.ic_home), "Launch MainActivity"); } }
dependencies { accomplishing fileTree(dir: 'libs', include: ['*.jar']) accomplishing 'androidx.appcompat:appcompat:1.0.0-alpha1' accomplishing 'androidx.constraintlayout:constraintlayout:1.1.0' accomplishing 'androidx.annotation:annotation:1.0.0-alpha1' accomplishing 'androidx.slice:slice-core:1.0.0-alpha2' accomplishing 'androidx.slice:slice-builders:1.0.0-alpha2' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.1.0-alpha2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-alpha2' }
<?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" tools:context=".MainActivity"> <TextView android:id="@+id/click_count" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:padding="20dp"/> <Button android:id="@+id/increase" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="+1"/> <Button android:id="@+id/decrease" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="-1"/> </LinearLayout>
<resources> <string name="app_name">dynamicSlice</string> <string name="click_string">Count: %d\u00B</string> </resources>
import android.os.Bundle; import android.content.Context; import android.widget.TextView; import android.net.Uri; import android.view.View; import androidx.appcompat.app.AppCompatActivity; import androidx.annotation.NonNull; public chic MainActivity extends AppCompatActivity accouterments View.OnClickListener { accessible changeless int clickCount = 0; clandestine TextView mTextView; @Override adequate abandoned onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = findViewById(R.id.click_count); findViewById(R.id.increase).setOnClickListener(this); findViewById(R.id.decrease).setOnClickListener(this); } @Override accessible abandoned onClick(View view) { int id = view.getId(); about-face (id) { case R.id.increase: //Increase the value// updateClickCount(getApplicationContext(), clickCount + 1); break; case R.id.decrease: //Decrease the value// updateClickCount(getApplicationContext(), clickCount - 1); break; } mTextView.setText(getClickString(getApplicationContext())); } accessible changeless Cord getClickString(@NonNull Ambience context) { acknowledgment context.getString(R.string.click_string, clickCount); } accessible changeless abandoned updateClickCount(Context context, int newValue) { if (newValue != clickCount) { clickCount = newValue; //Retrieve the URI that’s mapped to this slice// Uri uri = MySliceProvider.getUri(context, "clickCount"); //Notify the allotment about the adapted content// context.getContentResolver().notifyChange(uri, null); } } }
import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.app.PendingIntent; import android.net.Uri; import androidx.slice.builders.ListBuilder; import androidx.slice.Slice; import androidx.slice.builders.SliceAction; import androidx.slice.SliceProvider; import androidx.core.graphics.drawable.IconCompat; import changeless com.jessicathornsby.dynamicslice.MyBroadcastReceiver.ACTION_CHANGE_COUNT; import changeless com.jessicathornsby.dynamicslice.MyBroadcastReceiver.EXTRA_COUNT_VALUE; import changeless com.jessicathornsby.dynamicslice.MainActivity.getClickString; import changeless com.jessicathornsby.dynamicslice.MainActivity.clickCount; public chic MySliceProvider extends SliceProvider { clandestine Ambience context; clandestine changeless int calculation = 0; @Override accessible boolean onCreateSliceProvider() { ambience = getContext(); acknowledgment true; } @Override accessible Allotment onBindSlice(Uri sliceUri) { final Cord aisle = sliceUri.getPath(); about-face (path) { //Define the URI// case "/clickCount": acknowledgment createClickSlice(sliceUri); } acknowledgment null; } clandestine Allotment createClickSlice(Uri sliceUri) { //Define two SliceActions// SliceAction clickUp = new SliceAction(getChangeCountIntent(clickCount + 1), IconCompat.createWithResource(context, R.drawable.ic_count_up).toIcon(), "Increase count"); SliceAction clickDown = new SliceAction(getChangeCountIntent(clickCount - 1), IconCompat.createWithResource(context, R.drawable.ic_count_down).toIcon(), "Decrease count"); ListBuilder listBuilder = new ListBuilder(context, sliceUri); ListBuilder.RowBuilder clickRow = new ListBuilder.RowBuilder(listBuilder); clickRow.setTitle(getClickString(context)); //Add the accomplishments that’ll arise at the end of the row// clickRow.addEndItem(clickDown); clickRow.addEndItem(clickUp); //Add the row to the ancestor ListBuilder// listBuilder.addRow(clickRow); //Build the slice// acknowledgment listBuilder.build(); } //Define the PendingIntent that’ll eventually activate our advertisement receiver// clandestine PendingIntent getChangeCountIntent(int value) { Absorbed absorbed = new Intent(ACTION_CHANGE_COUNT); intent.setClass(context, MyBroadcastReceiver.class); intent.putExtra(EXTRA_COUNT_VALUE, value); acknowledgment PendingIntent.getBroadcast(getContext(), count++, intent, //If the PendingIntent already exists, again amend it with the new data// PendingIntent.FLAG_UPDATE_CURRENT); } accessible changeless Uri getUri(Context context, Cord path) { acknowledgment new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority(context.getPackageName()) .appendPath(path) .build(); } }
import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import changeless com.jessicathornsby.dynamicslice.MainActivity.clickCount; import changeless com.jessicathornsby.dynamicslice.MainActivity.updateClickCount; public chic MyBroadcastReceiver extends BroadcastReceiver { accessible changeless Cord ACTION_CHANGE_COUNT = "com.jessicathornsby.slicetesting.ACTION_CHANGE_COUNT"; accessible changeless Cord EXTRA_COUNT_VALUE = "com.jessicathornsby.slicetesting.EXTRA_COUNT_VALUE"; @Override accessible abandoned onReceive(Context context, Absorbed intent) { Cord activity = intent.getAction(); if (ACTION_CHANGE_COUNT.equals(action) && intent.getExtras() != null) { //Retrieve the new value// int newValue = intent.getExtras().getInt(EXTRA_COUNT_VALUE, clickCount); updateClickCount(context, newValue); } } }
- Control-click your project’s “src” package, got to New… > Added > Allotment Provider.
- Name this allotment provider “MySliceProvider.”
- Click “Finish.”
- A appellation item. This appears at the alpha of the row. The appellation account can be a timestamp, an image, or a SliceAction.
- A title. This is a distinct band of text, formatted as a title.
- A subtitle. This is a distinct band of text, formatted as approved text.
- A alpha item. This can be an icon, a timestamp, or a SliceAction.
- End items. These are items that arise at the end of anniversary row. You can accumulation assorted end items for anniversary row, but depending on the accessible amplitude some of these end items may not be displayed on assertive devices. Your alpha and end items can either be a timestamp, an icon, or a SliceAction.
- A primary action. This is the activity that’ll activate whenever the user curtains the row.
- Make abiding your Android accessory is absorbed to your development machine, or that your Android Virtual Accessory (AVD) is up and running.
- Download the Allotment Viewer app.
- Move the Allotment Viewer APK to your Android/sdk/platform-tools folder.
- Open a Command Prompt (Windows) or Terminal (Mac).
- Change agenda (“cd”), so the window is pointing at your Android/sdk/platform-tools folder, like this:
- Install the Allotment Viewer APK on your Android accessory or AVD, by accounting the afterward command into the Command Prompt or Terminal window, and again acute the Admission key:
- Go to Run > Edit Configurations… from the Android Studio toolbar.
- Click the little “+” figure and again baddest “Android App.”
- Enter “slice” into the Name field.
- Open the ‘Module’ dropdown, and again baddest ‘app.’
- Open the “Launch” dropdown, and baddest “URL.”
- Next, admission your slice’s URL, in the architecture slice-content://package-name/slice-URL. For example, my slice’s URL is:
- Click OK.
- Select Run > Run slice from the Android Studio toolbar, and baddest your device.
- Create a MySliceProvider class, by control-clicking your project’s “src” binder and selecting New… > Added > Allotment Provider.
- Add the afterward dependencies to your build.gradle file:
- Control-click your project’s “res” agenda and baddest New > Vector Asset.
- Click the little “Clip Art” icon.
- Select the “Arrow upward” resource, and again bang OK.
- Give your asset the name “ic_count_up,” and again bang Next.
- Click Finish.
- Create assorted SliceActions. We charge to ascertain abstracted allotment accomplishments for aback the user increases the value, and aback they abatement the value.
- Handle user input. We’ll additionally charge to ascertain a PendingIntent to annals our app’s amount change events. In the abutting step, we’ll be creating a BroadcastReceiver to handle these PendingIntents.
- Supply some end items. You can affectation timestamps, icons, and allotment accomplishments at the end of anniversary row. I’m activity to use the “Up” and “Down” vectors as my slice’s end items.
- Control-click your project’s “src” binder and baddest New > Added > Advertisement Receiver.
- Enter the name “MyBroadcastReceiver,” and again bang Finish.
- Open your MyBroadcastReceiver file, and add the following:
- Select Run > Edit Configurations from the Android Studio toolbar.
- Click the little “+” figure and baddest “Android App.”
- Give this agreement a name.
- Open the “Launch” dropdown, and again baddest “URL.”
- Enter the URI for triggering this slice. I’m appliance the following:
- Click “OK.”
- Select Run > Run slice from the Android Studio toolbar.
Comments
Post a Comment