01 Dec 2015, 06:25

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!

30 Nov 2015, 07:00

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.

25 Nov 2015, 07:30

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

25 Nov 2015, 07:06

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();

22 Nov 2015, 13:43

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

20 Nov 2015, 09:11

Content promotion on social media

I didn’t come up with the ideas here, these are my notes from a talk on lessons learned in how to do social media by Google’s social media team.

Social media is a way for you to deliver your content to your audience. It’s a way for you to find your audience and for your audience to find you. It allows you to have a conversation with your audience. It allows you to learn about what your audience likes, and what they want more of. This can help you to produce better content that they will like more. It’s also a way to build a community with your audience around your content, and shared interests.

General lessons

Questions to ask for each post:

  • Is this engaging?
  • Is there a call to action?
  • Will this foster and facilitate conversations?

Just like everywhere else, it’s a good idea to try out different things to see what works and what doesn’t, and iterate on the stuff that does work.

Other general learnings

  • Posting scheduling and consistency
  • Comment/reply management
  • Re-sharing/influencer engagement

Think about the split between native on the platform and off behind a link. Images, GIFs, and videos may work much better natively.

G+

Targeted & Technical

  • Long-form content including small code snippets
  • Video embed
  • Photos, GIFs, albums

Think about how your links will populate on G+.

  • Does the image get pulled through?
  • If it looks good, then use it
  • If it doesn’t, maybe use a photo, and add link in the post

Twitter

Quick & engaging

  • GIFs to drive brand awareness or accompanied by a link to more Content
  • Photos or memes
  • Live blogging
  • YouTube links no longer populate a video
  • Use a GIF of the video
  • Make sure to have room in the tweet for an image and link

Using Twitter to promote long-form content elsewhere is a great way to engage people on both platforms.

Facebook

Potential growth… with investment

  • Native video upload
  • Incremental reach

Pay-to-play, need to spend money to get views. Native uploads are more engaging on Facebook.

Organic growth on Facebook

Video & call to action (ask to like the page) will help to grow organically.

Blogs

Good for in-depth content, maybe less personal, whereas the social platforms may be better for a more personal, conversational. Depending on what you are doing. If this is a personal blog, then it should be personal. If it is a corporate blog, then maybe it’s more of the official corporate channel, and the voice should be more of a corporate voice.

Medium

  • Forum for a more opinionated, personalized voice
  • Emerging space for written long-form content

More personal, and conversational, while still allowing for long-form in-depth content.

“If you are persistent, you will get it. If you are consistent, you will keep it.”

Engagement

It is worth engaging with your audience as much as possible in the comments. Obviously, ignore the trolls, and the comments that don’t add value. If you can’t say anything that would add value to the current conversation, then don’t, leave it there. Engage with the comments that provide good feedback, positive or negative, and contribute to the conversation.

It’s also worth re-sharing other people’s content that you think is good. Just don’t go overboard with the re-shares.

Tracking and measuring

Doing social media is throwing spaghetti at the wall. Tracking and measuring is the only way that you know when something’s working, especially when you’re trying a bunch of random things.

  • Outline objectives up front
  • Identify potential data points to prove impact
  • Think of ways to make the data meaningful (e.g. Q/Q growth)

Know your audience. Pay attention to what they’re engaging with and do more of it. If you’re really unsure, ask. This is where tracking can come in handy to help growth. Social media is not just about reach. It’s about thoughtfully engaging.

Funnel

  • Reach - how many people saw our content?
    • followers
    • impressions
    • page views
    • modeled reach
  • Engagement - how many people interacted with the content?
    • types of engagements
    • engagement rates
  • Sentiment - did the content have positive brand impact? May not really be part of the funnel, since it’s so hard to measure.
    • positive
    • negative
    • neutral
  • Conversions - SDK downloads docs visits
    • GA - referrers

16 Nov 2015, 07:46

Managing Finances (Android Intents Part 5)

This is the fifth post in a series about sharing information between apps. (Series Table of Contents.) In this post, we’re going to create another demo of an Intents based API.

Series Table of Contents

Follow the series on the Table of Contents page.

