Wearing the Tasks App - Android Is More than Phones - Android Application Development For Dummies, 3rd Edition (2015)

Android Application Development For Dummies, 3rd Edition (2015)

Part IV. Android Is More than Phones

Chapter 18. Wearing the Tasks App

In This Chapter

arrow Understanding Android Wear

arrow Creating an Android Wear emulator

arrow Connecting Android Wear to your phone

arrow Building a Wear app

arrow Syncing data to your Wear app using Google Play

arrow Packaging your Wear app

Android Wear extends the Android platform to a new generation of devices, with a user experience that’s designed specifically for ­wearables.

Android Wear devices (usually watches) are designed to complement your existing Android phone or tablet (see Figure 18-1). They are not designed to be standalone and usually do not have their own Wi‐Fi or LTE radios, but instead communicate with the Internet through your phone over Bluetooth. Any Android phone running Android 4.3 or later can easily pair with an Android Wear watch.

image

Figure 181: A notification on an Android Wear watch.

Users typically interact with Android Wear in one of three ways:

· The Context stream: This is a vertically scrolling list of cards much like you might find in the Google Now app. The Context stream is a list of notifications that are relevant to you right now. They might include the current weather in your area, the traffic report for your evening commute, notifications from apps on your phone, or just about anything else. These notifications are usually the same notifications that show up on your Android phone.

· Voice control: Android Wear allows you to do many things simply by speaking to your watch. (To learn how to add voice control to your wearable app, visit the book’s online web extras at www.dummies.com/extras/androidappdevelopment ).

· Apps: You can install specially designed Android Wear apps on your watch. These are usually not the same as regular Android phone apps because they need to run on much smaller screens and use much less memory.

By default, any Android phone app that posts notifications automatically displays its notifications in the Context stream on your Android Wear watch. If you have an Android Wear device, you can try it now with the reminders from Part III!

Developers interested in bringing a richer experience to their apps on Android Wear devices have two main options:

· You can leave the app running on the phone, and improve the notifications to allow them to do more stuff on the watch. For example, you might enable users to snooze reminders for the Tasks app right from their watch without having to open the app on the phone. In this scenario, the app stays on the phone, but provides rich notification actions that can be used on the watch. See https://developer.android.com/training/wearables/notifications/ for more information about building rich Android Wear notifications.

· You can build a second app that runs directly on the watch, and ­synchronizes data with the app on your phone via Bluetooth.

This chapter focuses on building a second app that runs directly on the watch.

You can do much with Android Wear, much more than this chapter or book can explore. In many ways, Android Wear is its own platform. To learn more about it, visit https://developer.android.com/wear/.

At the time of this writing, you must have a real Android phone to develop for Android Wear. This is because you must install the Android Wear app on your phone, and the app is not currently available on the Android emulator. You do not need to have a real Android Wear watch though. It is possible to develop for Android Wear using a watch emulator.

Preparing Your Development Environment

Because Android Wear is a companion to your phone rather than a completely standalone product, the development environment is familiar but slightly different from developing on regular Android.

Prepping your Android phone

Before you create the Android Wear emulator, you should prepare your phone.

Checking for system updates

Android Wear is an evolving platform, and it requires that certain services be up to date on your phone. Go to your phone’s Settings page, scroll down to About phone, and click System updates to make sure your phone has the latest software.

Installing Android Wear on your phone

Visit the Google Play Store and search for Android Wear. Install the app and then run it. You should see something like Figure 18-2.

image

Figure 182: The Android Wear setup screen.

Setting up an Android Wear emulator

The next step is to create your Android Wear emulators. These are the devices on which you will develop your Wear apps.

1. In Android Studio, choose Tools⇒Android⇒AVD Manager.

2. Click Create Virtual Device.

3. Choose Wear, select Android Wear Square, and click Next.

4. Choose Lollipop API 21 x86, click Next, and then click Finished.

5. Start the emulator by pressing the green right‐pointing triangle Play button.

Wait until the emulator initializes and shows the Android Wear home screen as in Figure 18-3.

