Creating The Watch Faces For Android Wear

Introduction and Background

I wonder why designers yet aren't into these tech things, where most of the tech stuff already needs someone from designing and fine arts. I have a few friends of mine who have a great taste of designs, contrasts and other relative "fine arts" related stuff but they lack good programming skills, efficiently writing apps for mobile devices. Same applies to me, I although have (better, not great) experience of writing applications that do not conserve much resources but I lack a good taste of design principles. On a desktop, you can create a monster app, but once on a miniature device such as handhelds. You have to worry more about resources, because they don't have much. For example, battery, RAM and other hardware components are the resources that I am talking about.

While writing a great application for handhelds and other similar mini devices, you have to worry about battery consumption, CPU usage, RAM consumption and other things. Oh and did I mention you also have to make sure that user is paying attention to their surroundings and not wasting their time tapping your "Next", "Next" buttons? These are a few principles one has to follow while writing applications for handhelds and handworns (can I use that name for watches?). That is pretty much self-explanatory, you have to ensure that your application is not consuming too much of CPU, is not executing calculations on a continuous threads and also whether it is battery-friendly or not?

In the following article, you will learn how to create an Android Wear application that adds a new Watch Face to the list, plus that it follows the basic programming design patterns to ensure that the resources are not used too much. Where to use CPU and how much to use it, when to store the resources to be used while drawing the Face and where to, also how to draw the content on screen. If you need to add more components to the screen, how to do that. All of that is collected and compiled in one article for you!

Until the next section, read what I had a chance to hear from OriginalGriff in one of our conversations,

OriginalGriff says:

    I've written a few uController applications which run on batteries, and you have got to be really, really careful. Battery life is significant - and if you add too much processing, you can drastically shorten battery life. With embedded devices, that can mean the customer going to the competition.

    The current crop of watches are gawd awful because they are not trying to do that: they are trying to be a a "whole phone" on your wrist - and that uses power, lots of it. Where is the point of a watch that won't last all day unless you are really careful what you do with it? People get annoyed enough with modern phones not holding a charge for long enough.

I am sure you get the point of "being cautious" while developing the applications.

Little bit about Android Wear-Smartwatch of our subject!

Before moving one step forward, I wanted to share a bit about Android Wear itself. Android Wear is a new platform provided by Android under Smartwatch category. Android, the leading smartphone operating system, launched their smartwatch versions quite a time ago. It is gaining a limelight rapidly and soon would gain some market share also!

Little bit about Wear Apps

I have another article already set up for you to get yourself started with Android Wear applications, you can head on to that and read it as it has everything that you would require to understand before creating an actual application for Android Wear. The ABC of Android Wear applications.

In order to actually use an application on a real watch, you would have to connect it to your Android device and then install that application on your device. The device will then push the wear app to the wear device, from where it will run on your device.

Why this behavior?

In my opinion, I actually like this kind of framework because it leaves most of the job to the handheld. For example, if you want to download the files, get a reply from web service or to store the data on internet. You can do so from the handheld by passing the data to the handheld.

You would also be able to send requests for network resources to the handheld, handheld in any case will have more battery than the wear device itself. That is why, if you run most of the calculations and store most of the resources on the handheld. It can make your wear application better, in many ways!

Heading off to the studio

In this session I will be using Android Studio to create the wear application and to create the application which has the watch face embedded in itself. The article will only focus on the Watch face part of the application, what it is, how to create it and how to modify it. Plus, how does it look on each device and how to modify the watch based on the device's hardware (square or round). By the end, you will be able to port your ideas to a real Watch!

First of all, create a new project for Android wear (please read the above article, as it provides you with information about creating a project that can be ported on an Android Wear). Make sure you have selected Empty activities, because I will be guiding you step-by-step to create a new Watch face. So, no templating. Once you have created an empty project, read further. Until this stage you are only required to have IDE open and a project created.

Creating the Watch Face

If you have ever programmed an Android application then you already are well familiar with Android Services. Android Services are components that run in background to execute a task. They can start and stop, since they have no UI, they should stop once their work is finished. For example, you can play a music file in background using a service and once file has finished playing the service must stop itself (unless on a repeat). Same way, an Android Wear's Watch Face is a service component. It executes in background and keeps drawing a Face on your watch. In this section, I will walk you through many things about a Watch Face including the "What" and "How"s of Watch Face.

