Suggestions for Wearables and Automation

google glass

I’ve been thinking a lot about automation recently. One of the big questions that I ask myself over and over is, what can I stop spending so much time on? What are things that I’m not great at that could get better through automation?

I have a list of general things that I think would be fun to try automating (which I’ll write about later), but of the list of tools, I think that wearables present an interesting opportunity for automation. The problem is, that I am having trouble thinking of what I could do there.

So, I figured that I would ask what people think about this? I’m sure that there are lots of people out there with Glass or Android Wear who dreamed of what they could do with those devices. If those dreams involved automating something, what were they?

Testing in Android

On November 23 & 24, I attended the first ever Android Dev Summit. The following are notes that I took during talks. I have included the video of the talk as well. Again, these are my notes from the talk, not my original content.

Chiu-Ki Chan (Android GDE) took really great notes during the session as well, here’s what she had:

People want automated testing. Android has not provided a ton of testing APIs, and it hasn’t evolved much since API level 3. Until recently.

Android provided tools for testing:

  • Android Studio / Gradle
  • Android Testing Support Library
  • Espresso

Refactor across unit and instrumentation tests.

Hermetic testing

isolate tests from external dependencies

Flaky tests are worse than no tests. (When it fails sometimes.)

Steps:

  1. Isolate from external dependencies
  • Especially the network
  1. Replace the components that talk to external dependencies, with things that replace with fake data
  2. Using product flavor ‘mockDebug’, with source sets
  3. Inject lives in both mock and prod source sets

When to use:

  • Mock mode for manual testing & concurrent development
  • Prod mode for end-to-end tests
    • great pre-release check
testing pyramid

Unit testing

unit test business logic

Unit tests are fundamental. Run locally, generally fast, should be small and well contained. Focused on methods.

Don’t put everything into Activities, it makes it really hard to test. Move business logic out, so that it is more testable.

Mockable Android Jar.

Android Studio helps with Test-Driven-Development. If everything is set up correctly, AS can even help with new features, if tests are defined first.

UI Testing with Espresso

android espresso

Integration and UI tests check your application through its interface. You need a device or emulator for this. CloutTest Lab allows you to test on real devices.

UI testing is hard! Espresso helps with this.

Tried to ask: What would a user do?

Find, perform, check

onView(ViewMatcher)
    .perform(ViewAction)
    .check(ViewAssertion);

Keep it Secret, Keep it Safe

cover slide

On November 23 & 24, I attended the first ever Android Dev Summit. The following are notes that I took during talks. I have included the video of the talk as well. Again, these are my notes from the talk, not my original content.

Android SSL Training Docs.

Protecting the network traffic in your app.

  • High level TLS, and why you should use it
  • Using TLS, checking your work
  • Mistakes and misconfigurations

Why

The network is not to be trusted. This has always been true, but especially for mobile devices.

  • Devices connect to many different networks
    • Coffee shop wifi
    • Anyone can run an open wifi
  • Devices do sensitive transactions wirelessly

Canonical examples of sensitive traffic

  • login for banking
  • credit card details
  • private personal photos
  • emails
  • sensitive web traffic
  • health information

On the server-side, you should secure your traffic. All traffic must be protected.

  • Modify non-sensitive traffic (e.g. Time Warner)
    • inject exploits
    • modify content
    • replace images
  • Tracking and snooping

Goal the network cannot affect the security of your device.

Enter TLS

  • Transport Layer Security
    • Previously known as SSL
    • Usable with any protocol
  • Establishes an end-to-end channel between two peers
    • Integrety
    • Confidentiality
    • Hard part - knowing you’re talking to the right peer
  • Safe by default
    • Modern platforms have safe defaults and it is just as simple as using TLS.

Using TLS

Using standard HTTP libs, replace http:// with https://. Use SSLSocket for socket connectisons.

HostNameVerifier.verify

ssllabs has a good intro for server-side.

Checking your work

  • Hardcoded URLs - what about redirects?
  • Server provided - maybe you forget on the server?
  • Third-party code - how do you check them?

New feature in Marshmallow.

  • Strict mode clear text detection
  • usesClearTextTraffic flag in manifest

Strict Mode

Uses packet inspection to catch all non-TLS traffic. Useful during development to make sure that all traffic is going over TLS. Some false-positives, e.g. HTTP proxies, protocols with STARTTLS logic, or secure traffic that doesn’t begin with a TLS Hello.

usesClearTextTraffic

AndroidManifest.xml:

<application android:usesClearTextTraffic="false">

Supported:

  • HttpsUrlConnection
  • OkHttp, apache

