1816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko/* 2816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Copyright (C) 2015 The Android Open Source Project 3816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * 4816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License"); 5816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * you may not use this file except in compliance with the License. 6816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * You may obtain a copy of the License at 7816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * 8816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * http://www.apache.org/licenses/LICENSE-2.0 9816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * 10816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Unless required by applicable law or agreed to in writing, software 11816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS, 12816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * See the License for the specific language governing permissions and 14816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * limitations under the License. 15816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 16816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 17816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkopackage com.android.tv.data; 18816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 19816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.ContentResolver; 20816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.ContentValues; 21816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.Context; 227d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport android.content.SharedPreferences; 237d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport android.content.SharedPreferences.Editor; 246ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport android.content.res.AssetFileDescriptor; 25816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.database.ContentObserver; 266ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport android.database.sqlite.SQLiteException; 27816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.media.tv.TvContract; 28816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.media.tv.TvContract.Channels; 29816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.media.tv.TvInputManager.TvInputCallback; 306ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport android.os.AsyncTask; 31816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.os.Handler; 32816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.os.Looper; 33816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.os.Message; 346ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport android.support.annotation.AnyThread; 35ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.support.annotation.MainThread; 3607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport android.support.annotation.NonNull; 37816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.support.annotation.VisibleForTesting; 382e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalkoimport android.util.ArraySet; 39816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.util.Log; 40816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.util.MutableInt; 41816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 42ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport com.android.tv.common.SharedPreferencesUtils; 432e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalkoimport com.android.tv.common.SoftPreconditions; 4407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport com.android.tv.common.WeakHandler; 45816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.util.AsyncDbTask; 467d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport com.android.tv.util.PermissionUtils; 47816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.util.TvInputManagerHelper; 48816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.util.Utils; 49816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 506ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport java.io.IOException; 51816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.ArrayList; 52816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.Collections; 53816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.HashMap; 54816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.HashSet; 55816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.List; 56816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.Map; 57816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.Set; 5865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.concurrent.CopyOnWriteArraySet; 59816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 60816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko/** 61816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * The class to manage channel data. 62816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Basic features: reading channel list and each channel's current program, and updating 63816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * the values of {@link Channels#COLUMN_BROWSABLE}, {@link Channels#COLUMN_LOCKED}. 64816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * This class is not thread-safe and under an assumption that its public methods are called in 65816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * only the main thread. 66816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 676ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko@AnyThread 68816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkopublic class ChannelDataManager { 69816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final String TAG = "ChannelDataManager"; 70816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final boolean DEBUG = false; 71816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 72816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final int MSG_UPDATE_CHANNELS = 1000; 73816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 74816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final Context mContext; 75816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final TvInputManagerHelper mInputManager; 76816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private boolean mStarted; 77816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private boolean mDbLoadFinished; 78816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private QueryAllChannelsTask mChannelsUpdateTask; 79816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final List<Runnable> mPostRunnablesAfterChannelUpdate = new ArrayList<>(); 80816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 8165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final Set<Listener> mListeners = new CopyOnWriteArraySet<>(); 826ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko // Use container class to support multi-thread safety. This value can be set only on the main 836ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko // thread. 846ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko volatile private UnmodifiableChannelData mData = new UnmodifiableChannelData(); 85816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final Channel.DefaultComparator mChannelComparator; 86816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 87816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final Handler mHandler; 88816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final Set<Long> mBrowsableUpdateChannelIds = new HashSet<>(); 89816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final Set<Long> mLockedUpdateChannelIds = new HashSet<>(); 90816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 91816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final ContentResolver mContentResolver; 92816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final ContentObserver mChannelObserver; 937d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko private final boolean mStoreBrowsableInSharedPreferences; 947d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko private final SharedPreferences mBrowsableSharedPreferences; 95816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 96816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final TvInputCallback mTvInputCallback = new TvInputCallback() { 97816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 98816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void onInputAdded(String inputId) { 99816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko boolean channelAdded = false; 1006ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko ChannelData data = new ChannelData(mData); 1016ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko for (ChannelWrapper channel : mData.channelWrapperMap.values()) { 102816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (channel.mChannel.getInputId().equals(inputId)) { 103816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channel.mInputRemoved = false; 1046ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko addChannel(data, channel.mChannel); 105816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channelAdded = true; 106816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 107816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 108816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (channelAdded) { 1096ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko Collections.sort(data.channels, mChannelComparator); 1106ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko mData = new UnmodifiableChannelData(data); 111ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko notifyChannelListUpdated(); 112816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 113816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 114816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 115816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 116816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void onInputRemoved(String inputId) { 117816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko boolean channelRemoved = false; 118816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ArrayList<ChannelWrapper> removedChannels = new ArrayList<>(); 1196ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko for (ChannelWrapper channel : mData.channelWrapperMap.values()) { 120816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (channel.mChannel.getInputId().equals(inputId)) { 121816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channel.mInputRemoved = true; 122816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channelRemoved = true; 123816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko removedChannels.add(channel); 124816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 125816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 126816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (channelRemoved) { 1276ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko ChannelData data = new ChannelData(); 1286ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko data.channelWrapperMap.putAll(mData.channelWrapperMap); 1296ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko for (ChannelWrapper channelWrapper : data.channelWrapperMap.values()) { 130816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!channelWrapper.mInputRemoved) { 1316ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko addChannel(data, channelWrapper.mChannel); 132816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 133816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1346ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko Collections.sort(data.channels, mChannelComparator); 1356ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko mData = new UnmodifiableChannelData(data); 136ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko notifyChannelListUpdated(); 137816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (ChannelWrapper channel : removedChannels) { 138816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channel.notifyChannelRemoved(); 139816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 140816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 141816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 142816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko }; 143816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1446ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko @MainThread 145ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko public ChannelDataManager(Context context, TvInputManagerHelper inputManager) { 146ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko this(context, inputManager, context.getContentResolver()); 147816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 148816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1496ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko @MainThread 150816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @VisibleForTesting 151ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko ChannelDataManager(Context context, TvInputManagerHelper inputManager, 1527d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko ContentResolver contentResolver) { 153816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mContext = context; 154816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mInputManager = inputManager; 155816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mContentResolver = contentResolver; 156816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mChannelComparator = new Channel.DefaultComparator(context, inputManager); 157816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Detect duplicate channels while sorting. 158816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mChannelComparator.setDetectDuplicatesEnabled(true); 1597d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko mHandler = new ChannelDataManagerHandler(this); 160816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mChannelObserver = new ContentObserver(mHandler) { 161816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 162816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void onChange(boolean selfChange) { 163816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!mHandler.hasMessages(MSG_UPDATE_CHANNELS)) { 164816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mHandler.sendEmptyMessage(MSG_UPDATE_CHANNELS); 165816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 166816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 167816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko }; 1687d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko mStoreBrowsableInSharedPreferences = !PermissionUtils.hasAccessAllEpg(mContext); 169ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko mBrowsableSharedPreferences = context.getSharedPreferences( 170ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko SharedPreferencesUtils.SHARED_PREF_BROWSABLE, Context.MODE_PRIVATE); 171816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 172816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 173816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @VisibleForTesting 174816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ContentObserver getContentObserver() { 175816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mChannelObserver; 176816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 177816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 178816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 179816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Starts the manager. If data is ready, {@link Listener#onLoadFinished()} will be called. 180816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 1816ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko @MainThread 182816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void start() { 183816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mStarted) { 184816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 185816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 186816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mStarted = true; 187816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Should be called directly instead of posting MSG_UPDATE_CHANNELS message to the handler. 188816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // If not, other DB tasks can be executed before channel loading. 189816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko handleUpdateChannels(); 190ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko mContentResolver.registerContentObserver(TvContract.Channels.CONTENT_URI, true, 191ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko mChannelObserver); 192816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mInputManager.addCallback(mTvInputCallback); 193816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 194816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 195816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 196816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Stops the manager. It clears manager states and runs pending DB operations. Added listeners 197816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * aren't automatically removed by this method. 198816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 1996ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko @MainThread 2007d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko @VisibleForTesting 201816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void stop() { 202816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!mStarted) { 203816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 204816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 205816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mStarted = false; 206816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mDbLoadFinished = false; 207816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 208816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mInputManager.removeCallback(mTvInputCallback); 209816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mContentResolver.unregisterContentObserver(mChannelObserver); 210816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mHandler.removeCallbacksAndMessages(null); 211816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 212816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko clearChannels(); 213816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPostRunnablesAfterChannelUpdate.clear(); 214816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mChannelsUpdateTask != null) { 215816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mChannelsUpdateTask.cancel(true); 216816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mChannelsUpdateTask = null; 217816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 218816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko applyUpdatedValuesToDb(); 219816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 220816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 221816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 222816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Adds a {@link Listener}. 223816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 224816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void addListener(Listener listener) { 225ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko if (DEBUG) Log.d(TAG, "addListener " + listener); 226ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko SoftPreconditions.checkNotNull(listener); 227ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko if (listener != null) { 228ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko mListeners.add(listener); 229ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 230816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 231816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 232816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 233816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Removes a {@link Listener}. 234816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 235816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void removeListener(Listener listener) { 236ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko if (DEBUG) Log.d(TAG, "removeListener " + listener); 237ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko SoftPreconditions.checkNotNull(listener); 238ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko if (listener != null) { 239ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko mListeners.remove(listener); 240ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 241816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 242816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 243816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 244816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Adds a {@link ChannelListener} for a specific channel with the channel ID {@code channelId}. 245816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 246816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void addChannelListener(Long channelId, ChannelListener listener) { 2476ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko ChannelWrapper channelWrapper = mData.channelWrapperMap.get(channelId); 248816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (channelWrapper == null) { 249816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 250816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 251816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channelWrapper.addListener(listener); 252816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 253816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 254816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 255816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Removes a {@link ChannelListener} for a specific channel with the channel ID 256816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * {@code channelId}. 257816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 258816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void removeChannelListener(Long channelId, ChannelListener listener) { 2596ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko ChannelWrapper channelWrapper = mData.channelWrapperMap.get(channelId); 260816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (channelWrapper == null) { 261816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 262816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 263816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channelWrapper.removeListener(listener); 264816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 265816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 266816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 267816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Checks whether data is ready. 268816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 269816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public boolean isDbLoadFinished() { 270816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mDbLoadFinished; 271816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 272816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 273816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 274816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Returns the number of channels. 275816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 276816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public int getChannelCount() { 2776ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko return mData.channels.size(); 278816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 279816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 280816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 281816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Returns a list of channels. 282816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 283816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public List<Channel> getChannelList() { 2846ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko return new ArrayList<>(mData.channels); 285816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 286816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 287816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 288816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Returns a list of browsable channels. 289816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 290816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public List<Channel> getBrowsableChannelList() { 291816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko List<Channel> channels = new ArrayList<>(); 2926ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko for (Channel channel : mData.channels) { 293816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (channel.isBrowsable()) { 294816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channels.add(channel); 295816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 296816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 2972e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko return channels; 298816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 299816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 300816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 301816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Returns the total channel count for a given input. 302816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * 303816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * @param inputId The ID of the input. 304816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 305816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public int getChannelCountForInput(String inputId) { 3066ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko MutableInt count = mData.channelCountMap.get(inputId); 307816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return count == null ? 0 : count.value; 308816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 309816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 310816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 311d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * Checks if the channel exists in DB. 312d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * 313d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * <p>Note that the channels of the removed inputs can not be obtained from {@link #getChannel}. 314d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * In that case this method is used to check if the channel exists in the DB. 315d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko */ 316d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko public boolean doesChannelExistInDb(long channelId) { 3176ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko return mData.channelWrapperMap.get(channelId) != null; 318d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 319d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 320d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko /** 321816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Returns true if and only if there exists at least one channel and all channels are hidden. 322816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 323816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public boolean areAllChannelsHidden() { 3246ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko for (Channel channel : mData.channels) { 325816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (channel.isBrowsable()) { 326816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return false; 327816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 328816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 329816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return true; 330816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 331816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 332816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 333816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Gets the channel with the channel ID {@code channelId}. 334816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 335816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public Channel getChannel(Long channelId) { 3366ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko ChannelWrapper channelWrapper = mData.channelWrapperMap.get(channelId); 337816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (channelWrapper == null || channelWrapper.mInputRemoved) { 338816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return null; 339816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 340816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return channelWrapper.mChannel; 341816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 342816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 343816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 344816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * The value change will be applied to DB when applyPendingDbOperation is called. 345816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 346816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void updateBrowsable(Long channelId, boolean browsable) { 347816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko updateBrowsable(channelId, browsable, false); 348816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 349816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 350816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 351816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * The value change will be applied to DB when applyPendingDbOperation is called. 352816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * 353816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * @param skipNotifyChannelBrowsableChanged If it's true, {@link Listener 354816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * #onChannelBrowsableChanged()} is not called, when this method is called. 355816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * {@link #notifyChannelBrowsableChanged} should be directly called, once browsable 356816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * update is completed. 357816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 358816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void updateBrowsable(Long channelId, boolean browsable, 359816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko boolean skipNotifyChannelBrowsableChanged) { 3606ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko ChannelWrapper channelWrapper = mData.channelWrapperMap.get(channelId); 361816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (channelWrapper == null) { 362816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 363816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 364816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (channelWrapper.mChannel.isBrowsable() != browsable) { 365816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channelWrapper.mChannel.setBrowsable(browsable); 366816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (browsable == channelWrapper.mBrowsableInDb) { 367816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mBrowsableUpdateChannelIds.remove(channelWrapper.mChannel.getId()); 368816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 369816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mBrowsableUpdateChannelIds.add(channelWrapper.mChannel.getId()); 370816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 371816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channelWrapper.notifyChannelUpdated(); 372816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // When updateBrowsable is called multiple times in a method, we don't need to 373816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // notify Listener.onChannelBrowsableChanged multiple times but only once. So 374816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // we send a message instead of directly calling onChannelBrowsableChanged. 375816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!skipNotifyChannelBrowsableChanged) { 376816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko notifyChannelBrowsableChanged(); 377816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 378816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 379816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 380816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 381816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void notifyChannelBrowsableChanged() { 38265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko for (Listener l : mListeners) { 383816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko l.onChannelBrowsableChanged(); 384816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 385816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 386816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 387ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko private void notifyChannelListUpdated() { 38865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko for (Listener l : mListeners) { 389ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko l.onChannelListUpdated(); 390ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 391ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 392ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko 393ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko private void notifyLoadFinished() { 39465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko for (Listener l : mListeners) { 395ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko l.onLoadFinished(); 396ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 397ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 398ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko 399816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 400816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Updates channels from DB. Once the update is done, {@code postRunnable} will 401816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * be called. 402816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 403816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void updateChannels(Runnable postRunnable) { 404816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mChannelsUpdateTask != null) { 405816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mChannelsUpdateTask.cancel(true); 406816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mChannelsUpdateTask = null; 407816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 408816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPostRunnablesAfterChannelUpdate.add(postRunnable); 409816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!mHandler.hasMessages(MSG_UPDATE_CHANNELS)) { 410816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mHandler.sendEmptyMessage(MSG_UPDATE_CHANNELS); 411816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 412816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 413816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 414816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 415816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * The value change will be applied to DB when applyPendingDbOperation is called. 416816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 417816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void updateLocked(Long channelId, boolean locked) { 4186ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko ChannelWrapper channelWrapper = mData.channelWrapperMap.get(channelId); 419816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (channelWrapper == null) { 420816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 421816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 422816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (channelWrapper.mChannel.isLocked() != locked) { 423816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channelWrapper.mChannel.setLocked(locked); 424816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (locked == channelWrapper.mLockedInDb) { 425816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mLockedUpdateChannelIds.remove(channelWrapper.mChannel.getId()); 426816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 427816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mLockedUpdateChannelIds.add(channelWrapper.mChannel.getId()); 428816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 429816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channelWrapper.notifyChannelUpdated(); 430816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 431816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 432816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 433816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 434816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Applies the changed values by {@link #updateBrowsable} and {@link #updateLocked} 435816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * to DB. 436816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 437816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void applyUpdatedValuesToDb() { 4386ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko ChannelData data = mData; 439816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ArrayList<Long> browsableIds = new ArrayList<>(); 440816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ArrayList<Long> unbrowsableIds = new ArrayList<>(); 441816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (Long id : mBrowsableUpdateChannelIds) { 4426ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko ChannelWrapper channelWrapper = data.channelWrapperMap.get(id); 443816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (channelWrapper == null) { 444816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko continue; 445816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 446816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (channelWrapper.mChannel.isBrowsable()) { 447816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko browsableIds.add(id); 448816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 449816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko unbrowsableIds.add(id); 450816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 451816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channelWrapper.mBrowsableInDb = channelWrapper.mChannel.isBrowsable(); 452816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 453816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko String column = TvContract.Channels.COLUMN_BROWSABLE; 4547d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko if (mStoreBrowsableInSharedPreferences) { 4557d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko Editor editor = mBrowsableSharedPreferences.edit(); 4567d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko for (Long id : browsableIds) { 4577d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko editor.putBoolean(getBrowsableKey(getChannel(id)), true); 4587d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko } 4597d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko for (Long id : unbrowsableIds) { 4607d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko editor.putBoolean(getBrowsableKey(getChannel(id)), false); 4617d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko } 4627d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko editor.apply(); 4637d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko } else { 4646ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko if (!browsableIds.isEmpty()) { 4657d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko updateOneColumnValue(column, 1, browsableIds); 4667d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko } 4676ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko if (!unbrowsableIds.isEmpty()) { 4687d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko updateOneColumnValue(column, 0, unbrowsableIds); 4697d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko } 470816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 471816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mBrowsableUpdateChannelIds.clear(); 472816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 473816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ArrayList<Long> lockedIds = new ArrayList<>(); 474816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ArrayList<Long> unlockedIds = new ArrayList<>(); 475816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (Long id : mLockedUpdateChannelIds) { 4766ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko ChannelWrapper channelWrapper = data.channelWrapperMap.get(id); 477816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (channelWrapper == null) { 478816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko continue; 479816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 480816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (channelWrapper.mChannel.isLocked()) { 481816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko lockedIds.add(id); 482816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 483816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko unlockedIds.add(id); 484816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 485816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channelWrapper.mLockedInDb = channelWrapper.mChannel.isLocked(); 486816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 487816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko column = TvContract.Channels.COLUMN_LOCKED; 4886ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko if (!lockedIds.isEmpty()) { 489816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko updateOneColumnValue(column, 1, lockedIds); 490816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 4916ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko if (!unlockedIds.isEmpty()) { 492816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko updateOneColumnValue(column, 0, unlockedIds); 493816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 494816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mLockedUpdateChannelIds.clear(); 495816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (DEBUG) { 496816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Log.d(TAG, "applyUpdatedValuesToDb" 497816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko + "\n browsableIds size:" + browsableIds.size() 498816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko + "\n unbrowsableIds size:" + unbrowsableIds.size() 499816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko + "\n lockedIds size:" + lockedIds.size() 500816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko + "\n unlockedIds size:" + unlockedIds.size()); 501816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 502816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 503816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 5046ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko @MainThread 5056ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko private void addChannel(ChannelData data, Channel channel) { 5066ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko data.channels.add(channel); 507816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko String inputId = channel.getInputId(); 5086ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko MutableInt count = data.channelCountMap.get(inputId); 509816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (count == null) { 5106ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko data.channelCountMap.put(inputId, new MutableInt(1)); 511816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 512816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko count.value++; 513816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 514816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 515816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 5166ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko @MainThread 517816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private void clearChannels() { 5186ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko mData = new UnmodifiableChannelData(); 519816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 520816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 5216ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko @MainThread 522816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private void handleUpdateChannels() { 523816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mChannelsUpdateTask != null) { 524816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mChannelsUpdateTask.cancel(true); 525816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 526816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mChannelsUpdateTask = new QueryAllChannelsTask(mContentResolver); 527816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mChannelsUpdateTask.executeOnDbThread(); 528816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 529816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 5302e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko /** 5312e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko * Reloads channel data. 5322e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko */ 5332e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko public void reload() { 5342e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko if (mDbLoadFinished && !mHandler.hasMessages(MSG_UPDATE_CHANNELS)) { 5352e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko mHandler.sendEmptyMessage(MSG_UPDATE_CHANNELS); 5362e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko } 5372e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko } 5382e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko 5396ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko /** 5406ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko * A listener for ChannelDataManager. The callbacks are called on the main thread. 5416ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko */ 542816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public interface Listener { 543816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 544816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Called when data load is finished. 545816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 546816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void onLoadFinished(); 547816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 548816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 549816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Called when channels are added, deleted, or updated. But, when browsable is changed, 550816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * it won't be called. Instead, {@link #onChannelBrowsableChanged} will be called. 551816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 552816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void onChannelListUpdated(); 553816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 554816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 555816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Called when browsable of channels are changed. 556816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 557816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void onChannelBrowsableChanged(); 558816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 559816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 5606ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko /** 5616ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko * A listener for individual channel change. The callbacks are called on the main thread. 5626ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko */ 563816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public interface ChannelListener { 564816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 565816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Called when the channel has been removed in DB. 566816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 567816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void onChannelRemoved(Channel channel); 568816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 569816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 570816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Called when values of the channel has been changed. 571816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 572816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void onChannelUpdated(Channel channel); 573816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 574816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 575816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private class ChannelWrapper { 5762e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko final Set<ChannelListener> mChannelListeners = new ArraySet<>(); 577816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko final Channel mChannel; 578816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko boolean mBrowsableInDb; 579816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko boolean mLockedInDb; 580816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko boolean mInputRemoved; 581816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 582816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ChannelWrapper(Channel channel) { 583816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mChannel = channel; 584816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mBrowsableInDb = channel.isBrowsable(); 585816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mLockedInDb = channel.isLocked(); 586816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mInputRemoved = !mInputManager.hasTvInputInfo(channel.getInputId()); 587816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 588816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 589816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void addListener(ChannelListener listener) { 590816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mChannelListeners.add(listener); 591816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 592816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 593816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void removeListener(ChannelListener listener) { 594816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mChannelListeners.remove(listener); 595816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 596816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 597816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void notifyChannelUpdated() { 598816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (ChannelListener l : mChannelListeners) { 599816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko l.onChannelUpdated(mChannel); 600816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 601816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 602816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 603816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void notifyChannelRemoved() { 604816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (ChannelListener l : mChannelListeners) { 605816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko l.onChannelRemoved(mChannel); 606816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 607816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 608816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 609816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 6106ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko private class CheckChannelLogoExistTask extends AsyncTask<Void, Void, Boolean> { 6116ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko private final Channel mChannel; 6126ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko 6136ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko CheckChannelLogoExistTask(Channel channel) { 6146ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko mChannel = channel; 6156ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko } 6166ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko 6176ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko @Override 6186ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko protected Boolean doInBackground(Void... params) { 6196ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko try (AssetFileDescriptor f = mContext.getContentResolver().openAssetFileDescriptor( 6206ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko TvContract.buildChannelLogoUri(mChannel.getId()), "r")) { 6216ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko return true; 6226ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko } catch (SQLiteException | IOException | NullPointerException e) { 6236ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko // File not found or asset file not found. 6246ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko } 6256ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko return false; 6266ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko } 6276ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko 6286ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko @Override 6296ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko protected void onPostExecute(Boolean result) { 6306ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko ChannelWrapper wrapper = mData.channelWrapperMap.get(mChannel.getId()); 6316ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko if (wrapper != null) { 6326ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko wrapper.mChannel.setChannelLogoExist(result); 6336ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko } 6346ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko } 6356ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko } 6366ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko 637816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final class QueryAllChannelsTask extends AsyncDbTask.AsyncChannelQueryTask { 638816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 6396ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko QueryAllChannelsTask(ContentResolver contentResolver) { 640816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko super(contentResolver); 641816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 642816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 643816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 644816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko protected void onPostExecute(List<Channel> channels) { 645816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mChannelsUpdateTask = null; 646816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (channels == null) { 647816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (DEBUG) Log.e(TAG, "onPostExecute with null channels"); 648816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 649816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 6506ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko ChannelData data = new ChannelData(); 6516ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko data.channelWrapperMap.putAll(mData.channelWrapperMap); 6526ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko Set<Long> removedChannelIds = new HashSet<>(data.channelWrapperMap.keySet()); 653816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko List<ChannelWrapper> removedChannelWrappers = new ArrayList<>(); 654816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko List<ChannelWrapper> updatedChannelWrappers = new ArrayList<>(); 655816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 656816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko boolean channelAdded = false; 657816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko boolean channelUpdated = false; 658816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko boolean channelRemoved = false; 6597d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko Map<String, ?> deletedBrowsableMap = null; 6607d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko if (mStoreBrowsableInSharedPreferences) { 6617d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko deletedBrowsableMap = new HashMap<>(mBrowsableSharedPreferences.getAll()); 6627d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko } 663816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (Channel channel : channels) { 6647d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko if (mStoreBrowsableInSharedPreferences) { 6657d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko String browsableKey = getBrowsableKey(channel); 6667d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko channel.setBrowsable(mBrowsableSharedPreferences.getBoolean(browsableKey, 6677d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko false)); 6687d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko deletedBrowsableMap.remove(browsableKey); 6697d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko } 670816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long channelId = channel.getId(); 671816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko boolean newlyAdded = !removedChannelIds.remove(channelId); 672816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ChannelWrapper channelWrapper; 673816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (newlyAdded) { 6746ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko new CheckChannelLogoExistTask(channel) 6756ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko .executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); 676816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channelWrapper = new ChannelWrapper(channel); 6776ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko data.channelWrapperMap.put(channel.getId(), channelWrapper); 678816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!channelWrapper.mInputRemoved) { 679816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channelAdded = true; 680816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 681816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 6826ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko channelWrapper = data.channelWrapperMap.get(channelId); 683816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!channelWrapper.mChannel.hasSameReadOnlyInfo(channel)) { 684816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Channel data updated 685816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Channel oldChannel = channelWrapper.mChannel; 686816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // We assume that mBrowsable and mLocked are controlled by only TV app. 687816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // The values for mBrowsable and mLocked are updated when 688816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // {@link #applyUpdatedValuesToDb} is called. Therefore, the value 689816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // between DB and ChannelDataManager could be different for a while. 690816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Therefore, we'll keep the values in ChannelDataManager. 691816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channel.setBrowsable(oldChannel.isBrowsable()); 692816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channel.setLocked(oldChannel.isLocked()); 6936ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko channelWrapper.mChannel.copyFrom(channel); 694816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!channelWrapper.mInputRemoved) { 695816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channelUpdated = true; 696816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko updatedChannelWrappers.add(channelWrapper); 697816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 698816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 699816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 700816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 7017d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko if (mStoreBrowsableInSharedPreferences && !deletedBrowsableMap.isEmpty() 7027d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko && PermissionUtils.hasReadTvListings(mContext)) { 7037d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko // If hasReadTvListings(mContext) is false, the given channel list would 7047d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko // empty. In this case, we skip the browsable data clean up process. 7057d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko Editor editor = mBrowsableSharedPreferences.edit(); 7067d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko for (String key : deletedBrowsableMap.keySet()) { 7077d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko if (DEBUG) Log.d(TAG, "remove key: " + key); 7087d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko editor.remove(key); 7097d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko } 7107d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko editor.apply(); 7117d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko } 712816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 713816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (long id : removedChannelIds) { 7146ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko ChannelWrapper channelWrapper = data.channelWrapperMap.remove(id); 715816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!channelWrapper.mInputRemoved) { 716816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channelRemoved = true; 717816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko removedChannelWrappers.add(channelWrapper); 718816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 719816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 7206ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko for (ChannelWrapper channelWrapper : data.channelWrapperMap.values()) { 721816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!channelWrapper.mInputRemoved) { 7226ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko addChannel(data, channelWrapper.mChannel); 723816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 724816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 7256ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko Collections.sort(data.channels, mChannelComparator); 7266ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko mData = new UnmodifiableChannelData(data); 727816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 728816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!mDbLoadFinished) { 729816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mDbLoadFinished = true; 730ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko notifyLoadFinished(); 731816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else if (channelAdded || channelUpdated || channelRemoved) { 732ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko notifyChannelListUpdated(); 733816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 734816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (ChannelWrapper channelWrapper : removedChannelWrappers) { 735816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channelWrapper.notifyChannelRemoved(); 736816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 737816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (ChannelWrapper channelWrapper : updatedChannelWrappers) { 738816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channelWrapper.notifyChannelUpdated(); 739816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 740816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (Runnable r : mPostRunnablesAfterChannelUpdate) { 741816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko r.run(); 742816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 743816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPostRunnablesAfterChannelUpdate.clear(); 744816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 745816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 746816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 747816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 748816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Updates a column {@code columnName} of DB table {@code uri} with the value 749816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * {@code columnValue}. The selective rows in the ID list {@code ids} will be updated. 750816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * The DB operations will run on {@link AsyncDbTask#getExecutor()}. 751816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 752816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private void updateOneColumnValue( 753816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko final String columnName, final int columnValue, final List<Long> ids) { 7547d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko if (!PermissionUtils.hasAccessAllEpg(mContext)) { 7557d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko return; 7567d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko } 7576ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko AsyncDbTask.executeOnDbThread(new Runnable() { 758816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 759816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void run() { 760816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko String selection = Utils.buildSelectionForIds(Channels._ID, ids); 761816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ContentValues values = new ContentValues(); 762816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko values.put(columnName, columnValue); 763816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mContentResolver.update(TvContract.Channels.CONTENT_URI, values, selection, null); 764816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 765816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko }); 766816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 76707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko 7687d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko private String getBrowsableKey(Channel channel) { 7697d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko return channel.getInputId() + "|" + channel.getId(); 7707d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko } 7717d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko 7726ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko @MainThread 77307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko private static class ChannelDataManagerHandler extends WeakHandler<ChannelDataManager> { 7747d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko public ChannelDataManagerHandler(ChannelDataManager channelDataManager) { 7757d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko super(Looper.getMainLooper(), channelDataManager); 77607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko } 77707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko 77807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko @Override 77907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko public void handleMessage(Message msg, @NonNull ChannelDataManager channelDataManager) { 78007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko if (msg.what == MSG_UPDATE_CHANNELS) { 78107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko channelDataManager.handleUpdateChannels(); 78207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko } 78307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko } 78407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko } 7856ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko 7866ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko /** 7876ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko * Container class which includes channel data that needs to be synced. This class is 7886ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko * modifiable and used for changing channel data. 7896ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko * e.g. TvInputCallback, or AsyncDbTask.onPostExecute. 7906ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko */ 7916ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko @MainThread 7926ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko private static class ChannelData { 7936ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko final Map<Long, ChannelWrapper> channelWrapperMap; 7946ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko final Map<String, MutableInt> channelCountMap; 7956ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko final List<Channel> channels; 7966ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko 7976ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko ChannelData() { 7986ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko channelWrapperMap = new HashMap<>(); 7996ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko channelCountMap = new HashMap<>(); 8006ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko channels = new ArrayList<>(); 8016ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko } 8026ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko 8036ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko ChannelData(ChannelData data) { 8046ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko channelWrapperMap = new HashMap<>(data.channelWrapperMap); 8056ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko channelCountMap = new HashMap<>(data.channelCountMap); 8066ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko channels = new ArrayList<>(data.channels); 8076ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko } 8086ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko 8096ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko ChannelData(Map<Long, ChannelWrapper> channelWrapperMap, 8106ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko Map<String, MutableInt> channelCountMap, List<Channel> channels) { 8116ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko this.channelWrapperMap = channelWrapperMap; 8126ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko this.channelCountMap = channelCountMap; 8136ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko this.channels = channels; 8146ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko } 8156ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko } 8166ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko 8176ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko /** Unmodifiable channel data. */ 8186ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko @MainThread 8196ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko private static class UnmodifiableChannelData extends ChannelData { 8206ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko UnmodifiableChannelData() { 8216ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko super(Collections.unmodifiableMap(new HashMap<>()), 8226ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko Collections.unmodifiableMap(new HashMap<>()), 8236ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko Collections.unmodifiableList(new ArrayList<>())); 8246ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko } 8256ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko 8266ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko UnmodifiableChannelData(ChannelData data) { 8276ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko super(Collections.unmodifiableMap(data.channelWrapperMap), 8286ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko Collections.unmodifiableMap(data.channelCountMap), 8296ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko Collections.unmodifiableList(data.channels)); 8306ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko } 8316ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko } 832816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko} 833