6. Repeat the Steps 1 to 4 but create an Android Wear Round emulator.You don’t need to run it at this time, but you will need it later in the chapter.

image

Figure 183: The Android Wear Square emulator.

Pairing your phone with the Wear emulator

Now that your phone and emulator are ready to use Android Wear, you must pair them. Every Android Wear user pairs his watch to his phone, and the Android Wear app makes this easy. Because you are using an emulator rather than a physical device, you must go through a few extra steps to pair your devices:

1. Connect the phone to your machine through USB.

2. Forward the emulator’s communication port to the connected phone.

You must do this every time the phone is connected:

adb -d forward tcp:5601 tcp:5601

The wearable emulator and your phone should now be listed when you run the adb devices command.

$ adb devices
List of devices attached
emulator-5554 device
5b44a488839e3171 device

3. Start the Android Wear app on your phone and connect to the ­emulator.

You do this by choosing Pair with a new wearable and then choosing Pair with emulator from the overflow menu, as shown on the left and right images in Figure 18-4.

The Android Wear app should now report that your emulator is “Connected.” Your phone and emulator are now paired!

4. Check that the pairing is working.

You do this by tapping the menu on the top right corner of the Android Wear app, selecting Demo cards, then clicking a Demo card. As shown in Figure 18-5, the card you select (left) appears as a notification(s) on the home screen (right). You can dismiss the card by swiping it off the screen to the right.

Do you want to develop with a real Android device rather than the ­emulator? Then visit https://developer.android.com/training/wearables/apps/creating.html for more information.

image

Figure 184: Pairing your Android Wear ­emulator to your phone.

image

Figure 185: Sending a demo notification to your Android Wear ­emulator.

Creating a New Wear App

In this section, you create a new Android Wear app that runs directly on your watch emulator, and you add the code necessary to allow it to sync with your phone.

Creating a new module

You can create a new module in your Android Studio project by following these steps:

1. Choose File⇒New Module.

Select Android Wear module, and press Next.

2. Create a new Wear module using the settings in Table18-1.

The package name you use here MUST be the same as the package name you use for your Tasks app. If they’re not the same, your tasks won’t sync between the two apps.

3. Press Next and add a new Blank Wear Activity.

Use the settings from Table 18-2 for your new activity, and then click Finish.

4. Run your app on your Wear emulator.

You can do this by choosing Run⇒Run ‘TasksWear’ and choosing your emulator (not your phone). You should see the “Hello world” app ­running as shown in Figure 18-6.

Table 181 Settings for Creating a New Wear Module

Application Name

Tasks

Module name

TasksWear

Package name

com.dummies.tasks

Minimum SDK

API 21 Lollipop

Table 182 Settings for Creating a New Android Wear Activity

Activity Name

MainActivity

Layout name

activity_main

Round layout name

round_activity_main

Rectangular layout name

rect_activity_main

image

Figure 186: The Hello World app running on the Wear emulator.

Editing MainActivity

You’re now going to replace the sample code in MainActivity with the beginning of a real app.

Open MainActivity.java and replace the contents of that file with the ­following:

public class MainActivity extends Activity
{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}

This code loads the activity_main.xml layout into your activity.

Now open res/layout/activity_main.xml and replace it with the ­following code:

<?xml version="1.0" encoding="utf-8"?>
<android.support.wearable.view.BoxInsetLayout →2
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" →4
android:layout_height="match_parent"
android:layout_width="match_parent"
android:padding="15dp"> →7

android.support.wearable.view.WearableListView →9
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_box="all"/> →13

</android.support.wearable.view.BoxInsetLayout>

This layout uses a few views specific to Android Wear. They are included in the Android Wear support library. Chances are you have not encountered them yet while developing regular Android apps. Here is what the code is doing:

2 The outer view is the BoxInsetLayout. Think of this view as a fancy FrameLayout. Like a FrameLayout, the BoxInsetLayout lets you put other views inside it but doesn’t give you any sophisticated layout options. However, unlike a FrameLayout, the BoxInsetLayout knows the difference between round and rectangular Android Wear devices, and it resizes its children appropriately to make sure that they fit on a round watch face.