What is a Watch Face?

Putting it simple for you, Watch Face is a service that runs in the background and provides you with a facility to draw graphics in the canvas. The graphics are used to create different faces, write the text, build animations and perform other activities that a developer might want to in their graphical application for Watch. However there are a few things that Watch cannot do, or restrains the developer from doing. They are specially designed for system features only. For example while interaction with Watch Face is allowed, but only single tap is provided. Pinch, drag and other features like a handheld has are not provided in Watch Face.

Android Developers have a great documentation resource in this case and they also provide you with visual feedback and design approaches to be applied to your watches. In this guide you will be taught a few basics about these principles and how to make most out of them in your applications.

Now, if the purpose of a Watch Face is clear to you I think we should continue to next step and create something for this guide. Throughout the process I will explain every bit that I can so that you can understand the procedure of building the application yourself later when you want to.

Getting started-empty project

First of all create a new project, make sure it is empty. A Watch Face basically is a "draw canvas" service. So, in Android APIs there is a service CanvasWatchFaceService and CanvasWatchFaceService.Engine which you need to extend in your application create a Watch Face. The Engine class allows you to actually draw the Watch Face, the methods provided by this class are:

  1. onCreateEngine

    This function is used to get an instance of Engine object. Which can be then used later for different purposes, timers and drawing etc.

  2. invalidate

    This function is similar to View.invalidate function. It would prompt the system to re-draw your Watch Face.

  3. onDraw

    This function is the one we are interested in. It allows us to:

    • Draw the objects on canvas.
    • Provides us with the bounds of the device, to use while drawing over the canvas.
    • Canvas parameter can be used while drawing shapes.
    • Like other APIs, Android Canvas object also provides us with methods to draw shapes, such as:

      • Circles
      • Paths
      • Arcs
      • Bitmaps

        They are simply graphics, drawable in Android language. I will show you a code snippet to convert your drawables to Bitmaps, keep reading.
      • Text

    • Perform other Canvas based functions, provided under Android API.

  4. A few more functions that I won't talk about until I have explained the service itself.

That was simply an overview of the service, next I will show you how to implement it.

Note: For those who are using Eclipse, please download and install the support libraries and plugins before continuing. They are required, on Android Studio they are already installed once you configure your IDE.

Create a new service, implement the base services for creating a Watch Face. The services would then have the interfaces (functions I mean) for you to perform functions on the device and to determine when an event was occurred. Overriding those methods would allow you to perform you own actions.

I have created the following Java class that would act as the service for our Watch Face.

  1. import android.graphics.Canvas;  
  2. import android.graphics.Rect;  
  3. import android.os.Bundle;  
  4. import android.support.wearable.watchface.CanvasWatchFaceService;  
  5. import android.view.SurfaceHolder;  
  6.   
  7. /** 
  8.  * Created by AfzaalAhmad on 09/22/2015. 
  9.  */  
  10.   
  11. public class CSharpCornerWearFaceService extends CanvasWatchFaceService {  
  12.     @Override  
  13.     public Engine onCreateEngine() {  
  14.         return new Engine();  
  15.     }  
  16.   
  17.     private class Engine extends CanvasWatchFaceService.Engine {  
  18.         @Override  
  19.         public void onCreate(SurfaceHolder holder) {  
  20.             super.onCreate(holder);  
  21.         }  
  22.   
  23.         @Override  
  24.         public void onPropertiesChanged(Bundle properties) {  
  25.             super.onPropertiesChanged(properties);  
  26.         }  
  27.   
  28.         @Override  
  29.         public void onTimeTick() {  
  30.             super.onTimeTick();  
  31.         }  
  32.   
  33.         @Override  
  34.         public void onAmbientModeChanged(boolean inAmbientMode) {  
  35.             super.onAmbientModeChanged(inAmbientMode);  
  36.         }  
  37.   
  38.         @Override  
  39.         public void onDraw(Canvas canvas, Rect bounds) {  
  40.   
  41.         }  
  42.   
  43.         @Override  
  44.         public void onVisibilityChanged(boolean visible) {  
  45.             super.onVisibilityChanged(visible);  
  46.         }  
  47.     }  
  48. }  