Why block vs upgrade

  • Older devices left out.
  • Upgrade logic isn’t always well defined for non-HTTP protocols

Blocking will fail quickly, and obviously. Fixes will be true for all devices.

Mistakes and misconfigurations

Using the default implementation is a good idea. However, you can override the defaults with insecure code.

Legacy servers, with legacy clients choosing what cert to present for a connection.

TLS with Server Name Identifier (2.3+) extension solves some problems around knowing who you’re talking to.

Some internet routing may not support all of the TLS protocols, and may downgrade to SSLv3.

How do you know if you trust this chain of certificates? What ships with your devices?

How can you trust the CAs on the device? (e.g. Lenovo.)

Your app can use its own CA, which may make sense for various applications. However, the example code out there may not be great. Documentation.

What Google’s doing

Vulnerability alerts and blocks

Google is going to be scanning the Play Store, running static analysis on apps, looking for vulnerabilities. They will alert developers, and remove apps that have not been fixed. Here’s the slide that discusses that:

vulnerable apps

What is App Indexing?

And, what’s the difference between App Indexing and Deep Linking?

Deep Linking - Not new. Opening a link from search or any other app in your app. That same link that would normally open a web page, is opening the same (or very similar) content in your app.

App Indexing - New-ish. Reporting back to Google what content and web addresses are being opened by users in your app. Allows for auto-complete of searches with app and previously visited content.

Using both together allows Google to put a button on search results that allows users to install and open pages with your app using deep linking.

auto complete search

How do I enable App Indexing for my app?

  1. You have a website with content
  2. Verified website’s domain in the Google Search Console
  3. Have an app that opens content that is the same as your web content
  4. Implement an intent-filter for your site in your app
  5. Clicking links for your website opens the content in your app
  6. AppIndexing API called whenever content in app is loaded, or the user leaves the app
  7. Publish app
  8. Add the android-app:// version to your Google Search Console, and associate it with your website!
  9. Verify domain in Play Console

Aside from the Google Play app publishing process, this should be doable end-to-end in about two hours, if you are starting from scratch with your app. If you already have an app, you should be able to do it in less than 20 minutes.

Let’s take a quick look at all the pieces. The code that I’m using comes from the blog example that I built and have hosted on GitHub. First, we’ll go over the deep-linking part, then we’ll look at what needs to be added to make App Indexing work.

Deep-Linking

AndroidManifest.xml support for deep linking:

<activity
    android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="http"
            android:host="ejf.io" />
    </intent-filter>
</activity>

In onStart of the MainActivity, load up the URL in the webview to make the deep-linking work:

@Override
public void onStart() {
    super.onStart();
    mClient.connect();

    Intent intent = getIntent();
    String action = intent.getAction();
    Uri data = intent.getData();

    if (Intent.ACTION_VIEW.equals(action) && null != data)        
        webview.loadUrl(data.toString());
}

App Indexing

App’s build.gradle:

dependencies {
    // ...
    compile 'com.google.android.gms:play-services-appindexing:8.3.0'
}

AndroidManifest.xml support for Google APIs:

<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />

MainActivity get GoogleApiClient for App Indexing:

@Override
protected void onCreate(Bundle savedInstanceState) {
    // ...
    mClient = new GoogleApiClient.Builder(this).addApi(AppIndex.API).build();
    // ...
}

I added a listener on my WebView to run some code when the page finishes loading. This is we let the App Indexing API know that we’ve started viewing the page:

webview.setWebViewClient(new WebViewClient() {
    @Override
    public void onPageFinished(WebView view, String url) {
        // Define a title for your current page, shown in autocompletion UI
        final String title = view.getTitle();
        Uri webUri = Uri.parse(url);
        Uri appUri = Uri.parse("android-app://io.ejf.blogapp/https/"
                + webUri.getHost() + webUri.getPath());

        // Construct the Action performed by the user
        Action viewAction = Action.newAction(Action.TYPE_VIEW, title, webUri, appUri);
        // Call the App Indexing API start method after the view has completely rendered
        AppIndex.AppIndexApi.start(mClient, viewAction);
    }
});

We call the App Index end command in onStop:

@Override
public void onStop() {
    // Call end() and disconnect the client
    // Define a title for your current page, shown in autocompletion UI
    final String title = wv.getTitle();
    if (null != wv.getUrl() && null != wv.getUrl()) {
        Uri webUri = Uri.parse(wv.getUrl());
        Uri appUri = Uri.parse("android-app://io.ejf.blogapp/https/" + webUri.getHost() + webUri.getPath());
        Action viewAction = Action.newAction(Action.TYPE_VIEW, title, webUri, appUri);
        AppIndex.AppIndexApi.end(mClient, viewAction);
        mClient.disconnect();
    }
    super.onStop();
}