Because Android Wear devices come in many different shapes and sizes, it’s important to ensure your layouts work with them all. You have a few ways to make your layouts work with both round and rectangular devices. BoxInsetLayout is what you use in this chapter. Another useful tool is WatchViewStub. WatchViewStub is great when you want to use significantly different layouts for a round watch than a rectangular watch. For more information about WatchViewStub, visithttps://developer.android.com/training/wearables/apps/layouts.html.

4 You’ve been using the xmlns:android namespace for some time now and are probably quite used to it. This may be the first time you’ve seen a different namespace in your Android layouts. The BoxInsetLayout uses an additional layout parameter (layout_box on line 13) that is not present in the default xmlns:android namespace, so it must use its own namespace to add the layout_box parameter. I call this namespace app, but you can call it ­whatever you want as long as lines 4 and 13 agree.

7 This line assigns padding to the BoxInsetLayout element. Because the window insets on round devices are larger than 15dp, this padding applies only to square screens.

9 WearableListView is a Wear‐specific layout very similar to the regular Android ListView or RecyclerView. It shows items one at a time in a list, and creates only as many views as will cover the screen, no matter how long the list is.WearableListView is optimized for ease of use on small screen wearable devices.

13 This line ensures that the FrameLayout element and its children are boxed inside the area defined by the window insets on round screens. This line has no effect on square screens. Other options for layout_box are left, right, bottom, and top. See the documentation for BoxInsetLayout for more details.

You now have a Wear app that can theoretically show a list of data. However, you have no data yet to display. The next few sections will fix that.

Adding Google Play Services for data syncing

Android Wear apps use the Google Play Services library (which is different than the Google Play Store) for a lot of functionality. In this section you use Google Play Services to sync data between your phone and watch.

Open your AndroidManifest.xml and add the following meta‐data entry to your application element:

<meta-data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />

This meta‐data is necessary for apps that use the Google Play Services library.

You then open the TasksWear build.gradle and ensure that the Google Play dependency is listed there. If it’s not already there, then add it to the dependencies section:

dependencies {
. . .
compile 'com.google.android.gms:play-services-wearable:6.5.87'
}

Now add Google Play Services to your activity. Open MainActivity.java and add the code in bold:

public class MainActivity extends Activity →1
implements DataApi.DataListener, GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener
{

GoogleApiClient googleApiClient; →5


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

googleApiClient = new GoogleApiClient.Builder(this) →13
.addApi(Wearable.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
}

@Override
protected void onStart() { →21
super.onStart();

googleApiClient.connect(); →24
}

@Override
protected void onStop() {
super.onStop();

googleApiClient.disconnect(); →31
}

@Override
public void onConnected(Bundle bundle) { →35
Log.d("MainActivity", "onConnected"); →36

Wearable.DataApi.addListener(googleApiClient, this); →38

updateList(); →40
}

@Override
public void onDataChanged(DataEventBuffer dataEvents) {
Log.d("MainActivity", "onDataChanged");

dataEvents.release(); →47

updateList(); →49
}

@Override
public void onConnectionSuspended(int i) { →53
Log.d("MainActivity", "onConnectionSuspended"); →54
}

@Override
public void onConnectionFailed(ConnectionResult connectionResult) { →58
Log.d("MainActivity", "onConnectionFailed");
}

private void updateList() { →62
// TBD
}
}

This code adds support for Google Play data syncing. How does it do it? It basically connects to the Google Play service, subscribes to updates, and then calls updateList() whenever there is new data. Here is more detail about the code:

1 The main activity for this app. This activity implements the DataApi.DataListener interface to be notified of sync events. It also implements the GoogleApiClient ConnectionCallbacks and OnConnectionFailedListener to handle connectivity events. If you’re writing an app that needs to sync in the background (this one doesn’t), then consider using a WearableListenerService to run continuously in the background.

5 Wearables use the GoogleApiClient to communicate with their host devices (usually your phone).

13 This sets up the GoogleApiClient. You tell it you need the Wearable.API, and also that it should call you back on this for any connectivity notifications.