The class has the implementation of a service, it is almost ready. We need to provide the meta-data that Wear companion app and the device will use to determine the resources of our Watch Face. In the manifest, you need to register the activity as a service, Android will look into the manifest for any of such processes, so, edit the manifest and add the following service element.
  1. <service  
  2.      android:name=".CSharpCornerWearFace"  
  3.      android:label="@string/my_digital_name"  
  4.      android:permission="android.permission.BIND_WALLPAPER" >  
  5.      <meta-data  
  6.          android:name="android.service.wallpaper"  
  7.          android:resource="@xml/watch_face" />  
  8.      <meta-data  
  9.          android:name="com.google.android.wearable.watchface.preview"  
  10.          android:resource="@drawable/preview_digital" />  
  11.      <meta-data  
  12.          android:name="com.google.android.wearable.watchface.preview_circular"  
  13.          android:resource="@drawable/preview_digital_circular" />  
  14.   
  15.      <intent-filter>  
  16.          <action android:name="android.service.wallpaper.WallpaperService" />  
  17.   
  18.          <category android:name="com.google.android.wearable.watchface.category.WATCH_FACE" />  
  19.      </intent-filter>  
  20. </service>  
This would now register your service to be found. Although it is pretty much clear, but let me explain a few of these things here. Now, before we dissect this. I will talk about one more element, wallpaper element. Android Wear Face is drawn on a wallpaper element. You can create a new wallpaper simply. Create a new file under XML folder and name it what you want (in above case, it is watch_face). The code is just 2 lines code,
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <wallpaper xmlns:android="http://schemas.android.com/apk/res/android" />  
Now I will discuss some more about the service definition.
  1. android:name

    This attribute specifies the code file, Android uses the code file with this name and runs the code in it. You would then need to override and implement the functions required in that code file. We already did.

  2. android:label

    This attribute is used to name the Watch Face in the list. You can use any name, I used "C# Corner Wear", a literal string.

  3. meta-data

    These elements specify the meta-data for your service, Watch Face service specifically. Android Wear app or Wear device would use the data specified here and would show the data for your Watch Face.

    • android.service.wallpaper: This would specify the wallpaper element, I have described this above and the method to create it. You would need to create it, because these are required.

    • com.google...watchface.preview: This is the default preview for your Watch Face in a rectangular Wear device.

    • com.google...watchface.preview_circular: This is the default preview for your Watch Face in a circular Wear device.

  4. intent-filter

    The intents that this application shows interest in. They are self-explanatory.

    Once that has been done, we can now consider writing the code to actually draw something on the watch. Our Watch Face has been registered in Android app and would now execute the code in the service to perform actions, such as handling the events, drawing on canvas and hiding or showing the data.

Live preview example

I wanted to show you how our application looks right now, so I think displaying C# Corner on the device would suffice. So I altered the code in the onDraw function and wrote the following code in it,

  1. @Override  
  2. public void onDraw(Canvas canvas, Rect bounds) {  
  3.     final Typeface typeface = Typeface.DEFAULT;  
  4.     Paint paint = new Paint()   
  5.                   {{   
  6.                        setTextSize(40);   
  7.                        setARGB(255255255255);   
  8.                        setTypeface(typeface);   
  9.                   }};  
  10.     canvas.drawText("C# Corner"50100, paint);  
  11. }  
The code is easy and a very compact one, does one thing, "Display C# Corner on the watch!" It does, have a look at the following image. It has printed C# Corner using the indices (50, 100) and the paint object that we have passed to it. The paint object would have the graphics details that we want, for example:
  1. The size of text that we want.
  2. Color of the text.
  3. Typeface to use while drawing the text (font-family?)

The function is drawText. There are some other functions also available, such as drawBitmap and drawArc etc. I won't talk about most of them, but drawBitmap will be used here, soon.

Android Wear showing Csharp Corner
Figure 1: (Square) Android Wear showing C# Corner text rendered on it along with the status icons in the top left corner

Adding some features to it—intermediate level skill

So far I would expect that you are now aware of canvas, Wear service, CanvasWatchFaceService and the functions it provides us with. I will point out a few of these services and then we will head on to the main section to draw something useful on the canvas!

Collecting data

