Suggestions for Wearables and Automation
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:
Android Testing with @onlythoughtwork @ppvi @jfschmakeit #AndroidDevSummit #Sketchnotes pic.twitter.com/w30eLkXZvm
— Chiu-Ki Chan (@chiuki) November 24, 2015
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
Flaky tests are worse than no tests. (When it fails sometimes.)
Steps:
- Isolate from external dependencies
- Especially the network
- Replace the components that talk to external dependencies, with things that replace with fake data
- Using product flavor ‘mockDebug’, with source sets
- 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
Unit testing
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
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
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.
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:
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.
How do I enable App Indexing for my app?
- You have a website with content
- Verified website’s domain in the Google Search Console
- Have an app that opens content that is the same as your web content
- Implement an intent-filter for your site in your app
- Clicking links for your website opens the content in your app
- AppIndexing API called whenever content in app is loaded, or the user leaves the app
- Publish app
- Add the android-app://
version to your Google Search Console, and associate it with your website! - 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:
Support library by @chrisbanes and @adamwp #AndroidDevSummit #Sketchnotes pic.twitter.com/l3LurcYMeA
— Chiu-Ki Chan (@chiuki) November 23, 2015
- 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
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:
Building Public APIs on the Phone with intents by @emil10001 #gdgsv #devfest #Sketchnotes https://t.co/Bkd8DuGBEU pic.twitter.com/jUMSZaf4dO
— Chiu-Ki Chan (@chiuki) November 21, 2015
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:
The Android Build System by @droidxav @benol @dochez #AndroidDevSummit #Sketchnotes pic.twitter.com/ppZAfh5J3l
— Chiu-Ki Chan (@chiuki) November 24, 2015
Tips
- Use the daemon
--daemon
ororg.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:
- 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
.
It’s a mess!
Solution: Transform API
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.
$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 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:
What's in New Android Studio by Jamal Eason & @droidxav & @tornorbye #AndroidDevSummit #Sketchnotes pic.twitter.com/4Bo1Ma9XpB
— Chiu-Ki Chan (@chiuki) November 24, 2015
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:
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.
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:
- Visit a different activity than the click handler (
contentIntent
) - 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);
}
}
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
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.