21 The onStart method is an Android callback (not Google Play) that happens after onCreate. onStart is called when the activity is visible on the screen, although it may not necessarily be the frontmost activity. You want to make sure that you are connected to Google Play any time the activity is visible (and disconnect from Google Play after the activity is no longer visible), so you connect to Google Play in onStart and disconnect in onStop. For more information about onStart and onStop, refer to Chapter 5.

24 Connects to the GoogleApiClient. Upon connection, you will receive a callback on onConnected().

31 We’re done, so disconnect from the GoogleApiClient.

35 onConnected is a Google Play callback that is called after a connection has been established (which was initiated on line 24). It is defined in the ConnectionCallbacks interface. This method needs to do two things: It needs to start listening for data changes on line 38, and it needs to update the list with whatever data has already been cached on the watch on line 40.

36 Logs a simple message to let you know that onConnected has been called. This may be helpful later when you’re debugging your app and need to know whether a connection between the phone and the wearable has been established.

38 Subscribes for any more data updates. You will be called back on onDataChanged if anything is updated.

40 Updates the adapter as soon as you’re connected.

47 Always release the dataEvents when you’re done. In this case, you don’t use the dataEvents directly, so release them right away.

49 You were told there was an update, so this line updates your adapter.

53 onConnectionSuspended is also defined in the Connection Callbacks interface, and it is called whenever the Google Play connection has been shut down.

54 Just logs a message. You don’t have to do anything at all, but a log message can help you debug any issues.

58 onConnectionFailed is defined in OnConnectionFailed Listener. It is called if there was an error connecting to the device. There is nothing that you need to do here, but again you should log a message as an aid during debugging.

62 The updateList refreshes the UI with the latest data. At this time it doesn’t do anything yet, but you will implement it in the next section.

Creating the adapter

Recall that an adapter is responsible for taking data from a data source (such as a database) and creating views for each item in the list. See Chapter 9 for more information about adapters.

You created a list view earlier in the chapter. Now you just need an adapter to feed it data.

Create a new file WearableTaskListAdapter.java in the same directory as your MainActivity.java, and add the following to it:

/**
* A WearableListAdapter that knows how to display our Task items in a
* list.
*/
public class WearableTaskListAdapter
extends WearableListView.Adapter →6
{
static final String COLUMN_TITLE = "title"; →8
List<DataItem> dataItems; →10

LayoutInflater inflater; →12

public WearableTaskListAdapter(Context context) {
inflater = LayoutInflater.from(context); →15
}

@Override →18
public WearableListView.ViewHolder onCreateViewHolder(
ViewGroup viewGroup, int i) {

return new ViewHolder( →22
inflater.inflate(R.layout.item_task, null));
}

@Override →26
public void onBindViewHolder(
WearableListView.ViewHolder viewHolder, int i) {

DataItem dataItem = dataItems.get(i); →30

DataMap map →32
= DataMapItem.fromDataItem(dataItem).getDataMap();

((ViewHolder) viewHolder).titleView.setText( →35
map.getString(COLUMN_TITLE)
);
}

@Override
public int getItemCount() { →41
return dataItems != null ? dataItems.size() : 0;
}

public void setResults(List<DataItem> dataItems) { →45
this.dataItems = dataItems;
notifyDataSetChanged(); →47
}


static class ViewHolder extends WearableListView.ViewHolder { →51
TextView titleView;

public ViewHolder(View itemView) {
super(itemView);
titleView = (TextView) itemView.findViewById(R.id.title);
}

}
}

This adapter takes a list of DataItems and creates views for each one from item_task.xml. Here is a look at the code in detail:

6 All adapters used by the WearableListView must inherit from WearableListView.Adapter. The WearableListView.Adapter is pretty similar to the RecyclerView.Adapter you used in Chapter 9.

8 The name of the column containing the data you are looking for. The only column shown in this list view is the title, due to the ­limited screen real estate. This string must match the name used in the phone app.

10 The current list of dataItems. May be null.

12 The layout inflater used to inflate the views.

15 Retrieves a LayoutInflater from the current context.

18 Creates a ViewHolder that holds a reference to the views that you will need to update for each new item in the list.