Finance Management App

Let’s imagine a scenario, Mohammed has built an investing app. It allows users to buy stocks and build up a portfolio. His app is only available on smartphones. There’s no web version of the app, and no public-facing APIs available from our servers.

Now, let’s imagine that his friend Susan is building an Android app to help people manage their finances. She wants to give her users the most complete picture that she can. She already has bank and credit card integrations done, and now she’s looking to build a view of users’ investments. She knows that a lot of her customers are also customers of ours. She asks Mohammed if he can open up an API for her.

All she really needs is a current dollar value of the investments, along with the amount of cash invested. Mohammed decides that he can open up an intent API for her in our app.

For demo purposes, I’m not actually going to build out a stock app, I’m just going to build something that handles the Intent request, and returns a response.

Mohammed wants to have some amount of security, so he comes up with a scheme for calculating a client id and using a shared signature, to try to prevent random, unauthenticated apps from requesting data. Here is what the request looks like:

private void requestStockInfo() {
    Intent intent = new Intent(STOCKY_RECEIVER_ACTION);
    // calculate client_id, based on known algorithm
    intent.putExtra(CLIENT_ID, Hasher.hashIt("stocky_app" + getPackageName()));
    intent.putExtra(CLIENT_SECRET, CLIENT_SECRET);
    startActivityForResult(intent, REQUEST_CODE);
}

Disclaimer: Please do not actually do this with a financial app unless you consult with a security expert first. I would assume that this could be made to be safe, but I am not a security expert. The point is that there are ways of adding layers of security.

finance demo 1

Activity before request to Stocky

Within Stocky, Mohammed’s stock buying app, the intent is handled, checked and result returned:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    Intent intent = getIntent();
    String caller = Hasher.hashIt("stocky_app" + getCallingPackage());
    Intent sIntent = new Intent();

    if (Constants.STOCKY_RECEIVER_ACTION.equals(intent.getAction())
            && intent.hasExtra(Constants.CLIENT_ID)
            && intent.hasExtra(Constants.CLIENT_SECRET)) {

        Bundle extras = intent.getExtras();
        String clientId = extras.getString(Constants.CLIENT_ID);

        if (!caller.equals(clientId)) {
            setResult(RESULT_CANCELED, sIntent);
            finish();
            return;
        }

        // hardcoded information, I'm not building a full app here
        sIntent.putExtra(Constants.VALUE, 9830.00);
        sIntent.putExtra(Constants.INVESTED, 10000.00);
        setResult(RESULT_OK, sIntent);
    } else {
        setResult(RESULT_CANCELED, sIntent);
    }

    finish();
}

Then, back in Susan’s finance app, she handles the result:

@Override
public void onActivityResult( int requestCode, int resultCode, Intent data ) {
    if (RESULT_OK != resultCode)
        return;

    Bundle extras = data.getExtras();

    double value = 0L;
    double invested = 0L;
    if (extras.containsKey("VALUE"))
        value = extras.getDouble("VALUE");
    if (extras.containsKey("INVESTED"))
        invested = extras.getDouble("INVESTED");
    double gains = value - invested;
    // do something with the data
    stockInfo(value, invested, gains);
}

finance demo 2

Activity after result from Stocky

That’s it! There is a fully working demo in our repository. ( git checkout -b step8 step8 )

Conclusion

Today, we looked at another example of using public APIs on the phone. We thought through a new example of something that might be useful to do, and built a demo of that.

I have some ideas for the next few posts, but I don’t want to spoil the surprise (which is to say, I have no idea how I’m going to write up the next few posts).

Keep an eye on the Table of Contents for the latest entries in the series.

16 Nov 2015, 06:13

Editing Images (Android Intents Part 4)

This is the fourth post in a series about sharing information between apps. (Series Table of Contents.) In this post, we’re going to look at an example of an app that provides a public API. Our Aviary example offers a solid example of why this sort of thing might be beneficial, so let’s get right into it!

Series Table of Contents

Follow the series on the Table of Contents page.

Aviary