Testing deep-linking and App Indexing

In order to test deep-linking from cli:

# adb shell am start -a <action> -d <data> <package>
adb shell am start -a android.intent.action.VIEW -d “http://www.example.com/app/some-great-content” com.example.app

If you have App Indexing set up, you should be able to go to Google Search on your phone, and begin typing the title of the page that you just visited. After just a couple of characters, you should be given an auto-complete result for your app.

Why do I want App Indexing for my app?

There are two basic benefits of adding in App Indexing. The first is user acquisition, the second is reengagement.

Google’s basic argument is that as the number of available apps grows, and the content accessible in those apps grows, users are going to begin to rely more heavily on search. Users will start with a Google Search to look for content either on the web or on their device. If there are results that are app-based, users may tend to prefer those results over others that are purely web-based. App Indexing puts an app badge, and possible install button (if the user doesn’t have the app), on your search result. The other nice thing that it does is allows users to auto-complete a Search with content from an app.

For non-content focused apps, the benefits are obviously a little less clear. However, you could deep-link any pages that you have on your website, serve them up in-app, and then use traditional web SEO to drive installs. This is nice because web SEO is much easier to do than ASO (App Store/Search Optimization).

Some of the numbers that Google has given about success cases for this feature are as follows:

  • The Guardian - 4.5% increase in CTR from search results
  • Etsy - 11.6% increase in app traffic from referrals
  • Tabelog - 9.6% increase in app traffic to restaurant pages

This feature is also really easy to implement for apps that are already serving up content. Adding this feature really shouldn’t take more than about 20 minutes.

Notes on the Android Support Library

On November 23 & 24, I attended the first ever Android Dev Summit. The following are notes that I took during talks. I have included the video of the talk as well. Again, these are my notes from the talk, not my original content.

Chiu-Ki Chan (Android GDE) took really great notes during the session as well, here’s what she had:

  • What?
  • Why?
  • Gotchas?
  • How?
  • Bugs?

What?

Lots of stuff. Started out simple, but it’s grown. There are multiple support libs, providing different utilities. They’re a bridge for getting newer functionality on older API level devices.

The core v4/13:

Animation

ViewCompat.animate()

Fragments

FragmentActivity.getSupportFragmentManager()

Features

<android.supportv4.ViewPager> // or something

Infrastructure

  • RecyclerView
  • appcompat - large chunks of the UI toolkit

Higher Level:

  • mediarouter - chromecast
  • design
  • preferences
  • leanback - TV

Why?

Unbundled releases:

  • Not tied to platform releases
  • Bug fixes
  • New features

RecyclerView

  • Component providing data-set windowing
  • Improves upon an existing framework (?)
  • Backports to v7

Provides:

  • Animation
  • Pluggable
  • Enforcing

AppCompat

  • UI compatibility layer for v7+
  • Backport framework features, but no more
  • Goal: stay up to date with framework

Provides:

  • Themes
  • Toolbar
  • Tinting

Design

  • UI feature lib
  • Provides features not in the framework
  • Goal: implement high-level UI components

Provides:

  • FloatingActionButton - FAB
  • NavigationView - NV
  • Snackbar (like Toast with an action)
  • TabLayout - TL

Percent

  • UI feature lib
  • Provides percent based layouts
  • FrameLayout and RelativeLayout

Allows you to define layout elements as percentages of the parent.

Gotchas

Library major version number is the minimum compile sdk version.

E.g. lib v23.1.0 needs compileSdkVersion 23.

Dex limit, all of the compat libs add up to a third of the dex size. Using proguard: minifyEnabled true, you can cut it down to something more manageable. With new AS tools, no need to re-run proguard on every iteration.

How?

Using a variable can help to keep all the support dependencies at the same, correct, version in the build.gradle file.

ext {
    supportLibVersion = "23.1.1"
}

dependencies {
    compile "com.android.support:appcompat-v7:${supportLibVersion}"
    compile "com.android.support:design-v7:${supportLibVersion}"
}

Bugs

http://b.android.com

Feedback Q&A (Android Intents Part 6)

This is the sixth post in a series about sharing information between apps. (Series Table of Contents.) In this post, I’m going to discuss some of the feedback that I’ve received so far on this topic. No code this time, but it’s an important part of the discussion, and is going to set the stage for the next few installments.

Series Table of Contents

Follow the series on the Table of Contents page.

Feedback