Before you create something, you should ask yourself. "What difference does it make?" What more data does it provide your users, what things does it consider, is it even worthy to upload the application for users? These questions would give you an idea of "What" does the application do.
Although Wear does not have much features as a handheld would have, but you can however use your application to:

  1. Show the date and time

    I would personally recommend that you use Calendar object, but using Time would allow you to work with more features, I somehow still recommend using Calendar instance to get the date and time.

  2. Show addition information

    This is the interesting part, because this is where Wear differs from an ordinary watch. You get to display additional data in the screen also, what ordinary watch doesn't allow. Thus a smartwatch!

    • Show details of reminders, or a particular one coming in fast.
    • Show weather details.
    • Number of missed calls or messages available to read.
    • Total cards present.
    • Any other data from any API or data source!

Once you have collected the data, you can display that on the canvas just as you would draw anything else. Write the data, draw an icon and user would be able to see it right on their way.

By default, Android provides us with many API contracts, such as Calendar. Where you can get the details about reminders, events etc. These contracts allow your application to get data about user's own databases for content. Then using them you can design your own application in a very user-friendly way. That is exactly how other applications get the data for a user. If you are interested, you can always give a look at the WearableCalendarContact class in the support API.

Creating the members

As already mentioned many times, you must never perform most of the tasks in the Wear itself, it does not have much resources as any other handheld might have. That is why it is not preferred to always keep the CPU or RAM busy. In your onCreateEngine function you can initialize the members, or right in somewhere where you are not going to be most often.

A very legit reason to do so is that fo reach of the Bitmap that you try to render, each of the Paint object that you want to create needs to be in the RAM and also takes CPU. For example, the Bitmap to be rendered, each pixel needs CPU time to be rendered. Thus the more pixels you would need the more CPU time would be consumed and at the same time more battery would be drained that is why it is always recommended to not create (and/or initialize) the members again and again. Do them once, wisely.

I am going to think of what am I gonna make? Here is the list of them:

  1. Date-time text
  2. Background color
  3. Image of bob (I hope admins don't mind!)

So, to use them I would need to initialize them earlier, so that when CPU starts to render everything it already has everything and that this process does not gets cycled over and over again.

  1. // The paints that we are going to use in our application.  
  2. Paint textPaint, boldTextPaint, boldTimePaint, normalTimePain, datePaint, backGround;  
  3. Bitmap bob;  
These are the variables that I am going to use in my application. To provide them with a valid value, I will initialize them in the onCreate function.
  1. // Let us initialize them all here  
  2. bob = BitmapFactory.decodeResource(getResources(), R.drawable.logo);  
  3. backGround = new Paint() {{ setARGB(2552551400); }};  
  4. datePaint = createPaint(false25);  
  5. textPaint = createPaint(false40);  
  6. boldTextPaint = createPaint(true40);  
  7. boldTimePaint = createPaint(true40);  
  8. normalTimePaint = createPaint(false40);  
Tip: To convert a drawable resource to a Bitmap object you can use the BitmapFactory and call thedecodeResource on it. It would require the resources and the drawable resource to use.

The function that I am using to create a Paint object is defined as below:
  1. private Paint createPaint(boolean bold, final int fontSize) {  
  2.     Typeface typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL);  
  3.   
  4.     if(bold) {  
  5.         typeface.create(Typeface.DEFAULT, Typeface.BOLD);  
  6.     }  
  7.   
  8.     return new Paint()  
  9.     {{  
  10.          setARGB(255255255255);  
  11.          setTextSize(fontSize);  
  12.     }};  
  13. }  
This creates a new paint and allows us to use it later for rendering the images. I will try it later, before that I want to explain a few more things.

Responding to user-interactions

Users can interact with Android Wear, but only single interaction method is provided. Single tap, other taps and gestures are blocked by default because system wants to focus on them itself, why? I have no idea, I do not represent Android.

You are only allowed to use a single tap, to interact with user and then show him a response. You can for sure ignore that, but that is not the case here. You should consider responding to his request. To do so you would need to override onTapCommand function and also you would need to change the style and ensure that your Wear application is being provided with the tap events. First of all, inside the onCreate function change the style of your application and add the request to capture the tap events.
  1. setWatchFaceStyle(new WatchFaceStyle.Builder(CSharpCornerWearFace.this)  
  2.     .setAcceptsTapEvents(true)  
  3.     .build());  
