Category Archives: Android Developers Blog

An Open Handset Alliance Project

Android Things Developer Preview 7

Posted by Dave Smith, Developer Advocate for IoT

Today we're releasing Developer Preview 7 (DP7) of Android Things, Google's platform that enables Android developers to create Internet of Things (IoT) devices. The platform also supports powerful applications such as video and audio processing and on-board machine learning with TensorFlow.

The latest preview is based on Android 8.1 and is updated to support version 11.8.0 of Google Play Services. For all the details of what's included in DP7, see the release notes. Here are some of the highlights:

Console enhancements and device updates

New features are also available in the Android Things Console to enhance product management from prototype to production:

  • Product Models. Create multiple software variations of the same hardware product, and manage the builds and updates for each independently.
  • Product Sharing. Grant additional user accounts access to view and manage the models, builds, and updates for a given product.
  • Analytics. View metrics on device activations and update statistics for your products.
  • Update Channels. Deploy software builds to groups of devices for development or beta testing, without disrupting production devices in the field.

Devices can subscribe to different update channels using new APIs added to UpdateManager. See the updated Device Updates API guide and console documentation to learn more about configuring update channel subscriptions.

Addressing developer feedback

We've received tons of amazing feedback from developers so far, and focused heavily on addressing many of the top reported issues in this release:

  • Improved camera resolution support. Apps can now capture image data up to the full native resolution of the camera hardware.
  • Support for MIDI. Use the MidiManager API to build a virtual MIDI device in your app or interface with external MIDI controllers.
  • Better testability of Android Things apps. The Peripheral I/O API now exposes interfaces instead of abstract classes, allowing local unit tests to replace these objects with mocks and stubs more easily.
  • Consistent API naming. This release renames many of the existing Android Things API classes to provide a more consistent developer experience across the entire surface. See the updated API reference to review how package and class names have changed.

New Bluetooth APIs

Android mobile devices expose controls to users for pairing with and connecting to Bluetooth devices through the Settings app. IoT devices running Android Things need to programmatically perform these same operations. The new BluetoothConnectionManager API enables apps to take control of the pairing and connection process. See the new Bluetooth API guide for more details.

Sample updates

Last year at Google I/O, we demonstrated building an app using Kotlin on Android Things. For developers using Kotlin, we have started publishing Kotlin versions of the Android Things samples. Today you can download the Button and LED sample in both Kotlin and Java, with more samples to follow very soon.

We have also migrated the TensorFlow Image Classifier sample app to use the TensorFlow Lite library, reducing the size of the pre-trained TensorFlow model by over 90% and the time required to classify the images by approximately 50%.

Feedback

Please send us your feedback by filing bug reports and feature requests, as well as asking any questions on Stack Overflow. You can also join Google's IoT Developers Community on Google+, a great resource to get updates and discuss ideas. We look forward to seeing what you build with Android Things!

Improving Stability by Reducing Usage of non-SDK Interfaces

Posted by David Brazdil, Software Engineer

In Android, we're always looking for ways to improve the user and developer experience by making those experiences as stable as possible. In this spirit, we've been working to ensure that apps don't use non-SDK interfaces, since doing so risks crashes for users and emergency rollouts for developers. In Android N, we restricted the set of symbols that C/C++ code could use. This change ensured that apps using C++ rely on stable NDK interfaces rather than incur the incremental crashes caused by reliance on unstable, non-NDK interfaces. Starting in the next release of Android, we will further increase stability by expanding these restrictions to cover the Java language interfaces of the SDK.

What behavior will I see?

Starting in the next release of Android, some non-SDK methods and fields will be restricted so that you cannot access them -- either directly, via reflection, or JNI. If you try, you can see errors such as NoSuchFieldException or NoSuchMethodException.

Initially, this restriction will impact interfaces with low or no usage. It is an explicit goal of our planning and design to respect our developer community and create the absolute minimum of change while addressing app stability issues flagged by our users and device manufacturers. In cases where a migration to SDK methods will be possible but is likely to be technically challenging, we'll allow continued usage until your app is updated to target the latest API level. We plan to broaden these restrictions in future platform versions, giving developers time to migrate with long advance warning, and also giving us time to gather feedback about any needed SDK interfaces. We have always said that using non-SDK interfaces is always risky -- they might change in any release as we refactor code to add features or fix bugs. So if your app currently relies on non-SDK interfaces, you should begin planning a migration to SDK alternatives.

