• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.car.radio.storage;
18 
19 import android.content.Context;
20 import android.content.SharedPreferences;
21 import android.hardware.radio.ProgramSelector;
22 import android.net.Uri;
23 import android.os.AsyncTask;
24 import android.util.ArrayMap;
25 
26 import androidx.annotation.GuardedBy;
27 import androidx.annotation.NonNull;
28 import androidx.annotation.Nullable;
29 import androidx.lifecycle.LiveData;
30 
31 import com.android.car.broadcastradio.support.Program;
32 import com.android.car.broadcastradio.support.platform.ProgramSelectorExt;
33 import com.android.car.radio.SkipMode;
34 import com.android.car.radio.bands.ProgramType;
35 import com.android.car.radio.util.Log;
36 
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Objects;
40 
41 /**
42  * Manages persistent storage for broadcast radio application.
43  */
44 public class RadioStorage {
45     private static final String TAG = "BcRadioApp.storage";
46     private static final String PREF_NAME = "RadioAppPrefs";
47 
48     private static final String PREF_KEY_RECENT_TYPE = "recentProgramType";
49     private static final String PREF_KEY_RECENT_PROGRAM_PREFIX = "recentProgram-";
50 
51     private static final String PREF_KEY_SKIP_MODE = "smartSeekMode";
52 
53     private static final Object sLock = new Object();
54 
55     @GuardedBy("sLock")
56     private static Map<Integer, RadioStorage> sInstances = new ArrayMap<>();
57 
58     private final SharedPreferences mPrefs;
59     private final RadioDatabase mDatabase;
60     private final LiveData<List<Program>> mFavorites;
61 
RadioStorage(Context context)62     private RadioStorage(Context context) {
63         mPrefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
64         mDatabase = RadioDatabase.buildInstance(context);
65 
66         mFavorites = mDatabase.getAllFavorites();
67     }
68 
69     /**
70      * Returns singleton instance of {@link RadioStorage} for the user in context.
71      *
72      * @param context Application context
73      */
74     @NonNull
getInstance(Context context)75     public static RadioStorage getInstance(Context context) {
76         int userId = context.getUserId();
77         synchronized (sLock) {
78             if (sInstances.containsKey(userId)) {
79                 return sInstances.get(userId);
80             }
81             RadioStorage newInstance = new RadioStorage(context.getApplicationContext());
82             sInstances.put(userId, newInstance);
83             return newInstance;
84         }
85     }
86 
87     /**
88      * Returns a list of all favorites added previously by the user.
89      */
90     @NonNull
getFavorites()91     public LiveData<List<Program>> getFavorites() {
92         return mFavorites;
93     }
94 
95     /**
96      * Checks, if a given program is favorite.
97      *
98      * @param favorites List of favorites.
99      * @param selector Program to check.
100      */
isFavorite(@onNull List<Program> favorites, @NonNull ProgramSelector selector)101     public static boolean isFavorite(@NonNull List<Program> favorites,
102             @NonNull ProgramSelector selector) {
103         return favorites.contains(new Program(selector, ""));
104     }
105 
106     /**
107      * Checks, if a given program is favorite.
108      *
109      * @param selector Program to check.
110      */
isFavorite(@onNull ProgramSelector selector)111     public boolean isFavorite(@NonNull ProgramSelector selector) {
112         List<Program> favorites = mFavorites.getValue();
113         if (favorites == null) {
114             Log.w(TAG, "Database is not ready yet");
115             return false;
116         }
117         return isFavorite(favorites, selector);
118     }
119 
120     private class AddFavoriteTask extends AsyncTask<Program, Void, Void> {
121         @Override
doInBackground(Program... programs)122         protected Void doInBackground(Program... programs) {
123             mDatabase.insertFavorite(programs[0]);
124             return null;
125         }
126     }
127 
128     private class RemoveFavoriteTask extends AsyncTask<ProgramSelector, Void, Void> {
129         @Override
doInBackground(ProgramSelector... selectors)130         protected Void doInBackground(ProgramSelector... selectors) {
131             mDatabase.removeFavorite(selectors[0]);
132             return null;
133         }
134     }
135 
136     /**
137      * Adds a new program to the favorites list.
138      *
139      * After the operation succeeds, the list is refreshed via live object returned
140      * from {@link #getFavorites}.
141      *
142      * @param favorite A program to add.
143      */
addFavorite(@onNull Program favorite)144     public void addFavorite(@NonNull Program favorite) {
145         new AddFavoriteTask().execute(Objects.requireNonNull(favorite));
146     }
147 
148     /**
149      * Removes a program from the favorites list.
150      *
151      * After the operation succeeds, the list is refreshed via live object returned
152      * from {@link #getFavorites}.
153      *
154      * @param favorite A program to remove.
155      */
removeFavorite(@onNull ProgramSelector favorite)156     public void removeFavorite(@NonNull ProgramSelector favorite) {
157         new RemoveFavoriteTask().execute(Objects.requireNonNull(favorite));
158     }
159 
160     /**
161      * Stores recently selected program so it can be recalled on next app launch.
162      *
163      * @param sel Program to store as recently selected.
164      */
setRecentlySelected(@onNull ProgramSelector sel)165     public void setRecentlySelected(@NonNull ProgramSelector sel) {
166         ProgramType pt = ProgramType.fromSelector(sel);
167         int ptid = pt == null ? 0 : pt.id;
168 
169         SharedPreferences.Editor editor = mPrefs.edit();
170         boolean hasChanges = false;
171 
172         String prefName = PREF_KEY_RECENT_PROGRAM_PREFIX + ptid;
173         Uri selUri = ProgramSelectorExt.toUri(sel);
174         if (selUri == null) return;
175         String selUriStr = selUri.toString();
176         if (!mPrefs.getString(prefName, "").equals(selUriStr)) {
177             editor.putString(prefName, selUriStr);
178             hasChanges = true;
179         }
180 
181         if (mPrefs.getInt(PREF_KEY_RECENT_TYPE, -1) != ptid) {
182             editor.putInt(PREF_KEY_RECENT_TYPE, ptid);
183             hasChanges = true;
184         }
185 
186         if (hasChanges) editor.apply();
187     }
188 
189     /**
190      * Retrieves recently selected program.
191      *
192      * This function can either retrieve the recently selected program for a specific
193      * {@link ProgramType} (band) or just the recently selected program in general.
194      *
195      * @param pt Program type to filter the result on, or {@code null} for general check
196      * @return Selector of the recent program or {@code null}, if there was none saved
197      */
getRecentlySelected(@ullable ProgramType pt)198     public @Nullable ProgramSelector getRecentlySelected(@Nullable ProgramType pt) {
199         int ptid = pt != null ? pt.id : mPrefs.getInt(PREF_KEY_RECENT_TYPE, -1);
200         if (ptid == -1) return null;
201 
202         String selUriStr = mPrefs.getString(PREF_KEY_RECENT_PROGRAM_PREFIX + ptid, "");
203         if (selUriStr.equals("")) return null;
204 
205         return ProgramSelectorExt.fromUri(Uri.parse(selUriStr));
206     }
207 
208     /**
209      * Stores the last {@link SkipMode} set.
210      */
setSkipMode(@onNull SkipMode mode)211     public void setSkipMode(@NonNull SkipMode mode) {
212         int value = mode.ordinal();
213         SharedPreferences.Editor editor = mPrefs.edit();
214 
215         if (mPrefs.getInt(PREF_KEY_SKIP_MODE,
216                 SkipMode.DEFAULT_MODE.ordinal()) != value) {
217             editor.putInt(PREF_KEY_SKIP_MODE, value);
218             editor.apply();
219         }
220     }
221 
222     /**
223      * Gets the last {@link SkipMode} set.
224      */
225     @NonNull
getSkipMode()226     public SkipMode getSkipMode() {
227         int value = mPrefs.getInt(PREF_KEY_SKIP_MODE, SkipMode.DEFAULT_MODE.ordinal());
228         SkipMode mode = SkipMode.valueOf(value);
229         if (mode == null) {
230             Log.e(TAG, "getSkipMode(): invalid pref value " + value + "; returning "
231                     + SkipMode.DEFAULT_MODE + " instead");
232             mode = SkipMode.DEFAULT_MODE;
233         }
234         return mode;
235     }
236 }
237