Brian Gardner - Big Nerd Ranch Tue, 19 Oct 2021 17:47:16 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.5 [Updated] Splash Screens the Right Way https://bignerdranch.com/blog/splash-screens-the-right-way/ https://bignerdranch.com/blog/splash-screens-the-right-way/#respond Thu, 18 Feb 2021 15:00:37 +0000 https://nerdranchighq.wpengine.com/blog/splash-screens-the-right-way/ Splash screens are a big deal for Android developers. We're aiming to update the implementation with modern Android practices.

The post [Updated] Splash Screens the Right Way appeared first on Big Nerd Ranch.

]]>
Launching a Splash Screen Update

Splash screens are a big deal for Android developers. In fact, BNR’s VP of Engineering, Chris Stewart, wrote our original Splash Screens the Right Way blog post over five years ago and it is still our most popular post.

We’re aiming to update the implementation with modern Android practices and to fix a bug in the original post. The general implementation of the splash screen is the same but there are a few considerations to make about how to navigate. If you are interested, I uploaded some sample project code on Github.

Before getting started, the official Material name for a splash screen is now Launch Screen. And though there are several types of launch screens, we’ll focus on one that displays the app icon when the app process first loads.

Fixing a bug

Configuring the launch screen background works the same as the original post. A background XML drawable is created and set as the window background for a launch screen-specific theme.

The original implementation declared the background like this:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:drawable="@color/gray"/>

    <item>
        <bitmap
            android:gravity="center"
            android:src="@mipmap/ic_launcher"/>
    </item>

</layer-list>

This no longer works for more recently created Android apps for an interesting reason.

The <bitmap> tag expects an image @drawable resource, but it doesn’t allow specifying an XML drawable source file. When a newer Android app is created, the mipmap launcher icons include a vector version which is an XML file.

This is the reason some people run into this issue when creating a launch screen using the original blog post. Since one of the launcher icons is an XML file, the <bitmap> tag throws an exception. Trying to run the app in this configuration logs this error:

2020-09-15 13:14:18.365 25218-25218/com.example.splashscreen E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.splashscreen, PID: 25218
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.splashscreen/com.example.splashscreen.SplashActivity}: android.content.res.Resources$NotFoundException: Drawable com.example.splashscreen:drawable/background_splash with resource ID #0x7f07005f
        ...
    Caused by: android.content.res.Resources$NotFoundException: Drawable com.example.splashscreen:drawable/background_splash with resource ID #0x7f07005f
    Caused by: android.content.res.Resources$NotFoundException: File res/drawable/background_splash.xml from drawable resource ID #0x7f07005f
        ...
    Caused by: org.xmlpull.v1.XmlPullParserException: Binary XML file line #8: <bitmap> requires a valid 'src' attribute
2020-09-15 13:14:18.365 25218-25218/com.example.splashscreen E/AndroidRuntime:     at android.graphics.drawable.BitmapDrawable.inflate(BitmapDrawable.java:775)
    ...

The original blog post was created before vector resources were available on Android so all of the launcher icons are images, meaning no error is thrown by the <bitmap> tag.

Fixing this for apps with vector launcher icons requires deleting the <bitmap> tag and moving the drawable and gravity attributes to the <item> tag.

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:drawable="@color/gray"/>

    <item
        android:drawable="@mipmap/ic_launcher_round"
        android:gravity="center"/>

</layer-list>

Launch screen implementation

With the resource exception handled, the launch screen implementation is next. Much of this is similar to the previous post so I will breeze through it.

The first step is creating a custom theme for the launch screen.

<style name="LaunchTheme" parent="Theme.AppCompat.NoActionBar">
    <item name="android:windowBackground">@drawable/background_launch</item>
</style>

The style is then applied to the <activity> tag in the AndroidManifest.xml.

<activity
    android:name=".LaunchActivity"
    android:theme="@style/LaunchTheme">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

Modernization issues

In the spirit of modernizing the sample app, my first attempt at the launch screen navigation involved adding the Navigation Component. This involved adding the dependency to the app/build.gradle file, declaring a navigation graph resource, and adding the destinations. The LaunchActivity was declared as the home destination which has an action to go to the MainActivity.

This quickly revealed a problem. In order to perform navigation from an Activity, the findNavController(view: View) function is called passing in a view that is a NavHost or is within a NavHost. Normally this is not an issue for most activities because they have a view to set up and display to the user.

For the LaunchActivity, it does not have a view since it is just waiting for the onCreate() function to be called before navigating to a different activity. Using the navigation component to navigate from the LaunchActivity would require creating a layout file that contained a navigation host, setting up the layout in LaunchActivity‘s onCreate() function, accessing the NavController from the view, and then using it to navigate to the next screen.

The issue with this implementation is speed. As soon as the LaunchActivity is ready, it should immediately navigate the user to the next activity. Inflating a view and grabbing the NavController takes time causing the user to see the launch image longer than necessary.

For this reason, the navigation component should not be used to perform navigation from the LaunchActivity. Using the manual navigation with startActivity() is faster and more concise.

class LaunchActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Send user to MainActivity as soon as this activity loads
        val intent = Intent(this, MainActivity::class.java)
        startActivity(intent)

        // remove this activity from the stack
        finish()
    }
}

One more consideration

One common idea for launch screens is to perform app setup when the activity launches and navigate when the setup is finished. This sounds like a good idea because there may be certain components an app needs on the main screen, so setting them up in the launch activity seems like an obvious choice. Unfortunately, there is an issue with this.

If the app is evicted from memory while in the background, the system tracks the activities in the task so it can recreate them when the user returns to the app. The problem is that the launch activity is finished when the user leaves, meaning the system will not recreate the activity when the app is restarted. This means that any setup code will not run and the app may end up in a bad state.

This does not necessarily mean that any setup code in the launch activity is destined for bugs and crashes. Tasks like refreshing data from a server can be alright, as long as the app can handle a situation where that data is not present on other screens. If those other screens can themselves request the data again, then having that kind of logic in the launch activity is not a problem.

Where issues occur are in situations where critical components are set up in the launch activity and other screens expect those components to be initialized without checking first. For example, if an app uses the launch activity to initialize its Dagger component and the other activities expect it to be present, the app will not work when reinitialized by the system because the new app process will not have a new component available.

For this reason, a critical setup code should occur in an Application subclass. This ensures that the setup code runs whenever the application process is loaded into memory so the components are available to the rest of the app.

For more information, check out Andrew Bailey’s Falsehoods Android Developers Believe about Lifecycles talk from Droidcon Online.

The post [Updated] Splash Screens the Right Way appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/splash-screens-the-right-way/feed/ 0
An Updated Look at Launching a Splash Screen https://bignerdranch.com/blog/an-updated-look-at-launching-a-splash-screen/ Tue, 06 Oct 2020 20:16:48 +0000 https://www.bignerdranch.com/?p=4565 In an update to one of our older posts, we take a look at splash screens. While the general implementation of the splash screen is the same, there are a few considerations to make about how to best navigate it.

The post An Updated Look at Launching a Splash Screen appeared first on Big Nerd Ranch.

]]>
Launching a Splash Screen Update

Splash screens are a big deal for Android developers. In fact, BNR’s VP of Engineering, Chris Stewart, wrote our original Splash Screens the Right Way blog post over five years ago and it is still our most popular post.

We’re aiming to update the implementation with modern Android practices and to fix a bug in the original post. The general implementation of the splash screen is the same but there are a few considerations to make about how to navigate. If you are interested, I uploaded some sample project code on Github.

Before getting started, the official Material name for a splash screen is now Launch Screen. And though there are several types of launch screens, we’ll focus on one that displays the app icon when the app process first loads.

Fixing a bug

Configuring the launch screen background works the same as the original post. A background XML drawable is created and set as the window background for a launch screen-specific theme.

The original implementation declared the background like this:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:drawable="@color/gray"/>

    <item>
        <bitmap
            android:gravity="center"
            android:src="@mipmap/ic_launcher"/>
    </item>

</layer-list>

This no longer works for more recently created Android apps for an interesting reason.

The <bitmap> tag expects an image @drawable resource, but it doesn’t allow specifying an XML drawable source file. When a newer Android app is created, the mipmap launcher icons include a vector version which is an XML file.

This is the reason some people run into this issue when creating a launch screen using the original blog post. Since one of the launcher icons is an XML file, the <bitmap> tag throws an exception. Trying to run the app in this configuration logs this error:

2020-09-15 13:14:18.365 25218-25218/com.example.splashscreen E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.splashscreen, PID: 25218
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.splashscreen/com.example.splashscreen.SplashActivity}: android.content.res.Resources$NotFoundException: Drawable com.example.splashscreen:drawable/background_splash with resource ID #0x7f07005f
        ...
    Caused by: android.content.res.Resources$NotFoundException: Drawable com.example.splashscreen:drawable/background_splash with resource ID #0x7f07005f
    Caused by: android.content.res.Resources$NotFoundException: File res/drawable/background_splash.xml from drawable resource ID #0x7f07005f
        ...
    Caused by: org.xmlpull.v1.XmlPullParserException: Binary XML file line #8: <bitmap> requires a valid 'src' attribute