Recently, I gave a talk at Silicon Valley’s GDG DevFest on this series. The talk went fairly well, and I received a good amount of feedback, which was exactly what I was looking for out of the talk.

Chiu-Ki Chan (Android GDE) took really great notes during the session, here’s what she had:

By the way, I’ve been using Chiu-Ki’s notes in several of my recent blog posts because they’re really helpful. Make sure to follow her on Twitter or G+!

There were a number of great questions that I didn’t have great answers for. I thought that it would make sense to post them, and think about where to go from here. I’ll also try to categorize them, to make them easier to read together.

Security

Q: Rooted phones allow for apps to snoop on the Intents being passed around. How do we secure this?

There were a few Android GDEs in the room that chimed in and helped answer some of these questions. They pointed out, for example that on rooted phones, you really have no expectation of security.

Q: Locking down Intent filters to just one app?

Ryan Harter mentioned that you can lock down intent filters using signature checking, so that only apps signed with your signature are allowed to send and consume them. However, for that case, it’s not really a public API.

Q: How do I know that no other app is going to intercept an Intent? I.e. if another app starts listening for the exact same Intent, and the information is sensitive, that could be an issue.

I do think that this question deserves a better explanation. I have ‘security’ and ‘a deep dive on Intents’ on my roadmap for this series. So, my current answer is that I’ll try to cover those issues in a future post.

Scalability

Q: Wouldn’t it be more scalable to do this on the backend? In terms of sharing data with multiple platforms. If iOS needs the same integrations, and does them on the backend, providing a second set of APIs is lots more work.

As far as the scalability question goes, my immediate answer was that the authentication may be able to be done locally. I’m pretty sure that there are accounts APIs for this, and it may be necessary to open an additional in-app API for getting full auth details for the backend web requests. The point is that on-device public APIs may not be the answer to all inter-app communications issues, but they should at least be part of the conversation.

Android GDE Mike Wolfson also mentioned that using a local public APIs can really help with reducing network usage.

  • Good for offline cases
  • Emerging markets care a lot about this
  • Better for battery

Logistical Issues

Q: In the case where lots of developers open up APIs, how do other apps discover them?

The question around service discovery and consistent behavior may be able to be addressed by some sort of central repository of APIs. Such a repository may contain highly structured data around how to make requests to any given API, which apps provide such functionality.

Q: What happens when multiple apps provide the same Intent API, but don’t work the same?

This is why we chose Aviary to use with Talkray, over going with whatever was already on the device. Such a repository/project should also provide some sort of automated testing to attempt to validate that the functionality works consistently between apps claiming to provide similar features.

I have a feeling that once I finish laying the groundwork (and assuming that I’m not completely bored with this topic), that this is where I’ll be spending a lot of my time.

Monetization

Q: How to monetize?

I think that the monetization question was interesting, and I’ll certainly cover that in a future post in this series. I don’t think that it needs to be difficult, but I do think that it requires some thought around doing it in a way that maximizes benefit to you, your users, and your partners (those consuming your API).

Other thoughts

Ryan also brought up the point that the web paradigm might be a good metaphor for communicating the general idea:

  • Intents are like HTTP requests
  • Content providers
    • like building a REST interface
  • Accounts
    • maybe similar to OAuth

Conclusion

I’ll be circling back to what’s been brought up here in future posts. Stay tuned!

Android Build System

On November 23 & 24, I attended the first ever Android Dev Summit. The following are notes that I took during talks. I have included the video of the talk as well. Again, these are my notes from the talk, not my original content.

Chiu-Ki Chan (Android GDE) took really great notes during the session as well, here’s what she had:

Tips

  • Use the daemon
    • --daemon or org.gradle.debug = true
  • Give it enough memeory
  • Control the number of threads
  • Use the latest build tools
  • Get an SSD
  • Don’t use timestamps or commit numbers in debug build files

Try:

android {
    dexOptions {
        dexInProcess = true
    }
}

Consider having a development flavor using min SDK 21, as it’s going to be faster.

android.productFlavors {
    normal
    dev {
        minSdkVersion 21
    }
}

Testing

Test dependencies

dependencies {
    compile ".."
    testCompile "junit:junit:4.12"
    testCompile "org.mockito:mockito-core:1.9.5"
}

Unit testing

config:

unitTests.all {
    jvmArgs "-XX:MaxPermSize=256m"

    beforeTest { descriptor ->
        logger.lifecycle("Running test: " + descriptor)
    }
}

Using a test project

Use a separate project just for the test APK.

In main project:

apply plugin: 'com.android.application'
android {
    ...
    publishNonDefault true
}

