Archive

Android

This one took a while. Weighing in at 38 versions and I-don’t-know-how-many reflected and hacked APIs, Dashboard is now open-source for all to see. This was probably the app that took the most development time until now (still on-going!), and I think the one I’m most proud of.

I’m liking the idea of doing future releases via self-approved pull requests. Could be interesting!

>>> Dashboard source code <<< 

A few days ago, an interesting idea came up in the PebbleDev Slack channel: could a library make it easier for Pebble developers to integrate their apps with Android APIs, but save the pain of each and every one of them needing to publish an app with PebbleKit Android? This would be similar to how Dashboard operates, but by sharing the API access to other apps that are installed.

Turns out, it can! In one of my signature coding bursts I set to work, and prototyped a system that did just this. The result is the Dash API. With it, you can finally (and easily) provide one of the most common watchface widget requests – things like the phone’s battery level, or connectivity status.

How does it work?

The Dash API (named after Dashboard) Pebble package uses AppMessage to send requests to read data (such as WiFi network name, or phone battery level), or write to an API (such as turning off WiFi). Using a unique key to recognise Dash API messages, the Android app can respond to these by reading the data or manipulating the Android API and returning a response code. All the developer needs to do is instruct their users to install my Dash API Android app, and then their app (and any others that use the Dash API) can take advantage of the APIs presented through the library. This approach makes the Dash API an install once, use in many apps service.

The upshot of all this is that a C developer making a watchface that wants to show the phone battery level (such as the demo app Dual Gauge) need only use the Pebble package and not write a single line of Java, let alone go to the trouble of publishing an Android app on Google Play.

But how can one companion app target them all, without knowing they exist?

Luckily, you can extract the UUID of an app that sends an AppMessage packet to the phone from the Intent object broadcast from the Pebble Android app. Most (hopefully app) companion apps that manually register a BroadcastReceiver do a ‘good citizen’ UUID check to make sure they only respond to messages from their corresponding watchapp. This data can be used to simply redirect an incoming message’s result right back at it, without the need to create a PebbleKit Android app with baked in UUID for each individual app.

So how can I use it?

Simple. As explained in the GitHub README.md file, a C developer should first install the package:


$ pebble package install pebble-dash-api

Next, include the single library file and call the initialiser when your app is starting, supplying the app’s name (for presentation in the Android app) and an error callback for receiving any errors that may occur:

#include <pebble-dash-api/pebble-dash-api.h>;
#include <pebble-events/pebble-events.h>

#define APP_NAME "My App"

static void error_callback(ErrorCode code) {
  // Receive error codes here
}

static void init() {
  dash_api_init(APP_NAME, error_callback);
  events_app_message_open();

  /* Other init code */
}

Next, check the Android app is available and up to date:

dash_api_check_is_available();

The result will be passed to your error_callback. Once you get ErrorCodeSuccess, it is safe to start making queries, such as getting the battery level, etc. Code examples are included in the GitHub README.md file.

What else do I need to know?

As of 1.1 (released today), apps that read data and API states can do so invisibly. However, apps that wish to write to an API (such as turning off WiFi) will cause a notification to appear that will prompt the user to grant it access within the Dash API app. Once this is done, operation will continue without further intervention, unless permission is revoked. This should hopefully prevent any rogue apps messing with the phone, and give users visibility into which Pebble apps they have used are using the Dash API.

Links

GitHub Repo

Dash API Android app (direct your users here!)

NPM Package

Example App (Dual Gauge)

What’s next?

In the future, I’d like to expand the capabilities offered by the Dash API to other popular widget requests, things such as unread SMS count and next Calendar appointment. Perhaps for another day – coordinating GitHub repos, NPM packages, Android apps, and Pebble apps is quite tiring!

