1 /* 2 * Copyright 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.pump.db; 18 19 import android.Manifest; 20 import android.content.ContentResolver; 21 22 import androidx.annotation.NonNull; 23 import androidx.annotation.RequiresPermission; 24 import androidx.annotation.UiThread; 25 import androidx.collection.ArraySet; 26 27 import com.android.pump.concurrent.Executors; 28 import com.android.pump.util.Clog; 29 30 import java.io.IOException; 31 import java.util.ArrayList; 32 import java.util.Collection; 33 import java.util.Collections; 34 import java.util.List; 35 import java.util.Set; 36 import java.util.concurrent.Executor; 37 import java.util.concurrent.atomic.AtomicBoolean; 38 39 @UiThread 40 public class MediaDb implements MediaProvider { 41 private static final String TAG = Clog.tag(MediaDb.class); 42 43 private final AtomicBoolean mLoaded = new AtomicBoolean(); 44 45 private final Executor mExecutor; 46 47 private final AudioStore mAudioStore; 48 private final VideoStore mVideoStore; 49 private final DataProvider mDataProvider; 50 51 private final List<Audio> mAudios = new ArrayList<>(); 52 private final List<Artist> mArtists = new ArrayList<>(); 53 private final List<Album> mAlbums = new ArrayList<>(); 54 private final List<Genre> mGenres = new ArrayList<>(); 55 private final List<Playlist> mPlaylists = new ArrayList<>(); 56 57 private final List<Movie> mMovies = new ArrayList<>(); 58 private final List<Series> mSeries = new ArrayList<>(); 59 private final List<Episode> mEpisodes = new ArrayList<>(); 60 private final List<Other> mOthers = new ArrayList<>(); 61 62 private final Set<UpdateCallback> mAudioUpdateCallbacks = new ArraySet<>(); 63 private final Set<UpdateCallback> mArtistUpdateCallbacks = new ArraySet<>(); 64 private final Set<UpdateCallback> mAlbumUpdateCallbacks = new ArraySet<>(); 65 private final Set<UpdateCallback> mGenreUpdateCallbacks = new ArraySet<>(); 66 private final Set<UpdateCallback> mPlaylistUpdateCallbacks = new ArraySet<>(); 67 68 private final Set<UpdateCallback> mMovieUpdateCallbacks = new ArraySet<>(); 69 private final Set<UpdateCallback> mSeriesUpdateCallbacks = new ArraySet<>(); 70 private final Set<UpdateCallback> mEpisodeUpdateCallbacks = new ArraySet<>(); 71 private final Set<UpdateCallback> mOtherUpdateCallbacks = new ArraySet<>(); 72 73 public interface UpdateCallback { onItemsInserted(int index, int count)74 void onItemsInserted(int index, int count); onItemsUpdated(int index, int count)75 void onItemsUpdated(int index, int count); onItemsRemoved(int index, int count)76 void onItemsRemoved(int index, int count); 77 } 78 MediaDb(@onNull ContentResolver contentResolver, @NonNull DataProvider dataProvider, @NonNull Executor executor)79 public MediaDb(@NonNull ContentResolver contentResolver, @NonNull DataProvider dataProvider, 80 @NonNull Executor executor) { 81 Clog.i(TAG, "MediaDb(" + contentResolver + ", " + dataProvider + ", " + executor + ")"); 82 mDataProvider = dataProvider; 83 mExecutor = executor; 84 85 mAudioStore = new AudioStore(contentResolver, new AudioStore.ChangeListener() { 86 @Override 87 public void onAudiosAdded(@NonNull Collection<Audio> audios) { 88 Executors.uiThreadExecutor().execute(() -> addAudios(audios)); 89 } 90 91 @Override 92 public void onArtistsAdded(@NonNull Collection<Artist> artists) { 93 Executors.uiThreadExecutor().execute(() -> addArtists(artists)); 94 } 95 96 @Override 97 public void onAlbumsAdded(@NonNull Collection<Album> albums) { 98 Executors.uiThreadExecutor().execute(() -> addAlbums(albums)); 99 } 100 101 @Override 102 public void onGenresAdded(@NonNull Collection<Genre> genres) { 103 Executors.uiThreadExecutor().execute(() -> addGenres(genres)); 104 } 105 106 @Override 107 public void onPlaylistsAdded(@NonNull Collection<Playlist> playlists) { 108 Executors.uiThreadExecutor().execute(() -> addPlaylists(playlists)); 109 } 110 }, this); 111 112 mVideoStore = new VideoStore(contentResolver, new VideoStore.ChangeListener() { 113 @Override 114 public void onMoviesAdded(@NonNull Collection<Movie> movies) { 115 Executors.uiThreadExecutor().execute(() -> addMovies(movies)); 116 } 117 118 @Override 119 public void onSeriesAdded(@NonNull Collection<Series> series) { 120 Executors.uiThreadExecutor().execute(() -> addSeries(series)); 121 } 122 123 @Override 124 public void onEpisodesAdded(@NonNull Collection<Episode> episodes) { 125 Executors.uiThreadExecutor().execute(() -> addEpisodes(episodes)); 126 } 127 128 @Override 129 public void onOthersAdded(@NonNull Collection<Other> others) { 130 Executors.uiThreadExecutor().execute(() -> addOthers(others)); 131 } 132 }, this); 133 } 134 addAudioUpdateCallback(@onNull UpdateCallback callback)135 public void addAudioUpdateCallback(@NonNull UpdateCallback callback) { 136 addUpdateCallback(mAudioUpdateCallbacks, callback); 137 } 138 removeAudioUpdateCallback(@onNull UpdateCallback callback)139 public void removeAudioUpdateCallback(@NonNull UpdateCallback callback) { 140 removeUpdateCallback(mAudioUpdateCallbacks, callback); 141 } 142 addArtistUpdateCallback(@onNull UpdateCallback callback)143 public void addArtistUpdateCallback(@NonNull UpdateCallback callback) { 144 addUpdateCallback(mArtistUpdateCallbacks, callback); 145 } 146 removeArtistUpdateCallback(@onNull UpdateCallback callback)147 public void removeArtistUpdateCallback(@NonNull UpdateCallback callback) { 148 removeUpdateCallback(mArtistUpdateCallbacks, callback); 149 } 150 addAlbumUpdateCallback(@onNull UpdateCallback callback)151 public void addAlbumUpdateCallback(@NonNull UpdateCallback callback) { 152 addUpdateCallback(mAlbumUpdateCallbacks, callback); 153 } 154 removeAlbumUpdateCallback(@onNull UpdateCallback callback)155 public void removeAlbumUpdateCallback(@NonNull UpdateCallback callback) { 156 removeUpdateCallback(mAlbumUpdateCallbacks, callback); 157 } 158 addGenreUpdateCallback(@onNull UpdateCallback callback)159 public void addGenreUpdateCallback(@NonNull UpdateCallback callback) { 160 addUpdateCallback(mGenreUpdateCallbacks, callback); 161 } 162 removeGenreUpdateCallback(@onNull UpdateCallback callback)163 public void removeGenreUpdateCallback(@NonNull UpdateCallback callback) { 164 removeUpdateCallback(mGenreUpdateCallbacks, callback); 165 } 166 addPlaylistUpdateCallback(@onNull UpdateCallback callback)167 public void addPlaylistUpdateCallback(@NonNull UpdateCallback callback) { 168 addUpdateCallback(mPlaylistUpdateCallbacks, callback); 169 } 170 removePlaylistUpdateCallback(@onNull UpdateCallback callback)171 public void removePlaylistUpdateCallback(@NonNull UpdateCallback callback) { 172 removeUpdateCallback(mPlaylistUpdateCallbacks, callback); 173 } 174 addMovieUpdateCallback(@onNull UpdateCallback callback)175 public void addMovieUpdateCallback(@NonNull UpdateCallback callback) { 176 addUpdateCallback(mMovieUpdateCallbacks, callback); 177 } 178 removeMovieUpdateCallback(@onNull UpdateCallback callback)179 public void removeMovieUpdateCallback(@NonNull UpdateCallback callback) { 180 removeUpdateCallback(mMovieUpdateCallbacks, callback); 181 } 182 addSeriesUpdateCallback(@onNull UpdateCallback callback)183 public void addSeriesUpdateCallback(@NonNull UpdateCallback callback) { 184 addUpdateCallback(mSeriesUpdateCallbacks, callback); 185 } 186 removeSeriesUpdateCallback(@onNull UpdateCallback callback)187 public void removeSeriesUpdateCallback(@NonNull UpdateCallback callback) { 188 removeUpdateCallback(mSeriesUpdateCallbacks, callback); 189 } 190 addEpisodeUpdateCallback(@onNull UpdateCallback callback)191 public void addEpisodeUpdateCallback(@NonNull UpdateCallback callback) { 192 addUpdateCallback(mEpisodeUpdateCallbacks, callback); 193 } 194 removeEpisodeUpdateCallback(@onNull UpdateCallback callback)195 public void removeEpisodeUpdateCallback(@NonNull UpdateCallback callback) { 196 removeUpdateCallback(mEpisodeUpdateCallbacks, callback); 197 } 198 addOtherUpdateCallback(@onNull UpdateCallback callback)199 public void addOtherUpdateCallback(@NonNull UpdateCallback callback) { 200 addUpdateCallback(mOtherUpdateCallbacks, callback); 201 } 202 removeOtherUpdateCallback(@onNull UpdateCallback callback)203 public void removeOtherUpdateCallback(@NonNull UpdateCallback callback) { 204 removeUpdateCallback(mOtherUpdateCallbacks, callback); 205 } 206 207 @RequiresPermission(Manifest.permission.READ_EXTERNAL_STORAGE) load()208 public void load() { 209 Clog.i(TAG, "load()"); 210 if (mLoaded.getAndSet(true)) { 211 return; 212 } 213 214 mExecutor.execute(mAudioStore::load); 215 mExecutor.execute(mVideoStore::load); 216 } 217 getAudios()218 public @NonNull List<Audio> getAudios() { 219 return Collections.unmodifiableList(mAudios); 220 } getArtists()221 public @NonNull List<Artist> getArtists() { 222 return Collections.unmodifiableList(mArtists); 223 } getAlbums()224 public @NonNull List<Album> getAlbums() { 225 return Collections.unmodifiableList(mAlbums); 226 } getGenres()227 public @NonNull List<Genre> getGenres() { 228 return Collections.unmodifiableList(mGenres); 229 } getPlaylists()230 public @NonNull List<Playlist> getPlaylists() { 231 return Collections.unmodifiableList(mPlaylists); 232 } 233 getMovies()234 public @NonNull List<Movie> getMovies() { 235 return Collections.unmodifiableList(mMovies); 236 } getSeries()237 public @NonNull List<Series> getSeries() { 238 return Collections.unmodifiableList(mSeries); 239 } getEpisodes()240 public @NonNull List<Episode> getEpisodes() { 241 return Collections.unmodifiableList(mEpisodes); 242 } getOthers()243 public @NonNull List<Other> getOthers() { 244 return Collections.unmodifiableList(mOthers); 245 } 246 loadData(@onNull Audio audio)247 public void loadData(@NonNull Audio audio) { 248 // TODO(b/123707632) Ensure no concurrent runs for the same item !! 249 if (audio.isLoaded()) return; 250 251 mExecutor.execute(() -> { 252 boolean updated = mAudioStore.loadData(audio); 253 254 audio.setLoaded(); 255 if (updated) { 256 Executors.uiThreadExecutor().execute(() -> updateAudio(audio)); 257 } 258 }); 259 } 260 loadData(@onNull Artist artist)261 public void loadData(@NonNull Artist artist) { 262 // TODO(b/123707632) Ensure no concurrent runs for the same item !! 263 if (artist.isLoaded()) return; 264 265 mExecutor.execute(() -> { 266 try { 267 boolean updated = mDataProvider.populateArtist(artist); 268 269 updated |= mAudioStore.loadData(artist); 270 271 artist.setLoaded(); 272 if (updated) { 273 Executors.uiThreadExecutor().execute(() -> updateArtist(artist)); 274 } 275 } catch (IOException e) { 276 Clog.e(TAG, "Search for " + artist + " failed", e); 277 } 278 }); 279 } 280 loadData(@onNull Album album)281 public void loadData(@NonNull Album album) { 282 // TODO(b/123707632) Ensure no concurrent runs for the same item !! 283 if (album.isLoaded()) return; 284 285 mExecutor.execute(() -> { 286 try { 287 boolean updated = mDataProvider.populateAlbum(album); 288 289 updated |= mAudioStore.loadData(album); 290 291 album.setLoaded(); 292 if (updated) { 293 Executors.uiThreadExecutor().execute(() -> updateAlbum(album)); 294 } 295 } catch (IOException e) { 296 Clog.e(TAG, "Search for " + album + " failed", e); 297 } 298 }); 299 } 300 loadData(@onNull Genre genre)301 public void loadData(@NonNull Genre genre) { 302 // TODO(b/123707632) Ensure no concurrent runs for the same item !! 303 if (genre.isLoaded()) return; 304 305 mExecutor.execute(() -> { 306 boolean updated = mAudioStore.loadData(genre); 307 308 genre.setLoaded(); 309 if (updated) { 310 Executors.uiThreadExecutor().execute(() -> updateGenre(genre)); 311 } 312 }); 313 } 314 loadData(@onNull Playlist playlist)315 public void loadData(@NonNull Playlist playlist) { 316 // TODO(b/123707632) Ensure no concurrent runs for the same item !! 317 if (playlist.isLoaded()) return; 318 319 mExecutor.execute(() -> { 320 boolean updated = mAudioStore.loadData(playlist); 321 322 playlist.setLoaded(); 323 if (updated) { 324 Executors.uiThreadExecutor().execute(() -> updatePlaylist(playlist)); 325 } 326 }); 327 } 328 329 // TODO(b/123707018) Merge with loadData(episode)/loadData(other) loadData(@onNull Movie movie)330 public void loadData(@NonNull Movie movie) { 331 // TODO(b/123707632) Ensure no concurrent runs for the same item !! 332 if (movie.isLoaded()) return; 333 334 mExecutor.execute(() -> { 335 try { 336 boolean updated = mDataProvider.populateMovie(movie); 337 338 updated |= mVideoStore.loadData(movie); 339 340 movie.setLoaded(); 341 if (updated) { 342 Executors.uiThreadExecutor().execute(() -> updateMovie(movie)); 343 } 344 } catch (IOException e) { 345 Clog.e(TAG, "Search for " + movie + " failed", e); 346 } 347 }); 348 } 349 loadData(@onNull Series series)350 public void loadData(@NonNull Series series) { 351 // TODO(b/123707632) Ensure no concurrent runs for the same item !! 352 if (series.isLoaded()) return; 353 354 mExecutor.execute(() -> { 355 try { 356 boolean updated = mDataProvider.populateSeries(series); 357 358 updated |= mVideoStore.loadData(series); 359 360 series.setLoaded(); 361 if (updated) { 362 Executors.uiThreadExecutor().execute(() -> updateSeries(series)); 363 } 364 } catch (IOException e) { 365 Clog.e(TAG, "Search for " + series + " failed", e); 366 } 367 }); 368 } 369 370 // TODO(b/123707018) Merge with loadData(movie)/loadData(other) loadData(@onNull Episode episode)371 public void loadData(@NonNull Episode episode) { 372 // TODO(b/123707632) Ensure no concurrent runs for the same item !! 373 if (episode.isLoaded()) return; 374 375 mExecutor.execute(() -> { 376 try { 377 boolean updated = mDataProvider.populateEpisode(episode); 378 379 updated |= mVideoStore.loadData(episode); 380 381 episode.setLoaded(); 382 if (updated) { 383 Executors.uiThreadExecutor().execute(() -> updateEpisode(episode)); 384 } 385 } catch (IOException e) { 386 Clog.e(TAG, "Search for " + episode + " failed", e); 387 } 388 }); 389 } 390 391 // TODO(b/123707018) Merge with loadData(movie)/loadData(episode) loadData(@onNull Other other)392 public void loadData(@NonNull Other other) { 393 // TODO(b/123707632) Ensure no concurrent runs for the same item !! 394 if (other.isLoaded()) return; 395 396 mExecutor.execute(() -> { 397 boolean updated = mVideoStore.loadData(other); 398 399 other.setLoaded(); 400 if (updated) { 401 Executors.uiThreadExecutor().execute(() -> updateOther(other)); 402 } 403 }); 404 } 405 406 @Override getAudioById(long id)407 public @NonNull Audio getAudioById(long id) { 408 for (Audio audio : mAudios) { 409 if (audio.getId() == id) { 410 return audio; 411 } 412 } 413 throw new IllegalArgumentException("Audio with id " + id + " was not found"); 414 } 415 416 @Override getArtistById(long id)417 public @NonNull Artist getArtistById(long id) { 418 for (Artist artist : mArtists) { 419 if (artist.getId() == id) { 420 return artist; 421 } 422 } 423 throw new IllegalArgumentException("Artist with id " + id + " was not found"); 424 } 425 426 @Override getAlbumById(long id)427 public @NonNull Album getAlbumById(long id) { 428 for (Album album : mAlbums) { 429 if (album.getId() == id) { 430 return album; 431 } 432 } 433 throw new IllegalArgumentException("Album with id " + id + " was not found"); 434 } 435 436 @Override getGenreById(long id)437 public @NonNull Genre getGenreById(long id) { 438 for (Genre genre : mGenres) { 439 if (genre.getId() == id) { 440 return genre; 441 } 442 } 443 throw new IllegalArgumentException("Genre with id " + id + " was not found"); 444 } 445 446 @Override getPlaylistById(long id)447 public @NonNull Playlist getPlaylistById(long id) { 448 for (Playlist playlist : mPlaylists) { 449 if (playlist.getId() == id) { 450 return playlist; 451 } 452 } 453 throw new IllegalArgumentException("Playlist with id " + id + " was not found"); 454 } 455 456 @Override getMovieById(long id)457 public @NonNull Movie getMovieById(long id) { 458 for (Movie movie : mMovies) { 459 if (movie.getId() == id) { 460 return movie; 461 } 462 } 463 throw new IllegalArgumentException("Movie with id " + id + " was not found"); 464 } 465 466 @Override getSeriesById(@onNull String title)467 public @NonNull Series getSeriesById(@NonNull String title) { 468 for (Series series : mSeries) { 469 if (!series.hasYear() && series.getTitle().equals(title)) { 470 return series; 471 } 472 } 473 throw new IllegalArgumentException("Series '" + title + "' was not found"); 474 } 475 476 @Override getSeriesById(@onNull String title, int year)477 public @NonNull Series getSeriesById(@NonNull String title, int year) { 478 for (Series series : mSeries) { 479 if (series.hasYear() && series.getTitle().equals(title) && series.getYear() == year) { 480 return series; 481 } 482 } 483 throw new IllegalArgumentException("Series '" + title + "' (" + year + ") was not found"); 484 } 485 486 @Override getEpisodeById(long id)487 public @NonNull Episode getEpisodeById(long id) { 488 for (Episode episode : mEpisodes) { 489 if (episode.getId() == id) { 490 return episode; 491 } 492 } 493 throw new IllegalArgumentException("Episode with id " + id + " was not found"); 494 } 495 496 @Override getOtherById(long id)497 public @NonNull Other getOtherById(long id) { 498 for (Other other : mOthers) { 499 if (other.getId() == id) { 500 return other; 501 } 502 } 503 throw new IllegalArgumentException("Other with id " + id + " was not found"); 504 } 505 addUpdateCallback(@onNull Set<UpdateCallback> callbacks, @NonNull UpdateCallback callback)506 private void addUpdateCallback(@NonNull Set<UpdateCallback> callbacks, 507 @NonNull UpdateCallback callback) { 508 if (!callbacks.add(callback)) { 509 throw new IllegalArgumentException("Callback " + callback + " already added in " + 510 callbacks); 511 } 512 } 513 removeUpdateCallback(@onNull Set<UpdateCallback> callbacks, @NonNull UpdateCallback callback)514 private void removeUpdateCallback(@NonNull Set<UpdateCallback> callbacks, 515 @NonNull UpdateCallback callback) { 516 if (!callbacks.remove(callback)) { 517 throw new IllegalArgumentException("Callback " + callback + " not found in " + 518 callbacks); 519 } 520 } 521 addAudios(@onNull Collection<Audio> audios)522 private void addAudios(@NonNull Collection<Audio> audios) { 523 int audiosIndex = mAudios.size(); 524 int audiosCount = 0; 525 526 mAudios.addAll(audios); 527 audiosCount += audios.size(); 528 529 if (audiosCount > 0) { 530 for (UpdateCallback callback : mAudioUpdateCallbacks) { 531 callback.onItemsInserted(audiosIndex, audiosCount); 532 } 533 } 534 } 535 addArtists(@onNull Collection<Artist> artists)536 private void addArtists(@NonNull Collection<Artist> artists) { 537 int artistsIndex = mArtists.size(); 538 int artistsCount = 0; 539 540 mArtists.addAll(artists); 541 artistsCount += artists.size(); 542 543 if (artistsCount > 0) { 544 for (UpdateCallback callback : mArtistUpdateCallbacks) { 545 callback.onItemsInserted(artistsIndex, artistsCount); 546 } 547 } 548 } 549 addAlbums(@onNull Collection<Album> albums)550 private void addAlbums(@NonNull Collection<Album> albums) { 551 int albumsIndex = mAlbums.size(); 552 int albumsCount = 0; 553 554 mAlbums.addAll(albums); 555 albumsCount += albums.size(); 556 557 if (albumsCount > 0) { 558 for (UpdateCallback callback : mAlbumUpdateCallbacks) { 559 callback.onItemsInserted(albumsIndex, albumsCount); 560 } 561 } 562 } 563 addGenres(@onNull Collection<Genre> genres)564 private void addGenres(@NonNull Collection<Genre> genres) { 565 int genresIndex = mGenres.size(); 566 int genresCount = 0; 567 568 mGenres.addAll(genres); 569 genresCount += genres.size(); 570 571 if (genresCount > 0) { 572 for (UpdateCallback callback : mGenreUpdateCallbacks) { 573 callback.onItemsInserted(genresIndex, genresCount); 574 } 575 } 576 } 577 addPlaylists(@onNull Collection<Playlist> playlists)578 private void addPlaylists(@NonNull Collection<Playlist> playlists) { 579 int playlistsIndex = mPlaylists.size(); 580 int playlistsCount = 0; 581 582 mPlaylists.addAll(playlists); 583 playlistsCount += playlists.size(); 584 585 if (playlistsCount > 0) { 586 for (UpdateCallback callback : mPlaylistUpdateCallbacks) { 587 callback.onItemsInserted(playlistsIndex, playlistsCount); 588 } 589 } 590 } 591 addMovies(@onNull Collection<Movie> movies)592 private void addMovies(@NonNull Collection<Movie> movies) { 593 int moviesIndex = mMovies.size(); 594 int moviesCount = 0; 595 596 mMovies.addAll(movies); 597 moviesCount += movies.size(); 598 599 if (moviesCount > 0) { 600 for (UpdateCallback callback : mMovieUpdateCallbacks) { 601 callback.onItemsInserted(moviesIndex, moviesCount); 602 } 603 } 604 } 605 addSeries(@onNull Collection<Series> series)606 private void addSeries(@NonNull Collection<Series> series) { 607 int seriesIndex = mSeries.size(); 608 int seriesCount = 0; 609 610 mSeries.addAll(series); 611 seriesCount += series.size(); 612 613 if (seriesCount > 0) { 614 for (UpdateCallback callback : mSeriesUpdateCallbacks) { 615 callback.onItemsInserted(seriesIndex, seriesCount); 616 } 617 } 618 } 619 addEpisodes(@onNull Collection<Episode> episodes)620 private void addEpisodes(@NonNull Collection<Episode> episodes) { 621 int episodesIndex = mEpisodes.size(); 622 int episodesCount = 0; 623 624 mEpisodes.addAll(episodes); 625 episodesCount += episodes.size(); 626 627 if (episodesCount > 0) { 628 for (UpdateCallback callback : mEpisodeUpdateCallbacks) { 629 callback.onItemsInserted(episodesIndex, episodesCount); 630 } 631 } 632 } 633 addOthers(@onNull Collection<Other> others)634 private void addOthers(@NonNull Collection<Other> others) { 635 int othersIndex = mOthers.size(); 636 int othersCount = 0; 637 638 mOthers.addAll(others); 639 othersCount += others.size(); 640 641 if (othersCount > 0) { 642 for (UpdateCallback callback : mOtherUpdateCallbacks) { 643 callback.onItemsInserted(othersIndex, othersCount); 644 } 645 } 646 } 647 updateAudio(@onNull Audio audio)648 private void updateAudio(@NonNull Audio audio) { 649 int index = mAudios.indexOf(audio); 650 if (index != -1) { 651 for (UpdateCallback callback : mAudioUpdateCallbacks) { 652 callback.onItemsUpdated(index, 1); 653 } 654 } 655 } 656 updateArtist(@onNull Artist artist)657 private void updateArtist(@NonNull Artist artist) { 658 int index = mArtists.indexOf(artist); 659 if (index != -1) { 660 for (UpdateCallback callback : mArtistUpdateCallbacks) { 661 callback.onItemsUpdated(index, 1); 662 } 663 } 664 } 665 updateAlbum(@onNull Album album)666 private void updateAlbum(@NonNull Album album) { 667 int index = mAlbums.indexOf(album); 668 if (index != -1) { 669 for (UpdateCallback callback : mAlbumUpdateCallbacks) { 670 callback.onItemsUpdated(index, 1); 671 } 672 } 673 } 674 updateGenre(@onNull Genre genre)675 private void updateGenre(@NonNull Genre genre) { 676 int index = mGenres.indexOf(genre); 677 if (index != -1) { 678 for (UpdateCallback callback : mGenreUpdateCallbacks) { 679 callback.onItemsUpdated(index, 1); 680 } 681 } 682 } 683 updatePlaylist(@onNull Playlist playlist)684 private void updatePlaylist(@NonNull Playlist playlist) { 685 int index = mPlaylists.indexOf(playlist); 686 if (index != -1) { 687 for (UpdateCallback callback : mPlaylistUpdateCallbacks) { 688 callback.onItemsUpdated(index, 1); 689 } 690 } 691 } 692 updateMovie(@onNull Movie movie)693 private void updateMovie(@NonNull Movie movie) { 694 int index = mMovies.indexOf(movie); 695 if (index != -1) { 696 for (UpdateCallback callback : mMovieUpdateCallbacks) { 697 callback.onItemsUpdated(index, 1); 698 } 699 } 700 } 701 updateSeries(@onNull Series series)702 private void updateSeries(@NonNull Series series) { 703 int index = mSeries.indexOf(series); 704 if (index != -1) { 705 for (UpdateCallback callback : mSeriesUpdateCallbacks) { 706 callback.onItemsUpdated(index, 1); 707 } 708 } 709 } 710 updateEpisode(@onNull Episode episode)711 private void updateEpisode(@NonNull Episode episode) { 712 int index = mEpisodes.indexOf(episode); 713 if (index != -1) { 714 for (UpdateCallback callback : mEpisodeUpdateCallbacks) { 715 callback.onItemsUpdated(index, 1); 716 } 717 } 718 } 719 updateOther(@onNull Other other)720 private void updateOther(@NonNull Other other) { 721 int index = mOthers.indexOf(other); 722 if (index != -1) { 723 for (UpdateCallback callback : mOtherUpdateCallbacks) { 724 callback.onItemsUpdated(index, 1); 725 } 726 } 727 } 728 } 729