1 /* 2 * Copyright (C) 2019 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 package com.android.keyguard.clock; 17 18 import android.annotation.Nullable; 19 import android.content.ContentResolver; 20 import android.provider.Settings; 21 import android.util.Log; 22 23 import com.android.internal.annotations.VisibleForTesting; 24 25 import org.json.JSONException; 26 import org.json.JSONObject; 27 28 /** 29 * Wrapper around Settings used for testing. 30 */ 31 public class SettingsWrapper { 32 33 private static final String TAG = "ClockFaceSettings"; 34 private static final String CUSTOM_CLOCK_FACE = Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE; 35 private static final String DOCKED_CLOCK_FACE = Settings.Secure.DOCKED_CLOCK_FACE; 36 private static final String CLOCK_FIELD = "clock"; 37 38 private final ContentResolver mContentResolver; 39 private final Migration mMigration; 40 SettingsWrapper(ContentResolver contentResolver)41 SettingsWrapper(ContentResolver contentResolver) { 42 this(contentResolver, new Migrator(contentResolver)); 43 } 44 45 @VisibleForTesting SettingsWrapper(ContentResolver contentResolver, Migration migration)46 SettingsWrapper(ContentResolver contentResolver, Migration migration) { 47 mContentResolver = contentResolver; 48 mMigration = migration; 49 } 50 51 /** 52 * Gets the value stored in settings for the custom clock face. 53 * 54 * @param userId ID of the user. 55 */ getLockScreenCustomClockFace(int userId)56 String getLockScreenCustomClockFace(int userId) { 57 return decode( 58 Settings.Secure.getStringForUser(mContentResolver, CUSTOM_CLOCK_FACE, userId), 59 userId); 60 } 61 62 /** 63 * Gets the value stored in settings for the clock face to use when docked. 64 * 65 * @param userId ID of the user. 66 */ getDockedClockFace(int userId)67 String getDockedClockFace(int userId) { 68 return Settings.Secure.getStringForUser(mContentResolver, DOCKED_CLOCK_FACE, userId); 69 } 70 71 /** 72 * Decodes the string stored in settings, which should be formatted as JSON. 73 * @param value String stored in settings. If value is not JSON, then the settings is 74 * overwritten with JSON containing the prior value. 75 * @return ID of the clock face to show on AOD and lock screen. If value is not JSON, the value 76 * is returned. 77 */ 78 @VisibleForTesting decode(@ullable String value, int userId)79 String decode(@Nullable String value, int userId) { 80 if (value == null) { 81 return value; 82 } 83 JSONObject json; 84 try { 85 json = new JSONObject(value); 86 } catch (JSONException ex) { 87 Log.e(TAG, "Settings value is not valid JSON", ex); 88 // The settings value isn't JSON since it didn't parse so migrate the value to JSON. 89 // TODO(b/135674383): Remove this migration path in the following release. 90 mMigration.migrate(value, userId); 91 return value; 92 } 93 try { 94 return json.getString(CLOCK_FIELD); 95 } catch (JSONException ex) { 96 Log.e(TAG, "JSON object does not contain clock field.", ex); 97 return null; 98 } 99 } 100 101 interface Migration { migrate(String value, int userId)102 void migrate(String value, int userId); 103 } 104 105 /** 106 * Implementation of {@link Migration} that writes valid JSON back to Settings. 107 */ 108 private static final class Migrator implements Migration { 109 110 private final ContentResolver mContentResolver; 111 Migrator(ContentResolver contentResolver)112 Migrator(ContentResolver contentResolver) { 113 mContentResolver = contentResolver; 114 } 115 116 /** 117 * Migrate settings values that don't parse by converting to JSON format. 118 * 119 * Values in settings must be JSON to be backed up and restored. To help users maintain 120 * their current settings, convert existing values into the JSON format. 121 * 122 * TODO(b/135674383): Remove this migration code in the following release. 123 */ 124 @Override migrate(String value, int userId)125 public void migrate(String value, int userId) { 126 try { 127 JSONObject json = new JSONObject(); 128 json.put(CLOCK_FIELD, value); 129 Settings.Secure.putStringForUser(mContentResolver, CUSTOM_CLOCK_FACE, 130 json.toString(), 131 userId); 132 } catch (JSONException ex) { 133 Log.e(TAG, "Failed migrating settings value to JSON format", ex); 134 } 135 } 136 } 137 } 138