Because the Java language has different features from C++, this restriction will take a slightly different form than the previous symbol restriction. You should not access classes that are not part of our SDK, but you also need to be sure that you are only using the officially documented parts of each class. In particular, this means that you should not plan to access methods or fields that are not listed in the SDK when you interact with a class via semantics such as reflection.

What if there isn't a SDK alternative?

We know that some apps may be using non-SDK interfaces in ways that do not have an SDK alternative. We value your feedback about where and how we need to expand and improve the public APIs for you. If you feel that you'll need the SDK API expanded before you can stop using non-SDK ones, please tell us via our bug tracker. We will be monitoring this list closely and using this valuable feedback to prioritize. It is critical for us to get this feedback in a timely manner so that we can continue to both tune the blacklist to minimize developer impact and also begin developing any needed alternatives for future platforms.

What's coming next?

In the next Android developer preview, you'll be able to run your existing apps and see warnings when you use a non-SDK interface that will be subject to blacklist or greylist in the final release. It's always a best practice to make sure your app runs on the developer preview, but you should pay specific attention to the interface compatibility warnings if you are concerned that you may be impacted.

In conjunction with the next developer preview and the new bug tracker category, we'll be monitoring usage of non-SDK interfaces. In cases where official SDK alternatives already exist, we'll publish official guidance on how to migrate away from commonly used non-SDK interfaces.

Continuous Shared Element Transitions: RecyclerView to ViewPager

By Shalom Gibly, Software Engineer, Google's Material Gallery Team

Transitions in Material Design apps provide visual continuity. As the user navigates the app, views in the app change state. Motion and transformation reinforce the idea that interfaces are tangible, connecting common elements from one view to the next.

This post aims to provide guidelines and implementation for a specific continuous transition between Android Fragments. We will demonstrate how to implement a transition from an image in a RecyclerView into an image in a ViewPager and back, using 'Shared Elements' to determine which views participate in the transition and how. We will also handle the tricky case of transitioning back to the grid after paging to an item that was previously offscreen.

This is the result we are aiming for:

If you wish to skip the explanation and go straight to the code, you can find it here.

What are shared elements?

A shared element transition determines how views that are present in two fragments transition between them. For example, an image that is displayed on an ImageView on both Fragment A and Fragment B transitions from A to B when B becomes visible.

There are numerous previously published examples which explain how shared elements work and how to implement a basic Fragment transition. This post will skip most of the basics and will walk through the specifics on how to create a working transition into a ViewPager and back. However, if you'd like to learn more about transitions, I recommend starting by reading about transitions at the Android's developers website, and take the time to watch this 2016 Google I/O presentation.

The challenges

Shared Element mapping

We would like to support a seamless back and forth transition. This includes a transition from the grid to the pager, and then a transition back to the relevant image, even when the user paged to a different image.

To do so, we will need to find a way to dynamically remap the shared elements in order to provide the Android's transition system what it needs to do its magic!

Delayed loading

Shared element transitions are powerful, but can be tricky when dealing with elements that need to be loaded before we can transition to them. The transition may simply not work as expected when views at the target fragment are not laid out and ready.

In this project, there are two areas where a loading time affects the shared element transition:

  1. It takes a few milliseconds for the ViewPager to load its internal fragments. Additionally, it takes time to load an image into the displayed pager fragment (may even include a download time for the asset).
  2. The RecyclerView also faces a similar delay when loading the images into its views.

Demo app design

Basic structure

Before we dive into the juicy transitions, here is a little bit about how the demo app is structured.

The MainActivity loads a GridFragment to present a RecyclerView of images. The RecyclerView adapter loads the image items (a constant array that is defined at the ImageData class), and manages the onClick events by replacing the displayed GridFragment with an ImagePagerFragment.

The ImagePagerFragment adapter loads the nested ImageFragments to display the individual images when paging happens.

Note: The demo app implementation uses Glide, which loads images into views asynchronously. The images in the demo app are bundled with it. However, you may easily convert the ImageData class to hold URL strings that point to online images.

Coordinating a selected/displayed position

To communicate the selected image position between the fragments, we will use the MainActivity as a place to store the position.

When an item is clicked, or when a page is changed, the MainActivity is updated with the relevant item's position.

The stored position is later used in several places:

  • When determining which page to show in the ViewPager.
  • When navigating back to the grid and auto-scrolling to the position to make sure it's visible.
  • And of course, when hooking up the transitions callbacks, as we'll see in the next section.

