diff options
Diffstat (limited to 'src/com/android/tv/data/ChannelDataManager.java')
-rw-r--r-- | src/com/android/tv/data/ChannelDataManager.java | 320 |
1 files changed, 153 insertions, 167 deletions
diff --git a/src/com/android/tv/data/ChannelDataManager.java b/src/com/android/tv/data/ChannelDataManager.java index 6f93fbd1..1dfcf125 100644 --- a/src/com/android/tv/data/ChannelDataManager.java +++ b/src/com/android/tv/data/ChannelDataManager.java @@ -38,15 +38,15 @@ import android.support.annotation.VisibleForTesting; import android.util.ArraySet; import android.util.Log; import android.util.MutableInt; - -import com.android.tv.common.SharedPreferencesUtils; +import com.android.tv.TvSingletons; import com.android.tv.common.SoftPreconditions; import com.android.tv.common.WeakHandler; +import com.android.tv.common.util.PermissionUtils; +import com.android.tv.common.util.SharedPreferencesUtils; +import com.android.tv.data.api.Channel; import com.android.tv.util.AsyncDbTask; -import com.android.tv.util.PermissionUtils; import com.android.tv.util.TvInputManagerHelper; import com.android.tv.util.Utils; - import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -56,13 +56,13 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.Executor; /** - * The class to manage channel data. - * Basic features: reading channel list and each channel's current program, and updating - * the values of {@link Channels#COLUMN_BROWSABLE}, {@link Channels#COLUMN_LOCKED}. - * This class is not thread-safe and under an assumption that its public methods are called in - * only the main thread. + * The class to manage channel data. Basic features: reading channel list and each channel's current + * program, and updating the values of {@link Channels#COLUMN_BROWSABLE}, {@link + * Channels#COLUMN_LOCKED}. This class is not thread-safe and under an assumption that its public + * methods are called in only the main thread. */ @AnyThread public class ChannelDataManager { @@ -73,6 +73,7 @@ public class ChannelDataManager { private final Context mContext; private final TvInputManagerHelper mInputManager; + private final Executor mDbExecutor; private boolean mStarted; private boolean mDbLoadFinished; private QueryAllChannelsTask mChannelsUpdateTask; @@ -81,8 +82,8 @@ public class ChannelDataManager { private final Set<Listener> mListeners = new CopyOnWriteArraySet<>(); // Use container class to support multi-thread safety. This value can be set only on the main // thread. - volatile private UnmodifiableChannelData mData = new UnmodifiableChannelData(); - private final Channel.DefaultComparator mChannelComparator; + private volatile UnmodifiableChannelData mData = new UnmodifiableChannelData(); + private final ChannelImpl.DefaultComparator mChannelComparator; private final Handler mHandler; private final Set<Long> mBrowsableUpdateChannelIds = new HashSet<>(); @@ -93,81 +94,92 @@ public class ChannelDataManager { private final boolean mStoreBrowsableInSharedPreferences; private final SharedPreferences mBrowsableSharedPreferences; - private final TvInputCallback mTvInputCallback = new TvInputCallback() { - @Override - public void onInputAdded(String inputId) { - boolean channelAdded = false; - ChannelData data = new ChannelData(mData); - for (ChannelWrapper channel : mData.channelWrapperMap.values()) { - if (channel.mChannel.getInputId().equals(inputId)) { - channel.mInputRemoved = false; - addChannel(data, channel.mChannel); - channelAdded = true; + private final TvInputCallback mTvInputCallback = + new TvInputCallback() { + @Override + public void onInputAdded(String inputId) { + boolean channelAdded = false; + ChannelData data = new ChannelData(mData); + for (ChannelWrapper channel : mData.channelWrapperMap.values()) { + if (channel.mChannel.getInputId().equals(inputId)) { + channel.mInputRemoved = false; + addChannel(data, channel.mChannel); + channelAdded = true; + } + } + if (channelAdded) { + Collections.sort(data.channels, mChannelComparator); + mData = new UnmodifiableChannelData(data); + notifyChannelListUpdated(); + } } - } - if (channelAdded) { - Collections.sort(data.channels, mChannelComparator); - mData = new UnmodifiableChannelData(data); - notifyChannelListUpdated(); - } - } - @Override - public void onInputRemoved(String inputId) { - boolean channelRemoved = false; - ArrayList<ChannelWrapper> removedChannels = new ArrayList<>(); - for (ChannelWrapper channel : mData.channelWrapperMap.values()) { - if (channel.mChannel.getInputId().equals(inputId)) { - channel.mInputRemoved = true; - channelRemoved = true; - removedChannels.add(channel); - } - } - if (channelRemoved) { - ChannelData data = new ChannelData(); - data.channelWrapperMap.putAll(mData.channelWrapperMap); - for (ChannelWrapper channelWrapper : data.channelWrapperMap.values()) { - if (!channelWrapper.mInputRemoved) { - addChannel(data, channelWrapper.mChannel); + @Override + public void onInputRemoved(String inputId) { + boolean channelRemoved = false; + ArrayList<ChannelWrapper> removedChannels = new ArrayList<>(); + for (ChannelWrapper channel : mData.channelWrapperMap.values()) { + if (channel.mChannel.getInputId().equals(inputId)) { + channel.mInputRemoved = true; + channelRemoved = true; + removedChannels.add(channel); + } + } + if (channelRemoved) { + ChannelData data = new ChannelData(); + data.channelWrapperMap.putAll(mData.channelWrapperMap); + for (ChannelWrapper channelWrapper : data.channelWrapperMap.values()) { + if (!channelWrapper.mInputRemoved) { + addChannel(data, channelWrapper.mChannel); + } + } + Collections.sort(data.channels, mChannelComparator); + mData = new UnmodifiableChannelData(data); + notifyChannelListUpdated(); + for (ChannelWrapper channel : removedChannels) { + channel.notifyChannelRemoved(); + } } } - Collections.sort(data.channels, mChannelComparator); - mData = new UnmodifiableChannelData(data); - notifyChannelListUpdated(); - for (ChannelWrapper channel : removedChannels) { - channel.notifyChannelRemoved(); - } - } - } - }; + }; @MainThread public ChannelDataManager(Context context, TvInputManagerHelper inputManager) { - this(context, inputManager, context.getContentResolver()); + this( + context, + inputManager, + TvSingletons.getSingletons(context).getDbExecutor(), + context.getContentResolver()); } @MainThread @VisibleForTesting - ChannelDataManager(Context context, TvInputManagerHelper inputManager, + ChannelDataManager( + Context context, + TvInputManagerHelper inputManager, + Executor executor, ContentResolver contentResolver) { mContext = context; mInputManager = inputManager; + mDbExecutor = executor; mContentResolver = contentResolver; - mChannelComparator = new Channel.DefaultComparator(context, inputManager); + mChannelComparator = new ChannelImpl.DefaultComparator(context, inputManager); // Detect duplicate channels while sorting. mChannelComparator.setDetectDuplicatesEnabled(true); mHandler = new ChannelDataManagerHandler(this); - mChannelObserver = new ContentObserver(mHandler) { - @Override - public void onChange(boolean selfChange) { - if (!mHandler.hasMessages(MSG_UPDATE_CHANNELS)) { - mHandler.sendEmptyMessage(MSG_UPDATE_CHANNELS); - } - } - }; + mChannelObserver = + new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange) { + if (!mHandler.hasMessages(MSG_UPDATE_CHANNELS)) { + mHandler.sendEmptyMessage(MSG_UPDATE_CHANNELS); + } + } + }; mStoreBrowsableInSharedPreferences = !PermissionUtils.hasAccessAllEpg(mContext); - mBrowsableSharedPreferences = context.getSharedPreferences( - SharedPreferencesUtils.SHARED_PREF_BROWSABLE, Context.MODE_PRIVATE); + mBrowsableSharedPreferences = + context.getSharedPreferences( + SharedPreferencesUtils.SHARED_PREF_BROWSABLE, Context.MODE_PRIVATE); } @VisibleForTesting @@ -175,9 +187,7 @@ public class ChannelDataManager { return mChannelObserver; } - /** - * Starts the manager. If data is ready, {@link Listener#onLoadFinished()} will be called. - */ + /** Starts the manager. If data is ready, {@link Listener#onLoadFinished()} will be called. */ @MainThread public void start() { if (mStarted) { @@ -187,8 +197,8 @@ public class ChannelDataManager { // Should be called directly instead of posting MSG_UPDATE_CHANNELS message to the handler. // If not, other DB tasks can be executed before channel loading. handleUpdateChannels(); - mContentResolver.registerContentObserver(TvContract.Channels.CONTENT_URI, true, - mChannelObserver); + mContentResolver.registerContentObserver( + TvContract.Channels.CONTENT_URI, true, mChannelObserver); mInputManager.addCallback(mTvInputCallback); } @@ -218,9 +228,7 @@ public class ChannelDataManager { applyUpdatedValuesToDb(); } - /** - * Adds a {@link Listener}. - */ + /** Adds a {@link Listener}. */ public void addListener(Listener listener) { if (DEBUG) Log.d(TAG, "addListener " + listener); SoftPreconditions.checkNotNull(listener); @@ -229,9 +237,7 @@ public class ChannelDataManager { } } - /** - * Removes a {@link Listener}. - */ + /** Removes a {@link Listener}. */ public void removeListener(Listener listener) { if (DEBUG) Log.d(TAG, "removeListener " + listener); SoftPreconditions.checkNotNull(listener); @@ -252,8 +258,8 @@ public class ChannelDataManager { } /** - * Removes a {@link ChannelListener} for a specific channel with the channel ID - * {@code channelId}. + * Removes a {@link ChannelListener} for a specific channel with the channel ID {@code + * channelId}. */ public void removeChannelListener(Long channelId, ChannelListener listener) { ChannelWrapper channelWrapper = mData.channelWrapperMap.get(channelId); @@ -263,30 +269,22 @@ public class ChannelDataManager { channelWrapper.removeListener(listener); } - /** - * Checks whether data is ready. - */ + /** Checks whether data is ready. */ public boolean isDbLoadFinished() { return mDbLoadFinished; } - /** - * Returns the number of channels. - */ + /** Returns the number of channels. */ public int getChannelCount() { return mData.channels.size(); } - /** - * Returns a list of channels. - */ + /** Returns a list of channels. */ public List<Channel> getChannelList() { return new ArrayList<>(mData.channels); } - /** - * Returns a list of browsable channels. - */ + /** Returns a list of browsable channels. */ public List<Channel> getBrowsableChannelList() { List<Channel> channels = new ArrayList<>(); for (Channel channel : mData.channels) { @@ -329,9 +327,7 @@ public class ChannelDataManager { return true; } - /** - * Gets the channel with the channel ID {@code channelId}. - */ + /** Gets the channel with the channel ID {@code channelId}. */ public Channel getChannel(Long channelId) { ChannelWrapper channelWrapper = mData.channelWrapperMap.get(channelId); if (channelWrapper == null || channelWrapper.mInputRemoved) { @@ -340,9 +336,7 @@ public class ChannelDataManager { return channelWrapper.mChannel; } - /** - * The value change will be applied to DB when applyPendingDbOperation is called. - */ + /** The value change will be applied to DB when applyPendingDbOperation is called. */ public void updateBrowsable(Long channelId, boolean browsable) { updateBrowsable(channelId, browsable, false); } @@ -351,12 +345,12 @@ public class ChannelDataManager { * The value change will be applied to DB when applyPendingDbOperation is called. * * @param skipNotifyChannelBrowsableChanged If it's true, {@link Listener - * #onChannelBrowsableChanged()} is not called, when this method is called. - * {@link #notifyChannelBrowsableChanged} should be directly called, once browsable - * update is completed. + * #onChannelBrowsableChanged()} is not called, when this method is called. {@link + * #notifyChannelBrowsableChanged} should be directly called, once browsable update is + * completed. */ - public void updateBrowsable(Long channelId, boolean browsable, - boolean skipNotifyChannelBrowsableChanged) { + public void updateBrowsable( + Long channelId, boolean browsable, boolean skipNotifyChannelBrowsableChanged) { ChannelWrapper channelWrapper = mData.channelWrapperMap.get(channelId); if (channelWrapper == null) { return; @@ -396,10 +390,7 @@ public class ChannelDataManager { } } - /** - * Updates channels from DB. Once the update is done, {@code postRunnable} will - * be called. - */ + /** Updates channels from DB. Once the update is done, {@code postRunnable} will be called. */ public void updateChannels(Runnable postRunnable) { if (mChannelsUpdateTask != null) { mChannelsUpdateTask.cancel(true); @@ -411,9 +402,7 @@ public class ChannelDataManager { } } - /** - * The value change will be applied to DB when applyPendingDbOperation is called. - */ + /** The value change will be applied to DB when applyPendingDbOperation is called. */ public void updateLocked(Long channelId, boolean locked) { ChannelWrapper channelWrapper = mData.channelWrapperMap.get(channelId); if (channelWrapper == null) { @@ -430,10 +419,7 @@ public class ChannelDataManager { } } - /** - * Applies the changed values by {@link #updateBrowsable} and {@link #updateLocked} - * to DB. - */ + /** Applies the changed values by {@link #updateBrowsable} and {@link #updateLocked} to DB. */ public void applyUpdatedValuesToDb() { ChannelData data = mData; ArrayList<Long> browsableIds = new ArrayList<>(); @@ -493,11 +479,17 @@ public class ChannelDataManager { } mLockedUpdateChannelIds.clear(); if (DEBUG) { - Log.d(TAG, "applyUpdatedValuesToDb" - + "\n browsableIds size:" + browsableIds.size() - + "\n unbrowsableIds size:" + unbrowsableIds.size() - + "\n lockedIds size:" + lockedIds.size() - + "\n unlockedIds size:" + unlockedIds.size()); + Log.d( + TAG, + "applyUpdatedValuesToDb" + + "\n browsableIds size:" + + browsableIds.size() + + "\n unbrowsableIds size:" + + unbrowsableIds.size() + + "\n lockedIds size:" + + lockedIds.size() + + "\n unlockedIds size:" + + unlockedIds.size()); } } @@ -527,48 +519,34 @@ public class ChannelDataManager { mChannelsUpdateTask.executeOnDbThread(); } - /** - * Reloads channel data. - */ + /** Reloads channel data. */ public void reload() { if (mDbLoadFinished && !mHandler.hasMessages(MSG_UPDATE_CHANNELS)) { mHandler.sendEmptyMessage(MSG_UPDATE_CHANNELS); } } - /** - * A listener for ChannelDataManager. The callbacks are called on the main thread. - */ + /** A listener for ChannelDataManager. The callbacks are called on the main thread. */ public interface Listener { - /** - * Called when data load is finished. - */ + /** Called when data load is finished. */ void onLoadFinished(); /** - * Called when channels are added, deleted, or updated. But, when browsable is changed, - * it won't be called. Instead, {@link #onChannelBrowsableChanged} will be called. + * Called when channels are added, deleted, or updated. But, when browsable is changed, it + * won't be called. Instead, {@link #onChannelBrowsableChanged} will be called. */ void onChannelListUpdated(); - /** - * Called when browsable of channels are changed. - */ + /** Called when browsable of channels are changed. */ void onChannelBrowsableChanged(); } - /** - * A listener for individual channel change. The callbacks are called on the main thread. - */ + /** A listener for individual channel change. The callbacks are called on the main thread. */ public interface ChannelListener { - /** - * Called when the channel has been removed in DB. - */ + /** Called when the channel has been removed in DB. */ void onChannelRemoved(Channel channel); - /** - * Called when values of the channel has been changed. - */ + /** Called when values of the channel has been changed. */ void onChannelUpdated(Channel channel); } @@ -616,8 +594,10 @@ public class ChannelDataManager { @Override protected Boolean doInBackground(Void... params) { - try (AssetFileDescriptor f = mContext.getContentResolver().openAssetFileDescriptor( - TvContract.buildChannelLogoUri(mChannel.getId()), "r")) { + try (AssetFileDescriptor f = + mContext.getContentResolver() + .openAssetFileDescriptor( + TvContract.buildChannelLogoUri(mChannel.getId()), "r")) { return true; } catch (SQLiteException | IOException | NullPointerException e) { // File not found or asset file not found. @@ -637,7 +617,7 @@ public class ChannelDataManager { private final class QueryAllChannelsTask extends AsyncDbTask.AsyncChannelQueryTask { QueryAllChannelsTask(ContentResolver contentResolver) { - super(contentResolver); + super(mDbExecutor, contentResolver); } @Override @@ -663,8 +643,8 @@ public class ChannelDataManager { for (Channel channel : channels) { if (mStoreBrowsableInSharedPreferences) { String browsableKey = getBrowsableKey(channel); - channel.setBrowsable(mBrowsableSharedPreferences.getBoolean(browsableKey, - false)); + channel.setBrowsable( + mBrowsableSharedPreferences.getBoolean(browsableKey, false)); deletedBrowsableMap.remove(browsableKey); } long channelId = channel.getId(); @@ -698,7 +678,8 @@ public class ChannelDataManager { } } } - if (mStoreBrowsableInSharedPreferences && !deletedBrowsableMap.isEmpty() + if (mStoreBrowsableInSharedPreferences + && !deletedBrowsableMap.isEmpty() && PermissionUtils.hasReadTvListings(mContext)) { // If hasReadTvListings(mContext) is false, the given channel list would // empty. In this case, we skip the browsable data clean up process. @@ -745,24 +726,26 @@ public class ChannelDataManager { } /** - * Updates a column {@code columnName} of DB table {@code uri} with the value - * {@code columnValue}. The selective rows in the ID list {@code ids} will be updated. - * The DB operations will run on {@link AsyncDbTask#getExecutor()}. + * Updates a column {@code columnName} of DB table {@code uri} with the value {@code + * columnValue}. The selective rows in the ID list {@code ids} will be updated. The DB + * operations will run on {@link TvSingletons#getDbExecutor()}. */ private void updateOneColumnValue( final String columnName, final int columnValue, final List<Long> ids) { if (!PermissionUtils.hasAccessAllEpg(mContext)) { return; } - AsyncDbTask.executeOnDbThread(new Runnable() { - @Override - public void run() { - String selection = Utils.buildSelectionForIds(Channels._ID, ids); - ContentValues values = new ContentValues(); - values.put(columnName, columnValue); - mContentResolver.update(TvContract.Channels.CONTENT_URI, values, selection, null); - } - }); + mDbExecutor.execute( + new Runnable() { + @Override + public void run() { + String selection = Utils.buildSelectionForIds(Channels._ID, ids); + ContentValues values = new ContentValues(); + values.put(columnName, columnValue); + mContentResolver.update( + TvContract.Channels.CONTENT_URI, values, selection, null); + } + }); } private String getBrowsableKey(Channel channel) { @@ -784,9 +767,8 @@ public class ChannelDataManager { } /** - * Container class which includes channel data that needs to be synced. This class is - * modifiable and used for changing channel data. - * e.g. TvInputCallback, or AsyncDbTask.onPostExecute. + * Container class which includes channel data that needs to be synced. This class is modifiable + * and used for changing channel data. e.g. TvInputCallback, or AsyncDbTask.onPostExecute. */ @MainThread private static class ChannelData { @@ -806,8 +788,10 @@ public class ChannelDataManager { channels = new ArrayList<>(data.channels); } - ChannelData(Map<Long, ChannelWrapper> channelWrapperMap, - Map<String, MutableInt> channelCountMap, List<Channel> channels) { + ChannelData( + Map<Long, ChannelWrapper> channelWrapperMap, + Map<String, MutableInt> channelCountMap, + List<Channel> channels) { this.channelWrapperMap = channelWrapperMap; this.channelCountMap = channelCountMap; this.channels = channels; @@ -818,13 +802,15 @@ public class ChannelDataManager { @MainThread private static class UnmodifiableChannelData extends ChannelData { UnmodifiableChannelData() { - super(Collections.unmodifiableMap(new HashMap<>()), + super( + Collections.unmodifiableMap(new HashMap<>()), Collections.unmodifiableMap(new HashMap<>()), Collections.unmodifiableList(new ArrayList<>())); } UnmodifiableChannelData(ChannelData data) { - super(Collections.unmodifiableMap(data.channelWrapperMap), + super( + Collections.unmodifiableMap(data.channelWrapperMap), Collections.unmodifiableMap(data.channelCountMap), Collections.unmodifiableList(data.channels)); } |