Aviary (acquired by Adobe not too long ago) is a photo editing app on Android that provides an SDK to allow developers to use Aviary’s image editing tools within their app. The SDK works on Intents being fired at some Aviary provided Activities that you’re supposed to include in your app. This means that you’ve got to bundle Aviary’s SDK into your app, and add UI components which may complicate things for you (especially when you want to change compatibility libraries).

Luckily, Aviary also provides an external Intents API that works as long as you’ve got the Aviary app installed. The external API is really nice because you don’t even need credentials to send images to Aviary to be edited.

While the external API is fairly simple. The example app is going to be sending an Intent and expecting a response. This is a bit different from what we’ve seen before.

Aviary’s documentation actually gives a lot of information about how to do this, so I’ll just give highlights, and a link to the demo repository at this stage.

Our example uses a file that I’ve included in our resources:

String destFileName = "startup_unicorn.jpg";
File file = resToFile(R.drawable.startup_unicorn, destFileName);
log.d("image file: %s", file.getPath());
Uri uri = Uri.fromFile(file);

Once we have our file available in external storage, we can fire off our Intent:

Intent newIntent = new Intent( "aviary.intent.action.EDIT" );
newIntent.setDataAndType(uri, "image/*");
newIntent.putExtra("app-id", getPackageName());
startActivityForResult( newIntent, 1 );

That should send us off to Aviary, and wen we come back, we will need to handle the response:

@Override
public void onActivityResult( int requestCode, int resultCode, Intent data ) {
    if( resultCode != RESULT_CANCELED && requestCode == 1) {
        Bundle extras = data.getExtras();
        Uri imageUri = data.getData();
        File outFile = new File(getExternalFilesDir(null), "startup_unicorn_edited.jpg");
        if (null != extras) {
            if (extras.getBoolean("bitmap-changed"))
                saveFile(imageUri, outFile);
        }
    }
}

Now, we can see our file if we navigate to our demo app’s cache directory, shown in the image below:

aviary edited image

To see a fully working demo of this, check out step 7 in the demo repository. ( git checkout -b step7 step7 )

Conclusion

Today, we looked an example of using public APIs on the phone. We reviewed a real-world, working example with Aviary. Keep an eye on the Table of Contents for the latest entries in the series.

09 Nov 2015, 23:05

Make your phone talk (Android Intents Part 3)

This is the third post in a series about sharing information between apps. (Part 1 is here, and part 2 is here.) In this post, we’re going to create a public API that allows other apps to use our app to speak something out loud.

Series Table of Contents

Follow the series on the Table of Contents page.

Text to Speech Example

Back to looking at code! Yay!

We want to provide to other apps the ability to fire an intent at us, with some text, that will cause us to speak out the text passed in.

This example uses the text-to-speech API provided by Android. Since we’re discussing intents, and public app APIs, I’m actually going to skip the implementation of this. It doesn’t have much to do with the rest of the discussion, and actually, the ability to skip these sorts of implementation details is exactly what we’re after. That said, all of the code is on GitHub, if you’re interested. There isn’t a whole lot needed to do this.

Here’s what we will look at, providing a public API, and consuming that API.

Provider

On the provider side, it’s very similar to what we have in our BroadcastReceiver example from Part 1. We’ll start with the snip from the AndroidManifest.xml:

<receiver android:name=".say.TtsReceiver" >
    <intent-filter>
        <action android:name="io.ejf.intentexamples.say" >
        </action>
    </intent-filter>
</receiver>

This is our BroadcastReceiver.onReceive implementation:

@Override
public void onReceive(Context context, Intent intent) {
    Intent sIntent = new Intent(context, TtsService.class);

    if (!intent.hasExtra("SAY_TEXT"))
        return;

    sIntent.putExtra("SAY_TEXT", intent.getStringExtra("SAY_TEXT"));
    context.startService(sIntent);
}

Notice that we’re starting a service, as opposed to trying to run the text-to-speech code in the BroadcastReceiver. The reason for that is that there are some asynchronous components of Android’s text-to-speech implementation, and the BroadcastReceiver will die as soon as onReceive is finished executing.