This would now allow our application to handle the tap events, next we need to override the function to actually provide the features and services when user taps on our application.
  1. @Override  
  2. public void onTapCommand(  
  3.     @TapType int tapType, int x, int y, long eventTime) {  
  4.     switch (tapType) {  
  5.         case WatchFaceService.TAP_TYPE_TAP:  
  6.             // Handle the tap  
  7.             break;  
  8.   
  9.         // There are other cases, not mentioned here. <a href="https://developer.android.com/training/wearables/watch-faces/interacting.html">Read Android guide</a>  
  10.         default:  
  11.             super.onTapCommand(tapType, x, y, eventTime);  
  12.             break;  
  13.     }  
  14. }  
This way you can get the user-interactions. Plus if you give a look at the function's signature you will see that there are two co-ordinate parameters also provided; x and y. These parameters will let us know where user tapped on the screen so that we can trigger a function. Since the canvas is just a drawing board we cannot overlay any application or a hyperlink or any kind of trigger. That is why we would have to determine where our certain feature would be presented when our face is rendered so that we can know user clicked on it!

Reaching both the device sizes

Since Android Wear comes in both sizes, round and rectangular. It is your duty to make sure that your Watch Face looks alike on both faces and also follows the patterns for both the cases. Most of the UI for rectangular cases would fail on a circular one and the ones built for circular would fail on the rectangular. That is why you have to ensure that you are drawing carefully.

To actually get the View details you are not provided with any native Wear features or APIs, in this case you would be using Android wallpaper services functions and override them to provide additional view based stuff. I am talking about onApplyWindowInsets(WindowInsets) function of WallpaperService.Engine. The WindowInsets allows you to get whether the device is round or not. So you can create a member determining whether round data must be drawn or square. Depends on the device type.
  1. @Override  
  2. public void onApplyWindowInsets(WindowInsets insets) {  
  3.     super.onApplyWindowInsets(insets);  
  4.     boolean isRound = insets.isRound();  
  5.   
  6.     if(isRound) {  
  7.         // Render the Face in round mode.  
  8.     } else {  
  9.         // Render the Face in square (or rectangular) mode.  
  10.     }  
  11. }  
I am not going to get in the depth of all of these right now. I am just going to pass them by and share a few code tricks in the advanced level section at the end of the article.

Managing the Watch Style

Another major concept is to manage the style of the watch, what sort of Face do you want to draw and where do system gets to draw its own data. For example, the hot word "Ok Google" remains on the screen when user turns on the device. You have full privilege as to where does that gets drawn. How do the peak cards get shown, do system gets to preview the system time or you are going to do that on your own. All of these things are packed in the WatchFaceStyle object of Android API.
 
You create a new object and then provide some settings to it. The settings are applied to the renderer (Engine) and the Face is drawn based on those settings.
  1. WatchFaceStyle style = new WatchFaceStyle.Builder(CSharpCornerWatchFaceService.this)  
  2.     .setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT)  
  3.     .setBackgroundVisibility(WatchFaceStyle  
  4.                             .BACKGROUND_VISIBILITY_INTERRUPTIVE)  
  5.     .setHotwordIndicatorGravity(Gravity.TOP)  
  6.     .setAcceptsTapEvents(true)  
  7.     .setShowSystemUiTime(false)  
  8.     .build();  
  9.   
  10. setWatchFaceStyle(style);  
The above styles (the properties) are applied while creating the Watch Face. System would use these to provide you with services, such as event handling mechanism, or to draw the time on your canvas, or like rendering the hotwork.

Now if all of these points have been understood it is time to continue and do something else. Like to create the Watch Face, because you only need to understand these concepts.

Creating a simple Watch Face

In this section I will show you the code to actually create a simple Watch Face, the code would be simple enough but afterwards I will try to explain it in parts.

I managed to create a C# Corner Wear Face for this demonstration. First have a look at what is created,

Displaying the Wear Face on both device sizes
Figure 2: Displaying the Wear Face on both device sizes

That displayed, now I guess I would show the code that is used in the same procedure, not a very much long code a very much short piece of code was used to draw the data.