2020-09-15 13:14:18.365 25218-25218/com.example.splashscreen E/AndroidRuntime:     at android.graphics.drawable.BitmapDrawable.inflate(BitmapDrawable.java:775)
    ...

The original blog post was created before vector resources were available on Android so all of the launcher icons are images, meaning no error is thrown by the <bitmap> tag.

Fixing this for apps with vector launcher icons requires deleting the <bitmap> tag and moving the drawable and gravity attributes to the <item> tag.

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:drawable="@color/gray"/>

    <item
        android:drawable="@mipmap/ic_launcher_round"
        android:gravity="center"/>

</layer-list>

Launch screen implementation

With the resource exception handled, the launch screen implementation is next. Much of this is similar to the previous post so I will breeze through it.

The first step is creating a custom theme for the launch screen.

<style name="LaunchTheme" parent="Theme.AppCompat.NoActionBar">
    <item name="android:windowBackground">@drawable/background_launch</item>
</style>

The style is then applied to the <activity> tag in the AndroidManifest.xml.

<activity
    android:name=".LaunchActivity"
    android:theme="@style/LaunchTheme">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

Modernization issues

In the spirit of modernizing the sample app, my first attempt at the launch screen navigation involved adding the Navigation Component. This involved adding the dependency to the app/build.gradle file, declaring a navigation graph resource, and adding the destinations. The LaunchActivity was declared as the home destination which has an action to go to the MainActivity.

This quickly revealed a problem. In order to perform navigation from an Activity, the findNavController(view: View) function is called passing in a view that is a NavHost or is within a NavHost. Normally this is not an issue for most activities because they have a view to set up and display to the user.

For the LaunchActivity, it does not have a view since it is just waiting for the onCreate() function to be called before navigating to a different activity. Using the navigation component to navigate from the LaunchActivity would require creating a layout file that contained a navigation host, setting up the layout in LaunchActivity‘s onCreate() function, accessing the NavController from the view, and then using it to navigate to the next screen.

The issue with this implementation is speed. As soon as the LaunchActivity is ready, it should immediately navigate the user to the next activity. Inflating a view and grabbing the NavController takes time causing the user to see the launch image longer than necessary.

For this reason, the navigation component should not be used to perform navigation from the LaunchActivity. Using the manual navigation with startActivity() is faster and more concise.

class LaunchActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Send user to MainActivity as soon as this activity loads
        val intent = Intent(this, MainActivity::class.java)
        startActivity(intent)

        // remove this activity from the stack
        finish()
    }
}

One more consideration

One common idea for launch screens is to perform app setup when the activity launches and navigate when the setup is finished. This sounds like a good idea because there may be certain components an app needs on the main screen, so setting them up in the launch activity seems like an obvious choice. Unfortunately, there is an issue with this.

If the app is evicted from memory while in the background, the system tracks the activities in the task so it can recreate them when the user returns to the app. The problem is that the launch activity is finished when the user leaves, meaning the system will not recreate the activity when the app is restarted. This means that any setup code will not run and the app may end up in a bad state.

This does not necessarily mean that any setup code in the launch activity is destined for bugs and crashes. Tasks like refreshing data from a server can be alright, as long as the app can handle a situation where that data is not present on other screens. If those other screens can themselves request the data again, then having that kind of logic in the launch activity is not a problem.

Where issues occur are in situations where critical components are set up in the launch activity and other screens expect those components to be initialized without checking first. For example, if an app uses the launch activity to initialize its Dagger component and the other activities expect it to be present, the app will not work when reinitialized by the system because the new app process will not have a new component available.

For this reason, a critical setup code should occur in an Application subclass. This ensures that the setup code runs whenever the application process is loaded into memory so the components are available to the rest of the app.

For more information, check out Andrew Bailey’s Falsehoods Android Developers Believe about Lifecycles talk from Droidcon Online.

The post An Updated Look at Launching a Splash Screen appeared first on Big Nerd Ranch.

]]>
Juggling Daggers: Changing Things Up In Tests https://bignerdranch.com/blog/juggling-daggers-changing-things-up-in-tests/ https://bignerdranch.com/blog/juggling-daggers-changing-things-up-in-tests/#respond Mon, 25 Feb 2019 09:00:00 +0000 https://nerdranchighq.wpengine.com/blog/juggling-daggers-changing-things-up-in-tests/ Dependency injection is a great tool to break up your dependency creation into reuseable pieces. This helps separate your code into logical chunks where some create the dependencies and other consume them. This separation helps in your unit tests because you have a clear list of dependencies for each object so you can easily pass in mock versions.

The post Juggling Daggers: Changing Things Up In Tests appeared first on Big Nerd Ranch.

]]>

Dependency injection is a great tool to break up your dependency creation into reusable pieces.
This helps separate your code into logical chunks where some create the dependencies and others
consume them. This separation helps in your unit tests because you have a clear list of
dependencies for each object so you can easily pass in mock versions.

In this post I will examine the use of dependency injection in integration tests with Espresso.
Some components, such as a network or database layer, need to be swapped out with fake versions for
faster tests that are sealed off from the outside. A good dependency injection setup can provide a
path for you to change the components you provide in your test environment. The sample project
(linked at the end of this post) uses Dagger 2.15 along with the Dagger-Android libraries to
simplify the setup.

Production Setup

This post will start with the main Dagger setup code before moving over to the test setup. The
configuration is fairly standard, so not much time will be spent covering how it works in detail.

First, a Module is created to provide the dependencies the app needs. In this case, the module
provides a NerdStore that tracks a list of nerds. The function at the top of the module is the
main provider function. The second one at the bottom is an implementation detail that just provides
the dependencies the nerd store needs. Implementing modules in this way provides a nice separation
of concerns and ensures that if you want to replace your modules in test you only need to provide a
single dependency per module since none of the other dependencies are used elsewhere in the project.

@Module
class AppModule {

    @Provides
    @Singleton
    fun provideNerdStore(nerds: List<Nerd>): NerdStore {
        return LiveNerdStore(nerds)
    }

    // Implementation-detail provides functions below

    @Provides
    fun provideNerds(): List<Nerd> {
        return listOf(
                Nerd("Brian"),
                Nerd("Kristin"),
                Nerd("Chris")
        )
    }
}

Next, another Module is created to specify the classes that need the dependencies. Leaning on the
@ContributesAndroidInjector annotation here simplifies this file so separate subcomponents do not
need to be created for each Activity or added to a DispatchingAndroidInjector.

@Module
abstract class InjectorModule {

    @ContributesAndroidInjector
    abstract fun contributeNerdListActivityInjector(): NerdListActivity
}

With the modules out of the way, creating a component interface to orchestrate the injection is
next. The modules are listed in the @Component annotation alongside the
AndroidSupportInjectionModule. For this example, a plain Builder class annotated with
@Component.Builder is used so the setup is simplified.

@Singleton
@Component(modules = [
    AndroidSupportInjectionModule::class,
    InjectorModule::class,
    AppModule::class
])
interface AppComponent : AndroidInjector<NerdApplication> {
    @Component.Builder
    abstract class Builder : AndroidInjector.Builder<NerdApplication>()
}

Last but not least, an application subclass is needed to initialize the component. For simplicity,
the DaggerApplication class is extended and an override of the applicationInjector function is
implemented to return the generated DaggerAppComponent class. In this example, the component
class could be initialized in the applicationInjector function and returned. Instead, the
component is assigned to a property and initialized in onCreate. This is because the test
configuration will need to reassign the injector property in the tests to control which
dependencies are provided.

open class NerdApplication : DaggerApplication() {

    lateinit var injector: AndroidInjector<out DaggerApplication>

    override fun onCreate() {
        injector = DaggerAppComponent.builder().create(this)
        super.onCreate()
    }

    override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
        return injector
    }
}

With the NerdApplication class in place (and after registering it in the AndroidManifest.xml),
the setup is complete and the classes can request their dependencies. In the listing below, the
NerdListActivity class extends DaggerAppCompatActivity which automatically injects any
properties annotated with @Inject. In this case, the NerdStore property is annotated so the
component will create and inject an instance of it into the property.

class NerdListActivity : DaggerAppCompatActivity() {

    @Inject
    lateinit var nerdStore: NerdStore

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_nerd_list)
        for(nerd in nerdStore.nerds) {
            Log.d("NerdListActivity", "Have nerd $nerd")
        }
    }
}

Test Setup

With the production code in place, the testing harness can be set up. There is a fake version of the
LiveNerdStore that should be provided in the test classes, meaning a module needs to be created
to provide it as well as a test component to serve the module.

class OtherNerdStore(override val nerds: List<Nerd>)
    : NerdStore

The production modules and component do not need to be overridden, as long as the test component
provides all the dependencies the app needs. In this case, a test-specific module is created that
returns the OtherNerdStore class in place of the LiveNerdStore. Since the provide function only
specifies that a NerdStore is needed, this swap satisfies the application.

