Join David Gassner for an in-depth discussion in this video Asking for confirmations, part of Building Apps for Android Wear Devices.
- When the user takes an action on a wearable app, it's sometimes useful to give them time to cancel it. You see this with notifications. I'm showing my live device, a Nexus 6 connected to my emulator, and in the Android Wear App, I'll go to Try Out Watch Notifications and then I'll send a notification. Then on the emulator, I'll dismiss the notification and immediately swipe upward. And I see an Undo button within the animation. If I don't select that button within the available time, the notification is dismissed.
But now I'll send the notification again, and I'll dismiss it again and swipe upward again and this time, I'll touch that button and I undo the dismissal. You can create this same sort of confirmation user interface in your own wearable apps. I'll get rid of this notification, so I'm starting from scratch. In Android Studio, I'm working with the project confirmation, and I'll show you how to create the confirmation UI with a class-named delayed confirmation view.
This is a widget that goes onto your layout. You set the amount of delay in the confirmation view, and then it provides a nice animation. You then get events in your Java code indicating either that the delay ran to completion or the user selected the view presumably to cancel the action. You can then show an animation describing either completion or cancellation with your own custom message. So, I'll start in the layout file activity_main.xml.
Right now, I only have a couple of text-view components. And I'll look at the layout view in XML mode. I'm going to place my confirmation view right between the two text views, and notice that I've wrapped the second text view and the delayed confirmation view inside a linear layout with an orientation of horizontal. So, the component I'm about to add, that's going to display the confirmation view will be to the left, and the text view label will be to the right. I'll start typing a tag name of android.support.wearable and then I'll scroll down and select the Delayed Confirmation View component.
Then I'll add a bunch of attributes. First, I'll add an ID, and I'll add the usual ID prefix and then set an ID of delayed_confirm. Next I'll set the layout width and layout height, and I'll set them both to 40dp. Next, I'll set the source attribute. I'm going to be using this PNG file that's in the Drawable folder ic_clock.png.
Notice that it's white with a transparent background. I'll set the attribute android:src and then within quotes, "@drawable/ic_clock". Now at this point, you might not get all the auto complete in your typing that you want, but these attributes will be recognized. Next, I'll set a few attributes that are in the App prefix. These will describe some of the appearance of the component. The first one is named circle_border_color, and I'll set that to one of the colors that's included with the Android SDK of background_light.
Next, I'll set an attribute named circle_border_width, and I'll set that to a value of 4dp. And finally one named circle_radius, and I'll set that to 16dp. And that's the complete tag. Now, if I look at this in design view, I won't really see anything update. Everything's still invisible, and that's because my linear layout is set initially to a visibility of invisible, but if I click around in here, I can clearly see that there are two components.
My new Delayed Confirmation View and the Text View that was already there. Now, I'll add some code in my main activity class. I already have a private field named isRunning set to false. I'll be using this to track what's going on as the user navigates through the application. But I'll add another private field, and this one will be an instance of the class DelayedConfirmationView. I'll name it mDelayedView, and I won't instantiate it here, I'll do that in the onCreate method.
I'll go down to the onCreate method, and add code here after the call to set content view. I'll call it mDelayedView, and I'll get it's reference by calling findViewById and I'll pass in R.id.delayed_confirm. And I'll complete all that code including casting the result of FindViewById. This code's getting a little wide, so I'll expand my editor to full screen. My next step is to add a Listener. The Listener will react when either my Delayed Confirmation View completes it's action or the user selects it.
I'll need to add two methods for this. They're members of an interface named Delayed Confirmation Listener. So, I'll add implements and then DelayedConfirmationListener, and that expands to include the parent class of the interface. Then, I'll use an Intention Action and implement the methods. They're named onTimerfinished and onTimerselected. And those are added at the bottom of my code. I'll come back to those later, but my next step will be back in the onCreate method, and now I'll register the listener like this.
With mDelayedView.setlistener, and I'll pass in this as the object that will react when the events occur. You also have control over how long the Delayed Confirmation View takes to complete it's action, and you set that with a method named setTotalTimeMs. I'll pass in a value of 3000, and that's 3000 milliseconds or 3 seconds. Now, my next step is to launch the timer.
I'll expand this code here, and you'll see that I've already added code to handle a click event when the user touches the watch face. I'm reacting to the onClickListener for the relative layout that contains the entire interface. And I'll add this code. I'll start with a conditional block, and my expression will be if not isRunning. I want to make sure I'm only launching the timer if it's not already running and already visible.
I'll call my existing method called showTimer, and pass in a value of true, and then I'll call a method of the Delayed Confirmation View named start, and that launches the Delayed Confirmation View's action. So, first I'm making it visible, and then I'm starting it. And then I'll flip that flag isRunning and set it to a value of true. And now, I'm ready to launch for the first time. Let's take a look at the code before we see what it does.
Once again, I've added my Delayed Confirmation View to my layout, and I'm using my own custom icon. In my case, a clock. Then, in my Java class, I've gotten a reference to that component, and then I've set a few properties and I'm launching it here after making it visible. I'll run the app on my emulator. Because I set up the click handler on my watch face, I can now click anywhere and I see the animation launch.
But right now, I don't have a good way of running it again. So, let's go back to the code, and I'll fully implement these two event handlers. I'll start with onTimerFinished. This event occurs when the component runs to completion. I'll handle that by calling my showTimer method again, and I'll pass in a value of false, and I'll also reset my flag with isRunning=_false. Then I want to show the user what happened. I'm going to use a built in activity called the Confirmation Activity Class, but I'm going to be using the same activity regardless of whether the user lets the component run to completion or cancels.
And so, I'll create my own custom method. I'll name it showConfirmation. It doesn't exist yet, but I'll mock it up by calling it here. And I'm going to pass in a constant named ConfirmationActivtiy.SUCCESS_ANIMATION, and a string of Confirmed. Now again, this method doesn't exist yet, so I'll use an intention action and create it. My two generated arguments have been named based on what I'm passing in initially.
But I want to be able to use this method for either success or failure, so I'm going to rename these to more neutral names and call them animation and message. And here's the code. First, I'll create an intent object, and I'll instantiate it with new Intent, and I'll pass in this as the context, and ConfirmationActivity.class as the class. Next, I'll add a couple of extras.
I'll call intent.putExtra, and I'll add two extras to indicate the type of the animation and the message that will be displayed. The first Extra will be the type of animation, and I'll pass in the key with this constant ConfirmationActivity.EXTRA_ANIMATION_TYPE, and then I'll pass in my animation argument. I'll duplicate that line of code, and for the second Extra, I'll pass in a key of EXTRA_MESSAGE, and I'll pass in my message that I passed into the method.
And then finally, I'll start the activity. So now I have the code in place to handle what happens when the user lets the component run to completion. Let's test that much. When the app reloads, I'll tap anywhere. I'll let the component run to completion, and then I see an automatically generated success message. I'll let that run again, and you'll see that it includes a graphic, a check mark and my custom message which I set as Confirmed.
Now, I'll go back to the code, and I'll add my last little bit of custom code, and I'll place this in the onTimerSelected method. This code will be executed if the user touches the component before it runs to completion. And then once again, I'll call showTimer and pass in false, and I'll also set my isRunning attribute to false, but I'll also call mDelayedView.reset, and that will reset the confirmation view so that it can run again from scratch.
Then I'll call my showConfirmation method, and this time I'll pass in an animation constant of ConfirmationActivity.FAILURE_ANIMATION and a message of Cancelled. Once again, I can let the confirmation view run to completion, but now this time, I'll touch the confirmation view, and I get the Cancelled message. I have one more little bit of code to review. Notice in the onClick event handler, that I've wrapped all the code to make the timer visible and to start it up again with a conditional block that examines the isRunning bullion field.
This guarantees that if the component is already running, and if the user then touches the watch face around it, that the component won't just randomly restart. I'll run the app one more time and show the result of that. I'll click here, and then I'll click around on the watch face while it's running and it doesn't restart the component. But then once again, I'll cancel that operation and see the Cancelled message. Notice that the success and failure animations go by pretty quickly.
You don't have control over how long those take. That's defined within the confirmation view component, and it's not changeable by the developer. But this component, along with a bit of Java code, let's you create a simple confirmation user interface that lets these or catch issues if they react in time after they've touched something in your wearable app.
- Setting the SDK and devices for app development and testing
- Sending notifications to Android Wear devices
- Adding action buttons and voice input to notifications
- Stacking notifications
- Creating a new wearable app
- Managing layouts for different watch shapes
- Sending messages between phones and Android Wear devices
- Packaging an Android Wear app for distribution