1 /*
2  * Copyright (C) 2015 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.android.tv.tuner;
18 
19 import android.content.ContentResolver;
20 import android.content.ContentValues;
21 import android.content.Context;
22 import android.content.SharedPreferences;
23 import android.database.ContentObserver;
24 import android.database.Cursor;
25 import android.os.AsyncTask;
26 import android.os.Bundle;
27 import android.os.Handler;
28 import android.support.annotation.MainThread;
29 
30 import com.android.tv.common.SoftPreconditions;
31 import com.android.tv.tuner.TunerPreferenceProvider.Preferences;
32 import com.android.tv.tuner.util.TisConfiguration;
33 
34 /**
35  * A helper class for the USB tuner preferences.
36  */
37 public class TunerPreferences {
38     private static final String TAG = "TunerPreferences";
39 
40     private static final String PREFS_KEY_CHANNEL_DATA_VERSION = "channel_data_version";
41     private static final String PREFS_KEY_SCANNED_CHANNEL_COUNT = "scanned_channel_count";
42     private static final String PREFS_KEY_SCAN_DONE = "scan_done";
43     private static final String PREFS_KEY_LAUNCH_SETUP = "launch_setup";
44     private static final String PREFS_KEY_STORE_TS_STREAM = "store_ts_stream";
45 
46     private static final String SHARED_PREFS_NAME = "com.android.tv.tuner.preferences";
47 
48     public static final int CHANNEL_DATA_VERSION_NOT_SET = -1;
49 
50     private static final Bundle sPreferenceValues = new Bundle();
51     private static LoadPreferencesTask sLoadPreferencesTask;
52     private static ContentObserver sContentObserver;
53 
54     private static boolean sInitialized;
55 
56     /**
57      * Initializes the USB tuner preferences.
58      */
59     @MainThread
initialize(final Context context)60     public static void initialize(final Context context) {
61         if (sInitialized) {
62             return;
63         }
64         sInitialized = true;
65         if (useContentProvider(context)) {
66             loadPreferences(context);
67             sContentObserver = new ContentObserver(new Handler()) {
68                 @Override
69                 public void onChange(boolean selfChange) {
70                     loadPreferences(context);
71                 }
72             };
73             context.getContentResolver().registerContentObserver(
74                     TunerPreferenceProvider.Preferences.CONTENT_URI, true, sContentObserver);
75         } else {
76             new AsyncTask<Void, Void, Void>() {
77                 @Override
78                 protected Void doInBackground(Void... params) {
79                     getSharedPreferences(context);
80                     return null;
81                 }
82             }.execute();
83         }
84     }
85 
86     /**
87      * Releases the resources.
88      */
89     @MainThread
release(Context context)90     public static void release(Context context) {
91         if (useContentProvider(context) && sContentObserver != null) {
92             context.getContentResolver().unregisterContentObserver(sContentObserver);
93         }
94     }
95 
96     /**
97      * Loads the preferences from database.
98      * <p>
99      * This preferences is used across processes, so the preferences should be loaded again when the
100      * databases changes.
101      */
loadPreferences(Context context)102     public static synchronized void loadPreferences(Context context) {
103         if (sLoadPreferencesTask != null
104                 && sLoadPreferencesTask.getStatus() != AsyncTask.Status.FINISHED) {
105             sLoadPreferencesTask.cancel(true);
106         }
107         sLoadPreferencesTask = new LoadPreferencesTask(context);
108         sLoadPreferencesTask.execute();
109     }
110 
useContentProvider(Context context)111     private static boolean useContentProvider(Context context) {
112         // If TIS is a part of LC, it should use ContentProvider to resolve multiple process access.
113         return TisConfiguration.isPackagedWithLiveChannels(context);
114     }
115 
116     @MainThread
getChannelDataVersion(Context context)117     public static int getChannelDataVersion(Context context) {
118         SoftPreconditions.checkState(sInitialized);
119         if (useContentProvider(context)) {
120             return sPreferenceValues.getInt(PREFS_KEY_CHANNEL_DATA_VERSION,
121                     CHANNEL_DATA_VERSION_NOT_SET);
122         } else {
123             return getSharedPreferences(context)
124                     .getInt(TunerPreferences.PREFS_KEY_CHANNEL_DATA_VERSION,
125                             CHANNEL_DATA_VERSION_NOT_SET);
126         }
127     }
128 
129     @MainThread
setChannelDataVersion(Context context, int version)130     public static void setChannelDataVersion(Context context, int version) {
131         if (useContentProvider(context)) {
132             setPreference(context, PREFS_KEY_CHANNEL_DATA_VERSION, version);
133         } else {
134             getSharedPreferences(context).edit()
135                     .putInt(TunerPreferences.PREFS_KEY_CHANNEL_DATA_VERSION, version)
136                     .apply();
137         }
138     }
139 
140     @MainThread
getScannedChannelCount(Context context)141     public static int getScannedChannelCount(Context context) {
142         SoftPreconditions.checkState(sInitialized);
143         if (useContentProvider(context)) {
144             return sPreferenceValues.getInt(PREFS_KEY_SCANNED_CHANNEL_COUNT);
145         } else {
146             return getSharedPreferences(context)
147                     .getInt(TunerPreferences.PREFS_KEY_SCANNED_CHANNEL_COUNT, 0);
148         }
149     }
150 
151     @MainThread
setScannedChannelCount(Context context, int channelCount)152     public static void setScannedChannelCount(Context context, int channelCount) {
153         if (useContentProvider(context)) {
154             setPreference(context, PREFS_KEY_SCANNED_CHANNEL_COUNT, channelCount);
155         } else {
156             getSharedPreferences(context).edit()
157                     .putInt(TunerPreferences.PREFS_KEY_SCANNED_CHANNEL_COUNT, channelCount)
158                     .apply();
159         }
160     }
161 
162     @MainThread
isScanDone(Context context)163     public static boolean isScanDone(Context context) {
164         SoftPreconditions.checkState(sInitialized);
165         if (useContentProvider(context)) {
166             return sPreferenceValues.getBoolean(PREFS_KEY_SCAN_DONE);
167         } else {
168             return getSharedPreferences(context)
169                     .getBoolean(TunerPreferences.PREFS_KEY_SCAN_DONE, false);
170         }
171     }
172 
173     @MainThread
setScanDone(Context context)174     public static void setScanDone(Context context) {
175         if (useContentProvider(context)) {
176             setPreference(context, PREFS_KEY_SCAN_DONE, true);
177         } else {
178             getSharedPreferences(context).edit()
179                     .putBoolean(TunerPreferences.PREFS_KEY_SCAN_DONE, true)
180                     .apply();
181         }
182     }
183 
184     @MainThread
shouldShowSetupActivity(Context context)185     public static boolean shouldShowSetupActivity(Context context) {
186         SoftPreconditions.checkState(sInitialized);
187         if (useContentProvider(context)) {
188             return sPreferenceValues.getBoolean(PREFS_KEY_LAUNCH_SETUP);
189         } else {
190             return getSharedPreferences(context)
191                     .getBoolean(TunerPreferences.PREFS_KEY_LAUNCH_SETUP, false);
192         }
193     }
194 
195     @MainThread
setShouldShowSetupActivity(Context context, boolean need)196     public static void setShouldShowSetupActivity(Context context, boolean need) {
197         if (useContentProvider(context)) {
198             setPreference(context, PREFS_KEY_LAUNCH_SETUP, need);
199         } else {
200             getSharedPreferences(context).edit()
201                     .putBoolean(TunerPreferences.PREFS_KEY_LAUNCH_SETUP, need)
202                     .apply();
203         }
204     }
205 
206     @MainThread
getStoreTsStream(Context context)207     public static boolean getStoreTsStream(Context context) {
208         SoftPreconditions.checkState(sInitialized);
209         if (useContentProvider(context)) {
210             return sPreferenceValues.getBoolean(PREFS_KEY_STORE_TS_STREAM, false);
211         } else {
212             return getSharedPreferences(context)
213                     .getBoolean(TunerPreferences.PREFS_KEY_STORE_TS_STREAM, false);
214         }
215     }
216 
217     @MainThread
setStoreTsStream(Context context, boolean shouldStore)218     public static void setStoreTsStream(Context context, boolean shouldStore) {
219         if (useContentProvider(context)) {
220             setPreference(context, PREFS_KEY_STORE_TS_STREAM, shouldStore);
221         } else {
222             getSharedPreferences(context).edit()
223                     .putBoolean(TunerPreferences.PREFS_KEY_STORE_TS_STREAM, shouldStore)
224                     .apply();
225         }
226     }
227 
getSharedPreferences(Context context)228     private static SharedPreferences getSharedPreferences(Context context) {
229         return context.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE);
230     }
231 
232     @MainThread
setPreference(final Context context, final String key, final String value)233     private static void setPreference(final Context context, final String key, final String value) {
234         new AsyncTask<Void, Void, Void>() {
235             @Override
236             protected Void doInBackground(Void... params) {
237                 ContentResolver resolver = context.getContentResolver();
238                 ContentValues values = new ContentValues();
239                 values.put(Preferences.COLUMN_KEY, key);
240                 values.put(Preferences.COLUMN_VALUE, value);
241                 try {
242                     resolver.insert(Preferences.CONTENT_URI, values);
243                 } catch (Exception e) {
244                     SoftPreconditions.warn(TAG, "setPreference", "Error writing preference values",
245                             e);
246                 }
247                 return null;
248             }
249         }.execute();
250     }
251 
252     @MainThread
setPreference(Context context, String key, int value)253     private static void setPreference(Context context, String key, int value) {
254         sPreferenceValues.putInt(key, value);
255         setPreference(context, key, Integer.toString(value));
256     }
257 
258     @MainThread
setPreference(Context context, String key, boolean value)259     private static void setPreference(Context context, String key, boolean value) {
260         sPreferenceValues.putBoolean(key, value);
261         setPreference(context, key, Boolean.toString(value));
262     }
263 
264     private static class LoadPreferencesTask extends AsyncTask<Void, Void, Bundle> {
265         private final Context mContext;
LoadPreferencesTask(Context context)266         private LoadPreferencesTask(Context context) {
267             mContext = context;
268         }
269 
270         @Override
doInBackground(Void... params)271         protected Bundle doInBackground(Void... params) {
272             Bundle bundle = new Bundle();
273             ContentResolver resolver = mContext.getContentResolver();
274             String[] projection = new String[] { Preferences.COLUMN_KEY, Preferences.COLUMN_VALUE };
275             try (Cursor cursor = resolver.query(Preferences.CONTENT_URI, projection, null, null,
276                     null)) {
277                 if (cursor != null) {
278                     while (!isCancelled() && cursor.moveToNext()) {
279                         String key = cursor.getString(0);
280                         String value = cursor.getString(1);
281                         switch (key) {
282                             case PREFS_KEY_CHANNEL_DATA_VERSION:
283                             case PREFS_KEY_SCANNED_CHANNEL_COUNT:
284                                 try {
285                                     bundle.putInt(key, Integer.parseInt(value));
286                                 } catch (NumberFormatException e) {
287                                     // Does nothing.
288                                 }
289                                 break;
290                             case PREFS_KEY_SCAN_DONE:
291                             case PREFS_KEY_LAUNCH_SETUP:
292                             case PREFS_KEY_STORE_TS_STREAM:
293                                 bundle.putBoolean(key, Boolean.parseBoolean(value));
294                                 break;
295                         }
296                     }
297                 }
298             } catch (Exception e) {
299                 SoftPreconditions.warn(TAG, "getPreference", "Error querying preference values", e);
300                 return null;
301             }
302             return bundle;
303         }
304 
305         @Override
onPostExecute(Bundle bundle)306         protected void onPostExecute(Bundle bundle) {
307             sPreferenceValues.putAll(bundle);
308         }
309     }
310 }
311