In our Service, we have something like the following:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    if (!intent.hasExtra("SAY_TEXT"))
        return;
    if (null == textToSpeech)
        return;

    String sayText = intent.getStringExtra("SAY_TEXT");
    textToSpeech.speak(sayText, TextToSpeech.QUEUE_ADD, null, null);

    return super.onStartCommand(intent, flags, startId);
}

The above is a slightly modified version of what’s in the project. I skipped how the textToSpeech object gets instantiated.

Consumer

All we need to do to consume this API is fire off an intent:

Intent intent = new Intent();
intent.setAction("io.ejf.intentexamples.say");
intent.putExtra("SAY_TEXT", "Some string to speak");
sendBroadcast(intent);

The above works from apps other than our demo. This is a public API on the phone. In the example code on GitHub, I actually added the above to a separate app, called “SayLauncher”:

SayLauncher

Check out this tag on GitHub!

(Quick side-note here, if you want to check out the repository at this step, first check out the repo, then run the following command: git checkout -b step5 step5. That will get you a branch at the step1 tag.)

Sharing from any app

What we’re doing is a special case of the public APIs discussion, because there’s already a way of doing this that many apps know about, and use. That is generic text sharing. One note here is that text is generally shared to an Activity, but that’s not a big deal for us, we can simply implement a dummy Activity, forward the Intent, then kill the Activity. Let’s take a look.

First, implement the Activity in the AndroidManifest:

<activity android:name=".say.TtsDummyActivity" >
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="text/plain" />
    </intent-filter>
</activity>

Now, in the TtsDummyActivity:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    Intent intent = getIntent();
    String action = intent.getAction();
    String type = intent.getType();

    if (Intent.ACTION_SEND.equals(action) &&  "text/plain".equals(type)) {
        Intent sIntent = new Intent(this, TtsService.class);
        sIntent.putExtra(Constants.SAY_TEXT, intent.getStringExtra(Intent.EXTRA_TEXT));
        startService(sIntent);
    } else {
        Log.w(TAG, "received unknown intent");
    }

    finish();
}

share via screen

Whenever possible, it’s a good idea to use the popular way of providing APIs to other apps. That way, you don’t need specific implementations, and you can rely on the fact that lots of apps are going to be using these mechanisms. Sometimes, it might make sense to offer two ways of doing something, like we did in this example. That way, if there is an app that wants to integrate directly, because they want exactly the behavior that you provide, then they can do that.

Check out this tag on GitHub! ( git checkout -b step6 step6 )

Conclusion

We got a lot done here today. First, we finally got around to discussing why we’re bothering with this whole exercise. We discussed a real-world example of how this sort of thing is helpful to all parties, users, API providers, and API consumers. Then, we went through a concrete example and demonstrated a fun way to both produce and consume a public API on the phone.

Next time, we’ll start thinking of some other applications for APIs on the phone, and we’ll pick out another example to work through.

09 Nov 2015, 22:59

Why do we want public APIs? (Android Intents Part 2)

This is the second post in a series about sharing information between apps. (Part 1 is here.) In this post, we’re going to discuss why it might be nice to have a public API exposed from a third party app. I’ll be providing videos that cover the text contents of these posts (plus insightful things), so if you want to watch instead of read, check out the video below:

If you watched the video, feel free to skip to Part 3, where we’ll look at some code. This post is simply a text version of the video.

Series Table of Contents

Follow the series on the Table of Contents page.

Why do we want a public API on the phone?

There are three perspectives here, the user is one, and then the producing developer, and consuming developer are the other two. This isn’t as complicated as it sounds.

I’m going to put this in terms of a couple of concrete apps, so that we have some apps that we can look at, and think about, as opposed to talking so much in the abstract. We’ll talk about MyFitnessPal as the producing developer, offering the API. Strava and Withings will be the consuming developers, they consume MyFitnessPal’s APIs.

This discussion may seem kind of long, but it is the central idea of this entire series. I promise we’ll get back to code soon. Again, if you watch the video, you can skip straight to Part 3, because all of the long text part is covered in the video.