@Module
class FakeAppModule {
    @Provides
    fun provideNerdStore(): NerdStore {
        return OtherNerdStore(listOf())
    }
}

After that, a test component is created to use in the test classes. The InjectionModule can be
reused since there are no additional injections needed. If there were, another
TestInjectionModule could be added that would list all of the test classes that need dependencies
injected.

@Component(modules = [
    AndroidSupportInjectionModule::class,
    InjectorModule::class,
    FakeAppModule::class // Module that returns OtherNerdStore
])
interface TestComponent : AndroidInjector<NerdApplication> {
    @Component.Builder
    abstract class Builder : AndroidInjector.Builder<NerdApplication>()
}

Once the component is in place, the NerdApplication needs to be updated so the new component can
be set on it and used for the injection. Since this should only be accessible from the test
directory, this can be achieved by creating an extension function for NerdApplication in the test
directory to set the new component and setup the injection again.

fun NerdApplication.setApplicationInjector(injector: AndroidInjector<NerdApplication>) {
    this.injector = injector.also {
        it.inject(this)
    }
}

This extension function accepts an AndroidInjector class and sets it on the injector property on
NerdApplication. At the same time, the inject() function must be called on the injector,
passing in the application class as a parameter. This is necessary in order for the application to
use the new injector in the future.

One last bit of prep will help clean up the test code. When an Espresso test needs to start an
Activity, the ActivityTestRule is used to specify which class it should start. There is a
function on the test rule class called beforeActivityLaunched() that can be overridden. This is
where the production component will be swapped out for a test version before the activity launches.
This ensures that the activity uses the test component instead of the production one. To centralize
this, a subclass of ActivityTestRule is created that overrides the beforeActivityLaunched()
function.

class InjectionActivityTestRule<T : Activity>(
        activityClass: Class<T>,
        private val componentBuilder: AndroidInjector.Builder<NerdApplication>
) : ActivityTestRule<T>(activityClass) {

    override fun beforeActivityLaunched() {
        super.beforeActivityLaunched()
        // setup test component before activity launches
        val app = InstrumentationRegistry.getTargetContext().applicationContext as NerdApplication
        val testComponent = componentBuilder.create(app)
        app.setApplicationInjector(testComponent)
    }
}

This class takes in the activity class to start so it can pass it to the superclass
constructor. It also takes in a AndroidInjector.Builder class so it knows which component to set
on the application.

Then, in the beforeActivityLaunched() function, it accesses the application context from the
InstrumentationRegistry and casts it directly to NerdApplication. With the application
class, the test component can be fully created by calling create(app) on the builder. Finally,
the injector is configured on the application class by calling setApplicationInjector() and
passing in the test component as a parameter.

Having the extension function in the test directory ensures that the production code cannot call
it. This safety is not extended to the test classes. It is possible for a test class to call the
setApplicationInjector() function which could cause problems with the dependencies getting out
of sync.

For extra security, extension function can be relocated into the InjectionActivityTestRule
file and marked as private. This ensures that it is only accessible within that file so the test
classes will be unable to modify the component.

class InjectionActivityTestRule<T : Activity>(
        activityClass: Class<T>,
        private val componentBuilder: AndroidInjector.Builder<NerdApplication>
) : ActivityTestRule<T>(activityClass) {

    override fun beforeActivityLaunched() {
        super.beforeActivityLaunched()
        // setup test component before activity launches
        val app = InstrumentationRegistry.getTargetContext().applicationContext as NerdApplication
        val testComponent = componentBuilder.create(app)
        app.setApplicationInjector(testComponent)
    }
}

private fun NerdApplication.setApplicationInjector(injector: AndroidInjector<NerdApplication>) {
    this.injector = injector.also {
        it.inject(this)
    }
}

With the InjectionActivityTestRule in place, the Espresso test can be written. In this case, the
test verifies that the injected property on the NerdActivity class is the fake version,
OtherNerdStore, instead of the real LiveNerdStore.

@RunWith(AndroidJUnit4::class)
class NerdListActivityTest {

    @get:Rule
    val activityTestRule = InjectionActivityTestRule(
            NerdListActivity::class.java,
            DaggerTestComponent.builder()
    )

    @Test
    fun itInjectsTheCorrectNerdStoreImplementation() {
        val activity = activityTestRule.activity
        val nerdStore = activity.nerdStore
        assertThat(nerdStore, instanceOf(OtherNerdStore::class.java))
    }
}

Running this results in a passed test and a confirmation that the test component is being used to
provide the dependencies instead of the production version.

Even though this setup is somewhat complex, I’m a fan because it gives you a higher level of
control over the tests. Each test class can specify its own test component to provide the app
dependencies. Some test classes are fine with a higher level of faked dependencies than others so
you have more say in your test setup.

The component is also recreated before each test because the activity is recreated on each test
run. This may slow down the execution time but it can have huge benefits by ensuring your tests run
in an isolated fashion. All dependencies are created fresh when the component is recreated so even
your singletons will not bleed state. You may still run into issues if your singletons point to the
same files when saving data, but that can be remedied by providing unique file names to write to in
each test or replacing your data caching with a fake, in-memory version.

I hope you have enjoyed my post on setting up dependency injection in tests. You can find a working
example of the code from this post in my
Github Repo. If you have any questions or
comments, I would love to hear from you. Feel free to leave a comment below or you can reach out to
me on Twitter @BrianGardnerAtl. Thanks for reading!

The post Juggling Daggers: Changing Things Up In Tests appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/juggling-daggers-changing-things-up-in-tests/feed/ 0
The next edition of Android Programming is coming: Halfway to 4K https://bignerdranch.com/blog/the-next-edition-of-android-programming-is-coming-halfway-to-4k/ https://bignerdranch.com/blog/the-next-edition-of-android-programming-is-coming-halfway-to-4k/#respond Mon, 12 Nov 2018 09:00:00 +0000 https://nerdranchighq.wpengine.com/blog/the-next-edition-of-android-programming-is-coming-halfway-to-4k/ Over the past few months, Kristin Marsicano, myself, and many others on the Android team here at Big Nerd Ranch have worked on the fourth edition of our Android Programming book. Keep reading to find out what's in store!

The post The next edition of Android Programming is coming: Halfway to 4K appeared first on Big Nerd Ranch.

]]>

Halfway to 4K

Over the past few months, Kristin Marsicano, myself, and many others on the Android team here at
Big Nerd Ranch have worked on the fourth edition of Android Programming: The Big Nerd Ranch Guide.
We have a lot of good things in store for the next version, and we want to give you a
sneak peek of some of the improvements.

The biggest change in this version is that Big Nerd Ranch is going all-in on Kotlin. We’ve been
using Kotlin for our consulting projects for quite a while now, and since Kotlin now has
first-party support for Android, updating the book to use it was the obvious path forward. We
believe that using Kotlin makes development simpler and more fun, and we’re very excited to be
rolling out these updates. We have already taught our Android Essentials bootcamp using Kotlin and
we’ve gotten great feedback and support on the changes. This kind of feedback from our classes in
invaluable as it helps direct future changes to our books and class materials.

We are also overhauling the book to reflect current best practices in Android
development today. Architecture Components are a natural fit for the new materials as they provide
an opinionated guide to app architecture that is easy to implement and follow for new developers.
Room, ViewModels, and LiveData make appearances to teach best practices around data storage, as
well as loading and maintaining it across rotation. Other Architecture Components are being
investigated as well to see how we can add them to our existing examples.

The book will now use third-party libraries to focus on how modern Android applications are
developed, instead of strictly focusing only on the APIs within the framework. One example is
dropping HttpURLConnection in favor of modern libraries which allows you to focus more on what
you want your app to do instead of low-level implementation details. This is a big departure from
our previous books, but we believe that this will better prepare you to dive into professional
application development after reading our book.

At this point, the fourth edition of our book has fully been converted to use Kotlin. We are
finalizing decisions on new content. Writing that content will be our focus in the coming months.
Updating our books is always a huge undertaking, but we are very excited about what is in store, and
we can’t wait to get the book published. Stay tuned for more updates on the release timeline for
the fourth edition of our Android Programming guide, and leave a comment below if there is anything
you would like to see in the next version!

The post The next edition of Android Programming is coming: Halfway to 4K appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/the-next-edition-of-android-programming-is-coming-halfway-to-4k/feed/ 0
Icing on the Slice: Providing more value to users with range actions https://bignerdranch.com/blog/icing-on-the-slice-providing-more-value-to-users-with-range-actions/ https://bignerdranch.com/blog/icing-on-the-slice-providing-more-value-to-users-with-range-actions/#respond Mon, 22 Oct 2018 09:00:00 +0000 https://nerdranchighq.wpengine.com/blog/icing-on-the-slice-providing-more-value-to-users-with-range-actions/ While basic slices have their place, you will often want to provide users with additional information or controls. Range actions make your Slices more interactive by allowing users to control any field that accepts a discrete range of options, such as volume or brightness.