In test project:

apply plugin: 'com.android.test'
android {
    targetProjectPath ':app'
    targetVariant 'debug'

    defaultConfig {
        minSdkVersion 8
        testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
    }
}

Test code then lives in the main source set of your test project.

Instrumentation tag still needs to be added to the test project manifest, however they’re hoping to be able to remove this step soon:

<manifest ...>
    <instrumentation
        android:name="android.support.test.runner.AndroidJUnitRunner"
        android:targetPackage="com.example.app" />
</manifest>

Running task on test project:

./gradlew :testProject:connectedAndroidTest

One note on all of this is to make sure that you avoid build tools and dependency version conflicts! Main APK and Test APK need to have the same Build Tools version, and same versions for dependencies. This should be common sense (since you want to test the same versions of everything).

Native Support

New plugins

New plugins for native (content at link may change):

  • com.android.model.application - *.apk
  • com.android.model.library - *.aar
  • com.android.model.native - *.so

In build.gradle:

apply plugin: 'com.android.model.application'
model {
    android {
        compileSdkVersion = 22
        buildToolsVersion = "22.0.1"

        ndk.with {
            moduleName = "native"
            // and other configurations like the following
            CFlags.add("-DCUSTOM_DEFINE")
            cppFlags.add("-DCUSTOM_DEFINE")
            ldFlags.add("-L/custom/lib/path")
            ldLibs.add("log")
            stl = "stlport_static"
        }
    }
}

Inter-module dependencies

Inter-module dependencies for native code, using a library project for the native code:

apply plugin: 'com.android.model.application'
model {
    android.sources {
        main {
            jni {
                dependencies {
                    project ":lib"
                }
            }
        }
    }
}

Perhaps the lib project with native code is set up like this:

apply plugin: "com.android.model.native"
model {
    android {
        compileSdkVersion = 21
    }
    android.ndk {
        moduleName = "hello"
    }
}

Extending the Android plugin

afterEvaluate

Old way:

project.afterEvaluate {
    def task = project.tasks.getByName('transformClassesWithInstantRunForDebug')
    // ...
}
  • not part of the official API
  • no guarantee that task names will be stable
  • no guarantee that things won’t be merged or split

Here’s what the flow looks like if you want to write Gradle Android plugins:

gradle extension flow
  • If you want to work with tasks, you have to use afterEvaluate
  • If you want to work with build types/flavors, etc., you can’t use afterEvaluate
  • There is no way to prepend to the afterEvaluate queue

Variant API

The correct way to extend the Gradle Android plugin.

android {
    applicationVariants.all {
        variant -> ...
    }
}

Or:

android {
    libraryVariants.all {
        variant -> ...
    }
}

Note the .all, that’s important!

  • getName()
    • Unique Name for variants
  • getDirName()
    • Unique folder segment(s)
  • getApplicationId()
    • Computed application id

Read-only:

  • getBuildType()
  • getProductFlavors()
  • getMergedFlavor()
  • getTaskName()
  • registerJavaGeneratingTask()
  • registerResGeneratingTask()

Public and private task APIs may be broken. They’re trying to get better about it.

Transform API

Problem: inject tasks between javac and dx.

javac to dx

It’s a mess!

Solution: Transform API

javac to dx

Much cleaner!

  • Pipeline of transforms
  • API abstracts Gradle tasks, focus on the transform part
  • Handles dependencies between transforms
    • Just register a new transform
  • Support different types of content: class, resources and dex files
  • Support different scopes: Project, sub-projects, etc.

com.android.build.api.transform.Transform methods:

  • getName()
  • getInputTypes()
  • getScopes()
  • isIncremental()
  • transform(...)

Using it:

dependencies {
    compile "com.android.tools.build:transform-api:2.0.0"
}

The Transform API uses a fixed order, to reduce bugs.

Built-in transforms:

  • Jacoco
  • Proguard/ New shrinker
  • InstantRun
  • Dex

Limitations:

  • Global transforms only
  • No per-variant transform
  • No order control through API

Javadoc for Android Gradle DSL

Inspecting your builds

The following Gradle tasks will help you to inspect your builds:

  • $./gradlew :app:dependencies [--configuration <name>]
  • $./gradlew :app:sourceSets
  • $./gradlew :app:signingReport

Instant Run

Only compiles, dex’s, and delivers .class that has changed.

instant run loader
  • $override classes will appear in stack traces, and in the debugger
  • New method calls use new code
    • recursive calls can be a mix of old and new method code
  • Classes are loaded once, not reloaded
    • Class initializer changes are incompatible changes
  • Static variables are not reloaded
  • Methods are replaced
  • Existing instances are untouched
    • instances fields of existing instances are not reinitialized
    • also true for singletons
  • Restart app if you get weird behavior