Note: Just pardon the time conflict, it was because of the times when emulators started. Nothing else.
  1. @Override  
  2. public void onDraw(Canvas canvas, Rect bounds) {  
  3.     calendar = Calendar.getInstance(); // Re-instance the Calendar, to update the time  
  4.   
  5.     canvas.drawRect(0, 0, bounds.width(), bounds.height(), whiteBackground); // Entire background Canvas  
  6.     canvas.drawRect(0, 60, bounds.width(), 240, backGround); // Orange color  
  7.   
  8.     // Day  
  9.     canvas.drawText(new SimpleDateFormat("cccc").format(calendar.getTime()), 130, 120, textPaint);  
  10.   
  11.     // String time = String.format("%02d:%02d", mTime.hour, mTime.minute);  
  12.     String time = new SimpleDateFormat("hh:mm a").format(calendar.getTime());  
  13.     canvas.drawText(time, 130, 170, boldTextPaint);  
  14.   
  15.     String date = new SimpleDateFormat("MMMM dd, yyyy").format(calendar.getTime());  
  16.     canvas.drawText(date, 150, 200, darkText);  
  17.     canvas.drawBitmap(bob, 0, 40, null);  
  18. }  
This code would do the trick. As seen in both cases, the Watch Face is drawn without any trouble. But in most of the cases you might want to drop down a few pixels for sake of margins. Like have a look at the "Thursday". In square size it is fair, but in circular shape, it won't give any space while the text is being rendered for "Wednesday", the final "y" may go out of the screen. Always consider making sure that the UI is not having any troubles.

A general tip would be to first of all determine the screen sizes and then get a fraction of the pixel sizes to render using. Well, Bob looks happy in both the screens.

Service code