Setting up the transitions

As mentioned above, we will need to find a way to dynamically remap the shared elements in order to give the transition system what it needs to do its magic.

Using a static mapping by setting up transitionName attributes for the image views at the XML will not work, as we are dealing with an arbitrary amount of views that share the same layout (e.g. views inflated by the RecyclerView adapter, or views inflated by the ImageFragment).

To accomplish this, we'll use some of what the transition system provides us:

  1. We set a transition name on the image views by calling setTransitionName. This will identify the view with a unique name for the transition. setTransitionName is called when binding a view at the grid's RecyclerView adapter, and onCreateView at the ImageFragment. In both locations, we use the unique image resource as a name to identify the view.
  2. We set up SharedElementCallbacks to intercept onMapSharedElements and adjust the mapping of the shared element names to views. This will be done when exiting the GridFragment and when entering the ImagePagerFragment.

Setting the FragmentManager transaction

The first thing we set up to initiate a transition for a fragment replacement is at the FragmentManager transaction preparation. We need to inform the system that we have a shared element transition.

fragment.getFragmentManager()
   .beginTransaction()
   .setReorderingAllowed(true) // setAllowOptimization before 26.1.0
   .addSharedElement(imageView, imageView.getTransitionName())
   .replace(R.id.fragment_container, 
        new ImagePagerFragment(),
        ImagePagerFragment.class.getSimpleName())
   .addToBackStack(null)
   .commit();

The setReorderingAllowed is set to true. It will reorder the state changes of fragments to allow for better shared element transitions. Added fragments will have onCreate(Bundle) called before replaced fragments have onDestroy() called, allowing the shared view to get created and laid out before the transition starts.

Image transition

To define how the image transitions when it animates to its new location, we set up a TransitionSet in an XML file and load it at the ImagePagerFragment.

<ImagePagerFragment.java>
Transition transition =
   TransitionInflater.from(getContext())
       .inflateTransition(R.transition.image_shared_element_transition);
setSharedElementEnterTransition(transition);
<image_shared_element_transition.xml>
<?xml version="1.0" encoding="utf-8"?>
<transitionSet
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:duration="375"
   android:interpolator="@android:interpolator/fast_out_slow_in"
   android:transitionOrdering="together">
 <changeClipBounds/>
 <changeTransform/>
 <changeBounds/>
</transitionSet>

Adjusting the shared element mapping

We'll start by adjusting the shared element mapping when leaving the GridFragment. For that, we will call the setExitSharedElementCallback() and provide it with a SharedElementCallback which will map the element names to the views we'd like to include in the transition.

It's important to note that this callback will be called while exiting the Fragment when the fragment-transaction occurs, and while re-entering the Fragment when it's popped out of the backstack (on back navigation). We will use this behavior to remap the shared view and adjust the transition to handle cases where the view is changed after paging the images.

In this specific case, we are only interested in a single ImageView transition from the grid to the fragment the view-pager holds, so the mapping only needs to be adjusted for the first named element received at the onMapSharedElements callback.

<GridFragment.java>
setExitSharedElementCallback(
   new SharedElementCallback() {
     @Override
     public void onMapSharedElements(
         List<String> names, Map<String, View> sharedElements) {
       // Locate the ViewHolder for the clicked position.
       RecyclerView.ViewHolder selectedViewHolder = recyclerView
           .findViewHolderForAdapterPosition(MainActivity.currentPosition);
       if (selectedViewHolder == null || selectedViewHolder.itemView == null) {
         return;
       }

       // Map the first shared element name to the child ImageView.
       sharedElements
           .put(names.get(0),
                selectedViewHolder.itemView.findViewById(R.id.card_image));
     }
   });

We also need to adjust the shared element mapping when entering the ImagePagerFragment. For that, we will call the setEnterSharedElementCallback().

<ImagePagerFragment.java>
setEnterSharedElementCallback(
   new SharedElementCallback() {
     @Override
     public void onMapSharedElements(
         List<String> names, Map<String, View> sharedElements) {
          // Locate the image view at the primary fragment (the ImageFragment
          // that is currently visible). To locate the fragment, call
          // instantiateItem with the selection position.
          // At this stage, the method will simply return the fragment at the
          // position and will not create a new one.
       Fragment currentFragment = (Fragment) viewPager.getAdapter()
           .instantiateItem(viewPager, MainActivity.currentPosition);
       View view = currentFragment.getView();
       if (view == null) {
         return;
       }

       // Map the first shared element name to the child ImageView.
       sharedElements.put(names.get(0), view.findViewById(R.id.image));
     }
   });