API specific Multi-APKs

Deprecated way:

android {
    ...
    productFlavors {
        x86 {
            ndk {
                abiFilter "x86"
            }
        }
        ...
        full {...}
    }
}

New way of doing it:

android {
    splits {
        abi {
            enabled true
            reset()
            include 'x86', 'armeabi-v7a', 'mips'
            universalApk true
        }
    }
}

Resource Shrinker

android {
    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt')
        }
    }
}

Sharing constants between sub-projects

You can use Gradle extensions to create shared properties.

In /build.gradle:

buildscript {
    ...
}
ext {
    toolsVersion = "23.0.2"
    deps = [
        guava: "com.google.guava:guava:18.0",
        ...
    ]
}

In /app/build.gradle:

android {
    buildToolsVersion rootProject.toolsVersion
    ...
}
dependencies {
    compile rootProject.deps.guava
}

Scoping

The dependencies block is a top-level attribute in the current version. Even if you add dependencies only within specific flavors of your app, they will be added to all flavors. Instead, if you prepend the product flavor to the compile command in the dependencies block, you can restrict it that way.

android {
    productFlavors {
        free {...}
        paid {...}
    }
}
dependencies {
    freeCompile '...'
}

Flavor Dimensions

old:

android {
    productFlavors {
        freeBlue
        freeRed
        premiumBlue
        premiumRed
    }
}

New:

android {
    flavorDimensions "price", "color"
    productFlavors {
        free { dimension "price" }
        premium { dimension "price" }
        blue { dimension "color" }
        red  { dimension "color" }
    }
}

Variant filters

android {
    variantFilter { VariantFilter variant ->
        if (variant.flavors[0].name == "dev"
            && variant.buildType.name == "release") {
            // no point in having a devRelease variant
            variant.ignore = true
        }
    }
}

Vector Drawables

vector drawables

Vector in res/drawable/vector.xml (or xml), it will automatically generate all flavors. Generated flavors and densities can be configured in the build.gradle file. Officially supported in API 21, density specific pngs generated for older devices. Per flavor setting.

Android Studio 2.0 & Android Emulator 2

On November 23 & 24, I attended the first ever Android Dev Summit. The following are notes that I took during talks. I have included the video of the talk as well. Again, these are my notes from the talk, not my original content.

Chiu-Ki Chan (Android GDE) took really great notes during the session as well, here’s what she had:

Recap

Launched at I/O 2015:

  • GPU Monitor
  • Network Monitor
  • App Templates
  • Theme Editor
  • Vector Asset Studio
  • Lots of other stuff.

Bottlenecks

  • dx
  • proguard
  • aapt
  • Legacy multi-dex
  • Upload to device
  • Installation

Improvements

dx

  • Improved dx merger
    • Build Tools 23.0.2+.
  • Run dx in process
    • Gradle 2.4+
    • Plugin 2.0.0+
    • Build Tools 23.0.2+
  • New option to configure memory allocated to gradle
    • org.gradle.jvmargs=-Xmx4096m to change allocated memory for gradle.
    • long-lasting daemon, instead of short-lived instance
  • Run up to 4 dx tasks in parallel
  • Use environment variable android.dexerPoolSize to change

Here’s how to enable dex in process:

android {
    dexOptions {
        dexInProcess = true
    }
}

ProGuard

Issues

  • not incremental
  • disables pre-dexing
    • single jar output
    • full re-dexing every time

Improvements:

  • Some incremental support
  • does not disable pre-dexing

Implementation:

android {
    buildTypes {
        debug {
            minifyEnabled true
            useProguard false
        }
        release {
            minifyEnabled true
            useProguard true
        }
    }
}

Caveats:

  • Currently supports shrinking only
  • Configured per-variant: Use ProGuard for release (obfuscation, optimizations)
  • Will allow single pass legacy multi-dex
  • Coming in preview 2 (hopefully).

App Deployment

Improved deployment speed

Built specifically for the connected device during debug builds. (Android Studio only!)

Only package one set of assets (e.g. mdpi only)

Currently, only density is supported. Only package one set of assets (e.g. mdpi only).

Later:

  • ABIs
  • Multi-Dex

Install time for IO Schedule (5MB) on N6 was 12 seconds.

Instant Run

  • Deploy deltas only
  • avoid installation
  • no app killing

Scenarios:

  • Hot swap
  • Warm swap
  • Cold swap
  • Rebuild & Reinstall