That’s right, the latest and greatest update to Dashboard is now live (pending Google Play deployment delay). The new features are (in descending order of number of requests received):

  • A choice of Find Phone sound from system notification sounds. While these are guaranteed to be available, free-form choice of file proved too tricky to handle (see Android file manage content:// URI problems discussed elsewhere).
  • An option to post an Android notification (and by extension, to the watch) when the phone has fully charged (see details below). This approach is better than the original one planned which involved custom UI in the watchapp, but would rely on an assumption about how long PebbleKit Android would take to launch the watchapp before messaging it.
  • Holding down the button now uses repeat button clicks to reduce wear on the Pebble buttons while scrolling through toggles.
  • Since I’m making bigger use of Notifications, installed a proper white notification icon to improve upon the old ‘white circle’.
  • More misc UI improvements in both app components, including a donation link for those inclined, after some minor success in News Headlines.

So how does the new ‘full charge notification’ feature work? I’m glad you asked!

The Short Answer

Once your phone has reached 100% charge (when it was previously 99% or less), an Android notification is posted, which is displayed on Pebble (for free!) unless for some reason Dashboard was disabled in the Pebble app.

The Long Answer

Oh boy, this was a tricky one. While the ACTION_BATTERY_CHANGED Intent is available for apps to know when the battery level changes, it’s a special case that cannot be simply registered in AndroidManifest.xml for spin-up whenever required. It has to be programmatically registered. For an app designed to run with extremely minimal interaction with the Android app component, this is tricky.

So what have I done? When the user toggles the feature on in the Android app, the BroadcastReceiver for the above Intent is registered. Sounds simple, right? Well that works for anywhere between ten minutes and an hour or so. I was charging my phone from 99% in testing, and seeing the notification after a few minutes as expected, but a frustrating lack of notification after leaving the phone idle for couple of hours on longer charges.

For reasons unknown to me, the system kills my programmatically registered BroadcastReceiver at seemingly random intervals. Remember, this wouldn’t happen with a nice and convenient receiver registered in AndroidManifest.xml.

Undeterred by this (because this is a nice feature idea), I found another solution. I made the receiver object a static class member, and used the Android AlarmManager to check it was not null at infrequent intervals (currently half an hour). This seemed to work well, once I realised by trial and error that Android Studio appears to kill BroadcastReceivers and AlarmManager alarms, but not deliver the ACTION_PACKAGE_CHANGED which I use to restore the alarms after a theoretical future app update is installed.

In addition, new users would not have their ‘keep alive’ alarms registered upon first install, since ACTION_PACKAGE_ADDED is not delivered to the app that has just installed (why!?). Fancy another complication? Here you go: AlarmManager alarms are all killed when the device is rebooted, so I make additional use of ACTION_BOOT_COMPLETED to restore them.

Finally

So there we go! I’ve always been a vocal proponent of Android’s Intent system for IPC, but after this set of complications (which has still yielded an apparently solid solution), I’m not so sure. Ah, who am I kidding? Still a big fan!

Dashboard can be installed from the Pebble appstore and the Google Play Store.

blogbanner

It’s here! The redesign I’ve been planning since many months ago (the previous colour watchapp design was a bit of a bodge), and I’ve wanted to add in a few extra features:

  • GSM signal strength
  • Wifi network name
  • Phone free space
  • Resync every 30 seconds while the app is open
  • Option to jump straight to a certain toggle when the app is opened

…and all the usual refactoring (I can’t help myself) and some protocol simplification. Here’s a rule of thumb: if your AppMessage protocol uses arithmetic and nested switch statements, ditch it!

I also improved a lot of the Android code, added fail cases, and more logging etc. to better help me diagnose problems when a user sends me a debug log. Finally, I brought a little animation magic to the UI, and relied more heavily on system UI components instead of over-complicating things by rolling my own version of everything.

Download on Google Play and Pebble appstore!

A few weeks ago I wrote about transmitting images to Pebble Time from Android, as a quick and convenient way to open any image via an Intent and display it on the watch. At that time, I had problems sending some types of images, where only the blue channel appeared to make it across correctly. The only exception appeared to be the image bundled in the Android app’s resources, which always displayed correctly.

After a getting and re-write of the flaky transmission system (and a bonus first-time working solution!), I now have the app in a state where any image opened with the Android app from a file manager will be resized to fit the Pebble screen and transmitted for display.

Now, the focus will be to enable cropping and scaling of the image in the Android app to let the user choose which part of the image to display (as a photo shown on the watch loses detail somewhat!). In the meantime, here are some images!

Original Image (credit to gmiezis!)

11034411_954803447872213_3799211289901104533_o

Imported to the Android app

Screenshot_2015-04-19-17-45-46

Image on Pebble Time

IMG_20150419_174608

Long time no blog! There hasn’t been much time for experimentation (besides creating a couple of color apps for Pebble Time) in the months leading up the Pebble Time Kickstarter. Totally worth the effort though, after seeing the response to the new material on the Pebble Developers site.

This weekend, however, I found some time to bring to reality an idea I have had since I first learned of the ability to make color apps for Pebble. Being a big fan of PebbleKit Android (used in Dashboard, Wristponder, Watch Trigger etc) to remotely control/access the connected phone. The idea is this: create an app for Android that registers as a receptor of opening image files. This app then resizes and reduces the color palette of the file before piping it to an automatically opening watchapp for viewing. This stemmed partly from a desire to avoid constantly changing resource files and recompiling a simple app for viewing PNG files every time I wanted to see how a new image looked on Pebble Time.

The implementation was pretty straightforward: Make a simple Android app that included an Intent filter for image files, and a watchapp that simply accepted data packets and stored them in the image data allocated for a GBitmap of the right size. The difficulty came in the signaling between the two. I’m no stranger to establishing communication schemes between Android and Pebble apps, but I’m only human. For some reason I was trying to use a mix of PebbleDataReceivers and PebbleAckReceivers. The former is for processing messages from the watch, and the latter is for reacting to the event that the watch acknowledges a message from the phone. By sending the next data packet in an ‘ACK’ handler, you can ensure maximum transmission speed as no time is wasted between the watch processing one packet and being ready for the next. This is the same method that apps like Wristponder use for transferring lots of data (try 30 canned responses!). If you’re not careful, mixing these two modes (one of which can be considered manual, the other semi-automatic when set up in a continuous data transmission loop) can result in puzzling behavior that is difficult to debug.

Once debugged, however, the result is an app that sends a complete uncompressed image (save that for another day) 24k image to the watch in about 11 – 16 seconds (approx 2 kB/s) and then displays it on the watch. In the meantime, both the Android and Pebble apps show the progress of the transfer:

Screenshot_2015-03-08-18-41-59Screenshot_2015-03-08-18-19-10

transfer

 

 

 

 

 

 

 

 

 

 

 

The app sends a built-in image of a tree as a default option when it is launched by itself, or another image if presented one using ‘Open With’ actions, such as from file managers or emails. The result of sending the tree image is thus:

IMG_20150308_182032

For some reason I can’t quite fathom the color reduction process the app uses (admittedly brittle bit shifting) doesn’t handle all files as well, producing discolored results, which I will try and iron out with a better solution, perhaps in the Android SDK itself. When that day comes, I’ll polish both app components and hopefully make another Google Play app of it (free, of course, this doesn’t do anything particularly useful) and also a two-part library generalized to facilitating large data transfers between phone and watch.

Watch this space!

 

Dashboard for Pebble is now version 1.13. This version contains a new Materially design, as well as fixes to the Data and ringer toggles as a result of the changes in Android 5.0 Lollipop.

The major point to note is that the method reflection I was using to implement the Data relied on an internal API in the ConnectivityManager class (which has existed in Android since very early versions). This API has since been moved to a more system-exclusive location (the Telephony class) that cannot be invoked using method reflection and as such the feature stopped working on Android 5.

Being one of the main features of Dashboard (and half the sole purpose in the original Data Toggle watchapp), this outcome was unacceptable. After searching for an alternative and finding nothing but similarly disgruntled Android developers, I came across a widget developer named Cygery who had found a way to implement this behavior in Android 5, and after a brief email exchange I was informed of his method, which was quite ingenious.

As a result, full functionality has been restored on Android 5, but at a large price – the feature now requires root privileges to change that particular settings. This is obviously not ideal, but the only way I can see the feature working beyond Android 5. Users on KitKat and below remain unaffected and the app should work as it did for them. Therefore, the Dashboard Android app will request root when it starts, as well as post a notification if the Data toggle is used and root access is not given. Most SuperUser apps will allow this access to be given on a per-app basis, so please allow this if you are a Android 5 user and need to use the Data toggle.

Download

Get it on Google Play