22 Returns a new ViewHolder (see line 51 for class ViewHolder). Each view in your list will use the item_task.xml layout. Note that you haven’t created item_task.xml yet, but you will shortly.

26 Updates the views in the ViewHolder using the information in the item in position i.

30 Finds the DataItem for the item in position i.

32 Reconstructs the original DataMap for that item.

35 Sets the title view text based on the COLUMN_TITLE in the DataMap.

41 As in Chapter 13, getItemCount returns the count of items in the list. In this case it is equal to dataItems.getCount(), or zero if dataItems is null.

45 Updates the items in the list, and notifies listeners (particularly the ListView) that the data in the adapter has changed.

47 notifyDataSetChanged works the same way here as it did in Chapter 9. It notifies any listeners (in particular, the WearableListView) that the data has been updated and that the listener should be refreshed.

51 A simple ViewHolder that just holds the titleView for the list item. See Chapter 9 for more information about ViewHolders.

The previous code used a layout called item_task for each item in the list, so add a new layout file in res/layouts and call it item_task.xml:

<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/title"
android:gravity="center_vertical|start" →5
android:layout_width="wrap_content"
android:layout_marginEnd="16dp" →7
android:layout_height="80dp" →8
android:fontFamily="sans-serif-condensed-light" →9
android:lineSpacingExtra="-4sp" →10
android:textSize="16sp"/> →11

This layout is just a single, simple TextView.

5 Positions the text to the far left and centers it vertically in the view.

7‐11 These lines represent some styling choices to make sure the text fits on the screen and is readable. Feel free to play around with these values to find the right look for your app.

Now you need to hook the adapter up to the list view. Open MainActivity.java and add the code in bold:

public class MainActivity extends Activity
implements DataApi.DataListener, GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener
{

WearableTaskListAdapter adapter;5


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

. . .

adapter = new WearableTaskListAdapter(this);15

WearableListView listView =17
(WearableListView) findViewById(R.id.list);

listView.setAdapter(adapter);20
}


private void updateList() {
Wearable.DataApi.getDataItems(googleApiClient).setResultCallback(25
new ResultCallback<DataItemBuffer>() {
@Override
public void onResult(DataItemBuffer dataItems) {28
try {
List<DataItem> items30
= FreezableUtils.freezeIterable(dataItems);

adapter.setResults(items);33

Log.d("MainActivity", "adapter.setResults");35
} finally {

dataItems.release();38
}
}
});
}
}

If you run the app now, you won’t see anything because you haven’t ­published any data to the watch yet.

5 The adapter you just created.

15 Creates the adapter.

17 Finds the WearableListView in the view hierarchy.

20 Links the adapter and the ListView.

25 Retrieves the complete list of dataitems using DataApi getDataItems. Because this may involve a network sync and may take some time, you get the results back in a ResultCall back at a later time.

28 The onResult method of your callback is called when data is received. It returns to you the list of items that resulted from your query.

30 Before you start using dataItems, you must “freeze” them to make sure they don’t change while you are iterating over them.

33 Updates the adapter with the new items.

35 Logs a message to logcat to assist with debugging.

38 Always releases the dataItems when you are through.

Your Android Wear app is now complete! However, if you run it, you will see just a blank screen. This is because it does not yet have any data to show. You will fix that in the next section.

Publishing the Data from Your Phone

Now that you’ve created a Wear app, it’s time to publish the tasks from your phone so they can be synced to your Wear app. This involves adding the Google Play sync services to your phone and then testing the sync between your phone and Wear app. But before you do that, you need to do a little setup work.

Configuring the phone’s build

Open build.gradle in your original Tasks (not TasksWear) directory, and add the following dependency:

compile ‘com.google.android.gms:play-services-wearable:6.5.87’

Make sure the version you use agrees with the version in your TasksWear build.gradle from earlier in this chapter.

Next, open AndroidManifest.xml for your Tasks app and add the required meta‐data tag to your application element (like you did for TasksWear):

<meta-data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />

Publishing the data from the phone

