Tutorials  Android Things
ATHome

Android Things Development

When Googles IoT platform, Android Things was launched, it caused some rethinking for time-served Android developers. 

This was necessary in order to adapt to all the anticipated hardware applications it will be used for, and it meant many familar features had to be removed. The most radical of these was the default UI, since the UI is now optional. This doesn't mean there isn't one, it just means you have to roll your own entirely. Of course you do this using the standard Android View framework you're used to in its entirety, but by default, the system supplies nothing. That means there's no status bar, no navigation bar, no back button etc - and your device has no home screen.

The idea with Android Things is you're creating a hardware applicance. Typically, this runs only one app and it's this app which starts up when your device is booted, That's the production scenario which is the one your end user expects. For development, this can be a little restrictive. You can certainly install many different apps onto Android Things devices, and developers often do this to try multiple versions of their apps, run small self-contained test apps or measure performance variants of the same one, for example. This is cumbersome without a home screen or some kind of launcher, because each has to be built and installed fresh so it "wins" in the boot race.

ATHome

I decided I'd gone down this road once too many times, and since necessity is the mother of invention I wrote a tiny launcher to fix it. It's intended to be the only app started at boot whilst you are developing. It acts as a home screen and you can still attach the debugger to the apps it starts from Android Studio, or indeed just start your app from it and continue to debug as before - it won't clobber your main workflow. It's intentionally small and basic - just the thing for Android Things ;-)

The project is on GitHub - please feel free to submit pull requests.

ATHome screenshot 700x420

Since this is only used whilst developing, there are a couple of rules you should follow. You need to ensure ATHome is the default launch app for the target, which is done in its AndroidManifest.xml:


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

    <!-- Launch activity automatically on boot -->
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.IOT_LAUNCHER"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>
 

You'll also want to comment out the section to launch automatically on boot in your other apps. This is only whilst developing them, and as always you can directly launch them via adb with no change to how you've been working normally anyway.

To keep things as small and simple as possible, this app is nothing more than RecyclerView filled with calls to the Android Package manager to populate it's contents, each of which has an OnClickListener primed to start the Activity of the target app. The fun begins when that app has no top level BACK control! Remember, for regular Android coding, you're not supposed to code one on screen because it's guaranteed the system will provide one - but we're not in Kansas anymore. Your options are

  • code some temporary method intended to be removed for production, which could even be just a gesture with no visual UI
  • reset the device
  • when debugging use the command "adb shell input keyevent 4" aimed at the device

App.java: A simple class to hold the Apps details is defined:


class App {
    private String packageName;
    private String name;
    private Drawable icon;

    String getPackageName() {
        return packageName;
    }

    void setPackageName(String packageName) {
        this.packageName = packageName;
    }

    String getName() {
        return name;
    }

    void setName(String name) {
        this.name = name;
    }

    public Drawable getIcon() {
        return icon;
    }

    public void setIcon(Drawable icon) {
        this.icon = icon;
    }
}
 

AppAdapter.java: An adapter is created in which a ViewHolder has its onClickListener set to launch the target Activity


static class AppViewHolder extends RecyclerView.ViewHolder {
  final ImageView image;
  final TextView appName;
  final TextView packageName;

  AppViewHolder(final View itemView) {
	super(itemView);
	image = itemView.findViewById(R.id.app_icon_id);
	appName = itemView.findViewById(R.id.text_app_name_id);
	packageName = itemView.findViewById(R.id.text_package_name_id);

    itemView.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View view) { Log.d(TAG, "Package: " + packageName.getText()); Intent LaunchIntent = itemView.getContext().getPackageManager().getLaunchIntentForPackage(packageName.getText().toString()); itemView.getContext().startActivity( LaunchIntent ); } }); } }
 

MainActivity.java: The Apps are read into a List ready for binding to the adapter. System Apps can be filtered out.

private List getAllApplications(Context context, boolean includeSystemApps) {
  PackageManager packageManager = context.getPackageManager();
  List packages = packageManager.getInstalledPackages(0);

  List installedApps = new ArrayList<>();

  for (PackageInfo pkgInfo : packages) {
    if (pkgInfo.versionName == null) {
      continue;
    }

    App newApp = new App();
    boolean isSystemApp = ((pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);

    newApp.setPackageName(pkgInfo.packageName);
    newApp.setName(pkgInfo.applicationInfo.loadLabel(packageManager).toString());
    newApp.setIcon(pkgInfo.applicationInfo.loadIcon(packageManager));

    if (includeSystemApps || !isSystemApp) {
      installedApps.add(newApp);
    }
  }

  return installedApps;
}