The post Icing on the Slice: Providing more value to users with range actions appeared first on Big Nerd Ranch.

]]>

Slices provide the ability for your app to share
information and controls with other apps. In my previous post,
Share a Slice of Your App, I covered
the basics of Slices on Android. Introduced in Android Pie (API 28), they are packaged as a
library and work all the way back to Android KitKat (API 19). If you haven’t already, give my introduction to Slices a read through to
learn about the main concepts in Slices, how to create a simple one, and how to view them on your
devices. Before continuing with this post, make sure you have a SliceProvider in place to build off
of for the interactive slices.

In my previous post, I covered adding a toggle action to your Slice to make it more
interactive. In this post, I’ll show how to add a range action. Ranges allow users to control any
field that accepts a discrete range of options, such as volume or brightness. This type of control
also requires you to create a component to handle the data in your app. Read on to find out more!

Add range action

Similar to the toggle action, you need to provide a Service or BroadcastReceiver to handle the
data coming from the Slice. For this example, I’ll use a BroadcastReceiver. Don’t forget to
register your receiver in your AndroidManifest.xml so the system can start it properly.

class NerdLevelReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        // Handle the range data from the Intent
    }
}

Again, I’ll fill out the actual implementation once I complete my Slice. This will allow me to
create a valid PendingIntent for my Slice action.

Next, I’ll create a different path for my Slice called /range.

override fun onBindSlice(sliceUri: Uri): Slice? {
    val context = getContext() ?: return null
    return when (sliceUri.path) {
        "/range" -> {
            // Display range slice
        }
        else -> {
            ...
        }
    }
}

One important thing to know about the range action is that you need to specify a primary Slice
action. If you don’t, the app will crash with an IllegalStateException. So first, I’ll
create my primary action for my range Slice:

override fun onBindSlice(sliceUri: Uri): Slice? {
    context ?: return null
    return when (sliceUri.path) {
        "/range" -> {
            // Display range slice
            val primarySliceAction = createPrimarySliceAction()
        }
        else -> {
            ...
        }
    }
}

private fun createPrimarySliceAction(): SliceAction {
    val primaryTitle = context.getString(R.string.view_all_booking_options)
    val primaryIntent = Intent(context, BookingActivity::class.java).let {
        it.putExtra(EXTRA_BOOKING_STRING, primaryTitle)
        PendingIntent.getActivity(context, DEFAULT_REQUEST_CODE, it, 0)
    }
    val primaryBitmap = BitmapFactory.decodeResource(context.resources, R.drawable.ic_main)
    val primaryIcon = IconCompat.createWithBitmap(primaryBitmap)
    return SliceAction.create(
            primaryIntent,
            primaryIcon,
            ICON_IMAGE,
            primaryTitle
    )
}

With that in place, I can move on to creating my Slice. The inputRange is used to create the
range action. A PendingIntent is provided for the component responsible for handling the updates.
A max and current value are also provided to configure the current state of the range.

override fun onBindSlice(sliceUri: Uri): Slice? {
    context ?: return null
    return when (sliceUri.path) {
        "/range" -> {
            // Display range slice
            val primarySliceAction = createPrimarySliceAction()

            val nerdLevelIntent = Intent(context, NerdLevelReceiver::class.java).let {
                PendingIntent.getBroadcast(context, 0, it, 0)
            }

            val nerdLevelTitle = context.getString(R.string.nerd_level_title)
            list(context, sliceUri, ListBuilder.INFINITY) {
                inputRange {
                    title = nerdLevelTitle
                    inputAction = nerdLevelIntent
                    max = 100
                    value = 50
                    primaryAction = primarySliceAction
                }
            }
        }
        else -> {
            ...
        }
    }
}

With my slice implementation in place, I can fill out my BroadcastReceiver. Like the toggle
implementation, the Intent passed to the receiver has extra data about the current value of the
range. This method will be called many times as the user moves the slider so don’t take too long in
your receiver or you risk the system killing your receiver before it finishes.

class NerdLevelReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        if (intent.hasExtra(Slice.EXTRA_RANGE_VALUE)) {
            val rangeValue = intent.getIntExtra(Slice.EXTRA_RANGE_VALUE, -1)
            Log.d("NERD LEVEL", "Have range value: $rangeValue")

            NerdLevelManager.nerdLevel = rangeValue
        }
    }
}

With my receiver in place, I can run my Slice and see the range value output to LogCat as I move
the slider.

Example of a slice displaying a range control

Fin

Range actions provide a great user interface for users to control any functionality that needs a
value from a discrete range. You can either use a Service or a BroadcastReceiver to handle
the user’s input. Just configure a PendingIntent for your component and the system will start it
when the data changes.

I hope you enjoyed these posts on interactive Slices. Watch out for more posts on Slices in the
future. If you have any questions please feel free to comment below. Thanks for reading!

The post Icing on the Slice: Providing more value to users with range actions appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/icing-on-the-slice-providing-more-value-to-users-with-range-actions/feed/ 0
Icing on the Slice: Providing more value to users with toggle actions https://bignerdranch.com/blog/icing-on-the-slice-providing-more-value-to-users-with-toggle-actions/ https://bignerdranch.com/blog/icing-on-the-slice-providing-more-value-to-users-with-toggle-actions/#respond Sun, 30 Sep 2018 09:00:00 +0000 https://nerdranchighq.wpengine.com/blog/icing-on-the-slice-providing-more-value-to-users-with-toggle-actions/ While basic slices have their place, you will often want to provide uers with additional information or controls. Toggle actions make your Slices more interactive by providing a way for users to enable or disable certain controls in your application.

The post Icing on the Slice: Providing more value to users with toggle actions appeared first on Big Nerd Ranch.

]]>

Slices provide the ability for your app to share
information and controls with other apps. In my previous post,
Share a Slice of Your App, I covered
the basics of Slices on Android. Introduced in Android Pie (API 28), they are packaged as a
library and work all the way back to Android KitKat (API 19). If you haven’t already, give it a read through to
learn about the main concepts in Slices, how to create a simple one, and how to view them on your
devices. Before continuing with this post, make sure you have a SliceProvider in place to build off
of for the interactive slices.

In my previous post, I covered adding multiple actions to your Slice to make it more
interactive. In this post, I’ll show how to add a toggle action. This type of action is good for
controls the user can enable or disable in your application. This type of control also requires you
to create a component to handle the data in your app. Read on to find out more!

Add toggle action

For toggle actions, you will need to create either a Service or a BroadcastReceiver to handle
the updates. The Intent object that these components receive will contain extra data indicating
if the toggle is enabled or not. You can handle the action appropriately there. For this example
I’ll use a Service to handle the updates.

For now my Service will be very plain. I’ll fill out the actual implementation once I finish my
Slice. This will just act as a placeholder so I can create a valid PendingIntent.

class FocusService : IntentService("FocusService") {

    override fun onHandleIntent(intent: Intent?) {
        // Handle the toggle change
    }
}

Don’t forget to register your service in your AndroidManifest.xml so the system can start it
correctly.

The next step is to create a path for your toggle Slice. To continue with my creative streak, I’ll
call this path /toggle.

override fun onBindSlice(sliceUri: Uri): Slice? {
    context ?: return null
    return when (sliceUri.path) {
        "/toggle" -> {
            // Display toggle slice
        }
        else -> {
            ...
        }
    }
}

To create my toggle action I can use the createToggle() static method on the SliceAction class.
This method needs a PendingIntent, a title, and a boolean flag indicating if the toggle is
checked.

override fun onBindSlice(sliceUri: Uri): Slice? {
    context ?: return null
    return when (sliceUri.path) {
        "/toggle" -> {
            // Display toggle slice
            val toggleAction = createToggleAction()
        }
        else -> {
            ...
        }
    }
}

private fun createToggleAction(): SliceAction {
    val isFocusing = FocusManager.focusing
    val focusIntent = Intent(context, FocusService::class.java).let {
        PendingIntent.getService(context, 0, it, 0)
    }
    val toggleTitle = context.getString(R.string.toggle_title)
    return SliceAction.createToggle(
            focusIntent,
            toggleTitle,
            isFocusing
    )
}

The FocusManager is just an object responsible for determining the current state of my toggle.
The data is stored in a property that can be returned quickly back to my SliceProvider.

object FocusManager {
    public var focusing = false
}

Returning this value quickly is important so there isn’t a delay when your Slice is being
displayed. In fact, if you take too long to return the information, an exception is thrown, and your
Slice will not be shown. Even reading from SharedPreferences is too long in this case. If reading
your data takes too long, you will need to initialize your Slice with placeholder data while you
query the real data in the background. Once you have the data, you can notify your SliceProvider
that the data has changed and it will rebind accordingly.

Once I have my toggle action, I can create the rest of the data for my Slice. I’ll change the text
of my Slice based on my toggle state. If my user is not focusing, I will prompt them to start, and
if they are focusing, I’ll prompt them to turn it off when they are done.