Here is the entire code for the service (The service was generated using a template provided by Android Studio and then modified by myself to create the Watch Face for C# Corner theme, so most of the code is written by Android team themselves).
  1. package com.afzaalahmadzeeshan.wearfaces;  

  2. import android.content.BroadcastReceiver;  
  3. import android.content.Context;  
  4. import android.content.Intent;  
  5. import android.content.IntentFilter;  
  6. import android.graphics.Bitmap;  
  7. import android.graphics.BitmapFactory;  
  8. import android.graphics.Canvas;  
  9. import android.graphics.Paint;  
  10. import android.graphics.Rect;  
  11. import android.graphics.Typeface;  
  12. import android.os.Bundle;  
  13. import android.os.Handler;  
  14. import android.os.Message;  
  15. import android.support.wearable.watchface.CanvasWatchFaceService;  
  16. import android.support.wearable.watchface.WatchFaceService;  
  17. import android.support.wearable.watchface.WatchFaceStyle;  
  18. import android.view.SurfaceHolder;  
  19.   
  20. import java.lang.ref.WeakReference;  
  21. import java.text.SimpleDateFormat;  
  22. import java.util.Calendar;  
  23. import java.util.concurrent.TimeUnit;  
  24.   
  25. /** 
  26. * Analog watch face with a ticking second hand. In ambient mode, the second hand isn't shown. On 
  27. * devices with low-bit ambient mode, the hands are drawn without anti-aliasing in ambient mode. 
  28. */  
  29. public class CSharpCornerWear extends CanvasWatchFaceService {  
  30.   private static Paint textPaint, boldTextPaint, whiteBackground, darkText, orangeText, blueText;  
  31.   private static Bitmap logo;  
  32.   private static Calendar calendar;  
  33.   private static final long INTERACTIVE_UPDATE_RATE_MS = TimeUnit.SECONDS.toMillis(1);  
  34.   private static final int MSG_UPDATE_TIME = 0;  
  35.   
  36.   @Override  
  37.   public Engine onCreateEngine() {  
  38.       return new Engine();  
  39.   }  
  40.   
  41.   private class Engine extends CanvasWatchFaceService.Engine {  
  42.   
  43.       final Handler mUpdateTimeHandler = new EngineHandler(this);  
  44.   
  45.       final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {  
  46.           @Override  
  47.           public void onReceive(Context context, Intent intent) {  
  48.               calendar = Calendar.getInstance();  
  49.           }  
  50.       };  
  51.       boolean mRegisteredTimeZoneReceiver = false;  
  52.   
  53.       /** 
  54.        * Whether the display supports fewer bits for each color in ambient mode. When true, we 
  55.        * disable anti-aliasing in ambient mode. 
  56.        */  
  57.       boolean mLowBitAmbient;  
  58.   
  59.       @Override  
  60.       public void onCreate(SurfaceHolder holder) {  
  61.           super.onCreate(holder);  
  62.   
  63.           setWatchFaceStyle(new WatchFaceStyle.Builder(CSharpCornerWear.this)  
  64.                   .setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT)  
  65.                   .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)  
  66.                   .setShowSystemUiTime(false)  
  67.                   .build());  
  68.   
  69.           // Let us initialize them all here  
  70.           logo = BitmapFactory.decodeResource(getResources(), R.drawable.sitelogo);  
  71.           textPaint = createPaint(false40);  
  72.           orangeText = createPaint(false40);  
  73.           blueText = createPaint(false40);  
  74.           boldTextPaint = createPaint(true40);  
  75.           whiteBackground = createPaint(false0);  
  76.           whiteBackground.setARGB(255255255255);  
  77.           darkText = new Paint() {{ setARGB(255505050); setTextSize(18); }};  
  78.   
  79.           setWatchFaceStyle(new WatchFaceStyle.Builder(CSharpCornerWear.this)  
  80.                   .setAcceptsTapEvents(true)  
  81.                   .build());  
  82.   
  83.           calendar = Calendar.getInstance();  
  84.       }  
  85.   
  86.       private Paint createPaint(final boolean bold, final int fontSize) {  
  87.           final Typeface typeface = (bold) ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT;  
  88.   
  89.           return new Paint()  
  90.           {{  
  91.                   setARGB(255000);  
  92.                   setTextSize(fontSize);  
  93.                   setTypeface(typeface);  
  94.                   setAntiAlias(true);  
  95.           }};  
  96.       }  
  97.   
  98.       @Override  
  99.       public void onDestroy() {  
  100.           mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);  
  101.           super.onDestroy();  
  102.       }  
  103.   
  104.       @Override  
  105.       public void onPropertiesChanged(Bundle properties) {  
  106.           super.onPropertiesChanged(properties);  
  107.           mLowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false);  
  108.       }  
  109.   
  110.       @Override  
  111.       public void onTimeTick() {  
  112.           super.onTimeTick();  
  113.           invalidate();  
  114.       }  
  115.   
  116.       @Override  
  117.       public void onAmbientModeChanged(boolean inAmbientMode) {  
  118.           super.onAmbientModeChanged(inAmbientMode);  
  119.           if (inAmbientMode) {  
  120.               if (mLowBitAmbient) {  
  121.               }  
  122.               invalidate();  
  123.           }  
  124.   
  125.           // Whether the timer should be running depends on whether we're visible (as well as  
  126.           // whether we're in ambient mode), so we may need to start or stop the timer.  
  127.           updateTimer();  
  128.       }  
  129.   
  130.       @Override  
  131.       public void onDraw(Canvas canvas, Rect bounds) {  
  132.           calendar = Calendar.getInstance();  
  133.   
  134.           canvas.drawRect(00, bounds.width(), bounds.height(), whiteBackground); // Entire background Canvas  
  135.   
  136.           canvas.drawText(new SimpleDateFormat("cccc").format(calendar.getTime()), 8080, textPaint);  
  137.   
  138.           // String time = String.format("%02d:%02d", mTime.hour, mTime.minute);  
  139.           String time = new SimpleDateFormat("hh:mm a").format(calendar.getTime());  
  140.           orangeText.setARGB(255255700);  
  141.           canvas.drawText(time.substring(02), 110170, orangeText);  
  142.           blueText.setARGB(2550150240);  
  143.           canvas.drawText(time.substring(28), 158170, blueText);  
  144.   
  145.           String date = new SimpleDateFormat("MMMM dd, yyyy").format(calendar.getTime());  
  146.           canvas.drawText(date, bounds.width() / 4f, bounds.height() - 50, darkText);  
  147.           canvas.drawBitmap(logo, 20130null);  
  148.       }  
  149.   
  150.       @Override  
  151.       public void onVisibilityChanged(boolean visible) {  
  152.           super.onVisibilityChanged(visible);  
  153.   
  154.           if (visible) {  
  155.               registerReceiver();  
  156.               // Update time zone in case it changed while we weren't visible.  
  157.               calendar = Calendar.getInstance();  
  158.           } else {  
  159.               unregisterReceiver();  
  160.           }  
  161.   
  162.           // Whether the timer should be running depends on whether we're visible (as well as  
  163.           // whether we're in ambient mode), so we may need to start or stop the timer.  
  164.           updateTimer();  
  165.       }  
  166.   
  167.       private void registerReceiver() {  
  168.           if (mRegisteredTimeZoneReceiver) {  
  169.               return;  
  170.           }  
  171.           mRegisteredTimeZoneReceiver = true;  
  172.           IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);  
  173.           CSharpCornerWear.this.registerReceiver(mTimeZoneReceiver, filter);  
  174.       }  
  175.   
  176.       private void unregisterReceiver() {  
  177.           if (!mRegisteredTimeZoneReceiver) {  
  178.               return;  
  179.           }  
  180.           mRegisteredTimeZoneReceiver = false;  
  181.           CSharpCornerWear.this.unregisterReceiver(mTimeZoneReceiver);  
  182.       }  
  183.   
  184.       /** 
  185.        * Starts the {@link #mUpdateTimeHandler} timer if it should be running and isn't currently 
  186.        * or stops it if it shouldn't be running but currently is. 
  187.        */  
  188.       private void updateTimer() {  
  189.           mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);  
  190.           if (shouldTimerBeRunning()) {  
  191.               mUpdateTimeHandler.sendEmptyMessage(MSG_UPDATE_TIME);  
  192.           }  
  193.       }  
  194.   
  195.       /** 
  196.        * Returns whether the {@link #mUpdateTimeHandler} timer should be running. The timer should 
  197.        * only run when we're visible and in interactive mode. 
  198.        */  
  199.       private boolean shouldTimerBeRunning() {  
  200.           return isVisible() && !isInAmbientMode();  
  201.       }  
  202.   
  203.       /** 
  204.        * Handle updating the time periodically in interactive mode. 
  205.        */  
  206.       private void handleUpdateTimeMessage() {  
  207.           invalidate();  
  208.           if (shouldTimerBeRunning()) {  
  209.               long timeMs = System.currentTimeMillis();  
  210.               long delayMs = INTERACTIVE_UPDATE_RATE_MS  
  211.                       - (timeMs % INTERACTIVE_UPDATE_RATE_MS);  
  212.               mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);  
  213.           }  
  214.       }  
  215.   }  
  216.   
  217.   private static class EngineHandler extends Handler {  
  218.       private final WeakReference<CSharpCornerWear.Engine> mWeakReference;  
  219.   
  220.       public EngineHandler(CSharpCornerWear.Engine reference) {  
  221.           mWeakReference = new WeakReference<>(reference);  
  222.       }  
  223.   
  224.       @Override  
  225.       public void handleMessage(Message msg) {  
  226.           CSharpCornerWear.Engine engine = mWeakReference.get();  
  227.           if (engine != null) {  
  228.               switch (msg.what) {  
  229.                   case MSG_UPDATE_TIME:  
  230.                       engine.handleUpdateTimeMessage();  
  231.                       break;  
  232.               }  
  233.           }  
  234.       }  
  235.   }  
  236. }  