Notes:

  • Gradle behaves differently when run from Studio
    • -Pandroid.optional.compilation=INSTANT_DEV
  • Create Instant Run specific tasks.
  • Prepare APK for Instant Run
    • Bytecode instrumentation
    • Server inside app for IDE to talk to

Here’s a quick overview of what a build looks like with Instant Run turned on:

clean build

App Running with Server

  • IDE Checks
    • Is Gradle 2.0?
    • Do build IDs match?
  • Studio -> App connection
    • Is app running?
    • Is activity in foreground?
  • Custom Gradle run
    • Build deltas through a custom task
    • Runs a verifier to ensure that we can do HotSwap
    • Tells Studio
  • Optimization: Studio monitors file changes
    • Tells gradle to avoid no-op module builds
    • -Pandroid.optional.compilation=INSTANT_DEV,LOCAL_JAVA_ONLY

Hot swap: Resources

  • Send full resources (for now)
  • reflection hacks to make it work on the fly
  • incremental aapt on the way

Limitations

  • manifest changes detected by Studio, triggers full build
  • any ID changes, including values trigger a full/new build

Cold Swap

  • incompatible changes
    • require restarting the app, and full build/install
    • but should still trigger the delta update
  • working on it, demo soon

Instant Run and Build

Simple UI

  • Run
  • Instant Run
  • Debug
  • Instant Debug

Very easy:

  • Just press run
  • also have a stop button
  • optionally restart activity

Need to turn things on in the Instant Run prefs.

Data binding is broken in the preview build, working in nightlies.

Emulator

Performance - Faster deployment of apks

Features

New UI for lots of tools

  • Mock GPS locations
  • Mock different cellular network types
  • Fingerprint
  • Rotate
  • Resize

Notification Best Practices

On November 23 & 24, I attended the first ever Android Dev Summit. The following are notes that I took during talks. I have included the video of the talk as well. Again, these are my notes from the talk, not my original content.

Not what you can do, as a developer, but what are the devices for? They’re for people.

Respecting User Attention

  • Don’t annoy the user
  • Respect them
  • Empower them
  • Delight them
  • Connect them to the people that they care about

API Levels

Discussed in this post will be the following:

  • Lollipop
  • KitKat
  • Jelly Bean
  • All

Don’t annoy the user

Users can block notifications

You probably don’t want your app to get banned by the user. The system won’t tell you that you’ve been banned. The user can also uninstall you. Don’t push the user to this point.

bad notifications

Do Not Disturb Mode

I personally have this on all the time.

Notification Categories

As a developer, you can specify what type of notification you are. Are you an alarm, a message, or are you annoying garbage? (Sorry, but you know who you are.)

  • CATEGORY_ALARM
    • user requested alarms or timers
  • CATEGORY_REMINDER
    • user requested reminder
  • CATEGORY_EVENT
    • an event on the user’s calendar
  • CATEGORY_MESSAGE
    • an incoming direct message, not asynchronous messages
    • e.g. SMS, IM, text messages sent directly to the user
  • CATEGORY_CALL
    • an incoming voice or video call
  • CATEGORY_EMAIL
    • an incoming asynchronous message
    • e.g. email
  • CATEGORY_SOCIAL
    • an update from a social network
  • CATEGORY_RECOMMENDATION
    • a specific, timely recommendation for a single item

Respect them

No BuzzFeed type notifications, “You’ll never believe…”.

Priority

Users are more receptive to notifications that they get a the right time. For low priority notifications, that may be when the user has a few minutes of downtime. Min priority notifications are not shown in the status bar. Messages are maybe more important, users may want those immediately.

(Jelly Bean +)

Notification.compat.PRIORITY_LOW
  • PRIORITY_MAX
    • time-critical tasks
    • incoming calls, turn-by-turn directions
  • PRIORITY_HIGH
    • important communications
    • chats, texts, important emails
  • PRIORITY_LOW
    • not time sensitive
    • social broadcasts
  • PRIORITY_MIN
    • contextual or background information
    • recommendations, weather

Setting the priority:

new NotificationCompat.Builder(this)
    .setSmallIcon(...)
    .setContentTitle(title)
    .setContentText(text)
    .setPriority(
        NotificationCompat.PRIORITY_LOW)
    .build();

Fresh notifications

There are situations that you want to kick off a single notification, and alert the user only once. Timely information like weather, or a traffic incident.

NotificationCompat.Builder builder =
    new NotificationCompat.Builder(this)
        .setOnlyAlertOnce(true)
        .setProgress(100, 100, false);