override fun onBindSlice(sliceUri: Uri): Slice? {
    context ?: return null
    return when (sliceUri.path) {
        "/toggle" -> {
            // Display toggle slice
            val toggleAction = createToggleAction()

            val focusTitleText: String
            val focusSubtitleText: String
            if (FocusManager.focusing) {
                focusTitleText = context.getString(R.string.focus_title_text_enabled)
                focusSubtitleText = context.getString(R.string.focus_subtitle_text_enabled)
            } else {
                focusTitleText = context.getString(R.string.focus_title_text_disabled)
                focusSubtitleText = context.getString(R.string.focus_subtitle_text_disabled)
            }
        }
        else -> {
            ...
        }
    }
}

With my action, title, and subtitle ready, I can move on to creating my Slice. I can add my toggle
action as my only action so the user can see it and interact with it.

override fun onBindSlice(sliceUri: Uri): Slice? {
    context ?: return null
    return when (sliceUri.path) {
        "/toggle" -> {
            ...
            } else {
                focusTitleText = context.getString(R.string.focus_title_text_disabled)
                focusSubtitleText = context.getString(R.string.focus_subtitle_text_disabled)
            }

            list(context, sliceUri, ListBuilder.INFINITY) {
                header {
                    title = focusTitleText
                    subtitle = focusSubtitleText
                }
                addAction(toggleAction)
            }
        }
        else -> {
            ...
        }
    }
}

The last step is to fill out my FocusService to handle the toggle changes. I can pull out the
extra data from the intent and set it on my FocusManager. I will also notify the system that the
content for my Slice URI has changed so that they system can tell my SliceProvider to rebind my
Slice with the updated data.

class FocusService : IntentService("FocusService") {

    override fun onHandleIntent(intent: Intent?) {
        intent ?: return
        val toggleState = if (intent.hasExtra(Slice.EXTRA_TOGGLE_STATE)) {
            intent.getBooleanExtra(Slice.EXTRA_TOGGLE_STATE, false)
        } else {
            false
        }

        FocusManager.focusing = toggleState

        val sliceUri = Uri.parse("content://com.bignerdranch.android.icingontheslice/toggle")
        contentResolver.notifyChange(sliceUri, null)
    }
}

With this in place, I can run my Slice and see the content change when I enable and disable the
toggle.

Example of a slice displaying content based on its toggle disabled

Example of a slice displaying content based on its toggle enabled

Fin

Toggle actions provide an easy way for users to enable or disable specific functionality in your
app in an easy to find place. You can either use a Service or a BroadcastReceiver to handle
the user’s input. Just configure a PendingIntent for your component and the system will start it
when the data changes.

In the next post, I’ll cover how to add a range action to your Slice. This allows users to provide
a value from a discrete set of options.

I hope you enjoyed the post. If you have any questions please feel free to comment below. Thanks
for reading!

The post Icing on the Slice: Providing more value to users with toggle actions appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/icing-on-the-slice-providing-more-value-to-users-with-toggle-actions/feed/ 0
Icing on the Slice: Providing more value to users with multiple actions https://bignerdranch.com/blog/icing-on-the-slice-providing-more-value-to-users-with-multiple-actions/ https://bignerdranch.com/blog/icing-on-the-slice-providing-more-value-to-users-with-multiple-actions/#respond Mon, 10 Sep 2018 09:00:00 +0000 https://nerdranchighq.wpengine.com/blog/icing-on-the-slice-providing-more-value-to-users-with-multiple-actions/ While basic slices have their place, you will often want to provide uers with additional information or controls. One way to make a Slice more interactive is to provide multiple actions. These show up as tappable icons that will take the user to different screens in your app, or take the user to the same screen with different sets of data.

The post Icing on the Slice: Providing more value to users with multiple actions appeared first on Big Nerd Ranch.

]]>

Slices provide the ability for your app to share
information and controls with other apps. In my previous post,
Share a Slice of Your App, I covered
the basics of Slices on Android. Introduced in Android Pie (API 28), they are packaged as a support
library and work down to Android KitKat (API 19). If you haven’t already, give it a read through to
learn about the main concepts in Slices, how to create a simple one, and how to view them on your
devices. Before continuing with this post, make sure you have a SliceProvider in place to build off
of for the interactive Slices.

While basic Slices have their place, you will often want to provide users with more controls.
Over the next few posts I’ll cover the different kinds of actions you can add to Slices and how
they are used.

One way to make a Slice more interactive is to provide multiple actions. These show up as
tappable icons that will take the user to different screens in your app, or take the user to the
same screen with different sets of data. The icons should be descriptive enough that the user knows
which action will occur when they tap on them. In this example, my Slice will prompt users to enter
my app to book a vacation. Each icon will show the mode of transportation they can take to their
vacation: a car, a plane, or a boat.

Add multiple actions

The first step to adding this type of Slice is to create the new path handling logic in your
onBindSlice() method. When another app requests this path from my app I will return a Slice
configured with multiple actions the user can take. The path for my multiple action Slice will be
called /multiple.

override fun onBindSlice(sliceUri: Uri): Slice? {
    context ?: return null
    return when (sliceUri.path) {
        "/multiple" -> {
            // Display multiple action slice
        }
        else -> {
            ...
        }
    }
}

Next, I’ll need to create the different SliceAction objects I want to show in my Slice. Since I
have three different options I want to present, I’ll need three different actions. Each action
needs a PendingIntent which describes the action to fire when clicked, an icon to display, an
image mode constant that tells the slice how to display the action, and an action title which is
also used for the content description. I’ll use private functions to create each action to keep my
onBindSlice method from getting too crowded.

override fun onBindSlice(sliceUri: Uri): Slice? {
    context ?: return null
    return when (sliceUri.path) {
        "/multiple" -> {
            // Display multiple action slice
            val carAction = createCarAction()
            val planeAction = createPlaneAction()
            val boatAction = createBoatAction()
        }
        else -> {
            ...
        }
    }
}

private fun createCarAction(): SliceAction {
    val carText = context.getString(R.string.book_car_text)
    val carIntent = Intent(context, BookingActivity::class.java).let {
        it.putExtra(EXTRA_BOOKING_STRING, carText)
        PendingIntent.getActivity(context, CAR_REQUEST_CODE, it, /*flags:*/0)
    }
    val carBitmap = BitmapFactory.decodeResource(context.resources, R.drawable.ic_car)
    val carIcon = IconCompat.createWithBitmap(carBitmap)
    return SliceAction.create(
            carIntent,
            carIcon,
            ICON_IMAGE,
            carText
    )
}

private fun createPlaneAction(): SliceAction {
    val planeText = context.getString(R.string.book_plane_text)
    val planeIntent = Intent(context, BookingActivity::class.java).let {
        it.putExtra(EXTRA_BOOKING_STRING, planeText)
        PendingIntent.getActivity(context, PLANE_REQUEST_CODE, it, /*flags:*/0)
    }
    val planeBitmap = BitmapFactory.decodeResource(context.resources, R.drawable.ic_plane)
    val planeIcon = IconCompat.createWithBitmap(planeBitmap)
    return SliceAction.create(
            planeIntent,
            planeIcon,
            ICON_IMAGE,
            planeText
    )
}

private fun createBoatAction(): SliceAction {
    val boatText = context.getString(R.string.book_boat_text)
    val boatIntent = Intent(context, BookingActivity::class.java).let {
        it.putExtra(EXTRA_BOOKING_STRING, boatText)
        PendingIntent.getActivity(context, BOAT_REQUEST_CODE, it, /*flags:*/0)
    }
    val boatBitmap = BitmapFactory.decodeResource(context.resources, R.drawable.ic_boat)
    val boatIcon = IconCompat.createWithBitmap(boatBitmap)
    return SliceAction.create(
            boatIntent,
            boatIcon,
            ICON_IMAGE,
            boatText
    )
}

With the actions in place I can move on to creating my Slice. I’ll set an accent color to tint my
icons, and I’ll use the addAction() method to add all of my SliceAction objects.

override fun onBindSlice(sliceUri: Uri): Slice? {
    context ?: return null
    return when (sliceUri.path) {
        "/multiple" -> {
            // Display multiple action slice
            val carAction = createCarAction()
            val planeAction = createPlaneAction()
            val boatAction = createBoatAction()

            val vacationTitle = context.getString(R.string.vacation_title)
            val vacationSubtitle = context.getString(R.string.vacation_subtitle)
            list(context, sliceUri, ListBuilder.INFINITY) {
                setAccentColor(ContextCompat.getColor(context, R.color.colorPrimary))
                header {
                    title = vacationTitle
                    subtitle = vacationSubtitle
                }
                addAction(carAction)
                addAction(planeAction)
                addAction(boatAction)
            }
        }
        else -> {
            ...
        }
    }
}

With the Slice in place, I can create a run configuration to display my Slice in the SliceViewer.
If you don’t know how to run a Slice, check out
my intro post to see how. When I
run my Slice, I’ll see my actions displayed. When I tap on an action, it will take me into my app
based on its PendingIntent.

