App Development for Samsung Gear Fit

At the time of writing this blog, the Gear Fit is already an outdated wearable device. However, the watch can be purchased at a rather low price. For those who have it, it might be useful to experience more about what is possible with the watch and what not.

This article is not intended to be a tutorial for writing CUP apps. The programming guide is available in the internet and provides a number of examples on how to write applications. The topics described assume basic knowledge from the Samsung documentation.

The following topics are covered:

  • SDK Availability: Currently, Samsung does not make the SDK available for download
  • How do Applications work: A few words on the lifecycle of an app that utilizes CUP.
  • Considerations for writing CUP Applications: a collection of topics, I came across when writing apps for the Gear Fit. I figured out how to use a Service on the phone for this, which make application design for the phone to an extend easier.


SDK Availability

Samsung has withdrawn the necessary library for developing apps: cup-v1.0.0.jar. In order to obtain it anyhow there are two possibilities:

  • It is available in the xda developer forum: http://forum.xda-developers.com/gear-fit/general/samsung-gear-fit-sdk-available-t2872803
  • A second option is for more advanced users: Download a gear fit app from the app store. Extract the APK from the phone and then extract the CUP class files from the APK.


How do Applications work?

The Gear Fit device implements CUP - Companion UI Profile. This is a technique where the watch is kind of acting as external touch monitor. The behavior is similar to an Android Home Screen Widget. There is no application logic on the watch. The app cannot run when the phone is disconnected.

The SDK offers a class library to create various widgets and handle events for them.




The diagram shows how applications for the Gear Fit work:


  1. the user goes to the App Connect menu item. App Connect will only be in the menu, when there is a custom app registered.
  2. App Connect shows the registered applications. When the user taps on one of them, the corresponding app on the phone will be launched.
  3. the app on the phone should now construct the UI using widgets for the ScupDialog class. Event handlers can be set in order to react on the user.
  4. the UI can be updated. Existing Widgets can display new content by a call to "update()". When the user presses back (or the hardware button, the display on the phone finishes.
Samsung has published several wearables since beginning of 2014. To my knowledge no other device shares this technology and also this device is not working with any other wearable SDK also available from Samsung. This means that functions like heart beat rate measurement stay out of rich for a custom Gear Fit apps.




Considerations for writing a CUP Applications


Android Manifest File

The manifest file needs to have a meta-data section to define what is being registered on the watch: 

<meta-data
       android:name="SAMSUNG_CUP_APP" 

       android:value="app_name;ic_launcher;true" />

The value string is split into three fields. The first two are both mapped to resources of the application. The app name can be a string resource and can be a localized sting. If no string resource is found, the string itself is taken for the list in App Connect.

The second parameter relates to a drawable resource. I was able to verify in the log file that the drawable is resolved to a resource id. However, I never managed to get an Icon to be shown in App Connect.

  • app_name:  refers to a string resource.
  • ic_launcher: refers to a drawable resource
  • true: show in App Connect.
The activity itself needs to have an intent filter for CUP apps defined:

        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter >
                <action android:name="com.samsung.android.sdk.cup"/>
            </intent-filter>
        </activity>

The CUP libraries on the phone enumerate the apps on the devices and look for this intent-filter. If this filter is not set, it is not possible to launch the app from the phone. The CUP libs would simply ignore it. Vice versa it should be possible to bring up a dialog without registering the app with the phone.


Learning more about what is going on

In order to get a better feeling on how the interaction between Gear Fit and Phone works, the USB debugging should be set up with the phone. The logger can be configured to display more information:

The filters in LogCat for "ScupPackageReceiver" and / or "ScupService" show how an app is registered and show the communication between watch and phone. 


Determining who launched the Host App

When creating a application soon the question comes up whether the app has been launched from the launcher on the phone or by App Connect on the watch. The only way to find out is to inspect the properties passed on the application on the phone:  

Bundle bundle=getIntent().getExtras();
boolean startedByCUP=false;
if(bundle!=null) startedByCUP = bundle.getBoolean("START_BY_CUP");


The above code should part of the main activity. OnCreate() is a suitable method to query for this. In case the application has been launched by the launcher, there won't be any bundle, thus the bundle itself can already be null. 


Handling "Back pressed"

The CUP implementation for the Gear Fit has a substantial drawback. When the application is launched on the watch, it is launched in parallel on the phone. However, if the application is already running on the phone, nothing happens. 

In order to compensate this negative effect, it is advisable to finish the app on the phone when the user finishes the app on the phone. 


public class HelloCupDialog extends ScupDialog implements ScupDialog.BackPressedListener {


    @Override
    public void onBackPressed(ScupDialog arg0) {

       finish();
       if(activity!=null) {
           activity.finish();
       }
    }
}


Normally, applications should not finish by themselves. In this case, itmay be justifiable to do so, as there is no way to detect when the app on the phone is relaunched.


Using onPause()

A note on using OnPause(). With Android it is common practice to register /unregister receivers in the onResume() and onPause() methods.

When launching an app on a phone with a locked screen onPause() is called very soon after launch of the application. For the app on the phone this may be perfectly ok, however, the app on the phone is not in a pause state and may require a functioning app. For this reason, the receiver should be unregistered in the onDestroy() method.


Notifications

For many purposes it is sufficient to simply use the Gear Fit device for Notifications. In order to have a notification on the device triggered no special phone app is required. The Gear Fit Manager allow to select what (standard) notifications from normal apps are also being notified via the phone. This works for example for the (non-standard) Gmail App on the phone.

On the other hand, it is not possible to have a Gear Fit App trigger a notification only for the phone (in the built in Notification App). Still a custom app can let the watch vibrate. Together with bringing up a specific screen the user can be notified. However, this event will not be tracked in the watchs' list of notifications.



Starting a Service instead of an Application

The Programmers Guide explains that the intent-filter may also be used to launch a service. Unfortunately, there is no further explanation on how this works. Even more unfortunate is the fact that the developers have build in a bug that made it even more difficult to get it working - as you can see further below.

Why a service? A service runs in the background and has no own UI (on the phone). Usually, this is exactly what is needed. The UI on the phone not necessarily should change when an app is launched for the Gear Fit. Imagine a situation where a video or presentation is shown on the phone screen. You probably don't want the gear to disturb this. 

WIth activities the issue is that the gear UI cannot be launched in case the app is already running on the phone. With the service implementation the behvior is different. If the service is already or still running, the gear framework stops the service before it restarts. This means the UI on the phone is brought up in a much more realiable fashion.

The code for constructing the CUP classes should be placed in the onStartCommand method.

public class HelloCupService extends Service {


private static final String TAG="HelloCupService";
        HelloCupDialog mHelloCupDialog;


@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG,  "onStartCommand");
        Scup scup = new Scup();
        try {
        // initialize an instance of Scup 
        scup.initialize(getApplicationContext());
        } catch (SsdkUnsupportedException e) {
        if (e.getType() == SsdkUnsupportedException.VENDOR_NOT_SUPPORTED) {
        } }
        int versionCode = scup.getVersionCode(); String versionName = scup.getVersionName();
        
       HelloCupDialog mHelloCupDialog = new HelloCupDialog(
        getApplicationContext(), this); 
return android.app.Service.START_NOT_STICKY;
}
}