Postponing the transition

The images we would like to transition are loaded into the grid and the pager and take time to load. To make it work properly, we will need to postpone the transition until the participating views are ready (e.g. laid out and loaded with the image data).

To do so, we call a postponeEnterTransition() in our fragments' onCreateView(), and once the image is loaded, we start the transition by calling startPostponedEnterTransition().

Note: postpone is called for both the grid and the pager fragments to support both forward and backward transitions when navigating the app.

Since we are using Glide to load the images, we set up listeners that trigger the enter transition when images are loaded.

This is done in two places:

  1. When an ImageFragment image is loaded, a call is made to its parent ImagePagerFragment to start the transition.
  2. When transitioning back to the grid, a start transition is called after the "selected" image is loaded.

Here is how the ImageFragment loads an image and notifies its parent when it's ready.

Note that the postponeEnterTransition is made at the the ImagePagerFragment, while the startPostponeEnterTransition is called from the child ImageFragment that is created by the pager.

<ImageFragment.java>
Glide.with(this)
   .load(arguments.getInt(KEY_IMAGE_RES)) // Load the image resource
   .listener(new RequestListener<Drawable>() {
     @Override
     public boolean onLoadFailed(@Nullable GlideException e, Object model,
         Target<Drawable> target, boolean isFirstResource) {
       getParentFragment().startPostponedEnterTransition();
       return false;
     }

     @Override
     public boolean onResourceReady(Drawable resource, Object model,
         Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
       getParentFragment().startPostponedEnterTransition();
       return false;
     }
   })
   .into((ImageView) view.findViewById(R.id.image));

As you may have noticed, we also call to start the postponed transition when the loading fails. This is important to prevent the UI from hanging during failure.

Final touches

To make our transitions even smoother, we would like to fade out the grid items when the image transitions to the pager view.

To do that, we create a TransitionSet that is applied as an exit transition for the GridFragment.

<GridFragment.java>
setExitTransition(TransitionInflater.from(getContext())
   .inflateTransition(R.transition.grid_exit_transition));
<grid_exit_transition.xml>
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
   android:duration="375"
   android:interpolator="@android:interpolator/fast_out_slow_in"
   android:startDelay="25">
 <fade>
   <targets android:targetId="@id/card_view"/>
 </fade>
</transitionSet>

This is what the transition looks like after this exit transition is set up:

As you may have noticed, the transition is still not completely polished with this setup. The fade animation is running for all the grid's card views, including the card that holds the image that transitions to the pager.

To fix it, we exclude the clicked card from the exit transition before commiting the fragment transaction at the GridAdapter.

// The 'view' is the card view that was clicked to initiate the transition.
((TransitionSet) fragment.getExitTransition()).excludeTarget(view, true);