mNoMan.notify(ID, builder.build());

Stale notifications

Are all the notifications from the app useful? Make sure to cancel stale notifications. When you create the notification, you can set a cancellation policy.

Fullscreen & Peek

These are very disruptive experiences for users. In Lollipop, there’s now a ‘peek’ type of notification that allows apps to alert the user without interrupting them as much. This happens when the user has the screen on, and is interacting with their phone. This only works for PRIORITY_HIGH and PRIORITY_MAX, and when the notification is making noise or vibrating. Only use this if you mean to annoy the user.

Empowering the user

Notifications should be actionable.

  • The squash is ripe
    • Works well, user can go harvest squash
  • Buy power-up
    • Didn’t work, user wasn’t there, bad

Notification Preferences

(Lollipop+)

Long-press the notification, click the ‘gear’, go in and manage app’s preferences.

Add intent-filter in manifest to provide your own.

<activity android:name="SettingsActivity"
          android:label="@string/app_name">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.NOTIFICATION_PREFERENCES"/>
    </intent-filter>
</activity>

Allows for fine-grained control of preferences in your app.

Actions

(JB+)

Add up to 3 buttons can be used with any notification. Get things done in the notification.

Uses:

  1. Visit a different activity than the click handler (contentIntent)
  2. Take some action in the background

Protip: To show that an action has been taken, set the intent to null and re-notify().

E.g.

  • call notification actions
    • answer
    • decline
  • image notification actions
    • view
    • share

Styles

(JB+)

Rich media notifications shade. You can have the photo in the notification. This looks nice, and gives the user lots of info.

Global Dismissal

(All)

Sync dismissal of notifications across devices. Requires GCM.

Sending the dismiss action over GCM:

protected void onHandleIntent(Intent i) {
    if (DISMISS.equals(i.getAction())) {
        int id = i.getIntExtra("nid", 0);
        Bundle data = new Bundle();
        data.putInt("nid", nid);
        String msgId = ...;
        gcm.send(SENDER_ID, msgId, data);
    }
}

Handling the action on another device after the message has been received by GCM:

public void onMessageReceived(String from, Bundle data) {
    if (from.startsWith("/post/")) {
        int id = data.getInt("nid");
        n.setDeleteIntent(makeDI(id));
        mNoMan.notify(id, n.build());
    } else if (from.startsWith("/cancel/")) {
        int id = data.getInt("nid");
        mNoMan.cancel(id);
    }
}
flow of notification global dismissal

Ongoing notifications

Not able to dismiss these.

Disruptive work-flows. Annoying to users. Users may be unpredictable, and may not use the phone in the same way as you. (Trust me, if you do this wrong, users will yell loudly at you.) Use them rarely, if at all.

Use if:

  • startForeground()
  • incoming call
  • prefer snooze-and-repost pattern

Always give the user a way to dismiss:

  • pause/stop/cancel
  • consider LOW or MIN priority

(Using a HIGH+ priority on ongoing notifications will be bad for users, and they’ll get very annoyed.)

Delight the user

(All)

Sound. Users love ringtones. Allow users to set custom sounds for different notifications. You get this pretty much for free with RingtonePreference, which will get you a URI.

Connect people

People connect primarily through their phones.

Downtime & Important People

In Priority mode, you can set calls and messages to notify you for custom contacts. (E.g. starred contacts.)

If you give the system enough information, you can make the cut of apps that can notify the user in Priority Mode, for the contacts that they care about.

Why?

  • User knows who is important
  • App knows who the notification is about
  • System uses this to rank and filter

How?

Annotate notifications with:

  • Contact.CONTENT_LOOKUP_API
  • tel:
  • mailto:
  • Don’t need Contacts Permission!

Here’s how it might look:

new NotificationCompat.Builder(this)
    .setSmallIcon(...)
    .setContentTitle(title)
    .setContentText(text)
    .addAction(...)
    .addPerson(Uri.fromParts("tel",
        "1 (617) 555-1212", null).toString())
    .build();

Vist to Y Combinator offices

YC Y

I recently paid a visit to the Y Combinator offices with some fellow Google Experts. Michael Seibel talked about the YC philosophy, and he answered questions from the group. His thoughts on international startups were particularly interesting. The general idea was that international startups can and should focus on their markets, which may be plenty big enough, but that they should probably beware of local investors (with the exception of China). Valley investors will probably give better terms and valuations (again, with the exception of China) than local investors. He also thought that for many international companies, spending 3 months in Silicon Valley to go through the Y Combinator program was probably worthwhile.

Michael Seibel YC Wall Google Experts at YC