This code is also present in the source code that is packaged along with the article. Finally, the manifest for this one was edited as following. The manifest holds the information for the face to be drawn and would be used to provide our Wear Face in the list of faces in Android Wear app companion and the device itself.
  1. <service  
  2.      android:name=".CSharpCornerWear"  
  3.      android:label="CSharpCorner Wear"  
  4.      android:permission="android.permission.BIND_WALLPAPER" >  
  5.      <meta-data  
  6.          android:name="android.service.wallpaper"  
  7.          android:resource="@xml/watch_face" />  
  8.      <meta-data  
  9.          android:name="com.google.android.wearable.watchface.preview"  
  10.          android:resource="@drawable/preview_analog" />  
  11.      <meta-data  
  12.          android:name="com.google.android.wearable.watchface.preview_circular"  
  13.          android:resource="@drawable/preview_analog" />  
  14.   
  15.      <intent-filter>  
  16.          <action android:name="android.service.wallpaper.WallpaperService" />  
  17.   
  18.          <category android:name="com.google.android.wearable.watchface.category.WATCH_FACE" />  
  19.      </intent-filter>  
  20. </service>  
Go ahead and download the source code package provided and test it in your own environment!

Points of Interest

In this article you have seen how to create a Watch Face, a Watch Face is simply a service that renders objects on the screen for the watch. There are not much facilities provided, but the Canvas is provided which can be used to draw objects.

There is no TL;DR for the article, I hope you would not feel bored by reading it fully! 


Similar Articles