1 /*
2  * Copyright (C) 2017 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.util;
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 import com.example.android.wearable.watchface.watchface.DigitalWatchFaceService;
34 
35 public final class DigitalWatchFaceUtil {
36     private static final String TAG = "DigitalWatchFaceUtil";
37 
38     /**
39      * The {@link DataMap} key for {@link DigitalWatchFaceService} background color name.
40      * The color name must be a {@link String} recognized by {@link Color#parseColor}.
41      */
42     public static final String KEY_BACKGROUND_COLOR = "BACKGROUND_COLOR";
43 
44     /**
45      * The {@link DataMap} key for {@link DigitalWatchFaceService} hour digits color name.
46      * The color name must be a {@link String} recognized by {@link Color#parseColor}.
47      */
48     public static final String KEY_HOURS_COLOR = "HOURS_COLOR";
49 
50     /**
51      * The {@link DataMap} key for {@link DigitalWatchFaceService} minute digits color name.
52      * The color name must be a {@link String} recognized by {@link Color#parseColor}.
53      */
54     public static final String KEY_MINUTES_COLOR = "MINUTES_COLOR";
55 
56     /**
57      * The {@link DataMap} key for {@link DigitalWatchFaceService} second digits color name.
58      * The color name must be a {@link String} recognized by {@link Color#parseColor}.
59      */
60     public static final String KEY_SECONDS_COLOR = "SECONDS_COLOR";
61 
62     /**
63      * The path for the {@link DataItem} containing {@link DigitalWatchFaceService} configuration.
64      */
65     public static final String PATH_WITH_FEATURE = "/watch_face_config/Digital";
66 
67     /**
68      * Name of the default interactive mode background color and the ambient mode background color.
69      */
70     public static final String COLOR_NAME_DEFAULT_AND_AMBIENT_BACKGROUND = "Black";
71     public static final int COLOR_VALUE_DEFAULT_AND_AMBIENT_BACKGROUND =
72             parseColor(COLOR_NAME_DEFAULT_AND_AMBIENT_BACKGROUND);
73 
74     /**
75      * Name of the default interactive mode hour digits color and the ambient mode hour digits
76      * color.
77      */
78     public static final String COLOR_NAME_DEFAULT_AND_AMBIENT_HOUR_DIGITS = "White";
79     public static final int COLOR_VALUE_DEFAULT_AND_AMBIENT_HOUR_DIGITS =
80             parseColor(COLOR_NAME_DEFAULT_AND_AMBIENT_HOUR_DIGITS);
81 
82     /**
83      * Name of the default interactive mode minute digits color and the ambient mode minute digits
84      * color.
85      */
86     public static final String COLOR_NAME_DEFAULT_AND_AMBIENT_MINUTE_DIGITS = "White";
87     public static final int COLOR_VALUE_DEFAULT_AND_AMBIENT_MINUTE_DIGITS =
88             parseColor(COLOR_NAME_DEFAULT_AND_AMBIENT_MINUTE_DIGITS);
89 
90     /**
91      * Name of the default interactive mode second digits color and the ambient mode second digits
92      * color.
93      */
94     public static final String COLOR_NAME_DEFAULT_AND_AMBIENT_SECOND_DIGITS = "Gray";
95     public static final int COLOR_VALUE_DEFAULT_AND_AMBIENT_SECOND_DIGITS =
96             parseColor(COLOR_NAME_DEFAULT_AND_AMBIENT_SECOND_DIGITS);
97 
98     /**
99      * Callback interface to perform an action with the current config {@link DataMap} for
100      * {@link DigitalWatchFaceService}.
101      */
102     public interface FetchConfigDataMapCallback {
103         /**
104          * Callback invoked with the current config {@link DataMap} for
105          * {@link DigitalWatchFaceService}.
106          */
onConfigDataMapFetched(DataMap config)107         void onConfigDataMapFetched(DataMap config);
108     }
109 
parseColor(String colorName)110     private static int parseColor(String colorName) {
111         return Color.parseColor(colorName.toLowerCase());
112     }
113 
114     /**
115      * Asynchronously fetches the current config {@link DataMap} for {@link DigitalWatchFaceService}
116      * and passes it to the given callback.
117      * <p>
118      * If the current config {@link DataItem} doesn't exist, it isn't created and the callback
119      * receives an empty DataMap.
120      */
fetchConfigDataMap(final GoogleApiClient client, final FetchConfigDataMapCallback callback)121     public static void fetchConfigDataMap(final GoogleApiClient client,
122             final FetchConfigDataMapCallback callback) {
123         Wearable.NodeApi.getLocalNode(client).setResultCallback(
124                 new ResultCallback<NodeApi.GetLocalNodeResult>() {
125                     @Override
126                     public void onResult(NodeApi.GetLocalNodeResult getLocalNodeResult) {
127                         String localNode = getLocalNodeResult.getNode().getId();
128                         Uri uri = new Uri.Builder()
129                                 .scheme("wear")
130                                 .path(DigitalWatchFaceUtil.PATH_WITH_FEATURE)
131                                 .authority(localNode)
132                                 .build();
133                         Wearable.DataApi.getDataItem(client, uri)
134                                 .setResultCallback(new DataItemResultCallback(callback));
135                     }
136                 }
137         );
138     }
139 
140     /**
141      * Overwrites (or sets, if not present) the keys in the current config {@link DataItem} with
142      * the ones appearing in the given {@link DataMap}. If the config DataItem doesn't exist,
143      * it's created.
144      * <p>
145      * It is allowed that only some of the keys used in the config DataItem appear in
146      * {@code configKeysToOverwrite}. The rest of the keys remains unmodified in this case.
147      */
overwriteKeysInConfigDataMap(final GoogleApiClient googleApiClient, final DataMap configKeysToOverwrite)148     public static void overwriteKeysInConfigDataMap(final GoogleApiClient googleApiClient,
149             final DataMap configKeysToOverwrite) {
150 
151         DigitalWatchFaceUtil.fetchConfigDataMap(googleApiClient,
152                 new FetchConfigDataMapCallback() {
153                     @Override
154                     public void onConfigDataMapFetched(DataMap currentConfig) {
155                         DataMap overwrittenConfig = new DataMap();
156                         overwrittenConfig.putAll(currentConfig);
157                         overwrittenConfig.putAll(configKeysToOverwrite);
158                         DigitalWatchFaceUtil.putConfigDataItem(googleApiClient, overwrittenConfig);
159                     }
160                 }
161         );
162     }
163 
164     /**
165      * Overwrites the current config {@link DataItem}'s {@link DataMap} with {@code newConfig}.
166      * If the config DataItem doesn't exist, it's created.
167      */
putConfigDataItem(GoogleApiClient googleApiClient, DataMap newConfig)168     public static void putConfigDataItem(GoogleApiClient googleApiClient, DataMap newConfig) {
169         PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(PATH_WITH_FEATURE);
170         putDataMapRequest.setUrgent();
171         DataMap configToPut = putDataMapRequest.getDataMap();
172         configToPut.putAll(newConfig);
173         Wearable.DataApi.putDataItem(googleApiClient, putDataMapRequest.asPutDataRequest())
174                 .setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
175                     @Override
176                     public void onResult(DataApi.DataItemResult dataItemResult) {
177                         if (Log.isLoggable(TAG, Log.DEBUG)) {
178                             Log.d(TAG, "putDataItem result status: " + dataItemResult.getStatus());
179                         }
180                     }
181                 });
182     }
183 
184     private static class DataItemResultCallback implements ResultCallback<DataApi.DataItemResult> {
185 
186         private final FetchConfigDataMapCallback mCallback;
187 
DataItemResultCallback(FetchConfigDataMapCallback callback)188         public DataItemResultCallback(FetchConfigDataMapCallback callback) {
189             mCallback = callback;
190         }
191 
192         @Override
onResult(DataApi.DataItemResult dataItemResult)193         public void onResult(DataApi.DataItemResult dataItemResult) {
194             if (dataItemResult.getStatus().isSuccess()) {
195                 if (dataItemResult.getDataItem() != null) {
196                     DataItem configDataItem = dataItemResult.getDataItem();
197                     DataMapItem dataMapItem = DataMapItem.fromDataItem(configDataItem);
198                     DataMap config = dataMapItem.getDataMap();
199                     mCallback.onConfigDataMapFetched(config);
200                 } else {
201                     mCallback.onConfigDataMapFetched(new DataMap());
202                 }
203             }
204         }
205     }
206 
DigitalWatchFaceUtil()207     private DigitalWatchFaceUtil() { }
208 }
209