With those preliminaries over, it’s time to add Google Play data syncing to your phone’s TaskProvider. Go back to TaskProvider.java in your Tasks app, and add the code in bold:

/**
* A Content Provider that knows how to read and write tasks from our
* tasks database.
*/
public class TaskProvider extends ContentProvider
implements GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener7
{
. . .

// Google Play Constants
private static final String PLAY_BASE_URL = "/" + DATABASE_TABLE;12

GoogleApiClient googleApiClient;14

@Override
public boolean onCreate() {
. . .

googleApiClient = new GoogleApiClient.Builder(getContext())20
.addApi(Wearable.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
googleApiClient.connect();25

. . .
}


@Override
public Uri insert(Uri uri, ContentValues values) {

. . .

// Save to google Play for wearable support
PutDataMapRequest dataMap = PutDataMapRequest.create(
PLAY_BASE_URL + "/" + id);38
DataMap map = dataMap.getDataMap();39
map.putLong(COLUMN_TASKID, id);40
map.putString(COLUMN_TITLE, values.getAsString(COLUMN_TITLE));
map.putLong(COLUMN_DATE_TIME, values.getAsLong(COLUMN_DATE_TIME));
map.putString(COLUMN_NOTES, values.getAsString(COLUMN_NOTES));43
PutDataRequest request = dataMap.asPutDataRequest();44
Wearable.DataApi.putDataItem(googleApiClient, request);45

. . .
}

/**
* This method is called when someone wants to delete something
* from our content provider.
*/
@Override
public int delete(Uri uri, String ignored1, String[] ignored2) {

. . .

// Delete from google Play for wearable support
long id = ContentUris.parseId(uri);60
Uri wearUri
= new Uri.Builder().scheme(PutDataRequest.WEAR_URI_SCHEME)
.path(PLAY_BASE_URL + "/" + id).build();63
Wearable.DataApi.deleteDataItems(googleApiClient, wearUri);64

. . .
}

/**
* This method is called when someone wants to update something
* in our content provider.
*/
@Override
public int update(Uri uri, ContentValues values, String ignored1,
String[] ignored2) {
. . .

// Update to google Play for wearable support
long id = ContentUris.parseId(uri);79
PutDataMapRequest dataMap = PutDataMapRequest.create(
PLAY_BASE_URL + "/" + id);
DataMap map = dataMap.getDataMap();
map.putLong(COLUMN_TASKID, values.getAsLong(COLUMN_TASKID));
map.putString(COLUMN_TITLE, values.getAsString(COLUMN_TITLE));
map.putLong(COLUMN_DATE_TIME, values.getAsLong(COLUMN_DATE_TIME));
map.putString(COLUMN_NOTES, values.getAsString(COLUMN_NOTES));
PutDataRequest request = dataMap.asPutDataRequest();
Wearable.DataApi.putDataItem(googleApiClient, request);88

. . .
}


@Override
public void onConnected(Bundle bundle) {
Log.d("TaskProvider", "connected to Google Play");
}

@Override
public void onConnectionSuspended(int i) {
Log.d("TaskProvider", "Google Play connection suspended");
}

@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.e("TaskProvider", "Google Play connection failed");
}
}

You recall that the TaskProvider manages all of your database access for you. This code added the ability to sync data to Google Play whenever your database changes. Here is more detail about how this code works:

7 As you did for the Wear app, add the ConnectionCallbacks and OnConnectionFailedListener interfaces to get notified when you connect and disconnect from Google Play.

12 Every item that is to be synced with Google Play must have a unique URI. This URI must be unique for all items within your app, but does not have to be unique globally across all apps. For this reason, you use a very simple URI which looks like /tasks to reference all tasks, or /tasks/<id> to reference a specific task by id.

14 Google Play API client, used for Android Wearable syncing.

20 This block of code connects to Google Play. It is identical to the connection you made for the Wear app earlier in this chapter.

25 Connects to the Google Play services. In the TaskWear app, you waited to connect to Google Play until the onStart method. TaskProviders do not have an onStart method, so they connect right away.