It is essential to declare the service in the manifest. 

          <service
            android:name="eu.liebrand.gearfit.HelloCupService"
          
            >
            <intent-filter >
        <action android:name="com.samsung.android.sdk.cup"></action>
        <action android:name="eu.liebrand.gearfit.HelloCupService"></action>
            </intent-filter> 
          </service>


This declaration has two things to note:
  1. The intent-filter is used during app install on the phone to identify this service as relevant for CUP. Together with the information in meta-data this completes the install data for the Gear Fit. It appears that the framework uses an implicit intents to start the service. For (newer) Android versions prior to 5, using implicit intents cause a warning.With version 5, it causes an exception. This means that the service approach will stop working for newer devices at some point in the future, if Samsung does not fix it
  2. Scanning the log file for ScupService reveals that the framework tries to launch the service by its class name (as defined by android:name). As implicit intents are used, the actual class name is irrelevant.  For this reason the log file file reports that the service could not be found. Android looks for a matching intent filter. This was probably not intended by the Samsung Developers. It can be fixed easiliy by repeating the class name as action in the intent-filter.

Update Jan 2015: The service implementing the CUP Host needs to be the first service in the manifest. Otherwise the wrong service name is picked.

Update Feb 2015: Testing with a Lollipop Device shows that launching a service via intent still works fine. The inStartCommand() method can build the dialog as described above. Not working anymore is to bind to a service using an implicit intent. The cup-v1.0.0.jar uses this approach to link to the framework on the host device. See  my other post regarding how to fix this.