1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.wearable.watchface;
18 
19 import android.graphics.Color;
20 import android.net.Uri;
21 import android.util.Log;
22 
23 import com.google.android.gms.common.api.GoogleApiClient;
24 import com.google.android.gms.common.api.ResultCallback;
25 import com.google.android.gms.wearable.DataApi;
26 import com.google.android.gms.wearable.DataItem;
27 import com.google.android.gms.wearable.DataMap;
28 import com.google.android.gms.wearable.DataMapItem;
29 import com.google.android.gms.wearable.NodeApi;
30 import com.google.android.gms.wearable.PutDataMapRequest;
31 import com.google.android.gms.wearable.Wearable;
32 
33 public final class DigitalWatchFaceUtil {
34     private static final String TAG = "DigitalWatchFaceUtil";
35 
36     /**
37      * The {@link DataMap} key for {@link DigitalWatchFaceService} background color name.
38      * The color name must be a {@link String} recognized by {@link Color#parseColor}.
39      */
40     public static final String KEY_BACKGROUND_COLOR = "BACKGROUND_COLOR";
41 
42     /**
43      * The {@link DataMap} key for {@link DigitalWatchFaceService} hour digits color name.
44      * The color name must be a {@link String} recognized by {@link Color#parseColor}.
45      */
46     public static final String KEY_HOURS_COLOR = "HOURS_COLOR";
47 
48     /**
49      * The {@link DataMap} key for {@link DigitalWatchFaceService} minute digits color name.
50      * The color name must be a {@link String} recognized by {@link Color#parseColor}.
51      */
52     public static final String KEY_MINUTES_COLOR = "MINUTES_COLOR";
53 
54     /**
55      * The {@link DataMap} key for {@link DigitalWatchFaceService} second digits color name.
56      * The color name must be a {@link String} recognized by {@link Color#parseColor}.
57      */
58     public static final String KEY_SECONDS_COLOR = "SECONDS_COLOR";
59 
60     /**
61      * The path for the {@link DataItem} containing {@link DigitalWatchFaceService} configuration.
62      */
63     public static final String PATH_WITH_FEATURE = "/watch_face_config/Digital";
64 
65     /**
66      * Name of the default interactive mode background color and the ambient mode background color.
67      */
68     public static final String COLOR_NAME_DEFAULT_AND_AMBIENT_BACKGROUND = "Black";
69     public static final int COLOR_VALUE_DEFAULT_AND_AMBIENT_BACKGROUND =
70             parseColor(COLOR_NAME_DEFAULT_AND_AMBIENT_BACKGROUND);
71 
72     /**
73      * Name of the default interactive mode hour digits color and the ambient mode hour digits
74      * color.
75      */
76     public static final String COLOR_NAME_DEFAULT_AND_AMBIENT_HOUR_DIGITS = "White";
77     public static final int COLOR_VALUE_DEFAULT_AND_AMBIENT_HOUR_DIGITS =
78             parseColor(COLOR_NAME_DEFAULT_AND_AMBIENT_HOUR_DIGITS);
79 
80     /**
81      * Name of the default interactive mode minute digits color and the ambient mode minute digits
82      * color.
83      */
84     public static final String COLOR_NAME_DEFAULT_AND_AMBIENT_MINUTE_DIGITS = "White";
85     public static final int COLOR_VALUE_DEFAULT_AND_AMBIENT_MINUTE_DIGITS =
86             parseColor(COLOR_NAME_DEFAULT_AND_AMBIENT_MINUTE_DIGITS);
87 
88     /**
89      * Name of the default interactive mode second digits color and the ambient mode second digits
90      * color.
91      */
92     public static final String COLOR_NAME_DEFAULT_AND_AMBIENT_SECOND_DIGITS = "Gray";
93     public static final int COLOR_VALUE_DEFAULT_AND_AMBIENT_SECOND_DIGITS =
94             parseColor(COLOR_NAME_DEFAULT_AND_AMBIENT_SECOND_DIGITS);
95 
96     /**
97      * Callback interface to perform an action with the current config {@link DataMap} for
98      * {@link DigitalWatchFaceService}.
99      */
100     public interface FetchConfigDataMapCallback {
101         /**
102          * Callback invoked with the current config {@link DataMap} for
103          * {@link DigitalWatchFaceService}.
104          */
onConfigDataMapFetched(DataMap config)105         void onConfigDataMapFetched(DataMap config);
106     }
107 
parseColor(String colorName)108     private static int parseColor(String colorName) {
109         return Color.parseColor(colorName.toLowerCase());
110     }
111 
112     /**
113      * Asynchronously fetches the current config {@link DataMap} for {@link DigitalWatchFaceService}
114      * and passes it to the given callback.
115      * <p>
116      * If the current config {@link DataItem} doesn't exist, it isn't created and the callback
117      * receives an empty DataMap.
118      */
fetchConfigDataMap(final GoogleApiClient client, final FetchConfigDataMapCallback callback)119     public static void fetchConfigDataMap(final GoogleApiClient client,
120             final FetchConfigDataMapCallback callback) {
121         Wearable.NodeApi.getLocalNode(client).setResultCallback(
122                 new ResultCallback<NodeApi.GetLocalNodeResult>() {
123                     @Override
124                     public void onResult(NodeApi.GetLocalNodeResult getLocalNodeResult) {
125                         String localNode = getLocalNodeResult.getNode().getId();
126                         Uri uri = new Uri.Builder()
127                                 .scheme("wear")
128                                 .path(DigitalWatchFaceUtil.PATH_WITH_FEATURE)
129                                 .authority(localNode)
130                                 .build();
131                         Wearable.DataApi.getDataItem(client, uri)
132                                 .setResultCallback(new DataItemResultCallback(callback));
133                     }
134                 }
135         );
136     }
137 
138     /**
139      * Overwrites (or sets, if not present) the keys in the current config {@link DataItem} with
140      * the ones appearing in the given {@link DataMap}. If the config DataItem doesn't exist,
141      * it's created.
142      * <p>
143      * It is allowed that only some of the keys used in the config DataItem appear in
144      * {@code configKeysToOverwrite}. The rest of the keys remains unmodified in this case.
145      */
overwriteKeysInConfigDataMap(final GoogleApiClient googleApiClient, final DataMap configKeysToOverwrite)146     public static void overwriteKeysInConfigDataMap(final GoogleApiClient googleApiClient,
147             final DataMap configKeysToOverwrite) {
148 
149         DigitalWatchFaceUtil.fetchConfigDataMap(googleApiClient,
150                 new FetchConfigDataMapCallback() {
151                     @Override
152                     public void onConfigDataMapFetched(DataMap currentConfig) {
153                         DataMap overwrittenConfig = new DataMap();
154                         overwrittenConfig.putAll(currentConfig);
155                         overwrittenConfig.putAll(configKeysToOverwrite);
156                         DigitalWatchFaceUtil.putConfigDataItem(googleApiClient, overwrittenConfig);
157                     }
158                 }
159         );
160     }
161 
162     /**
163      * Overwrites the current config {@link DataItem}'s {@link DataMap} with {@code newConfig}.
164      * If the config DataItem doesn't exist, it's created.
165      */
putConfigDataItem(GoogleApiClient googleApiClient, DataMap newConfig)166     public static void putConfigDataItem(GoogleApiClient googleApiClient, DataMap newConfig) {
167         PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(PATH_WITH_FEATURE);
168         putDataMapRequest.setUrgent();
169         DataMap configToPut = putDataMapRequest.getDataMap();
170         configToPut.putAll(newConfig);
171         Wearable.DataApi.putDataItem(googleApiClient, putDataMapRequest.asPutDataRequest())
172                 .setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
173                     @Override
174                     public void onResult(DataApi.DataItemResult dataItemResult) {
175                         if (Log.isLoggable(TAG, Log.DEBUG)) {
176                             Log.d(TAG, "putDataItem result status: " + dataItemResult.getStatus());
177                         }
178                     }
179                 });
180     }
181 
182     private static class DataItemResultCallback implements ResultCallback<DataApi.DataItemResult> {
183 
184         private final FetchConfigDataMapCallback mCallback;
185 
DataItemResultCallback(FetchConfigDataMapCallback callback)186         public DataItemResultCallback(FetchConfigDataMapCallback callback) {
187             mCallback = callback;
188         }
189 
190         @Override
onResult(DataApi.DataItemResult dataItemResult)191         public void onResult(DataApi.DataItemResult dataItemResult) {
192             if (dataItemResult.getStatus().isSuccess()) {
193                 if (dataItemResult.getDataItem() != null) {
194                     DataItem configDataItem = dataItemResult.getDataItem();
195                     DataMapItem dataMapItem = DataMapItem.fromDataItem(configDataItem);
196                     DataMap config = dataMapItem.getDataMap();
197                     mCallback.onConfigDataMapFetched(config);
198                 } else {
199                     mCallback.onConfigDataMapFetched(new DataMap());
200                 }
201             }
202         }
203     }
204 
DigitalWatchFaceUtil()205     private DigitalWatchFaceUtil() { }
206 }
207