38 Lines 38–45 sync a new task to Google Play. PutDataMap Request.create() on line 37 creates a new PutDataMap Request that requests that you sync a DataMap to Google Play. The DataMap is basically a hashmap that contains all the data for the task.

39 Gets the DataMap from the PutDataMapRequest so you can put your data in it.

40—43 These lines insert all the task data into the hashmap. This includes the task ID, title, notes, and date/time.

44 Converts the PutDataMapRequest to a PutDataRequest so that it can be sent to Google Play.

45 Finally, this line sends the request to Google Play. The call to Wearable.DataApi.putDataItem is what actually does the syncing to the watch.

60 Lines 60–64 delete a task from Google Play. First you must determine the task ID from the task provider URI.

63 Once you have the task ID from line 60, you must determine the Google Play URI for that task. This is done by using a URI.Builder to create a new URI with the PutDataRequest.WEAR_URI_SCHEME scheme, followed by the path to your task. The path to the task is /tasks/<id> as was indicated in the description for line 12.

64 Deletes the task identified by the wearUri.

78—87 These lines update a task. The code is identical to lines 38–45 when you inserted a task into Google Play. If the task already exists, it is updated with the new data.

Testing the sync

Congratulations, you should now have a working sync between your Tasks phone app and your Tasks Wear app!

To test it out, follow these steps:

1. Uninstall the app from your phone and watch emulator.

This is important to clear out the database.

adb –e uninstall com.dummies.tasks # uninstall from the emulator
adb –d uninstall com.dummies.tasks # uninstall from your phone

2. Run the Android Wear app and make sure that it still says “Connected” at the top.

If not, go back to the section “Preparing Your Development Environment” and reconnect your emulator to your phone.

3. Run the app on your phone and add a few new tasks.

4. Run the TasksWear app on your emulator.

You should see the items you just added in your emulator, as in Figure 18-7. If you add more items, they should appear immediately on your emulator.

5. Shut down the Android Wear Square emulator, then start up the Android Wear Round emulator that you created earlier in this ­chapter.

Connect the round emulator to your phone using the Android Wear app. Then run the app on that emulator to make sure that all the text is visible on a round display. You should see something like Figure 18-8.

image

Figure 187: The Tasks app showing one item on the rectangular emulator.

image

Figure 188: The Tasks app on the round ­emulator.

Running the App without Android Studio

So far, you’ve been using Android Studio to run your Android Wear app. Most users are not developers, of course, and will not have Android Studio handy.

Unlike regular Android, you have no launcher on Android Wear, which can make it hard to find the app that you installed on your watch. How do you run your app without a launcher or Android Studio?

You have two ways:

· You can tap on your watch face and wait until it says Speak Now, and then say “Start tasks.” Your watch will launch the Tasks app.

· If you prefer the long way, you can tap on your watch face and wait until it says Speak Now, and then scroll down to the Start option. Then choose the Tasks app.

Packaging the App

One more difference between Android and Android Wear is that you have no Google Play Store for Android Wear. Wait, there’s no Play Store for Android Wear? How are people supposed to download and install your app?

The answer is that you need to bundle your Android Wear app into your phone app so that it automatically installs when your phone app does.

To bundle your Wear app inside your phone app, add the following line to your build.gradle in Tasks (not TasksWear):

dependencies {
. . .
wearApp project(':TasksWear')
}

Now, choose Build⇒Generate Signed APK and generate a signed APK for your Tasks app (not TasksWear). Now if you install the signed APK, the Wear app automatically installs on your Wear watch.

What’s Next?

This is just the tip of the iceberg. There are plenty of additional things you can do with your new Android Wear app. Consider making a few of the ­following changes:

· Improve your phone’s notifications to make them actionable from your watch.

· Allow users to be able to click into individual tasks and view them.

· Add voice control so users can edit tasks or search them.

· Use GridViewPager and CardFragment to add images and make a more visually appealing interface.

· Create a WearableListenerService to sync data from your phone while the MainActivity isn’t running.

Visit https://developer.android.com/wear for more information about how to add some of these exciting features.

To learn how to add voice control to your Android Wear apps, visit the book’s online web extras at www.dummies.com/extras/androidappdevelopment.