Example of a slice displaying three vacation actions as tappable icons

Fin

Adding multiple actions to your Slices provide a quick way for users to navigate directly to a
screen they’re interested in within your app. These actions can be modified based on how people use
your app so you always provide actions that are most relevant to each user. Getting this right means
users won’t have to go searching through your app for the specific functionality they want. They
can see exactly what they want in your Slice and act on it appropriately.

In the following couple posts we’ll see how to how to add toggle actions and range actions to
your Slices. These kinds of actions are more interesting because you have to create components to
handle data changes when the user interacts with them.

I hope you enjoyed this post. If you have any questions please feel free to comment below. Thanks
for reading!

The post Icing on the Slice: Providing more value to users with multiple actions appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/icing-on-the-slice-providing-more-value-to-users-with-multiple-actions/feed/ 0
Share a Slice of your App https://bignerdranch.com/blog/share-a-slice-of-your-app/ https://bignerdranch.com/blog/share-a-slice-of-your-app/#respond Mon, 30 Jul 2018 09:22:00 +0000 https://nerdranchighq.wpengine.com/blog/share-a-slice-of-your-app/ Providing users quick access to data and controls for your app is a good user experience, it lets them see the info they care about in the places that are important to them.

The post Share a Slice of your App appeared first on Big Nerd Ranch.

]]>

fancy slice

Providing users quick access to data and controls for your app is part of a good user experience. It lets
them see the info they care about in the places that are important to them.
App Actions provide one way of accomplishing this.
When the user searches in the Google Search app or in the Google Assistant, apps can
provide actions from your app that are relevant to the user’s query. These were previously fairly limited,
mostly just providing a link into your app with some specific data. The new
Slices API provides a way for you to improve the
user experience by showing more information as well as a variety of controls, such as sliders and
toggles, directly in the Search app or the Assistant.

The ability to add custom controls makes slices a very useful tool for giving users control over
your application while they aren’t directly using it. These controls can be provided in a variety
of ways, from buttons, to toggles, and sliders. You can also provide a click action to take the
user into your app when they click on the slice itself. This way the user can use your app’s normal
UI to accomplish their task if they so choose. In this post I’ll discuss how to create a basic
slice, then show how to make it more interactive and valuable to your users. I’ll wrap up with
showing how you can test your slices using the SliceViewer.

Implement a Slice

While not required, a good first step to implementing your slice is to install Android Studio 3.2.
It includes a few niceties such as new lint checks and a template you can use to build your
provider. It also includes a tool to make refactoring from the support libraries to Android X
easier.

In this example I’m going to use the AndroidX dependency to implement my slice, but when I started
my project was still using the old Android support libraries. I first had to convert my project to
the new dependencies before I could get started. Luckily, Android Studio did all of the heavy
lifting for me in this regard. Under “Refactor” there is a menu item called “Migrate to AndroidX”.

migration tool

When you select the migration tool it will prompt you to back up your project just in case. While
it seems unlikely that anything will go horribly wrong in the process it’s probably a good idea to
do it just in case. That said, if you’re using some kind of source control you can easily revert your
changes.

backup prompt

After the backup prompt, Studio will search your project for any usages of the support libraries
and present you with a refactoring preview. This is the same kind of preview you are shown when you
rename something. If nothing looks too out of the ordinary you can click on ‘Do Refactor’ to
perform the migration.

refactor preview

Once the migration completes, Studio will prompt you to do another Gradle sync to pull down the new
AndroidX dependencies. When the sync completes you are ready to start implementing your slice.

First up is adding the slice dependency to your project. Open up your app-level build.gradle file
and add it.

dependencies {
    ...

    implementation "androidx.slice:slice-builders-ktx:1.0.0-alpha4"

    ...
}

Once the dependency is in place, do another Gradle sync to pull it down. Once it is ready you can
move on to implementing your provider.

The mechanism that the system uses to provide a slice is called a
SliceProvider. Instead
of creating one manually you can use the built in templates in Studio 3.2. Right click on the java
folder and select New > Other > Slice Provider.

slice provider template

This will open a form that prompts you for the details of your slice provider. You need to provide
the class name of the object to be created. You also need to provide the authorities that the
provider will handle. If this strikes you as similar to the ContentProvider authorities, that’s
because it is. SliceProvider is actually a subclass of the ContentProvider.

slice provider form

Next, you can provide a host URL. This will be set in the AndroidManifest.xml as the data tag for
your provider’s intent filter. Last, you will supply the path prefix you want your data tag to be
configured with. When you submit you info it will create your provider class and add a registration
in the manifest. If you don’t want to set this info you don’t have to. It just means a little more
work later on to figure out which slice to display.

<provider
    android:name=".JustASliceProvider"
    android:authorities="com.bignerdranch.android.justaslice"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.app.slice.category.SLICE" />
        <data
            android:host="android.bignerdranch.com"
            android:pathPrefix="/"
            android:scheme="http"/>
    </intent-filter>
</provider>

The provider tag in the manifest is filled out with the data you provided. The authority and class
name are in the top-level provider tag. Your host url and path are included on the data tag in the
intent filter. Any time a SliceHost wants to display one of your app’s slices it will create the
view slice intent and send it to your app. Your provider will be started and it will return the
slice back to the host.

The SliceProvider implementation has a lot going on, but for a basic slice there is not much for you
to actually create. The first method to note is the onCreateSliceProvider method. This method is
called when your provider is initialized. You can setup your provider here but you should remember
that this method is run on the main thread so you shouldn’t do any long running operations here. If
your initialization succeeds, then the method should return true to tell the system your provider
is ready.

class JustASliceProvider : SliceProvider() {

    override fun onCreateSliceProvider(): Boolean {
        // initialize your provider dependencies here
        return true
    }

	...
}

The main method we will focus on is the onBindSlice method. This method takes a Uri as a
parameter and will return a Slice. The default implementation from the template indicates what we
are trying to do in this method.

override fun onBindSlice(sliceUri: Uri): Slice? {
    val context = getContext() ?: return null
    return if (sliceUri.path == "/") {
        ListBuilder(context, sliceUri, ListBuilder.INFINITY)
                .addRow { it.setTitle("URI found.") }
                .build()
    } else {
        // Unknown path
        ListBuilder(context, sliceUri, ListBuilder.INFINITY)
                .addRow { it.setTitle("URI not found.") }
                .build()
    }
}

If we do not have access to a context object we return null because we can’t build our slice
without one. The Slice we return from this method depends on the Uri parameter we receive. If we
recognize the path of the Uri we can build the appropriate slice for the data requested. If we
can’t, we just return a plain slice with an error message. Next, let’s tweak the recognized slice
and dive more into how it works.

Slices are created using various builders. The default example uses a ListBuilder object
which allows you to display a list of individual rows to the user. I won’t delve too far into
specifics in this post, but there are plenty of other
templates) you can use to build a rich UI
for your content.

To create your list you need to pass it the context, the slice Uri parameter, and a time to live
parameter. If your information is time sensitive in the slice you will provide a number of
milliseconds that the slice is valid for. In this case the data is not time sensitive so we pass in
the INFINITY constant.

Once you have your builder you can add additional rows to it. This uses a lambda to simplify
creating the RowBuilder, which is the type of the it in the curly braces. In this case we are
just setting the title to indicate we know what the Uri is. Last up, we call build() to create
the slice and return it.

What we will do next is change the title of our row as well as add a primary action to our slice.
Slices require a primary action so the host knows what to do if the user clicks on your slice. In
most cases it will open up the screen in your app that is relevant to the data being displayed.

Creating a SliceAction)
does take a bit of work. You need to provide the PendingIntent to fire when it is clicked. It also
needs an icon, an image mode, and a title.

// Create action for slice
val pendingIntent = Intent(context, MainActivity::class.java).let { intent ->
    PendingIntent.getActivity(context, 0, intent, 0)
}
val bitmap = BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher)
val icon = IconCompat.createWithBitmap(bitmap)
val imageMode = ListBuilder.SMALL_IMAGE
val title = "Action Title"
val sliceAction = SliceAction.create(pendingIntent, icon, imageMode, title)

Once the SliceAction is set up you can apply it to your RowBuilder using the setPrimaryAction()
method. While adding it the title can also be changed too.

ListBuilder(context, sliceUri, ListBuilder.INFINITY)
        .addRow { with(it) {
            setTitle("I'm just a boring slice!")
            setPrimaryAction(sliceAction)
        } }
        .build()

This will add your SliceAction to the row item. When the user clicks on that row it will open the
MainActivity of the app.

Test your slice

Once you create your Slice, you’ll no doubt want to test it to ensure it looks correct. Google
created a Slice Viewer project that allows
you to test your slices without needing to implement a SliceView yourself. Go to the linked Github
page and view the releases. Download the slice-viewer.apk file from there to your computer.

Once the apk has been downloaded you need to install it on your device. You can do so using adb.

adb install -r -t slice-viewer.apk