I’d also like to mention up front that these apps already do integrate with each other, but they do it via the web, not on the phone. I’ll discuss the difference in the ‘user’ section. There’s also Google Fit, which intends to take care of this particular issue, and IIRC provides some useful integrations on the device side. In short, this particular example is almost completely solved already, but it’s an easy one to think about, so we’re going to keep it.

Producing Developer

MyFitnessPal sketch

MyFitnessPal, lot’s of typing!

Imagine that you’re MyFitnessPal, and an app that does calorie counting. MyFitnessPal provides users a way to enter their food and exercise and your app tells them how many calories they’ve consumed and how many they’ve burned. Now, you care about gathering fitness data from users, but your main method of getting that data is asking users to enter it manually. Then, along comes Strava, they do automatic tracking for bicycle riding and running. If your user is using both Strava and MyFitnessPal, you could open up an API for Strava (and any other activity tracking app) to send data to MyFitnessPal, so that users don’t have to manually input something that was already captured automatically for them.

Withings sketch

Withings, works just like an ordinary scale, but it syncs your weight to its service

It’s the same deal with Withings, they provide some passive tracking hardware, like a digital scale that sends your current weight to their app, an activity tracking watch, as well as some other products. You, as MyFitnessPal, could open another API that would allow Withings to sync up the user’s weight with you. Withings could use the same APIs for exercise tracking that you opened up for Strava.

The apparent downside is that users will no longer need to enter your app to manually enter this data. However, by offering an API for other apps to consume, you’re actually making both your app and the other app more powerful. Now the user needs to do less work, and they’re going to appreciate that they get stuff for free (free data entry, which is a pain to do on your phone). When you start offering APIs that other apps can consume, users are going to like you more because make the their life easier.

Consuming Developer

Strava sketch

Strava doesn’t require much typing, it records your session.

Now, imagine you’re Strava, you do a great job tracking users’ runs and bike rides, but your users are asking for food tracking as well. That’s a difficult problem, and not really your core competency. It’s something that you really don’t want to do yourself. Luckily, you know that MyFitnessPal does food tracking really well, and they also do exercise tracking. What’s more, they offer an API that allows apps like Strava to send them activity data. All you need to do is implement an API, and now you can tell users to install MyFitnessPal when they ask about food tracking. The best part is that it’s automatic and in the background, users don’t need to think about it, and you’ve done almost no work. It’s a win-win!

User

Imagine that you’re a user that wants to count calories, track your weight, and goes running. MyFitnessPal, Withings, and Strava all work together to allow you to do most of this tacking passively, cutting down the amount of manual data entry you have to do, and saving you time. If there’s a run tracking app that you like, but doesn’t integrate with MyFitnessPal, is that extra friction worth it, or are you going to switch over to Strava?

As I mentioned before, these apps already do integrate with one another, and it’s not exclusive to the listed apps, most of the worthwhile fitness apps I’ve come across integrate with each other. The one big downside is that they integrate in the backend, not on the phone. What this means is that users need to sign into different services multiple times on their phones, or set up those integrations on their computers. This is friction. Why not just do it seamlessly on the phone? Is there a reason to even ask the user to log in, when they’re already logged in on the phone in the other app?

Instead of integrating on the backend, it seems possible to integrate on the frontend. The apps just fire intents at each other, and allow the providing app to do the auth in their app. This could be as simple as popping a dialog and asking the user if it’s OK if the other app send it data. What’s more, it means that you don’t need an internet connection for the apps to sync with each other (either via intents, or maybe a combination of intents and content providers). Currently, these services sync up on their own schedule, and it can cause users to manually enter information that will be automatically synced later. Friction.

Integration sketch

The above is a sketch of Withings and Strava talking to MyFitnessPal, if you couldn’t tell. =)

Conclusion

In this post I explained why we’re bothering with this whole exercise. We discussed a real-world example of how this sort of thing is helpful to all parties, users, API providers, and API consumers. Hopefully this has given you some perspective on what this series is all about, and where we might want to go from here. Incidentally, if you have ideas for this series, please feel free to reach out.

Next time, we’ll look at an example that demonstrates a fun way to both produce and consume a public API on the phone.