Last year in Android Nougat, we introduced APIs for retrieving Global Navigation Satellite System (GNSS) Raw measurements from Android devices. This past week, we publicly released GNSS Analysis Tools to process and analyze these measurements.
Android powers over 2 billion devices, and Android phones are made by many different manufacturers. The primary intent of these tools is to enable device manufacturers to see in detail how well the GNSS receivers are working in each particular device design, and thus improve the design and GNSS performance in their devices. However, with the tools publicly available, there is also significant value to the research and app developer community.
The GNSS Analysis Tool is a desktop application that takes in raw the GNSS Measurements logged from your Android device as input.
This desktop application provides interactive plots, organized into three columns showing the behavior of the RF, Clock, and Measurements. This data allows you to see the behavior of the GNSS receiver in great detail, including receiver clock offset and drift to the order of 1 nanosecond and 1 ppb and measurement errors on a satellite-by-satellite basis. This allows you to do sophisticated analysis at a level that, until now, was almost inaccessible to anyone but the chip manufacturers themselves.
The tools support multi-constellation (GPS, GLONASS, Galileo, BeiDou and QZSS) and multi-frequency. The image below shows the satellite locations for L1, L5, E1 and E5 signals tracked by a dual frequency chip.
The tools provide an interactive control screen from which you can manipulate the plots, shown below. From this control screen, you can change the background color, enable the Menu Bars for printing or saving, and select specific satellites for the plots.
The tools also provide automatic test reports of receivers. Click "Make Report" to automatically create the test report. The report evaluates the API implementation, Received Signal, Clock behavior, and Measurement accuracy. In each case it will report PASS or FAIL based on the performance against known good benchmarks. This test report is primarily meant for the device manufacturers to use as they iterate on the design and implementation of a new device. A sample report is shown below.
Our goal with providing these Analysis Tools is to empower device manufacturers, researchers, and developers with data and knowledge to make Android even better for our customers. You can visit the GNSS Measurement site to learn more and download this application.
Today we're giving you an early look at Android 8.1. This update to Android Oreo includes a set of targeted enhancements including optimizations for Android Go (for devices with 1GB or less of memory) and a new Neural Networks API to accelerate on-device machine intelligence. We've also included a few smaller enhancements to Oreo in response to user and developer feedback.
We're bringing you this Developer Preview so you can get your apps ready; we've already been helping device makers prepare for this new version. We recommend starting soon -- we're expecting the final public version in December.
It's easy to get Android 8.1 Developer Preview on your Pixel or Nexus device. Just enroll in the Android Beta Program -- you'll soon receive an over-the-air update to Android 8.1 beta. If you enrolled previously, you're all set, there's no need to re-enroll. The Developer Preview will be available for Pixel 2 and Pixel 2 XL devices, as well as for Pixel, Pixel XL, Pixel C, Nexus 5X, Nexus 6P, and the Android emulator.
Android 8.1 includes select new features and developer APIs (API level 27), along with the latest optimizations, bug fixes, and security patches. Some of the new APIs include:
Take a look at Android 8.1 site for more information, including the diff report and updated API reference docs.
With the consumer launch coming in December, it's important to test your current app now. This gives users a seamless transition to Android 8.1 when it arrives on their devices.
Just enroll your eligible device in Android Beta to get the latest update, then install your app from Google Play and test. If you don't have a Pixel or Nexus device, you can set up an Android 8.1 emulator for testing instead. If you notice any issues, fix them and update your app in Google Play right away -- without changing the app's platform targeting.
When you're ready, take advantage of the new features and APIs in Android 8.1, which we've already finalized as API Level 27. For an overview of what's new, take a look at Android 8.1 for Developers. You can also extend your apps with established Android Oreo features as well, see the Android Oreo site for details.
If your app uses forms, make sure to test them with autofill so that users can take advantage of this convenient feature. Enable "Autofill with Google" or a similar service in Settings and test the form fills to make sure they work as expected. We strongly recommend providing explicit hints about your fields, and also associating your website and mobile app, so that logins can be shared between them.
To build with Android 8.1, we recommend updating to Android Studio 3.0, which is now available from the stable channel. On top of the new app performance profiling tools, support for the Kotlin programming language, and Gradle build optimizations, Android Studio 3.0 makes it easier to develop with Android Oreo features like Instant Apps, XML Fonts, downloadable fonts, and adaptive icons.
We also recommend updating to the Android Support Library 27.0.0, which is available from Google's Maven repository. New in this version are: a ContentPager library for efficiently loading "paged" data on a background thread; ViewCompat wrappers for Autofill methods; an AmbientMode headless fragment that improves Wear ambient mode support, fullscreen Trusted Web Activities, and more. See the version notes for more information.
You can update your project's compileSdkVersion to API 27 to compile against the official Android 8.1 APIs. We also recommend updating your app's targetSdkVersion to API 27 to test with compatibility behaviors disabled. See the this guide for details on how to set up your environment to build with Android 8.1.
compileSdkVersion
targetSdkVersion
The Android 8.1 APIs are already final, so we've opened Google Play for apps compiled against or targeting API level 27. When you're ready, you can publish your APK updates in your alpha, beta, or production channels. Make sure that your updated app runs well on Android 8.1 as well as older versions. We recommend using Google Play's beta testing feature to run an alpha test on small group of users, then run a much larger open beta test. When you're ready to launch your update, you can use a staged rollout. We're looking forward to seeing your app updates!
As always, your feedback is crucial, so please let us know what you think. We've set up different hotlists where you report Android platform and tools issues, app compatibility issues, and third-party SDKs and tools issues. We also have a new hotlist for Neural Networks API issues.
You can also give us feedback through the Android Developer community or Android Beta community as we work towards the consumer release in December.
Android Studio 3.0 is ready to download today. Announced at Google I/O 2017, Android Studio 3.0 is a large update focused on accelerating your app development on Android.
This release of Android Studio is packed with many new updates, but there are three major feature areas you do not want to miss, including: a new suite of app profiling tools to quickly diagnose performance issues, support for the Kotlin programming language, and a new set of tools and wizards to accelerate your development on the latest Android Oreo APIs.
We also invested time in improving stability and performance across many areas of Android Studio. Thanks to your feedback during the preview versions of Android Studio 3.0! If you are looking for high stability, want to build high quality apps for Android Oreo, develop with the Kotlin language, or use the latest in Android app performance tools, then you should download Android Studio 3.0 today.
Check out the the list of new features in Android Studio 3.0 below, organized by key developer flows.
This release of Android Studio is the first milestone of bundles the Kotlin language support inside the IDE. Many of your favorite features such as code completion and syntax highlighting work well this release and we will continue to improve the remaining editor features in upcoming release. You can choose to add Kotlin to your project using the built-in conversion tool found under Code → Convert Java File to Kotlin File, or create a Kotlin enabled project with the New Project Wizard. Lean more about Kotlin language support in Android Studio.
build.gradle
To ensure app security and a consistent experience with physical devices, the emulator system images with the Google Play store included are signed with a release key. This means you will not be able to get elevated privileges. If you require elevated privileges (root) to aid with your app troubleshooting, you can use the Android Open Source Project (AOSP) emulator system images that do not include Google apps or services. Learn more.
To recap, Android Studio 3.0 includes these new major features:
Develop
Build
Optimize
Check out the release notes for more details.
Download
Test & Debug
If you are using a previous version of Android Studio, you can upgrade to Android Studio 3.0 today or you can download the update from the official Android Studio Preview download page. As mentioned in this blog, there are some breaking Gradle Plugin API changes to support new features in the IDE. Therefore, you should also update your Android Gradle plugin version to 3.0.0 in your current project to test and validate your app project setup.
We appreciate any feedback on things you like, issues or features you would like to see. If you find a bug or issue, feel free to file an issue. Connect with us -- the Android Studio development team ‐ on our Google+ page or on Twitter
Following last year's success, today we're announcing the second annual Google Play Indie Games Contest in Europe, expanding to more countries and bigger prizes. The contest rewards your passion, creativity and innovation, and provides support to help bring your game to more people.
If you're based in one of the 28 eligible countries, have 30 or less full time employees, and published a new game on Google Play after 1 January 2017, you may now be eligible to enter the contest. If you're planning on publishing a new game soon, you can also enter by submitting a private beta. Check out all the details in the terms and conditions. Submissions close on 31 December 2017.
Up to 20 finalists will showcase their games at an open event at the Saatchi Gallery in London on the 13th February 2018. At the event, the top 10 will be selected by the event attendees and the Google Play team. The top 10 will then pitch to the jury of industry experts, from which the final winner and runners up will be selected.
Anyone can register to attend the final showcase event at the Saatchi Gallery in London on 13 February 2018. Play some great indie games and have fun with indie developers,industry experts, and the Google Play team.
Visit the contest site to find out more and enter the Indie Games Contest now.
Posted by Maxim Mai, Partner Development Manager, Google Play
On October 16, people from around the world come together for World Food Day, with the goal to promote awareness and action for those who suffer from hunger and to advocate for food security and nutritious diets for all.
To raise funds and awareness for this cause, Google Play has joined forces with 12 popular apps and games to create the Apps and Games Against Hunger collection available in North and Latin America.
From now until October 21, 100% of revenue from designated in-app purchases made in Google Play's Apps and Games Against Hunger collection will be donated to World Food Program USA.
World Food Program USA supports the mission of the UN World Food Programme, the leading agency fighting hunger, by mobilizing individuals, lawmakers and businesses in the U.S. to advance the global movement to end hunger, feeding families in need around the world.
These are the 12 global leading apps and games taking part in this special fundraising collection on Google Play:
ShareTheMeal–Help children
Peak–Brain Games & Training
Dragon City
Cooking Fever
Animation Throwdown: TQFC
Legendary: Game of Heroes
My Cafe: Recipes & Stories - World Cooking Game
TRANSFORMERS: Forged to Fight
Rodeo Stampede: Sky Zoo Safari
Jurassic World™: The Game
MARVEL Contest of Champions
Sling Kong
Thank you to all our users and developers for supporting World Food Day.
To build apps that make use of phone numbers, it's often crucial to verify that the user owns a number. Doing this can be tricky from a UX perspective, not least in understanding phone number formats in different locales, but also in providing a verification mechanism that isn't cumbersome or using intrusive device permissions, such as the ability to read all of a user's SMS.
There are many libraries for efficient pre-built phone authentication, such as Firebase Phone Auth, but if you are an advanced developer and need to build this functionality yourself, Google Play Services has two new APIs that help you obtain a user's phone number and verify it via SMS without device permissions: the Phone Selector and SMS Retriever. Apps like Flipkart have seen a 12% increase of success rates in phone number sign-up flows using these methods.
The steps for using these with your server can be seen here:
In this post we'll show the code that you need to provide a phone number selector to your users, and then use this with the SMS retriever API to request a verification code from your server that the Android device will automatically receive and parse with no input from the user.
Note: Before you begin you'll need to build and test this is a device with a phone number that can receive SMS and runs Google Play services 10.2.x and higher.
The first step is to have the user initiate SMS verification from within your app. Your app might prompt the user to enter a phone number, and you can use the Phone Selector to make this easier, using code like this:
// Construct a request for phone numbers and show the picker private void requestHint() { HintRequest hintRequest = new HintRequest.Builder() .setPhoneNumberIdentifierSupported(true) .build(); PendingIntent intent = Auth.CredentialsApi.getHintPickerIntent( apiClient, hintRequest); startIntentSenderForResult(intent.getIntentSender(), RESOLVE_HINT, null, 0, 0, 0); }
The HintRequest builder tells Play Services that a phone number identifier is needed. This is then used to create and start an intent, which will show a Play Service dialog to the user allowing them to select their phone number to share with the app. This API does not require any permissions, and displays the number(s) available on the phone or Google Account for the user to select.
When the user selects a phone number it will be returned to the application in onActivityResult in E164 format on devices running the latest version of Play Services. Note that in some cases, depending on your phone, you may not get a phone number, so be sure to check if the credential is non-null. If you don't have a number, you'll need to provide a way for your user to type it in manually.
// Obtain the phone number from the result @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == RESOLVE_HINT) { if (resultCode == RESULT_OK) { Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY); // credential.getId(); <-- E.164 format phone number on 10.2.+ devices } } }
At this point you'll have a phone number string for your user. While this is useful, you'll likely want to verify that the user owns this particular number, for example to allow them to send or retrieve message with other users or identifying themselves with this number.
A simple way to verify phone number ownership is by sending an SMS to the number, containing a one time verification code, and having them enter that into your app. The SMS Verification API gives you the ability for the app to listen for an incoming SMS from which it can parse the code automatically.
To get started, your app will SmsRetrieverClient with code like this:
SmsRetrieverClient
SmsRetrieverClient client = SmsRetriever.getClient(this /* context */); Task<Void> task = client.startSmsRetriever(); task.addOnSuccessListener(new OnSuccessListener<Void>() { @Override public void onSuccess(Void aVoid) { // successfully started an SMS Retriever for one SMS message } }); task.addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { }); );
It's pretty simple -- you get an SMS Retriever client and then start a task for it. The task has an on Success listener as well as an on Failure one to override. After starting the SMS Retriever, you'd send the user's phone number to your server and start it's workflow for generating the message and sending it to that number.
The message needs to be constructed in a specific way. The message must fit in an SMS message, so it can't be longer than 140 bytes. It needs to start with a specific prefix: '<#>' or two consecutive zero-width space characters (U+200B). See the documentation for your more information. It must end with an 11-character hash that identifies your app, described below.
Example:
<#> Use 123456 as your verification code in Example App!
FA+9qCX9VSu
The one-time verification code can be any string: you can simply generate a random number. The message needs to end with a hash that is determined according to the procedures here. Google Play services will use this hash to determine which app the verification message is for. You only need to generate this hash once for your app package and signing certificate: it won't change and shouldn't be supplied by the client app.
Your server can then send the message to the phone using your existing SMS infrastructure or service. When this message is received, Google Play services broadcasts an intent which contains the text of the message. Here's the code:
public class MySMSBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) { Bundle extras = intent.getExtras(); Status status = (Status) extras.get(SmsRetriever.EXTRA_STATUS); switch(status.getStatusCode()) { case CommonStatusCodes.SUCCESS: String message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE); break; case CommonStatusCodes.TIMEOUT: break; } } } }
In the onReceive of the broadcast receiver you get the extras, and pull the status from there. If the status indicates that the message was successfully received, you can pull the message from the extras. From here you can parse out the verification code and send it back to your server to confirm phone number ownership.
For more information, check out the full documentation and this year's Google I/O talk.
Our early partners who use this API love it. Here are some testimonials from them:
Twilio observed and blogged that Android SMS Verification has never been easier.
"If you're a developer building mobile apps on Android that use phone numbers to register and identify user accounts, you should be using Twilio Verification SDK for Android for the quickest way to solve the problem of providing a smooth, secure and easy sign-up flow." - Simon Thorpe, Product Owner at Twilio
Authy loved the fact that these APIs work with their existing SMS infrastructure without requiring many changes.
"Adding Phone Selector + SMS Retriever into Authy 2FA app delivers magical UX for users while retaining the high security our application requires." -- Serge Kruppa, Head of Authy Engineering
Telesign observed better UX, increased security and higher conversion rates with the same backend framework.
"One significant advantage of this verification mode with lower friction is that customers might be able to see increased conversion rates for user sign-up and registration scenarios.
Enhanced security is also a benefit as Google Play Services only provides access to the SMS message to the targeted application based on the application hash inside the message." -- Priyesh Jain (Post author)
Since our public launch at Google I/O, we've been working hard to improve the developer experience of building instant apps. Today, we're excited to announce availability of the Android Instant Apps SDK 1.1 with some highly-requested features such as improved NDK support, configuration APKs for binary size reduction, and a new API to maintain user's context when they transition from an instant app to the installed app.
For a great instant app experience, app binaries need to be lean and well structured. That's why we're introducing configuration APKs.
Configuration APKs allow developers to isolate device-specific resources and native libraries into independent APKs. For an application that uses configuration APKs, the Android Instant Apps framework will only load the resources and native libraries relevant to the user's device, thereby reducing the total size of the instant app on the device.
We currently support configuration APKs for display density, CPU architecture (ABI), and language. With these, we have seen an average reduction of 10% in the size of the binaries loaded. Actual savings for a given app depend on the number of resource files and native libraries that can be configured.
As an example, a user on an ARM device with LDPI screen density and language set to Chinese would then receive device-agnostic code and resources, and then only get the configuration APKs that have ARM native libraries, the Chinese language, and LDPI resources. They would not receive any of the other configuration APKs such as the x86 libraries, Spanish language strings, or HDPI resources.
Setting up configuration APKs for your app is a simple change to your gradle setup. Just follow the steps in our public documentation.
On Android Oreo, the internal storage of the instant version of the app is directly available to the installed version of the app. With this release of the SDK, we are enabling this functionality on older versions of the Android Framework, including Lollipop, Marshmallow, and Nougat devices.
To extract the internal storage of the instant app, installed apps can now call InstantAppsClient.getInstantAppData() using the Instant Apps Google Play Services API and get a ZIP file of the instant app's internal storage.
Check out our code sample and documentation for more details on how to use this API.
It's simple to start building your instant app on the latest SDK. Just open the SDK Manager in Android Studio and update your Instant Apps Development SDK to 1.1.0. We can't wait to see what instant app experiences you build with these new features.
The Google Assistant is available across phones, speakers, Android TV and more. And it can help users get more done throughout their day—where they need to add something to a to-do list, turn on the lights, or play a song.
With music specifically, the Assistant has a lot to offer. It can understand media commands across devices ("play rock music") and supports media controls (like pause, skip, fast forward, thumbs up). And users can also control Android media playback through the Google Assistant. For example, you can pause playback by telling the Google Assistant to "pause playback" without lifting a finger. Or play music by saying "play rock on Universal Music Player".
In order for the user to control playback in your Android Media app via the Google Assistant, you can use the MediaSession APIs to make this happen. We recommend that you use MediaSession over intents as you craft your app's integration with the Google Assistant.
Your app must implement a MediaSession that handles a prescribed set of actions as described in Interacting with the Google Assistant.
Here are some sample projects that can help you get started using MediaSession in your media apps:
To learn more about MediaSession here are some good resources:
The Google Assistant uses MediaSession in the same way as other external controllers such as Android Wear do. Each of these controllers cause state changes inside your app, and there needs to be a way to respond to these changes. This is where MediaSession comes into play.
By using MediaSession, the Google Assistant can control your app's media playback, as well as stay synced with its current state.
Once you implement MediaSession you will probably want to test how playback can be controlled outside of your app. The Media Controller Test tool was created to emulate external control of your media playback. You can verify that when other apps (like Google Assistant) interact with your app, that it works as expected.
In its continuous effort to improve user experience, the Android platform has introduced strict limitations on background services starting in API level 26. Basically, unless your app is running in the foreground, the system will stop all of your app's background services within minutes.
As a result of these restrictions on background services, JobScheduler jobs have become the de facto solution for performing background tasks. For people familiar with services, JobScheduler is generally straightforward to use: except in a few cases, one of which we shall explore presently.
JobScheduler
Imagine you are building an Android TV app. Since channels are very important to TV Apps, your app should be able to perform at least five different background operations on channels: publish a channel, add programs to a channel, send logs about a channel to your remote server, update a channel's metadata, and delete a channel. Prior to Android 8.0 (Oreo) each of these five operations could be implemented within background services. Starting in API 26, however, you must be judicious in deciding which should be plain old background Services and which should be JobServices.
Service
JobService
In the case of a TV app, of the five operations mentioned above, only channel publication can be a plain old background service. For some context, channel publication involves three steps: first the user clicks on a button to start the process; second the app starts a background operation to create and submit the publication; and third, the user gets a UI to confirm subscription. So as you can see, publishing channels requires user interactions and therefore a visible Activity. Hence, ChannelPublisherService could be an IntentService that handles the background portion. The reason you should not use a JobService here is because JobService will introduce a delay in execution, whereas user interaction usually requires immediate response from your app.
IntentService
For the other four operations, however, you should use JobServices; that's because all of them may execute while your app is in the background. So respectively, you should have ChannelProgramsJobService, ChannelLoggerJobService, ChannelMetadataJobService, and ChannelDeletionJobService.
ChannelProgramsJobService
ChannelLoggerJobService
ChannelMetadataJobService
ChannelDeletionJobService
Since all the four JobServices above deal with Channel objects, it should be convenient to use the channelId as the jobId for each one of them. But because of the way JobServices are designed in the Android Framework, you can't. The following is the official description of jobId
Channel
channelId
jobId
Application-provided id for this job. Subsequent calls to cancel, or jobs created with the same jobId, will update the pre-existing job with the same id. This ID must be unique across all clients of the same uid (not just the same package). You will want to make sure this is a stable id across app updates, so probably not based on a resource ID.
What the description is telling you is that even though you are using 4 different Java objects (i.e. -JobServices), you still cannot use the same channelId as their jobIds. You don't get credit for class-level namespace.
This indeed is a real problem. You need a stable and scalable way to relate a channelId to its set of jobIds. The last thing you want is to have different channels overwriting each other's operations because of jobId collisions. Were jobId of type String instead of Integer, the solution would be easy: jobId= "ChannelPrograms" + channelId for ChannelProgramsJobService, jobId= "ChannelLogs" + channelId for ChannelLoggerJobService, etc. But since jobId is an Integer and not a String, you have to devise a clever system for generating reusable jobIds for your jobs. And for that, you can use something like the following JobIdManager.
jobId= "ChannelPrograms" + channelId
ChannelProgramsJobService, jobId= "ChannelLogs" + channelId
ChannelLoggerJobService,
JobIdManager
JobIdManager is a class that you tweak according to your app's needs. For this present TV app, the basic idea is to use a single channelId over all jobs dealing with Channels. To expedite clarification: let's first look at the code for this sample JobIdManager class, and then we'll discuss.
public class JobIdManager { public static final int JOB_TYPE_CHANNEL_PROGRAMS = 1; public static final int JOB_TYPE_CHANNEL_METADATA = 2; public static final int JOB_TYPE_CHANNEL_DELETION = 3; public static final int JOB_TYPE_CHANNEL_LOGGER = 4; public static final int JOB_TYPE_USER_PREFS = 11; public static final int JOB_TYPE_USER_BEHAVIOR = 21; @IntDef(value = { JOB_TYPE_CHANNEL_PROGRAMS, JOB_TYPE_CHANNEL_METADATA, JOB_TYPE_CHANNEL_DELETION, JOB_TYPE_CHANNEL_LOGGER, JOB_TYPE_USER_PREFS, JOB_TYPE_USER_BEHAVIOR }) @Retention(RetentionPolicy.SOURCE) public @interface JobType { } //16-1 for short. Adjust per your needs private static final int JOB_TYPE_SHIFTS = 15; public static int getJobId(@JobType int jobType, int objectId) { if ( 0 < objectId && objectId < (1<< JOB_TYPE_SHIFTS) ) { return (jobType << JOB_TYPE_SHIFTS) + objectId; } else { String err = String.format("objectId %s must be between %s and %s", objectId,0,(1<<JOB_TYPE_SHIFTS)); throw new IllegalArgumentException(err); } } }
As you can see, JobIdManager simply combines a prefix with a channelId to get a jobId. This elegant simplicity, however, is just the tip of the iceberg. Let's consider the assumptions and caveats beneath.
First insight: you must be able to coerce channelId into a Short, so that when you combine channelId with a prefix you still end up with a valid Java Integer. Now of course, strictly speaking, it does not have to be a Short. As long as your prefix and channelId combine into a non-overflowing Integer, it will work. But margin is essential to sound engineering. So unless you truly have no choice, go with a Short coercion. One way you can do this in practice, for objects with large IDs on your remote server, is to define a key in your local database or content provider and use that key to generate your jobIds.
Second insight: your entire app ought to have only one JobIdManager class. That class should generate jobIds for all your app's jobs: whether those jobs have to do with Channels, Users, or Cats and Dogs. The sample JobIdManager class points this out: not all JOB_TYPEs have to do with Channel operations. One job type has to do with user prefs and one with user behavior. The JobIdManager accounts for them all by assigning a different prefix to each job type.
User
Cat
Dog
JOB_TYPE
Third insight: for each -JobService in your app, you must have a unique and final JOB_TYPE_ prefix. Again, this must be an exhaustive one-to-one relationship.
-JobService
JOB_TYPE_
The following code snippet from ChannelProgramsJobService demonstrates how to use a JobIdManager in your project. Whenever you need to schedule a new job, you generate the jobId using JobIdManager.getJobId(...).
JobIdManager.getJobId(...)
import android.app.job.JobInfo; import android.app.job.JobParameters; import android.app.job.JobService; import android.content.ComponentName; import android.content.Context; import android.os.PersistableBundle; public class ChannelProgramsJobService extends JobService { private static final String CHANNEL_ID = "channelId"; . . . public static void schedulePeriodicJob(Context context, final int channelId, String channelName, long intervalMillis, long flexMillis) { JobInfo.Builder builder = scheduleJob(context, channelId); builder.setPeriodic(intervalMillis, flexMillis); JobScheduler scheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); if (JobScheduler.RESULT_SUCCESS != scheduler.schedule(builder.build())) { //todo what? log to server as analytics maybe? Log.d(TAG, "could not schedule program updates for channel " + channelName); } } private static JobInfo.Builder scheduleJob(Context context,final int channelId){ ComponentName componentName = new ComponentName(context, ChannelProgramsJobService.class); final int jobId = JobIdManager .getJobId(JobIdManager.JOB_TYPE_CHANNEL_PROGRAMS, channelId); PersistableBundle bundle = new PersistableBundle(); bundle.putInt(CHANNEL_ID, channelId); JobInfo.Builder builder = new JobInfo.Builder(jobId, componentName); builder.setPersisted(true); builder.setExtras(bundle); builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); return builder; } ... }
Footnote: Thanks to Christopher Tate and Trevor Johns for their invaluable feedback
adb shell am start -a "android.search.action.GLOBAL_SEARCH" --es query \"The Incredibles\"
import static android.support.v4.content.IntentCompat.EXTRA_START_PLAYBACK; public class SearchableActivity extends Activity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getIntent() != null) { // Retrieve video from getIntent().getData(). boolean startPlayback = getIntent().getBooleanExtra(EXTRA_START_PLAYBACK, false); Log.d(TAG, "Should start playback? " + (startPlayback ? "yes" : "no")); if (startPlayback) { // Start playback. startActivity(...); } else { // Show details for movie. startActivity(...); } } finish(); } }
adb shell 'am start -a android.intent.action.VIEW --ez android.intent.extra.START_PLAYBACK true -d <URI> -f 0x14000000'
adb shell 'am start -a android.intent.action.VIEW --ez android.intent.extra.START_PLAYBACK true -d content://com.example.android.assistantplayback/video/2 -n com.example.android.assistantplayback/.SearchableActivity -f 0x14000000'
public class MyMediaSessionCallback extends MediaSessionCompat.Callback { private final PlaybackTransportControlGlue<?> mGlue; public MediaSessionCallback(PlaybackTransportControlGlue<?> glue) { mGlue = glue; } @Override public void onPlay() { Log.d(TAG, "MediaSessionCallback: onPlay()"); mGlue.play(); updateMediaSessionState(...); } @Override public void onPause() { Log.d(TAG, "MediaSessionCallback: onPause()"); mGlue.pause(); updateMediaSessionState(...); } @Override public void onSeekTo(long position) { Log.d(TAG, "MediaSessionCallback: onSeekTo()"); mGlue.seekTo(position); updateMediaSessionState(...); } @Override public void onStop() { Log.d(TAG, "MediaSessionCallback: onStop()"); // Handle differently based on your use case. } @Override public void onSkipToNext() { Log.d(TAG, "MediaSessionCallback: onSkipToNext()"); playAndUpdateMediaSession(...); } @Override public void onSkipToPrevious() { Log.d(TAG, "MediaSessionCallback: onSkipToPrevious()"); playAndUpdateMediaSession(...); } }