Once the apk is installed on your device you next create a run configuration to easily run your
slice in the viewer. Go to Run > Edit Configurations to get started.

edit configurations

Click the plus icon to add a new run configuration. Select Android App from the list of options.

add new configuration

Fill out the run configuration. You should give it a descriptive name so you know it is for running
your slice. Select your app as the module to run. Under the launch options, select “URL” for your
launch mode and enter in the URL you want it to launch. Use slice-content:// scheme and add your
provider authority as the rest of the URL.

slice configuration

Click OK to save your configuration, then run it to see your slice at work.

almost done

Well, almost. When you run your slice configuration it will start the slice viewer and it will
display something. If you read the text on what is displayed it says that the slice viewer sample
wants to show your slice. You just need to give it permission to. Click on the slice to view the
permission dialog in your app.

slice permission dialog

Accepting the permission will send you back to the slice viewer, where your slice should now be
visible.

successful slice

Issues

While building out this example I ran into a problem creating the IconCompat object for my
SliceAction. I tried using the createWithResource() method to pass in my launcher icon but the
icon was not displayed and I got errors in LogCat saying that it could not find the resource. I
switched to creating a bitmap out of the resource and used the createWithBitmap() method to
create my IconCompat object and that worked instead. I’m not sure if this is an issue with the
createWithResource() method or not, but it’s something to watch out for.

Fin

Minus the hiccups, implementing a slice is fairly quick. In this post we’ve looked at the main
parts of a slice and how to get your device setup to display them. In the following posts we will
take a deeper look at the more creative things you can do with slices. I hope you enjoyed the post.
If you have any questions please feel free to comment below. Thanks for reading!

The post Share a Slice of your App appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/share-a-slice-of-your-app/feed/ 0
Introducing Material Theming https://bignerdranch.com/blog/introducing-material-theming/ https://bignerdranch.com/blog/introducing-material-theming/#respond Tue, 29 May 2018 10:00:23 +0000 https://nerdranchighq.wpengine.com/blog/introducing-material-theming/ One exciting announcement from this year’s Google I/O is the new Material Theming. Google realized that Material Design seemed to push some designers and companies away because they thought it was too restrictive and they wouldn’t be able to represent their branding using it. To help onboard more people, Google is offering more guidance on how to customize Material Design for your specific needs. They’re now calling this Material Theming.

The post Introducing Material Theming appeared first on Big Nerd Ranch.

]]>

One exciting announcement from this year’s Google I/O is the new Material Theming (just don’t call
it Material Design 2). Google realized that Material Design seemed to push some designers and
companies away because they thought it was too restrictive and they wouldn’t be able to represent
their branding using it. To help onboard more people, Google is offering more guidance on how to
customize Material Design for your specific needs. They’re now calling this Material Theming.

The goal of Material Theming is to make it very simple to customize the rules of Material Design
in a way that works for everyone. These changes should not only help designers use Material Design to fit
their brand, but also help developers implement the designs in the apps themselves. Material
Theming consists of three parts: customizing the theme, applying the custom theme to the design,
and implementing the design in code.

Customization is king

Material Design comes with a base theme that works fine on its own, but it really excels once it is
customized. There are four main ways you can customize the base design to fit your branding: color,
typography, iconography, and shape.

Color

Color is one of the main defining characteristics of Material Design. Bright, bold colors are used
to draw the attention of users to main actions on the screen. This is combined with whitespace to
separate the content into different areas of the screen. Color is still the most important aspect for
fitting Material Design to your branding.

Typography

Typography was difficult to customize in early versions of Android.
Third-party libraries like Calligraphy made it easier, and in Android Oreo the font resource type was
introduced to include simple font customization in the platform. With Material Theming, Google is pushing
developers and designers to use their own fonts. Custom fonts can go a long way to give your apps a
branded look and feel, rather than using the default Roboto font.

Iconography

Iconography is a new way to tweak the base Material Design, and a very welcome one. Instead of
being restricted to a single set of icons, Google provides several different versions. As of now
there are 5 sets: filled (the original Material Design icons), sharp, rounded, outlined, and two-toned. This
allows you to select the version that fits best with your brand. It’s still a good idea to stick
with one type of icon style to keep things consistent across your app, though.

different icon styles

Shape

Finally, you now have more control over the shape of the views and containers on the screen.
Instead of having the default, rounded corners for widgets, you can modify them how you want. You
can control the radii of the corners like before or you can pick another option. One is to have no
rounding, which produces sharp corners for your views. You can also use a cut corner, which creates
a straight corner at 45 degrees to the horizontal and vertical axis. These shapes can be used on
regular view components or containing views like CardView to show off branding or the state of that
widget.

different container shapes

Applying the theme

Once you research the different options available for customizing your design, the next step is to
apply it to your design mock-ups. You can apply changes to color, typography, iconography, or shape.
To make this process easier, Google created the
Material Theme Editor. Currently this just integrates
with Sketch but more integrations are planned soon.

The Theme Editor makes applying your design customizations simple. It provides tooling for each of
the customization options, and any time you change one of the options it will be reflected
throughout your design. This allows you to iterate on your app design quickly and get prompt
feedback.

The first area you’ll likely customize in your theme is color. 13 colors make up your app’s
palette. You can specify your primary and secondary colors, just like previous versions of Material
design, and also specify an additional variant of your primary color to help with accessibility.
Next, you can add colors for your window background, other surfaces in your app such as CardViews,
and an error color for those hopefully unlikely failure states. Lastly, you can specify what are
known as ‘on’ colors. These are colors for things drawn on top of other elements. For example, ‘on
primary’ is the color of the text drawn on top of your primary color, and ‘on surface’ is the color
used to draw things on the various surfaces in your app.

The next area you can customize is the typography used throughout your app. Like color, there are
13 different typography categories for all of your type needs in your app. There are several
different headers, subtitles, body text, button text, and others that make up the text styling for
your brand. To configure these categories you can modify things like the type family, the font,
case, and size of the text. You can configure these based on how visible things need to be in your
app.

The Theme Editor also allows you to modify the shape of different components in your design. You
can specify how the corners look on both Views and ViewGroups, such as Buttons and CardViews.
Rounded, cut, and sharp corners are all available, depending on what fits best for your design and
brand.

Last, but not least, you can modify how the icons look in your application. Originally, you just
had the default Material icons available. You could tint the icons but there was very little
control over how they looked. Now, there are five different options available for your icon themes.
Filled icons are the originals, but you can also use icons that are sharp, rounded, outlined, or
two-toned. Sharp icons remove any corner radii for the filled icons. On the opposite side, rounded
icons smooth out any sharp corners on the filled icons so they all have the same corner radius.
Outlined icons remove most of the fill to the original icons. These are useful for showing a
deselected state. Two-tone icons allow the use of a fill color and opacity for the most
configuration options.

Using your theme

Once you configure your custom Material Design theme using the Theme Editor you are ready to apply the
theme to the app itself. The values provided by the Theme Editor can be used in your app to
configure the styling for the different elements. You can apply your styles to your views, text,
and icons to make sure your branding is consistent across all of the screens in your app.

Closing thoughts

Google has made great strides to improve Material Design and simplify the process of customizing it
to fit a wide variety of styles and brands. The new Theme Editor should prove to be very useful to
both designers and developers when creating apps, and it should achieve wider adoption when it
expands beyond just Sketch. I look forward to your comments on the new Material Theming. Please
drop any questions or comments below, I’m especially curious about people’s opinions on the new
Bottom App Bar. Thanks for reading!

The post Introducing Material Theming appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/introducing-material-theming/feed/ 0
What’s New In Android? https://bignerdranch.com/blog/whats-new-in-android/ https://bignerdranch.com/blog/whats-new-in-android/#respond Thu, 17 May 2018 10:00:23 +0000 https://nerdranchighq.wpengine.com/blog/whats-new-in-android/ Android will be 10 years old this year and the team at Google introduced plenty of new updates at Google I/O on May 8th. These changes range from user-facing improvements to API updates that developers will be able to take advantage of in the near future. If you weren’t able to attend I/O this year or watch the recordings on Youtube, I’ll recap some of what you missed.

The post What’s New In Android? appeared first on Big Nerd Ranch.

]]>

Android will be 10 years old this year and the team at Google introduced plenty of new updates at
Google I/O on May 8th. These changes range from user-facing improvements to API updates that
developers will be able to take advantage of in the near future. If you weren’t able to attend I/O
this year or watch the
recordings on Youtube, I’ll recap some of
what you missed.

Introducing Jetpack

One of the most significant changes that will affect developers is to the support library. Google
is continuing to build opinionated libraries to help developers, especially new developers, answer the question
of how to architect their apps. To provide even more help, Google has consolidated these libraries
into a set called Android Jetpack. This set of
libraries is offered so you can mix and match them based on your app’s needs.

Two new libraries were introduced alongside the existing Jetpack libraries:
WorkManager and the
Navigation Architecture Component.

