1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tv.data; 18 19 import static android.support.test.InstrumentationRegistry.getInstrumentation; 20 import static android.support.test.InstrumentationRegistry.getTargetContext; 21 import static com.google.common.truth.Truth.assertThat; 22 import static com.google.common.truth.Truth.assertWithMessage; 23 24 import android.content.ContentProvider; 25 import android.content.ContentUris; 26 import android.content.ContentValues; 27 import android.content.Context; 28 import android.database.ContentObserver; 29 import android.database.Cursor; 30 import android.media.tv.TvContract; 31 import android.media.tv.TvContract.Channels; 32 import android.net.Uri; 33 import android.os.AsyncTask; 34 import android.support.test.filters.SmallTest; 35 import android.support.test.runner.AndroidJUnit4; 36 import android.test.MoreAsserts; 37 import android.test.mock.MockContentProvider; 38 import android.test.mock.MockContentResolver; 39 import android.test.mock.MockCursor; 40 import android.text.TextUtils; 41 import android.util.Log; 42 import android.util.SparseArray; 43 import com.android.tv.data.api.Channel; 44 import com.android.tv.testing.constants.Constants; 45 import com.android.tv.testing.data.ChannelInfo; 46 import com.android.tv.util.TvInputManagerHelper; 47 import java.util.ArrayList; 48 import java.util.Arrays; 49 import java.util.List; 50 import java.util.concurrent.CountDownLatch; 51 import java.util.concurrent.TimeUnit; 52 import org.junit.After; 53 import org.junit.Before; 54 import org.junit.Test; 55 import org.junit.runner.RunWith; 56 import org.mockito.Matchers; 57 import org.mockito.Mockito; 58 59 /** 60 * Test for {@link ChannelDataManager} 61 * 62 * <p>A test method may include tests for multiple methods to minimize the DB access. Note that all 63 * the methods of {@link ChannelDataManager} should be called from the UI thread. 64 */ 65 @SmallTest 66 @RunWith(AndroidJUnit4.class) 67 public class ChannelDataManagerTest { 68 private static final boolean DEBUG = false; 69 private static final String TAG = "ChannelDataManagerTest"; 70 71 // Wait time for expected success. 72 private static final long WAIT_TIME_OUT_MS = 1000L; 73 private static final String DUMMY_INPUT_ID = "dummy"; 74 private static final String COLUMN_BROWSABLE = "browsable"; 75 private static final String COLUMN_LOCKED = "locked"; 76 77 private ChannelDataManager mChannelDataManager; 78 private TestChannelDataManagerListener mListener; 79 private FakeContentResolver mContentResolver; 80 private FakeContentProvider mContentProvider; 81 82 @Before setUp()83 public void setUp() { 84 assertWithMessage("More than 2 channels to test") 85 .that(Constants.UNIT_TEST_CHANNEL_COUNT > 2) 86 .isTrue(); 87 88 mContentProvider = new FakeContentProvider(getTargetContext()); 89 mContentResolver = new FakeContentResolver(); 90 mContentResolver.addProvider(TvContract.AUTHORITY, mContentProvider); 91 mListener = new TestChannelDataManagerListener(); 92 getInstrumentation() 93 .runOnMainSync( 94 new Runnable() { 95 @Override 96 public void run() { 97 TvInputManagerHelper mockHelper = 98 Mockito.mock(TvInputManagerHelper.class); 99 Mockito.when(mockHelper.hasTvInputInfo(Matchers.anyString())) 100 .thenReturn(true); 101 mChannelDataManager = 102 new ChannelDataManager( 103 getTargetContext(), 104 mockHelper, 105 AsyncTask.SERIAL_EXECUTOR, 106 mContentResolver); 107 mChannelDataManager.addListener(mListener); 108 } 109 }); 110 } 111 112 @After tearDown()113 public void tearDown() { 114 getInstrumentation() 115 .runOnMainSync( 116 new Runnable() { 117 @Override 118 public void run() { 119 mChannelDataManager.stop(); 120 } 121 }); 122 } 123 startAndWaitForComplete()124 private void startAndWaitForComplete() throws InterruptedException { 125 getInstrumentation() 126 .runOnMainSync( 127 new Runnable() { 128 @Override 129 public void run() { 130 mChannelDataManager.start(); 131 } 132 }); 133 assertThat(mListener.loadFinishedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS)) 134 .isTrue(); 135 } 136 restart()137 private void restart() throws InterruptedException { 138 getInstrumentation() 139 .runOnMainSync( 140 new Runnable() { 141 @Override 142 public void run() { 143 mChannelDataManager.stop(); 144 mListener.reset(); 145 } 146 }); 147 startAndWaitForComplete(); 148 } 149 150 @Test testIsDbLoadFinished()151 public void testIsDbLoadFinished() throws InterruptedException { 152 startAndWaitForComplete(); 153 assertThat(mChannelDataManager.isDbLoadFinished()).isTrue(); 154 } 155 156 /** 157 * Test for following methods - {@link ChannelDataManager#getChannelCount} - {@link 158 * ChannelDataManager#getChannelList} - {@link ChannelDataManager#getChannel} 159 */ 160 @Test testGetChannels()161 public void testGetChannels() throws InterruptedException { 162 startAndWaitForComplete(); 163 164 // Test {@link ChannelDataManager#getChannelCount} 165 assertThat(mChannelDataManager.getChannelCount()) 166 .isEqualTo(Constants.UNIT_TEST_CHANNEL_COUNT); 167 168 // Test {@link ChannelDataManager#getChannelList} 169 List<ChannelInfo> channelInfoList = new ArrayList<>(); 170 for (int i = 1; i <= Constants.UNIT_TEST_CHANNEL_COUNT; i++) { 171 channelInfoList.add(ChannelInfo.create(getTargetContext(), i)); 172 } 173 List<Channel> channelList = mChannelDataManager.getChannelList(); 174 for (Channel channel : channelList) { 175 boolean found = false; 176 for (ChannelInfo channelInfo : channelInfoList) { 177 if (TextUtils.equals(channelInfo.name, channel.getDisplayName())) { 178 found = true; 179 channelInfoList.remove(channelInfo); 180 break; 181 } 182 } 183 assertWithMessage("Cannot find (" + channel + ")").that(found).isTrue(); 184 } 185 186 // Test {@link ChannelDataManager#getChannelIndex()} 187 for (Channel channel : channelList) { 188 assertThat(mChannelDataManager.getChannel(channel.getId())).isEqualTo(channel); 189 } 190 } 191 192 /** Test for {@link ChannelDataManager#getChannelCount} when no channel is available. */ 193 @Test testGetChannels_noChannels()194 public void testGetChannels_noChannels() throws InterruptedException { 195 mContentProvider.clear(); 196 startAndWaitForComplete(); 197 assertThat(mChannelDataManager.getChannelCount()).isEqualTo(0); 198 } 199 200 /** 201 * Test for following methods and channel listener with notifying change. - {@link 202 * ChannelDataManager#updateBrowsable} - {@link ChannelDataManager#applyUpdatedValuesToDb} 203 */ 204 @Test testBrowsable()205 public void testBrowsable() throws InterruptedException { 206 startAndWaitForComplete(); 207 208 // Test if all channels are browsable 209 List<Channel> channelList = mChannelDataManager.getChannelList(); 210 List<Channel> browsableChannelList = mChannelDataManager.getBrowsableChannelList(); 211 for (Channel browsableChannel : browsableChannelList) { 212 boolean found = channelList.remove(browsableChannel); 213 assertWithMessage("Cannot find (" + browsableChannel + ")").that(found).isTrue(); 214 } 215 assertThat(channelList).isEmpty(); 216 217 // Prepare for next tests. 218 channelList = mChannelDataManager.getChannelList(); 219 TestChannelDataManagerChannelListener channelListener = 220 new TestChannelDataManagerChannelListener(); 221 Channel channel1 = channelList.get(0); 222 mChannelDataManager.addChannelListener(channel1.getId(), channelListener); 223 224 // Test {@link ChannelDataManager#updateBrowsable} & notification. 225 mChannelDataManager.updateBrowsable(channel1.getId(), false, false); 226 assertThat(mListener.channelBrowsableChangedCalled).isTrue(); 227 assertThat(mChannelDataManager.getBrowsableChannelList()).doesNotContain(channel1); 228 MoreAsserts.assertContentsInAnyOrder(channelListener.updatedChannels, channel1); 229 channelListener.reset(); 230 231 // Test {@link ChannelDataManager#applyUpdatedValuesToDb} 232 // Disable the update notification to avoid the unwanted call of "onLoadFinished". 233 mContentResolver.mNotifyDisabled = true; 234 mChannelDataManager.applyUpdatedValuesToDb(); 235 restart(); 236 browsableChannelList = mChannelDataManager.getBrowsableChannelList(); 237 assertThat(browsableChannelList).hasSize(Constants.UNIT_TEST_CHANNEL_COUNT - 1); 238 assertThat(browsableChannelList).doesNotContain(channel1); 239 } 240 241 /** 242 * Test for following methods and channel listener without notifying change. - {@link 243 * ChannelDataManager#updateBrowsable} - {@link ChannelDataManager#applyUpdatedValuesToDb} 244 */ 245 @Test testBrowsable_skipNotification()246 public void testBrowsable_skipNotification() throws InterruptedException { 247 startAndWaitForComplete(); 248 249 List<Channel> channels = mChannelDataManager.getChannelList(); 250 // Prepare for next tests. 251 TestChannelDataManagerChannelListener channelListener = 252 new TestChannelDataManagerChannelListener(); 253 Channel channel1 = channels.get(0); 254 Channel channel2 = channels.get(1); 255 mChannelDataManager.addChannelListener(channel1.getId(), channelListener); 256 mChannelDataManager.addChannelListener(channel2.getId(), channelListener); 257 258 // Test {@link ChannelDataManager#updateBrowsable} & skip notification. 259 mChannelDataManager.updateBrowsable(channel1.getId(), false, true); 260 mChannelDataManager.updateBrowsable(channel2.getId(), false, true); 261 mChannelDataManager.updateBrowsable(channel1.getId(), true, true); 262 assertThat(mListener.channelBrowsableChangedCalled).isFalse(); 263 List<Channel> browsableChannelList = mChannelDataManager.getBrowsableChannelList(); 264 assertThat(browsableChannelList).contains(channel1); 265 assertThat(browsableChannelList).doesNotContain(channel2); 266 267 // Test {@link ChannelDataManager#applyUpdatedValuesToDb} 268 // Disable the update notification to avoid the unwanted call of "onLoadFinished". 269 mContentResolver.mNotifyDisabled = true; 270 mChannelDataManager.applyUpdatedValuesToDb(); 271 restart(); 272 browsableChannelList = mChannelDataManager.getBrowsableChannelList(); 273 assertThat(browsableChannelList).hasSize(Constants.UNIT_TEST_CHANNEL_COUNT - 1); 274 assertThat(browsableChannelList).doesNotContain(channel2); 275 } 276 277 /** 278 * Test for following methods and channel listener. - {@link ChannelDataManager#updateLocked} - 279 * {@link ChannelDataManager#applyUpdatedValuesToDb} 280 */ 281 @Test testLocked()282 public void testLocked() throws InterruptedException { 283 startAndWaitForComplete(); 284 285 // Test if all channels aren't locked at the first time. 286 List<Channel> channelList = mChannelDataManager.getChannelList(); 287 for (Channel channel : channelList) { 288 assertWithMessage(channel + " is locked").that(channel.isLocked()).isFalse(); 289 } 290 291 // Prepare for next tests. 292 Channel channel = mChannelDataManager.getChannelList().get(0); 293 294 // Test {@link ChannelDataManager#updateLocked} 295 mChannelDataManager.updateLocked(channel.getId(), true); 296 assertThat(mChannelDataManager.getChannel(channel.getId()).isLocked()).isTrue(); 297 298 // Test {@link ChannelDataManager#applyUpdatedValuesToDb}. 299 // Disable the update notification to avoid the unwanted call of "onLoadFinished". 300 mContentResolver.mNotifyDisabled = true; 301 mChannelDataManager.applyUpdatedValuesToDb(); 302 restart(); 303 assertThat(mChannelDataManager.getChannel(channel.getId()).isLocked()).isTrue(); 304 305 // Cleanup 306 mChannelDataManager.updateLocked(channel.getId(), false); 307 } 308 309 /** Test ChannelDataManager when channels in TvContract are updated, removed, or added. */ 310 @Test testChannelListChanged()311 public void testChannelListChanged() throws InterruptedException { 312 startAndWaitForComplete(); 313 314 // Test channel add. 315 mListener.reset(); 316 long testChannelId = Constants.UNIT_TEST_CHANNEL_COUNT + 1; 317 ChannelInfo testChannelInfo = ChannelInfo.create(getTargetContext(), (int) testChannelId); 318 testChannelId = Constants.UNIT_TEST_CHANNEL_COUNT + 1; 319 mContentProvider.simulateInsert(testChannelInfo); 320 assertThat(mListener.channelListUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS)) 321 .isTrue(); 322 assertThat(mChannelDataManager.getChannelCount()) 323 .isEqualTo(Constants.UNIT_TEST_CHANNEL_COUNT + 1); 324 325 // Test channel update 326 mListener.reset(); 327 TestChannelDataManagerChannelListener channelListener = 328 new TestChannelDataManagerChannelListener(); 329 mChannelDataManager.addChannelListener(testChannelId, channelListener); 330 String newName = testChannelInfo.name + "_test"; 331 mContentProvider.simulateUpdate(testChannelId, newName); 332 assertThat(mListener.channelListUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS)) 333 .isTrue(); 334 assertThat( 335 channelListener.channelChangedLatch.await( 336 WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS)) 337 .isTrue(); 338 assertThat(channelListener.removedChannels).isEmpty(); 339 assertThat(channelListener.updatedChannels).hasSize(1); 340 Channel updatedChannel = channelListener.updatedChannels.get(0); 341 assertThat(updatedChannel.getId()).isEqualTo(testChannelId); 342 assertThat(updatedChannel.getDisplayNumber()).isEqualTo(testChannelInfo.number); 343 assertThat(updatedChannel.getDisplayName()).isEqualTo(newName); 344 assertThat(mChannelDataManager.getChannelCount()) 345 .isEqualTo(Constants.UNIT_TEST_CHANNEL_COUNT + 1); 346 347 // Test channel remove. 348 mListener.reset(); 349 channelListener.reset(); 350 mContentProvider.simulateDelete(testChannelId); 351 assertThat(mListener.channelListUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS)) 352 .isTrue(); 353 assertThat( 354 channelListener.channelChangedLatch.await( 355 WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS)) 356 .isTrue(); 357 assertThat(channelListener.removedChannels).hasSize(1); 358 assertThat(channelListener.updatedChannels).isEmpty(); 359 Channel removedChannel = channelListener.removedChannels.get(0); 360 assertThat(removedChannel.getDisplayName()).isEqualTo(newName); 361 assertThat(removedChannel.getDisplayNumber()).isEqualTo(testChannelInfo.number); 362 assertThat(mChannelDataManager.getChannelCount()) 363 .isEqualTo(Constants.UNIT_TEST_CHANNEL_COUNT); 364 } 365 366 private static class ChannelInfoWrapper { 367 public ChannelInfo channelInfo; 368 public boolean browsable; 369 public boolean locked; 370 ChannelInfoWrapper(ChannelInfo channelInfo)371 public ChannelInfoWrapper(ChannelInfo channelInfo) { 372 this.channelInfo = channelInfo; 373 browsable = true; 374 locked = false; 375 } 376 } 377 378 private class FakeContentResolver extends MockContentResolver { 379 boolean mNotifyDisabled; 380 381 @Override notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork)382 public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) { 383 super.notifyChange(uri, observer, syncToNetwork); 384 if (DEBUG) { 385 Log.d( 386 TAG, 387 "onChanged(uri=" 388 + uri 389 + ", observer=" 390 + observer 391 + ") - Notification " 392 + (mNotifyDisabled ? "disabled" : "enabled")); 393 } 394 if (mNotifyDisabled) { 395 return; 396 } 397 // Do not call {@link ContentObserver#onChange} directly to run it on the correct 398 // thread. 399 if (observer != null) { 400 observer.dispatchChange(false, uri); 401 } else { 402 mChannelDataManager.getContentObserver().dispatchChange(false, uri); 403 } 404 } 405 } 406 407 // This implements the minimal methods in content resolver 408 // and detailed assumptions are written in each method. 409 private class FakeContentProvider extends MockContentProvider { 410 private final SparseArray<ChannelInfoWrapper> mChannelInfoList = new SparseArray<>(); 411 FakeContentProvider(Context context)412 public FakeContentProvider(Context context) { 413 super(context); 414 for (int i = 1; i <= Constants.UNIT_TEST_CHANNEL_COUNT; i++) { 415 mChannelInfoList.put( 416 i, new ChannelInfoWrapper(ChannelInfo.create(getTargetContext(), i))); 417 } 418 } 419 420 /** 421 * Implementation of {@link ContentProvider#query}. This assumes that {@link 422 * ChannelDataManager} queries channels with empty {@code selection}. (i.e. channels are 423 * always queries for all) 424 */ 425 @Override query( Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)426 public Cursor query( 427 Uri uri, 428 String[] projection, 429 String selection, 430 String[] selectionArgs, 431 String sortOrder) { 432 if (DEBUG) { 433 Log.d(TAG, "dump query"); 434 Log.d(TAG, " uri=" + uri); 435 Log.d(TAG, " projection=" + Arrays.toString(projection)); 436 Log.d(TAG, " selection=" + selection); 437 } 438 assertChannelUri(uri); 439 return new FakeCursor(projection); 440 } 441 442 /** 443 * Implementation of {@link ContentProvider#update}. This assumes that {@link 444 * ChannelDataManager} update channels only for changing browsable and locked. 445 */ 446 @Override update(Uri uri, ContentValues values, String selection, String[] selectionArgs)447 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 448 if (DEBUG) Log.d(TAG, "update(uri=" + uri + ", selection=" + selection); 449 assertChannelUri(uri); 450 List<Long> channelIds = new ArrayList<>(); 451 try { 452 long channelId = ContentUris.parseId(uri); 453 channelIds.add(channelId); 454 } catch (NumberFormatException e) { 455 // Update for multiple channels. 456 if (TextUtils.isEmpty(selection)) { 457 for (int i = 0; i < mChannelInfoList.size(); i++) { 458 channelIds.add((long) mChannelInfoList.keyAt(i)); 459 } 460 } else { 461 // See {@link Utils#buildSelectionForIds} for the syntax. 462 String selectionForId = 463 selection.substring( 464 selection.indexOf("(") + 1, selection.lastIndexOf(")")); 465 String[] ids = selectionForId.split(", "); 466 if (ids != null) { 467 for (String id : ids) { 468 channelIds.add(Long.parseLong(id)); 469 } 470 } 471 } 472 } 473 int updateCount = 0; 474 for (long channelId : channelIds) { 475 boolean updated = false; 476 ChannelInfoWrapper channel = mChannelInfoList.get((int) channelId); 477 if (channel == null) { 478 return 0; 479 } 480 if (values.containsKey(COLUMN_BROWSABLE)) { 481 updated = true; 482 channel.browsable = (values.getAsInteger(COLUMN_BROWSABLE) == 1); 483 } 484 if (values.containsKey(COLUMN_LOCKED)) { 485 updated = true; 486 channel.locked = (values.getAsInteger(COLUMN_LOCKED) == 1); 487 } 488 updateCount += updated ? 1 : 0; 489 } 490 if (updateCount > 0) { 491 if (channelIds.size() == 1) { 492 mContentResolver.notifyChange(uri, null); 493 } else { 494 mContentResolver.notifyChange(Channels.CONTENT_URI, null); 495 } 496 } else { 497 if (DEBUG) { 498 Log.d(TAG, "Update to channel(uri=" + uri + ") is ignored for " + values); 499 } 500 } 501 return updateCount; 502 } 503 504 /** 505 * Simulates channel data insert. This assigns original network ID (the same with channel 506 * number) to channel ID. 507 */ simulateInsert(ChannelInfo testChannelInfo)508 public void simulateInsert(ChannelInfo testChannelInfo) { 509 long channelId = testChannelInfo.originalNetworkId; 510 mChannelInfoList.put( 511 (int) channelId, 512 new ChannelInfoWrapper( 513 ChannelInfo.create(getTargetContext(), (int) channelId))); 514 mContentResolver.notifyChange(TvContract.buildChannelUri(channelId), null); 515 } 516 517 /** Simulates channel data delete. */ simulateDelete(long channelId)518 public void simulateDelete(long channelId) { 519 mChannelInfoList.remove((int) channelId); 520 mContentResolver.notifyChange(TvContract.buildChannelUri(channelId), null); 521 } 522 523 /** Simulates channel data update. */ simulateUpdate(long channelId, String newName)524 public void simulateUpdate(long channelId, String newName) { 525 ChannelInfoWrapper channel = mChannelInfoList.get((int) channelId); 526 ChannelInfo.Builder builder = new ChannelInfo.Builder(channel.channelInfo); 527 builder.setName(newName); 528 channel.channelInfo = builder.build(); 529 mContentResolver.notifyChange(TvContract.buildChannelUri(channelId), null); 530 } 531 assertChannelUri(Uri uri)532 private void assertChannelUri(Uri uri) { 533 assertWithMessage("Uri(" + uri + ") isn't channel uri") 534 .that(uri.toString().startsWith(Channels.CONTENT_URI.toString())) 535 .isTrue(); 536 } 537 clear()538 public void clear() { 539 mChannelInfoList.clear(); 540 } 541 get(int position)542 public ChannelInfoWrapper get(int position) { 543 return mChannelInfoList.get(mChannelInfoList.keyAt(position)); 544 } 545 getCount()546 public int getCount() { 547 return mChannelInfoList.size(); 548 } 549 keyAt(int position)550 public long keyAt(int position) { 551 return mChannelInfoList.keyAt(position); 552 } 553 } 554 555 private class FakeCursor extends MockCursor { 556 private final String[] allColumns = { 557 Channels._ID, 558 Channels.COLUMN_DISPLAY_NAME, 559 Channels.COLUMN_DISPLAY_NUMBER, 560 Channels.COLUMN_INPUT_ID, 561 Channels.COLUMN_VIDEO_FORMAT, 562 Channels.COLUMN_ORIGINAL_NETWORK_ID, 563 COLUMN_BROWSABLE, 564 COLUMN_LOCKED 565 }; 566 private final String[] mColumns; 567 private int mPosition; 568 FakeCursor(String[] columns)569 public FakeCursor(String[] columns) { 570 mColumns = (columns == null) ? allColumns : columns; 571 mPosition = -1; 572 } 573 574 @Override getColumnName(int columnIndex)575 public String getColumnName(int columnIndex) { 576 return mColumns[columnIndex]; 577 } 578 579 @Override getColumnIndex(String columnName)580 public int getColumnIndex(String columnName) { 581 for (int i = 0; i < mColumns.length; i++) { 582 if (mColumns[i].equalsIgnoreCase(columnName)) { 583 return i; 584 } 585 } 586 return -1; 587 } 588 589 @Override getLong(int columnIndex)590 public long getLong(int columnIndex) { 591 String columnName = getColumnName(columnIndex); 592 switch (columnName) { 593 case Channels._ID: 594 return mContentProvider.keyAt(mPosition); 595 default: // fall out 596 } 597 if (DEBUG) { 598 Log.d(TAG, "Column (" + columnName + ") is ignored in getLong()"); 599 } 600 return 0; 601 } 602 603 @Override getString(int columnIndex)604 public String getString(int columnIndex) { 605 String columnName = getColumnName(columnIndex); 606 ChannelInfoWrapper channel = mContentProvider.get(mPosition); 607 switch (columnName) { 608 case Channels.COLUMN_DISPLAY_NAME: 609 return channel.channelInfo.name; 610 case Channels.COLUMN_DISPLAY_NUMBER: 611 return channel.channelInfo.number; 612 case Channels.COLUMN_INPUT_ID: 613 return DUMMY_INPUT_ID; 614 case Channels.COLUMN_VIDEO_FORMAT: 615 return channel.channelInfo.getVideoFormat(); 616 default: // fall out 617 } 618 if (DEBUG) { 619 Log.d(TAG, "Column (" + columnName + ") is ignored in getString()"); 620 } 621 return null; 622 } 623 624 @Override getInt(int columnIndex)625 public int getInt(int columnIndex) { 626 String columnName = getColumnName(columnIndex); 627 ChannelInfoWrapper channel = mContentProvider.get(mPosition); 628 switch (columnName) { 629 case Channels.COLUMN_ORIGINAL_NETWORK_ID: 630 return channel.channelInfo.originalNetworkId; 631 case COLUMN_BROWSABLE: 632 return channel.browsable ? 1 : 0; 633 case COLUMN_LOCKED: 634 return channel.locked ? 1 : 0; 635 default: // fall out 636 } 637 if (DEBUG) { 638 Log.d(TAG, "Column (" + columnName + ") is ignored in getInt()"); 639 } 640 return 0; 641 } 642 643 @Override getCount()644 public int getCount() { 645 return mContentProvider.getCount(); 646 } 647 648 @Override moveToNext()649 public boolean moveToNext() { 650 return ++mPosition < mContentProvider.getCount(); 651 } 652 653 @Override close()654 public void close() { 655 // No-op. 656 } 657 } 658 659 private static class TestChannelDataManagerListener implements ChannelDataManager.Listener { 660 public CountDownLatch loadFinishedLatch = new CountDownLatch(1); 661 public CountDownLatch channelListUpdatedLatch = new CountDownLatch(1); 662 public boolean channelBrowsableChangedCalled; 663 664 @Override onLoadFinished()665 public void onLoadFinished() { 666 loadFinishedLatch.countDown(); 667 } 668 669 @Override onChannelListUpdated()670 public void onChannelListUpdated() { 671 channelListUpdatedLatch.countDown(); 672 } 673 674 @Override onChannelBrowsableChanged()675 public void onChannelBrowsableChanged() { 676 channelBrowsableChangedCalled = true; 677 } 678 reset()679 public void reset() { 680 loadFinishedLatch = new CountDownLatch(1); 681 channelListUpdatedLatch = new CountDownLatch(1); 682 channelBrowsableChangedCalled = false; 683 } 684 } 685 686 private static class TestChannelDataManagerChannelListener 687 implements ChannelDataManager.ChannelListener { 688 public CountDownLatch channelChangedLatch = new CountDownLatch(1); 689 public final List<Channel> removedChannels = new ArrayList<>(); 690 public final List<Channel> updatedChannels = new ArrayList<>(); 691 692 @Override onChannelRemoved(Channel channel)693 public void onChannelRemoved(Channel channel) { 694 removedChannels.add(channel); 695 channelChangedLatch.countDown(); 696 } 697 698 @Override onChannelUpdated(Channel channel)699 public void onChannelUpdated(Channel channel) { 700 updatedChannels.add(channel); 701 channelChangedLatch.countDown(); 702 } 703 reset()704 public void reset() { 705 channelChangedLatch = new CountDownLatch(1); 706 removedChannels.clear(); 707 updatedChannels.clear(); 708 } 709 } 710 } 711