After this change, the animation looks much better (the clicked card doesn't fade out as part of the exit transition, while the rest of the cards fade out):

As a final touch, we set up the GridFragment to scroll and reveal the card we transition to when navigating back from the pager (done at the onViewCreated):

<GridFragment.java>
recyclerView.addOnLayoutChangeListener(
   new OnLayoutChangeListener() {
      @Override
      public void onLayoutChange(View view,
                int left, 
                int top, 
                int right, 
                int bottom, 
                int oldLeft, 
                int oldTop, 
                int oldRight, 
                int oldBottom) {
         recyclerView.removeOnLayoutChangeListener(this);
         final RecyclerView.LayoutManager layoutManager =
            recyclerView.getLayoutManager();
         View viewAtPosition = 
            layoutManager.findViewByPosition(MainActivity.currentPosition);
         // Scroll to position if the view for the current position is null (not   
         // currently part of layout manager children), or it's not completely
         // visible.
         if (viewAtPosition == null 
             || layoutManager.isViewPartiallyVisible(viewAtPosition, false, true)){
            recyclerView.post(() 
               -> layoutManager.scrollToPosition(MainActivity.currentPosition));
         }
     }
});

Wrapping up

In this article, we implemented a smooth transition from a RecyclerView to a ViewPager and back.

We showed how to postpone a transition and start it after the views are ready. We also implemented shared element remapping to get the transition going when shared views are changing dynamically while navigating the app.

These changes transformed our app's fragment transitions to provide better visual continuity as users interact with it.

The code for the demo app can be found here.

A better way to track your promotions on Google Play Billing

Posted by Neto Marin, Developer Advocate

Promotions can be a valuable tool to increase user engagement or attract new users by offering content or features to a limited number of users free of charge.

We are happy to share an improvement in the Google Play Developer API that makes it easier to track your promotions from your own backend. Starting today, the API for Purchases.products will return "Promo" as a new value for the field purchaseType when the user redeems a promo code. Now, the possible values are:

  • 0. Test (test purchases)
  • 1. Promo (Promo code redemption purchase)

For purchases made using the standard in-app billing flow, the field will continue to not be set in the API response.

Please note: This state is only returned by the Purchases.products API. For subscriptions you may use Free Trials to offer free of charge subscription periods.

For more details about how to create and redeem promo codes, check the In-app Promotions documentation. For more details about the server-side API, check the Google Play Developer API documentation.

Congratulations to the winners of the Google Play Indie Games Contest 2017 in Europe

Posted by Adriana Puchianu, Developer Marketing Google Play

We have just wrapped up the second edition of the Google Play Indie Games Contest in Europe! The iconic Saatchi Gallery in London welcomed 20 developers, from 12 countries, who showcased their games to the audience of gamers, industry experts, and journalists.

The finalists' games were on show to the public, who spent three hours trying out their games and voting for their favourites, alongside the Google Play team. The top 10 finalists were then selected, and went on to pitch their games, and compete for the big prizes in front of our jury.

Please join us in congratulating the winners! They will be bringing home a well-deserved diploma, along with a prize package that will help them reach more gamers worldwide; including premium placement on the Google Play Store, marketing campaigns of up to 100,000 EUR and influencer campaigns of up to 50,000 EUR, the latest Google hardware, tickets to Google I/O, and much more.

It's really inspiring to see the excitement around this second edition, and great to see the new wave of indie games coming from Europe. We are already looking forward to playing the games that will be developed in 2018!

Check out the main winners and the other finalists on the Google Play Store!

Winner

Bury me, my love

Playdius

France

A reality-inspired interactive fiction designed for mobile phones. It tells the story of Nour, a Syrian woman trying to reach Europe in hope of a better life.

Runners up

Old Man's Journey

Broken Rules Interactive Media GmbH

Austria

A story game about life's precious moments, broken dreams, and changed plans.

Yellow

Bart Bonte

Belgium

A puzzle game for you! A love letter to a marvelous colour and to the little wonder called touchscreens. Warning: very yellow!

The other games that have made it into top 10 are:

Captain Tom Galactic Traveler

Picodongames

France

An open world platformer and space exploration game. Embark on an exploratory mission, discover planets, collect oxygen, play with gravity.

I Love Hue

Zut!

United Kingdom

A minimalist, ambient puzzle game influenced by mindfulness apps and abstract art. Players arrange shuffled mosaics of coloured tiles into perfectly ordered palettes.

Jodeo

Gamebra.in

Turkey

Jodeo is a 2D jelly critter. There's something it's curious about: what if 3D objects and 2D physics are in the same game? How can 2D objects interact with 3D objects?

Kami 2

State of Play

United Kingdom

The calming yet addictive puzzle game is back! With over 100 handcrafted puzzles, it takes you on a mind-twisting journey that combines logic and problem-solving.

Kenshō

FIFTYTWO

Russia

A tile sliding puzzle with a wonderful soundtrack. Mysterious things happen in a ruined room. Doors inside that room lead to different worlds and beautiful landscapes.

No More Buttons

Tommy Søreide Kjær

Norway

A hand-drawn platformer where the buttons are part of the environment.

The Big Journey

Catfishbox

Ukraine

Designed for kids and adults alike, this a beautiful, casual adventure. Tilt to roll around and explore a beautiful world with Mr. Whiskers.

How useful did you find this blogpost?

Introducing Android KTX: Even Sweeter Kotlin Development for Android

Posted by Jake Wharton (@JakeWharton), Florina Muntenescu (@FMuntenescu) & James Lau (@jmslau)

Today, we are announcing the preview of Android KTX - a set of extensions designed to make writing Kotlin code for Android more concise, idiomatic, and pleasant. Android KTX provides a nice API layer on top of both Android framework and Support Library to make writing your Kotlin code more natural.

The portion of Android KTX that covers the Android framework is now available in our GitHub repo. We invite you to try it out to give us your feedback and contributions. The other parts of Android KTX that cover the Android Support Library will be available in upcoming Support Library releases.

Let's take a look at some examples of how Android KTX can help you write more natural and concise Kotlin code.

Code Samples Using Android KTX

String to Uri

Let's start with this simple example. Normally, you'd call Uri.parse(uriString). Android KTX adds an extension function to the String class that allows you to convert strings to URIs more naturally.

Kotlin
Kotlin with Android KTX
val uri = Uri.parse(myUriString)
val uri = myUriString.toUri()

Edit SharedPreferences

Editing SharedPreferences is a very common use case. The code using Android KTX is slightly shorter and more natural to read and write.

Kotlin
Kotlin with Android KTX
sharedPreferences.edit()
           .putBoolean(key, value)
           .apply()
sharedPreferences.edit { 
    putBoolean(key, value) 
}

 

Translating path difference

In the code below, we translate the difference between two paths by 100px.

Kotlin
Kotlin with Android KTX
val pathDifference = Path(myPath1).apply {
   op(myPath2, Path.Op.DIFFERENCE)
}

val myPaint = Paint()

canvas.apply {
   val checkpoint = save()
   translate(0F, 100F)
   drawPath(pathDifference, myPaint)
   restoreToCount(checkpoint)
}


val pathDifference = myPath1 - myPath2

canvas.withTranslation(y = 100F) {
   drawPath(pathDifference, myPaint)
}

Action on View onPreDraw

This example triggers an action with a View's onPreDraw callback. Without Android KTX, there is quite a bit of code you need to write.

Kotlin
view.viewTreeObserver.addOnPreDrawListener(
       object : ViewTreeObserver.OnPreDrawListener {
           override fun onPreDraw(): Boolean {
               viewTreeObserver.removeOnPreDrawListener(this)
               actionToBeTriggered()
               return true
           }
       })
Kotlin with Android KTX
view.doOnPreDraw { actionToBeTriggered() }

There are many more places where Android KTX can simplify your code. You can read the full API reference documentation on GitHub.

Getting Started

To start using Android KTX in your Android Kotlin projects, add the following to your app module's build.gradle file:

repositories {
    google()
}

dependencies {
    // Android KTX for framework API
    implementation 'androidx.core:core-ktx:0.1'
    ...
}

Then, after you sync your project, the extensions appear automatically in the IDE's auto-complete list. Selecting an extension automatically adds the necessary import statement to your file.

Beware that the APIs are likely to change during the preview period. If you decide to use it in your projects, you should expect breaking changes before we reach the stable version.

androidx: Hello World!

You may notice that Android KTX uses package names that begin with androidx. This is a new package name prefix that we will be using in future versions of Android Support Library. We hope the division between android.* and androidx.* makes it more obvious which APIs are bundled with the platform, and which are static libraries for app developers that work across different versions of Android.

What's Next?

Today's preview launch is only the beginning. Over the next few months, we will iterate on the API as we incorporate your feedback and contributions. When the API has stabilized and we can commit to API compatibility, we plan to release Android KTX as part of the Android Support Library.

We look forward to building Android KTX together with you. Happy Kotlin-ing!

IoT Developer Story: Deeplocal

Posted by Dave Smith, Developer Advocate for IoT

Deeplocal is a Pittsburgh-based innovation studio that makes inventions as marketing to help the world's most loved brands tell their stories. The team at Deeplocal built several fun and engaging robotics projects using Android Things. Leveraging the developer ecosystem surrounding the Android platform and the compute power of Android Things hardware, they were able to quickly and easily create robots powered by computer vision and machine learning.

DrawBot

DrawBot is a DIY drawing robot that transforms your selfies into physical works of art.

"The Android Things platform helped us move quickly from an idea, to prototype, to final product. Switching from phone apps to embedded code was easy in Android Studio, and we were able to pull in OpenCV modules, motor drivers, and other libraries as needed. The final version of our prototype was created two weeks after unboxing our first Android Things developer kit."

- Brian Bourgeois, Producer, Deeplocal

Want to build your own DrawBot? See the Hackster.io project for all the source code, schematics, and 3D models.

HandBot

A robotic hand that learns and reacts to hand gestures, HandBot visually recognizes gestures and applies machine learning.

"The Android Things platform made integration work for Handbot a breeze. Using TensorFlow, we were able to train a neural network to recognize hand gestures. Once this was created, we were able to use Android Things drivers to implement games in easy-to-read Android code. In a matter of weeks, we went from a fresh developer kit to competing against a robot hand in Rock, Paper, Scissors."

- Mike Derrick, Software Engineer, Deeplocal

Want to build your own HandBot? See the Hackster.io project for all the source code, schematics, and 3D models.

Visit the Google Hackster community to explore more inspiring ideas just like these, and join Google's IoT Developers Community on Google+ to get the latest platform updates, ask questions, and discuss ideas.

Android Developer Story: Big Fish Games uses open beta testing to de-risk their game launch

Posted by Kacey Fahey, Developer Marketing, Google Play

Based in Seattle, Big Fish Games was founded in 2002. Starting as a game studio, they quickly turned into a major publisher and distributor of casual games. Leading up to the launch of their hit time management game, Cooking Craze, the team ran an open beta on Google Play.

Big Fish Games found that using open beta provided more than 10x the amount of user feedback from around the world, and also gave them access to key metrics and Android Vitals in the Play Console. The ability to monitor game performance metrics pre-launch allowed the team to focus on areas of improvement, which lead to a 21% reduction in crash rate. The larger sample size of beta testers also provided more insights on player behavior and helped achieve a +7% improvement in day 1, day 7, and day 30 retention rates.

You can also learn more pre-launch best practices and strategies to improve performance post-launch at our Google Developer Day on Monday, March 19th at GDC. Sign up to stay informed.

How useful did you find this blogpost?

How we fought bad apps and malicious developers in 2017

Posted by Andrew Ahn, Product Manager, Google Play

Apps bring devices to life -- letting you book a ride instantly, connect and share memories with friends, be alerted about current events, play games with someone across the globe, and get work done in the office or on the road. Google Play is committed to providing a safe experience for billions of Android users to find and discover such apps. Over the years, this commitment has made Google Play a more trusted and safer place. Last year we've more than halved the probability of a user installing a bad app, protecting people and their devices from harm's way, and making Google Play a more challenging place for those who seek to abuse the app ecosystem for their own gain.

In 2017, we took down more than 700,000 apps that violated the Google Play policies, 70% more than the apps taken down in 2016. Not only did we remove more bad apps, we were able to identify and action against them earlier. In fact, 99% of apps with abusive contents were identified and rejected before anyone could install them. This was possible through significant improvements in our ability to detect abuse - such as impersonation, inappropriate content, or malware - through new machine learning models and techniques.

We've also developed new detection models and techniques that can identify repeat offenders and abusive developer networks at scale. This resulted in taking down of 100,000 bad developers in 2017, and made it more difficult for bad actors to create new accounts and attempt to publish yet another set of bad apps.

Here are a few examples of bad apps we took action against in 2017:

Copycats

Attempting to deceive users by impersonating famous apps is one of the most common violations. Famous titles get a lot of search traffic for particular keywords, so the bad actors try to amass installs leveraging such traffic. They do this by trying to sneak in impersonating apps to the Play Store through deceptive methods such as using confusable unicode characters or hiding impersonating app icons in a different locale. In 2017, we took down more than a quarter of a million of impersonating apps.

Inappropriate content

We don't allow apps that contain or promote inappropriate content, such as pornography, extreme violence, hate, and illegal activities. The improved machine learning models sift through massive amounts of incoming app submissions and flag them for potential violations, aiding the human reviewers in effectively detecting and enforcing on the problematic apps. Tens of thousands of apps with inappropriate content were taken down last year as a result of such improved detection methods.

Potentially Harmful Applications (PHAs)

PHAs are a type of malware that can harm people or their devices -- e.g., apps that conduct SMS fraud, act as trojans, or phishing user's information. While small in volume, PHAs pose a threat to Android users and we invest heavily in keeping them out of the Play Store. Finding these bad apps is non-trivial as the malicious developers go the extra mile to make their app look as legitimate as possible, but with the launch of Google Play Protect in 2017 we've reduced the rate of PHA installs by a factor of 10 compared to 2016.

Despite the new and enhanced detection capabilities that led to a record-high takedowns of bad apps and malicious developers, we know a few still manage to evade and trick our layers of defense. We take these extremely seriously, and will continue to innovate our capabilities to better detect and protect against abusive apps and the malicious actors behind them. We are committed to make Google Play the most trusted and safe app store in the world.

How useful did you find this blogpost?