The WorkManager is a new API that is built to perform background work for you without requiring
extra effort to deal with the different Android API versions. It will pick the best implementation
for the particular device it is running on, from JobScheduler to FirebaseJobDispatcher to
AlarmManager, so you can just focus on what you want WorkManager to do, not how it should do it. It
also provides additional features, such as the ability to chain work together so they execute in an
order you specify, and it provides a mechanism to check the state of your tasks and query their
return values by using LiveData.

The Navigation Component is built to make it easier to add navigation to your apps. It provides a
graphical editor for specifying the flow of screens in your apps. It looks very similar to the
storyboards from XCode. The Navigation Component does seem to work better with a single Activity
architecture, where it performs the navigation by swapping fragments in and out based on what
screen the user needs to go to. It is possible to use it with a multi-Activity setup but it is a
little more work, so that is something to consider if you want to try it in your projects.

The Paging Library is now
version 1.0. This library makes it simple to load data into a RecyclerView, then load additional
items as needed. Keeping everything asynchronous is important since scrolling lists is one of the
easiest places to spot visual jank in an app. Luckily the paging library loads your app’s data in
the background to keep your UI looking smooth. You can load your data with this new API no matter
where your data is actually stored, whether it is on a local database or on a remote server.

One other big announcement is that Google is heavily refactoring the package names for their
support libraries. They are collecting them all under the
AndroidX package.
AndroidX stands for Android extension libraries, and it will be the home of the support libraries,
architecture components, and any future libraries added to Jetpack.

There were also a couple changes announced that aren’t specifically relevant to the support
library, but they have big ramifications for developers. These updates aim to keep the system more
secure and make sure apps are keeping current with newer version of Android.

One is that the Android framework is going to start disallowing calls to private APIs in the system. For now these calls
will result in a Toast and a log message saying that the API call is illegal. In the future the
system will throw an exception if you try to access them. However, the list of restricted APIs are
not yet set in stone. If you find that an API your app relies on is going to be made restricted you
can submit a bug to
request that the API remains available.

The last announcement was about the new
deprecation policy
going into effect later this year. Any new apps submitted to the play store after August of 2018
must target API 26 or higher. If the app does not meet this criteria then it will not be allowed on
the store. Updates to existing apps will be held to the same restriction as of November of this
year. Google aims to prevent apps from targeting older versions of the operating system so users
will benefit from the security and performance improvements of the new version. This version will
remain at API level 26 for now, but it will be bumped up periodically to keep up with new releases.
Along with the target version restrictions, apps that include native libraries will be forced to
include 64 bit versions of their libraries. 32 bit versions will also be allowed, but the app must
also include the 64 bit version. This will allow users on devices that support 64 bit to benefit
from the new library.

Testing Changes

Another exciting announcement revolves around testing. The folks at Google recognized that writing
tests for Android apps is really hard, particularly because the different kinds of tests you write
(unit, integration, and end to end) require different kinds of setups and different tools. The cost
of learning all of these tools and maintaining the different types of tests is very large, and can
drive people away from writing tests altogether.

Included in Jetpack is a new Android Test library that aims to make testing more painless, no
matter what kind of test you are writing. Android Test includes the existing test libraries like
Espresso, includes new APIs and Kotlin support, and the same tools will be used for on and off
device tests. Google’s goal is to allow developers to write their tests once and run them anywhere.

The tests are broken up into several parts; scaffolding, given, when, and then. The scaffolding
includes any of the general setup for your tests. It has been expanded to include things like
InstrumentationRegistry for your on and off device tests. The given part is where you setup the
specific dependencies for your system under test. Activity JUnit rules have been rolled into
Jetpack, ActivityTestRule is now available for off-device tests, and the ability to drive the app’s
lifecycle will be added in the future. The when part is all about driving your functionality under
test. With Espresso 2 joining Jetpack you will be able to use its API to write your tests and they
currently have preliminary support for off-device tests. Lastly, the then section verifies the
results of your tests. New APIs will help reduce boilerplate by making assertions more readable,
both in code and in the failure messages they print to the console.

While the new Android Test library looks very exciting, Google is also dropping Project Nitrogen,
which will be open source later this year. Project Nitrogen is meant as a single entry point for
your Android tests, no matter what kind of test you are running. It manages setting up the
environment by installing any test artifacts, running your custom fixture scripts, prepping a
simulated or real device to run the tests on, and preps everything for execution. Hermetic tests
are a main goal of Nitrogen so it uses the Android Test Orchestrator to collect all of your tests and executes them while trying to minimize any state leakage.
The tests are run in separate process to try and minimize those impacts across your test suite.
Once everything has executed, Nitrogen collects all of your test results and offers a unified
reporting format that will help you track down your test failures and identify why they failed. It
will include the relevant Logcat output from your test, screenshots, and profiling data which will
help fix those tricky bugs.

One last change that is sure to be a hit with Kotlin users is the updates to the Mockito library.
Recent changes now allow you to mock final classes, which is great considering that Kotlin classes are final by default. This means you no longer have to mark your classes as open or make unnecessary
interfaces just for testing. Static function mocking will be supported in the future as well, along
with the ability to mock system created objects like Activities. This is one thing I’m really
looking forward to, and I’m excited to see what changes are made to support this kind of mocking.

Performance Improvements

Another welcome focus this year is on performance improvements, both user-visible and
developer-facing. All of these will come together for a better user experience in your apps and a
better device experience overall for Android users.

Google continues to focus on battery life improvements. Building off of existing
functionality like Doze and App Standby, the Android team is offering the system more fine-grained
control over what services apps have access to. They refer to this as ‘App Standby Buckets’.
Effectively the system will determine the level of access to things like jobs, alarms, and the
network based on how frequently and recently the user has used an app. If the user hasn’t used an
app in a long time then it is going to be restricted from making network requests and its jobs will
be deferred until it is no longer in standby. This will help preserve the user’s battery life since
these infrequently used apps will not be running in the background without reason. Users will also
have the option to manually restrict poorly behaving apps from the Settings app. This will provide
more control to users over their device experience, although I’m hesitant to believe that many
users will know about the setting and use it in practice. Still, it reflects well on the Android
team to continually improve the OS and provide users with a better experience.

Another big announcement involved the introduction of the
Android App Bundle. This is a new
way of packaging apps that will reduce the download size for your users, which will particularly
help adoption rates in emerging markets. The app bundle involves splitting up your app in two ways:
by configuration, and by feature.

The configuration APKs will generate separate versions of your app based on different device
configurations. This helps because typically the largest contributor to bloat in an app is the
resources. By splitting these up, the Play Store can just provide the configuration relevant to a
user’s device which helps save bandwidth. If the user changes something on their device that
requires a different configuration bundle, the device will automatically download it from the play
store as needed.

The feature APKs allow you to separate your application by features. This will allow users to only
download the features that they need, while still allowing the Play Store to download additional
features when they’re required. This seems like a natural continuation of Instant Apps, which are
supported with App Bundles. This will certainly be more work that the resource configuration split
but you will end up with more modular code, faster downloads, and better build times.

Developers will also see improvements on their side. Performance improvements have been made for
Kotlin code by optimizing ART, D8, and R8. Google have been working to add more nullability
annotations to Java APIs which means any Kotlin code that interfaces with it can take advantage of
the nullable types, without having to assume everything is nullable.
Android KTX aims to make development easier and more
concise with a set of common extension functions specific to Android development. While it doesn’t
add any new functionality to existing APIs it will certainly make those APIs more intuitive to use.

System UI Changes

Last but not least are the system UI changes that Google announced, as well as the APIs we have for
supporting them. There is a new display cut out API, support for Slices, and new Actions.

No matter what you call it; a display cut out, a notch, or a unibrow, it looks like they are here
to stay at least for a little while. To respond, Google introduced a new API to help your apps know where it is safe to draw on a screen that has a cut out. There are a range of options you can use, from playing it safe and not drawing around the notch at all, to getting the exact bounds around it and drawing on the entire screen. You can also test your devices on a new notched emulator instead of buying a device just for the cut out.

The introduction of Slices is a big opportunity for apps to share their information with other
apps. Your app can provide content to display as well as hints for how to display it, and a
SliceHost will be able to take that data and show it to the user. It’s also meant for more than
static content as well. User should be able to interact with the UI to accomplish their tasks
easily, no matter where they are.

Actions provide a way for users to take specific actions in your app, rather than just going to a
particular place. We can think of these like deep links with extra information about what the user
wants to do, such as play a particular song on Spotify, or make a reservation at a particular
restaurant on OpenTable. Actions also integrate with the assistant so users will be able to control
your app while interacting with the assistant.

Where to go from here?

The Android team has shown a continual commitment to improving the platform for everyone that uses
it, both the end users with their Android devices, to developers who help make the ecosystem so
vibrant. Google I/O 2018 has proven to be no exception. There are plenty of new APIs to experiment
with that will be keeping us all busy in the coming months. Be on the lookout for more blog posts
coming soon that dig deeper into these topics, and if you have any questions please feel free to
comment below. Thanks for reading!

The post What’s New In Android? appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/whats-new-in-android/feed/ 0