From 402fe961ba765024d0eae202b17ca0b2ed4ccf63 Mon Sep 17 00:00:00 2001 From: tzugen Date: Fri, 21 May 2021 18:50:21 +0200 Subject: [PATCH 01/17] Merge MediaPlayerController interface & implementation --- .../service/JukeboxMediaPlayer.java | 2 +- .../service/MediaPlayerController.java | 585 ++++++++++++++-- .../service/MediaPlayerControllerImpl.java | 661 ------------------ .../service/MediaPlayerLifecycleSupport.java | 4 +- .../moire/ultrasonic/di/MediaPlayerModule.kt | 7 +- 5 files changed, 539 insertions(+), 720 deletions(-) delete mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerControllerImpl.java diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/JukeboxMediaPlayer.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/JukeboxMediaPlayer.java index e6764129..26ecdc30 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/JukeboxMediaPlayer.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/JukeboxMediaPlayer.java @@ -73,7 +73,7 @@ public class JukeboxMediaPlayer private final Context context; // TODO: These create circular references, try to refactor - private final Lazy mediaPlayerControllerLazy = inject(MediaPlayerControllerImpl.class); + private final Lazy mediaPlayerControllerLazy = inject(MediaPlayerController.class); private final Downloader downloader; // TODO: Report warning if queue fills up. diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerController.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerController.java index 4384ad90..7ffd904a 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerController.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerController.java @@ -18,112 +18,595 @@ */ package org.moire.ultrasonic.service; +import android.content.Context; +import android.content.Intent; +import timber.log.Timber; + +import org.koin.java.KoinJavaComponent; +import org.moire.ultrasonic.data.ActiveServerProvider; +import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.domain.MusicDirectory.Entry; import org.moire.ultrasonic.domain.PlayerState; import org.moire.ultrasonic.domain.RepeatMode; +import org.moire.ultrasonic.domain.UserInfo; +import org.moire.ultrasonic.featureflags.Feature; +import org.moire.ultrasonic.featureflags.FeatureStorage; +import org.moire.ultrasonic.util.ShufflePlayBuffer; +import org.moire.ultrasonic.util.Util; +import java.util.Iterator; import java.util.List; +import kotlin.Lazy; + +import static org.koin.java.KoinJavaComponent.inject; + /** - * This interface contains all functions which are necessary for the Application UI + * The implementation of the Media Player Controller. + * This class contains everything that is necessary for the Application UI * to control the Media Player implementation. * - * @author Sindre Mehus + * @author Sindre Mehus, Joshua Bahnsen * @version $Id$ */ -public interface MediaPlayerController +public class MediaPlayerController { - void download(List songs, boolean save, boolean autoplay, boolean playNext, boolean shuffle, boolean newPlaylist); + private boolean created = false; + private String suggestedPlaylistName; + private boolean keepScreenOn; - void downloadBackground(List songs, boolean save); + private boolean showVisualization; + private boolean autoPlayStart; - void setShufflePlayEnabled(boolean enabled); + private final Context context; + private final Lazy jukeboxMediaPlayer = inject(JukeboxMediaPlayer.class); + private final Lazy activeServerProvider = inject(ActiveServerProvider.class); - boolean isShufflePlayEnabled(); + private final DownloadQueueSerializer downloadQueueSerializer; + private final ExternalStorageMonitor externalStorageMonitor; + private final Downloader downloader; + private final ShufflePlayBuffer shufflePlayBuffer; + private final LocalMediaPlayer localMediaPlayer; - void shuffle(); + public MediaPlayerController(Context context, DownloadQueueSerializer downloadQueueSerializer, + ExternalStorageMonitor externalStorageMonitor, Downloader downloader, + ShufflePlayBuffer shufflePlayBuffer, LocalMediaPlayer localMediaPlayer) + { + this.context = context; + this.downloadQueueSerializer = downloadQueueSerializer; + this.externalStorageMonitor = externalStorageMonitor; + this.downloader = downloader; + this.shufflePlayBuffer = shufflePlayBuffer; + this.localMediaPlayer = localMediaPlayer; - RepeatMode getRepeatMode(); + Timber.i("MediaPlayerController constructed"); + } - void setRepeatMode(RepeatMode repeatMode); + public void onCreate() + { + if (created) return; + this.externalStorageMonitor.onCreate(this::reset); - boolean getKeepScreenOn(); + setJukeboxEnabled(activeServerProvider.getValue().getActiveServer().getJukeboxByDefault()); + created = true; - void setKeepScreenOn(boolean screenOn); + Timber.i("MediaPlayerController created"); + } - boolean getShowVisualization(); + public void onDestroy() + { + if (!created) return; + externalStorageMonitor.onDestroy(); + context.stopService(new Intent(context, MediaPlayerService.class)); + downloader.onDestroy(); + created = false; - void setShowVisualization(boolean showVisualization); + Timber.i("MediaPlayerController destroyed"); + } + public synchronized void restore(List songs, final int currentPlayingIndex, final int currentPlayingPosition, final boolean autoPlay, boolean newPlaylist) + { + download(songs, false, false, false, false, newPlaylist); - void clear(); + if (currentPlayingIndex != -1) + { + MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> + { + mediaPlayerService.play(currentPlayingIndex, autoPlayStart); - void clearIncomplete(); + if (localMediaPlayer.currentPlaying != null) + { + if (autoPlay && jukeboxMediaPlayer.getValue().isEnabled()) + { + jukeboxMediaPlayer.getValue().skip(downloader.getCurrentPlayingIndex(), currentPlayingPosition / 1000); + } + else + { + if (localMediaPlayer.currentPlaying.isCompleteFileAvailable()) + { + localMediaPlayer.play(localMediaPlayer.currentPlaying, currentPlayingPosition, autoPlay); + } + } + } + autoPlayStart = false; + return null; + } + ); + } + } - void remove(DownloadFile downloadFile); + public synchronized void preload() + { + MediaPlayerService.getInstance(context); + } - void play(int index); + public synchronized void play(final int index) + { + MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> { + mediaPlayerService.play(index, true); + return null; + } + ); + } - void seekTo(int position); + public synchronized void play() + { + MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> { - void previous(); + mediaPlayerService.play(); + return null; + } + ); + } - void next(); + public synchronized void resumeOrPlay() + { + MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> { + mediaPlayerService.resumeOrPlay(); + return null; + } + ); + } - void pause(); + public synchronized void togglePlayPause() + { + if (localMediaPlayer.playerState == PlayerState.IDLE) autoPlayStart = true; + MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> { + mediaPlayerService.togglePlayPause(); + return null; + } + ); + } - void stop(); + public synchronized void start() + { + MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> { + mediaPlayerService.start(); + return null; + } + ); + } - void start(); + public synchronized void seekTo(final int position) + { + MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); + if (mediaPlayerService != null) mediaPlayerService.seekTo(position); + } - void reset(); + public synchronized void pause() + { + MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); + if (mediaPlayerService != null) mediaPlayerService.pause(); + } - PlayerState getPlayerState(); + public synchronized void stop() + { + MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); + if (mediaPlayerService != null) mediaPlayerService.stop(); + } - int getPlayerPosition(); + public synchronized void download(List songs, boolean save, boolean autoPlay, boolean playNext, boolean shuffle, boolean newPlaylist) + { + downloader.download(songs, save, autoPlay, playNext, newPlaylist); + jukeboxMediaPlayer.getValue().updatePlaylist(); - int getPlayerDuration(); + if (shuffle) shuffle(); - void delete(List songs); + if (!playNext && !autoPlay && (downloader.downloadList.size() - 1) == downloader.getCurrentPlayingIndex()) + { + MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); + if (mediaPlayerService != null) mediaPlayerService.setNextPlaying(); + } - void unpin(List songs); + if (autoPlay) + { + play(0); + } + else + { + if (localMediaPlayer.currentPlaying == null && downloader.downloadList.size() > 0) + { + localMediaPlayer.currentPlaying = downloader.downloadList.get(0); + localMediaPlayer.currentPlaying.setPlaying(true); + } - void setSuggestedPlaylistName(String name); + downloader.checkDownloads(); + } - String getSuggestedPlaylistName(); + downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition()); + } - boolean isJukeboxEnabled(); + public synchronized void downloadBackground(List songs, boolean save) + { + downloader.downloadBackground(songs, save); + downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition()); + } - boolean isJukeboxAvailable(); + public synchronized void setCurrentPlaying(DownloadFile currentPlaying) + { + MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); + if (mediaPlayerService != null) localMediaPlayer.setCurrentPlaying(currentPlaying); + } - void setJukeboxEnabled(boolean b); + public synchronized void setCurrentPlaying(int index) + { + MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); + if (mediaPlayerService != null) mediaPlayerService.setCurrentPlaying(index); + } - void adjustJukeboxVolume(boolean up); + public synchronized void setPlayerState(PlayerState state) + { + MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); + if (mediaPlayerService != null) localMediaPlayer.setPlayerState(state); + } - void togglePlayPause(); + public void stopJukeboxService() + { + jukeboxMediaPlayer.getValue().stopJukeboxService(); + } - void setVolume(float volume); + public synchronized void setShufflePlayEnabled(boolean enabled) + { + shufflePlayBuffer.isEnabled = enabled; + if (enabled) + { + clear(); + downloader.checkDownloads(); + } + } - void restore(List songs, int currentPlayingIndex, int currentPlayingPosition, boolean autoPlay, boolean newPlaylist); + public boolean isShufflePlayEnabled() + { + return shufflePlayBuffer.isEnabled; + } - void stopJukeboxService(); + public synchronized void shuffle() + { + downloader.shuffle(); - void updateNotification(); + downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition()); + jukeboxMediaPlayer.getValue().updatePlaylist(); - void setSongRating(final int rating); + MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); + if (mediaPlayerService != null) mediaPlayerService.setNextPlaying(); + } - DownloadFile getCurrentPlaying(); + public RepeatMode getRepeatMode() + { + return Util.getRepeatMode(); + } - int getPlaylistSize(); + public synchronized void setRepeatMode(RepeatMode repeatMode) + { + Util.setRepeatMode(repeatMode); + MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); + if (mediaPlayerService != null) mediaPlayerService.setNextPlaying(); + } - int getCurrentPlayingNumberOnPlaylist(); + public boolean getKeepScreenOn() + { + return keepScreenOn; + } - DownloadFile getCurrentDownloading(); + public void setKeepScreenOn(boolean keepScreenOn) + { + this.keepScreenOn = keepScreenOn; + } - List getPlayList(); + public boolean getShowVisualization() + { + return showVisualization; + } - long getPlayListUpdateRevision(); + public void setShowVisualization(boolean showVisualization) + { + this.showVisualization = showVisualization; + } - long getPlayListDuration(); + public synchronized void clear() + { + clear(true); + } - DownloadFile getDownloadFileForSong(Entry song); -} + public synchronized void clear(boolean serialize) + { + MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); + if (mediaPlayerService != null) { + mediaPlayerService.clear(serialize); + } else { + // If no MediaPlayerService is available, just empty the playlist + downloader.clear(); + if (serialize) { + downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, + downloader.getCurrentPlayingIndex(), getPlayerPosition()); + } + } + + jukeboxMediaPlayer.getValue().updatePlaylist(); + } + + public synchronized void clearIncomplete() + { + reset(); + Iterator iterator = downloader.downloadList.iterator(); + + while (iterator.hasNext()) + { + DownloadFile downloadFile = iterator.next(); + if (!downloadFile.isCompleteFileAvailable()) + { + iterator.remove(); + } + } + + downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition()); + jukeboxMediaPlayer.getValue().updatePlaylist(); + } + + public synchronized void remove(DownloadFile downloadFile) + { + if (downloadFile == localMediaPlayer.currentPlaying) + { + reset(); + setCurrentPlaying(null); + } + + downloader.removeDownloadFile(downloadFile); + + downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition()); + jukeboxMediaPlayer.getValue().updatePlaylist(); + + if (downloadFile == localMediaPlayer.nextPlaying) + { + MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); + if (mediaPlayerService != null) mediaPlayerService.setNextPlaying(); + } + } + + public synchronized void delete(List songs) + { + for (MusicDirectory.Entry song : songs) + { + downloader.getDownloadFileForSong(song).delete(); + } + } + + public synchronized void unpin(List songs) + { + for (MusicDirectory.Entry song : songs) + { + downloader.getDownloadFileForSong(song).unpin(); + } + } + + public synchronized void previous() + { + int index = downloader.getCurrentPlayingIndex(); + if (index == -1) + { + return; + } + + // Restart song if played more than five seconds. + if (getPlayerPosition() > 5000 || index == 0) + { + play(index); + } + else + { + play(index - 1); + } + } + + public synchronized void next() + { + int index = downloader.getCurrentPlayingIndex(); + if (index != -1) + { + switch (getRepeatMode()) + { + case SINGLE: + case OFF: + if (index + 1 >= 0 && index + 1 < downloader.downloadList.size()) { + play(index + 1); + } + break; + case ALL: + play((index + 1) % downloader.downloadList.size()); + break; + default: + break; + } + } + } + + public synchronized void reset() + { + MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); + if (mediaPlayerService != null) localMediaPlayer.reset(); + } + + public synchronized int getPlayerPosition() + { + MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); + if (mediaPlayerService == null) return 0; + return mediaPlayerService.getPlayerPosition(); + } + + public synchronized int getPlayerDuration() + { + if (localMediaPlayer.currentPlaying != null) + { + Integer duration = localMediaPlayer.currentPlaying.getSong().getDuration(); + if (duration != null) + { + return duration * 1000; + } + } + + MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); + if (mediaPlayerService == null) return 0; + return mediaPlayerService.getPlayerDuration(); + } + + public PlayerState getPlayerState() { return localMediaPlayer.playerState; } + + public void setSuggestedPlaylistName(String name) + { + this.suggestedPlaylistName = name; + } + + public String getSuggestedPlaylistName() + { + return suggestedPlaylistName; + } + + public boolean isJukeboxEnabled() + { + return jukeboxMediaPlayer.getValue().isEnabled(); + } + + public boolean isJukeboxAvailable() + { + try + { + String username = activeServerProvider.getValue().getActiveServer().getUserName(); + UserInfo user = MusicServiceFactory.getMusicService().getUser(username); + return user.getJukeboxRole(); + } + catch (Exception e) + { + Timber.w(e, "Error getting user information"); + } + + return false; + } + + public void setJukeboxEnabled(boolean jukeboxEnabled) + { + jukeboxMediaPlayer.getValue().setEnabled(jukeboxEnabled); + setPlayerState(PlayerState.IDLE); + + if (jukeboxEnabled) + { + jukeboxMediaPlayer.getValue().startJukeboxService(); + + reset(); + + // Cancel current download, if necessary. + if (downloader.currentDownloading != null) + { + downloader.currentDownloading.cancelDownload(); + } + } + else + { + jukeboxMediaPlayer.getValue().stopJukeboxService(); + } + } + + public void adjustJukeboxVolume(boolean up) + { + jukeboxMediaPlayer.getValue().adjustVolume(up); + } + + public void setVolume(float volume) + { + MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); + if (mediaPlayerService != null) localMediaPlayer.setVolume(volume); + } + + public void updateNotification() + { + MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); + if (mediaPlayerService != null) mediaPlayerService.updateNotification(localMediaPlayer.playerState, localMediaPlayer.currentPlaying); + } + + public void toggleSongStarred() { + if (localMediaPlayer.currentPlaying == null) + return; + + final Entry song = localMediaPlayer.currentPlaying.getSong(); + + // Trigger an update + localMediaPlayer.setCurrentPlaying(localMediaPlayer.currentPlaying); + + song.setStarred(!song.getStarred()); + } + + public void setSongRating(final int rating) + { + if (!KoinJavaComponent.get(FeatureStorage.class).isFeatureEnabled(Feature.FIVE_STAR_RATING)) + return; + + if (localMediaPlayer.currentPlaying == null) + return; + + final Entry song = localMediaPlayer.currentPlaying.getSong(); + song.setUserRating(rating); + + new Thread(() -> { + try + { + MusicServiceFactory.getMusicService().setRating(song.getId(), rating); + } + catch (Exception e) + { + Timber.e(e); + } + }).start(); + + updateNotification(); + } + + public DownloadFile getCurrentPlaying() { + return localMediaPlayer.currentPlaying; + } + + public int getPlaylistSize() { + return downloader.downloadList.size(); + } + + public int getCurrentPlayingNumberOnPlaylist() { + return downloader.getCurrentPlayingIndex(); + } + + public DownloadFile getCurrentDownloading() { + return downloader.currentDownloading; + } + + public List getPlayList() { + return downloader.downloadList; + } + + public long getPlayListUpdateRevision() { + return downloader.getDownloadListUpdateRevision(); + } + + public long getPlayListDuration() { + return downloader.getDownloadListDuration(); + } + + public DownloadFile getDownloadFileForSong(Entry song) { + return downloader.getDownloadFileForSong(song); + } +} \ No newline at end of file diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerControllerImpl.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerControllerImpl.java deleted file mode 100644 index 7a9d6af6..00000000 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerControllerImpl.java +++ /dev/null @@ -1,661 +0,0 @@ -/* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - - Copyright 2009 (C) Sindre Mehus - */ -package org.moire.ultrasonic.service; - -import android.content.Context; -import android.content.Intent; -import timber.log.Timber; - -import org.koin.java.KoinJavaComponent; -import org.moire.ultrasonic.data.ActiveServerProvider; -import org.moire.ultrasonic.domain.MusicDirectory; -import org.moire.ultrasonic.domain.MusicDirectory.Entry; -import org.moire.ultrasonic.domain.PlayerState; -import org.moire.ultrasonic.domain.RepeatMode; -import org.moire.ultrasonic.domain.UserInfo; -import org.moire.ultrasonic.featureflags.Feature; -import org.moire.ultrasonic.featureflags.FeatureStorage; -import org.moire.ultrasonic.util.ShufflePlayBuffer; -import org.moire.ultrasonic.util.Util; - -import java.util.Iterator; -import java.util.List; - -import kotlin.Lazy; - -import static org.koin.java.KoinJavaComponent.inject; - -/** - * The implementation of the Media Player Controller. - * This class contains everything that is necessary for the Application UI - * to control the Media Player implementation. - * - * @author Sindre Mehus, Joshua Bahnsen - * @version $Id$ - */ -public class MediaPlayerControllerImpl implements MediaPlayerController -{ - private boolean created = false; - private String suggestedPlaylistName; - private boolean keepScreenOn; - - private boolean showVisualization; - private boolean autoPlayStart; - - private final Context context; - private final Lazy jukeboxMediaPlayer = inject(JukeboxMediaPlayer.class); - private final Lazy activeServerProvider = inject(ActiveServerProvider.class); - - private final DownloadQueueSerializer downloadQueueSerializer; - private final ExternalStorageMonitor externalStorageMonitor; - private final Downloader downloader; - private final ShufflePlayBuffer shufflePlayBuffer; - private final LocalMediaPlayer localMediaPlayer; - - public MediaPlayerControllerImpl(Context context, DownloadQueueSerializer downloadQueueSerializer, - ExternalStorageMonitor externalStorageMonitor, Downloader downloader, - ShufflePlayBuffer shufflePlayBuffer, LocalMediaPlayer localMediaPlayer) - { - this.context = context; - this.downloadQueueSerializer = downloadQueueSerializer; - this.externalStorageMonitor = externalStorageMonitor; - this.downloader = downloader; - this.shufflePlayBuffer = shufflePlayBuffer; - this.localMediaPlayer = localMediaPlayer; - - Timber.i("MediaPlayerControllerImpl constructed"); - } - - public void onCreate() - { - if (created) return; - this.externalStorageMonitor.onCreate(this::reset); - - setJukeboxEnabled(activeServerProvider.getValue().getActiveServer().getJukeboxByDefault()); - created = true; - - Timber.i("MediaPlayerControllerImpl created"); - } - - public void onDestroy() - { - if (!created) return; - externalStorageMonitor.onDestroy(); - context.stopService(new Intent(context, MediaPlayerService.class)); - downloader.onDestroy(); - created = false; - - Timber.i("MediaPlayerControllerImpl destroyed"); - } - - @Override - public synchronized void restore(List songs, final int currentPlayingIndex, final int currentPlayingPosition, final boolean autoPlay, boolean newPlaylist) - { - download(songs, false, false, false, false, newPlaylist); - - if (currentPlayingIndex != -1) - { - MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> - { - mediaPlayerService.play(currentPlayingIndex, autoPlayStart); - - if (localMediaPlayer.currentPlaying != null) - { - if (autoPlay && jukeboxMediaPlayer.getValue().isEnabled()) - { - jukeboxMediaPlayer.getValue().skip(downloader.getCurrentPlayingIndex(), currentPlayingPosition / 1000); - } - else - { - if (localMediaPlayer.currentPlaying.isCompleteFileAvailable()) - { - localMediaPlayer.play(localMediaPlayer.currentPlaying, currentPlayingPosition, autoPlay); - } - } - } - autoPlayStart = false; - return null; - } - ); - } - } - - public synchronized void preload() - { - MediaPlayerService.getInstance(context); - } - - @Override - public synchronized void play(final int index) - { - MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> { - mediaPlayerService.play(index, true); - return null; - } - ); - } - - public synchronized void play() - { - MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> { - - mediaPlayerService.play(); - return null; - } - ); - } - - public synchronized void resumeOrPlay() - { - MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> { - mediaPlayerService.resumeOrPlay(); - return null; - } - ); - } - - - @Override - public synchronized void togglePlayPause() - { - if (localMediaPlayer.playerState == PlayerState.IDLE) autoPlayStart = true; - MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> { - mediaPlayerService.togglePlayPause(); - return null; - } - ); - } - - - @Override - public synchronized void start() - { - MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> { - mediaPlayerService.start(); - return null; - } - ); - } - - @Override - public synchronized void seekTo(final int position) - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) mediaPlayerService.seekTo(position); - } - - @Override - public synchronized void pause() - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) mediaPlayerService.pause(); - } - - @Override - public synchronized void stop() - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) mediaPlayerService.stop(); - } - - @Override - public synchronized void download(List songs, boolean save, boolean autoPlay, boolean playNext, boolean shuffle, boolean newPlaylist) - { - downloader.download(songs, save, autoPlay, playNext, newPlaylist); - jukeboxMediaPlayer.getValue().updatePlaylist(); - - if (shuffle) shuffle(); - - if (!playNext && !autoPlay && (downloader.downloadList.size() - 1) == downloader.getCurrentPlayingIndex()) - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) mediaPlayerService.setNextPlaying(); - } - - if (autoPlay) - { - play(0); - } - else - { - if (localMediaPlayer.currentPlaying == null && downloader.downloadList.size() > 0) - { - localMediaPlayer.currentPlaying = downloader.downloadList.get(0); - localMediaPlayer.currentPlaying.setPlaying(true); - } - - downloader.checkDownloads(); - } - - downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition()); - } - - @Override - public synchronized void downloadBackground(List songs, boolean save) - { - downloader.downloadBackground(songs, save); - downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition()); - } - - public synchronized void setCurrentPlaying(DownloadFile currentPlaying) - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) localMediaPlayer.setCurrentPlaying(currentPlaying); - } - - public synchronized void setCurrentPlaying(int index) - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) mediaPlayerService.setCurrentPlaying(index); - } - - public synchronized void setPlayerState(PlayerState state) - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) localMediaPlayer.setPlayerState(state); - } - - @Override - public void stopJukeboxService() - { - jukeboxMediaPlayer.getValue().stopJukeboxService(); - } - - @Override - public synchronized void setShufflePlayEnabled(boolean enabled) - { - shufflePlayBuffer.isEnabled = enabled; - if (enabled) - { - clear(); - downloader.checkDownloads(); - } - } - - @Override - public boolean isShufflePlayEnabled() - { - return shufflePlayBuffer.isEnabled; - } - - @Override - public synchronized void shuffle() - { - downloader.shuffle(); - - downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition()); - jukeboxMediaPlayer.getValue().updatePlaylist(); - - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) mediaPlayerService.setNextPlaying(); - } - - @Override - public RepeatMode getRepeatMode() - { - return Util.getRepeatMode(); - } - - @Override - public synchronized void setRepeatMode(RepeatMode repeatMode) - { - Util.setRepeatMode(repeatMode); - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) mediaPlayerService.setNextPlaying(); - } - - @Override - public boolean getKeepScreenOn() - { - return keepScreenOn; - } - - @Override - public void setKeepScreenOn(boolean keepScreenOn) - { - this.keepScreenOn = keepScreenOn; - } - - @Override - public boolean getShowVisualization() - { - return showVisualization; - } - - @Override - public void setShowVisualization(boolean showVisualization) - { - this.showVisualization = showVisualization; - } - - @Override - public synchronized void clear() - { - clear(true); - } - - public synchronized void clear(boolean serialize) - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) { - mediaPlayerService.clear(serialize); - } else { - // If no MediaPlayerService is available, just empty the playlist - downloader.clear(); - if (serialize) { - downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, - downloader.getCurrentPlayingIndex(), getPlayerPosition()); - } - } - - jukeboxMediaPlayer.getValue().updatePlaylist(); - } - - @Override - public synchronized void clearIncomplete() - { - reset(); - Iterator iterator = downloader.downloadList.iterator(); - - while (iterator.hasNext()) - { - DownloadFile downloadFile = iterator.next(); - if (!downloadFile.isCompleteFileAvailable()) - { - iterator.remove(); - } - } - - downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition()); - jukeboxMediaPlayer.getValue().updatePlaylist(); - } - - @Override - public synchronized void remove(DownloadFile downloadFile) - { - if (downloadFile == localMediaPlayer.currentPlaying) - { - reset(); - setCurrentPlaying(null); - } - - downloader.removeDownloadFile(downloadFile); - - downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition()); - jukeboxMediaPlayer.getValue().updatePlaylist(); - - if (downloadFile == localMediaPlayer.nextPlaying) - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) mediaPlayerService.setNextPlaying(); - } - } - - @Override - public synchronized void delete(List songs) - { - for (MusicDirectory.Entry song : songs) - { - downloader.getDownloadFileForSong(song).delete(); - } - } - - @Override - public synchronized void unpin(List songs) - { - for (MusicDirectory.Entry song : songs) - { - downloader.getDownloadFileForSong(song).unpin(); - } - } - - @Override - public synchronized void previous() - { - int index = downloader.getCurrentPlayingIndex(); - if (index == -1) - { - return; - } - - // Restart song if played more than five seconds. - if (getPlayerPosition() > 5000 || index == 0) - { - play(index); - } - else - { - play(index - 1); - } - } - - @Override - public synchronized void next() - { - int index = downloader.getCurrentPlayingIndex(); - if (index != -1) - { - switch (getRepeatMode()) - { - case SINGLE: - case OFF: - if (index + 1 >= 0 && index + 1 < downloader.downloadList.size()) { - play(index + 1); - } - break; - case ALL: - play((index + 1) % downloader.downloadList.size()); - break; - default: - break; - } - } - } - - @Override - public synchronized void reset() - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) localMediaPlayer.reset(); - } - - @Override - public synchronized int getPlayerPosition() - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService == null) return 0; - return mediaPlayerService.getPlayerPosition(); - } - - @Override - public synchronized int getPlayerDuration() - { - if (localMediaPlayer.currentPlaying != null) - { - Integer duration = localMediaPlayer.currentPlaying.getSong().getDuration(); - if (duration != null) - { - return duration * 1000; - } - } - - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService == null) return 0; - return mediaPlayerService.getPlayerDuration(); - } - - @Override - public PlayerState getPlayerState() { return localMediaPlayer.playerState; } - - @Override - public void setSuggestedPlaylistName(String name) - { - this.suggestedPlaylistName = name; - } - - @Override - public String getSuggestedPlaylistName() - { - return suggestedPlaylistName; - } - - @Override - public boolean isJukeboxEnabled() - { - return jukeboxMediaPlayer.getValue().isEnabled(); - } - - @Override - public boolean isJukeboxAvailable() - { - try - { - String username = activeServerProvider.getValue().getActiveServer().getUserName(); - UserInfo user = MusicServiceFactory.getMusicService().getUser(username); - return user.getJukeboxRole(); - } - catch (Exception e) - { - Timber.w(e, "Error getting user information"); - } - - return false; - } - - @Override - public void setJukeboxEnabled(boolean jukeboxEnabled) - { - jukeboxMediaPlayer.getValue().setEnabled(jukeboxEnabled); - setPlayerState(PlayerState.IDLE); - - if (jukeboxEnabled) - { - jukeboxMediaPlayer.getValue().startJukeboxService(); - - reset(); - - // Cancel current download, if necessary. - if (downloader.currentDownloading != null) - { - downloader.currentDownloading.cancelDownload(); - } - } - else - { - jukeboxMediaPlayer.getValue().stopJukeboxService(); - } - } - - @Override - public void adjustJukeboxVolume(boolean up) - { - jukeboxMediaPlayer.getValue().adjustVolume(up); - } - - @Override - public void setVolume(float volume) - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) localMediaPlayer.setVolume(volume); - } - - @Override - public void updateNotification() - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) mediaPlayerService.updateNotification(localMediaPlayer.playerState, localMediaPlayer.currentPlaying); - } - - public void toggleSongStarred() { - if (localMediaPlayer.currentPlaying == null) - return; - - final Entry song = localMediaPlayer.currentPlaying.getSong(); - - // Trigger an update - localMediaPlayer.setCurrentPlaying(localMediaPlayer.currentPlaying); - - song.setStarred(!song.getStarred()); - } - - public void setSongRating(final int rating) - { - if (!KoinJavaComponent.get(FeatureStorage.class).isFeatureEnabled(Feature.FIVE_STAR_RATING)) - return; - - if (localMediaPlayer.currentPlaying == null) - return; - - final Entry song = localMediaPlayer.currentPlaying.getSong(); - song.setUserRating(rating); - - new Thread(() -> { - try - { - MusicServiceFactory.getMusicService().setRating(song.getId(), rating); - } - catch (Exception e) - { - Timber.e(e); - } - }).start(); - - updateNotification(); - } - - @Override - public DownloadFile getCurrentPlaying() { - return localMediaPlayer.currentPlaying; - } - - @Override - public int getPlaylistSize() { - return downloader.downloadList.size(); - } - - @Override - public int getCurrentPlayingNumberOnPlaylist() { - return downloader.getCurrentPlayingIndex(); - } - - @Override - public DownloadFile getCurrentDownloading() { - return downloader.currentDownloading; - } - - @Override - public List getPlayList() { - return downloader.downloadList; - } - - @Override - public long getPlayListUpdateRevision() { - return downloader.getDownloadListUpdateRevision(); - } - - @Override - public long getPlayListDuration() { - return downloader.getDownloadListDuration(); - } - - @Override - public DownloadFile getDownloadFileForSong(Entry song) { - return downloader.getDownloadFileForSong(song); - } -} \ No newline at end of file diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.java index 10abc831..5357b0dd 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.java @@ -44,14 +44,14 @@ public class MediaPlayerLifecycleSupport { private boolean created = false; private DownloadQueueSerializer downloadQueueSerializer; // From DI - private final MediaPlayerControllerImpl mediaPlayerController; // From DI + private final MediaPlayerController mediaPlayerController; // From DI private final Downloader downloader; // From DI private Context context; private BroadcastReceiver headsetEventReceiver; public MediaPlayerLifecycleSupport(Context context, DownloadQueueSerializer downloadQueueSerializer, - final MediaPlayerControllerImpl mediaPlayerController, final Downloader downloader) + final MediaPlayerController mediaPlayerController, final Downloader downloader) { this.downloadQueueSerializer = downloadQueueSerializer; this.mediaPlayerController = mediaPlayerController; diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt index 33cddf3e..1811a053 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt @@ -9,7 +9,6 @@ import org.moire.ultrasonic.service.ExternalStorageMonitor import org.moire.ultrasonic.service.JukeboxMediaPlayer import org.moire.ultrasonic.service.LocalMediaPlayer import org.moire.ultrasonic.service.MediaPlayerController -import org.moire.ultrasonic.service.MediaPlayerControllerImpl import org.moire.ultrasonic.service.MediaPlayerLifecycleSupport import org.moire.ultrasonic.util.ShufflePlayBuffer @@ -17,9 +16,7 @@ import org.moire.ultrasonic.util.ShufflePlayBuffer * This Koin module contains the registration of classes related to the media player */ val mediaPlayerModule = module { - single { - MediaPlayerControllerImpl(androidContext(), get(), get(), get(), get(), get()) - } + single { MediaPlayerController(androidContext(), get(), get(), get(), get(), get()) } single { JukeboxMediaPlayer(androidContext(), get()) } single { MediaPlayerLifecycleSupport(androidContext(), get(), get(), get()) } @@ -31,5 +28,5 @@ val mediaPlayerModule = module { single { AudioFocusHandler(get()) } // TODO Ideally this can be cleaned up when all circular references are removed. - single { MediaPlayerControllerImpl(androidContext(), get(), get(), get(), get(), get()) } + single { MediaPlayerController(androidContext(), get(), get(), get(), get(), get()) } } From b25a1181484cef755622a99fb76558441f7f9861 Mon Sep 17 00:00:00 2001 From: tzugen Date: Fri, 21 May 2021 18:50:57 +0200 Subject: [PATCH 02/17] Remove unused context from CacheCleaner --- .../org/moire/ultrasonic/fragment/PlaylistsFragment.java | 2 +- .../ultrasonic/service/MediaPlayerLifecycleSupport.java | 2 +- .../main/java/org/moire/ultrasonic/util/CacheCleaner.java | 7 ++----- .../kotlin/org/moire/ultrasonic/service/DownloadFile.kt | 2 +- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PlaylistsFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PlaylistsFragment.java index e98e5798..e78777b6 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PlaylistsFragment.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PlaylistsFragment.java @@ -131,7 +131,7 @@ public class PlaylistsFragment extends Fragment { List playlists = musicService.getPlaylists(refresh); if (!ActiveServerProvider.Companion.isOffline()) - new CacheCleaner(getContext()).cleanPlaylists(playlists); + new CacheCleaner().cleanPlaylists(playlists); return playlists; } diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.java index 5357b0dd..0203e7a2 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.java @@ -92,7 +92,7 @@ public class MediaPlayerLifecycleSupport } }); - new CacheCleaner(context).clean(); + new CacheCleaner().clean(); created = true; Timber.i("LifecycleSupport created"); } diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/CacheCleaner.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/CacheCleaner.java index a61e0946..6368e001 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/CacheCleaner.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/CacheCleaner.java @@ -1,6 +1,5 @@ package org.moire.ultrasonic.util; -import android.content.Context; import android.os.AsyncTask; import android.os.StatFs; import timber.log.Timber; @@ -31,13 +30,11 @@ public class CacheCleaner { private static final long MIN_FREE_SPACE = 500 * 1024L * 1024L; - private final Context context; private Lazy downloader = inject(Downloader.class); private Lazy activeServerProvider = inject(ActiveServerProvider.class); - public CacheCleaner(Context context) + public CacheCleaner() { - this.context = context; } public void clean() @@ -168,7 +165,7 @@ public class CacheCleaner } } - Timber.i("Deleted : %s", Util.formatBytes(bytesDeleted)); + Timber.i("Deleted: %s", Util.formatBytes(bytesDeleted)); } private static void findCandidatesForDeletion(File file, List files, List dirs) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadFile.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadFile.kt index 3ccdd06c..6e773940 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadFile.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadFile.kt @@ -309,7 +309,7 @@ class DownloadFile( Timber.i("Released wake lock %s", wakeLock) } wifiLock?.release() - CacheCleaner(context).cleanSpace() + CacheCleaner().cleanSpace() downloader.value.checkDownloads() } } From 7922d6caadb43ea6a57b8d1537fa8240fa209b7e Mon Sep 17 00:00:00 2001 From: tzugen Date: Fri, 21 May 2021 19:11:26 +0200 Subject: [PATCH 03/17] Prevent leaks by making cleanup tasks static --- .../moire/ultrasonic/util/CacheCleaner.java | 39 +++++++------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/CacheCleaner.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/CacheCleaner.java index 6368e001..a63c8875 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/CacheCleaner.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/CacheCleaner.java @@ -13,7 +13,6 @@ import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -30,9 +29,6 @@ public class CacheCleaner { private static final long MIN_FREE_SPACE = 500 * 1024L * 1024L; - private Lazy downloader = inject(Downloader.class); - private Lazy activeServerProvider = inject(ActiveServerProvider.class); - public CacheCleaner() { } @@ -76,7 +72,7 @@ public class CacheCleaner } } - private void deleteEmptyDirs(Iterable dirs, Collection doNotDelete) + private static void deleteEmptyDirs(Iterable dirs, Collection doNotDelete) { for (File dir : dirs) { @@ -105,7 +101,7 @@ public class CacheCleaner } } - private long getMinimumDelete(List files) + private static long getMinimumDelete(List files) { if (files.isEmpty()) { @@ -194,20 +190,14 @@ public class CacheCleaner private static void sortByAscendingModificationTime(List files) { - Collections.sort(files, new Comparator() - { - @Override - public int compare(File a, File b) - { - return Long.compare(a.lastModified(), b.lastModified()); - - } - }); + Collections.sort(files, (a, b) -> Long.compare(a.lastModified(), b.lastModified())); } - private Set findFilesToNotDelete() + private static Set findFilesToNotDelete() { - Set filesToNotDelete = new HashSet(5); + Set filesToNotDelete = new HashSet<>(5); + + Lazy downloader = inject(Downloader.class); for (DownloadFile downloadFile : downloader.getValue().getDownloads()) { @@ -219,7 +209,7 @@ public class CacheCleaner return filesToNotDelete; } - private class BackgroundCleanup extends AsyncTask + private static class BackgroundCleanup extends AsyncTask { @Override protected Void doInBackground(Void... params) @@ -227,8 +217,8 @@ public class CacheCleaner try { Thread.currentThread().setName("BackgroundCleanup"); - List files = new ArrayList(); - List dirs = new ArrayList(); + List files = new ArrayList<>(); + List dirs = new ArrayList<>(); findCandidatesForDeletion(FileUtil.getMusicDirectory(), files, dirs); sortByAscendingModificationTime(files); @@ -247,7 +237,7 @@ public class CacheCleaner } } - private class BackgroundSpaceCleanup extends AsyncTask + private static class BackgroundSpaceCleanup extends AsyncTask { @Override protected Void doInBackground(Void... params) @@ -255,8 +245,8 @@ public class CacheCleaner try { Thread.currentThread().setName("BackgroundSpaceCleanup"); - List files = new ArrayList(); - List dirs = new ArrayList(); + List files = new ArrayList<>(); + List dirs = new ArrayList<>(); findCandidatesForDeletion(FileUtil.getMusicDirectory(), files, dirs); long bytesToDelete = getMinimumDelete(files); @@ -276,13 +266,14 @@ public class CacheCleaner } } - private class BackgroundPlaylistsCleanup extends AsyncTask, Void, Void> + private static class BackgroundPlaylistsCleanup extends AsyncTask, Void, Void> { @Override protected Void doInBackground(List... params) { try { + Lazy activeServerProvider = inject(ActiveServerProvider.class); Thread.currentThread().setName("BackgroundPlaylistsCleanup"); String server = activeServerProvider.getValue().getActiveServer().getName(); SortedSet playlistFiles = FileUtil.listFiles(FileUtil.getPlaylistDirectory(server)); From f596dc77c17df8c96e69fa1e6ad11b9de18b2829 Mon Sep 17 00:00:00 2001 From: tzugen Date: Fri, 21 May 2021 19:15:41 +0200 Subject: [PATCH 04/17] Make MediaPlayerLifecycleSupport context-free --- .../service/MediaPlayerLifecycleSupport.java | 10 +++++----- .../org/moire/ultrasonic/di/MediaPlayerModule.kt | 4 +--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.java index 0203e7a2..5cf4aa70 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.java @@ -30,6 +30,7 @@ import timber.log.Timber; import android.view.KeyEvent; import org.moire.ultrasonic.R; +import org.moire.ultrasonic.app.UApp; import org.moire.ultrasonic.domain.PlayerState; import org.moire.ultrasonic.util.CacheCleaner; import org.moire.ultrasonic.util.Constants; @@ -46,16 +47,14 @@ public class MediaPlayerLifecycleSupport private DownloadQueueSerializer downloadQueueSerializer; // From DI private final MediaPlayerController mediaPlayerController; // From DI private final Downloader downloader; // From DI - private Context context; private BroadcastReceiver headsetEventReceiver; - public MediaPlayerLifecycleSupport(Context context, DownloadQueueSerializer downloadQueueSerializer, + public MediaPlayerLifecycleSupport(DownloadQueueSerializer downloadQueueSerializer, final MediaPlayerController mediaPlayerController, final Downloader downloader) { this.downloadQueueSerializer = downloadQueueSerializer; this.mediaPlayerController = mediaPlayerController; - this.context = context; this.downloader = downloader; Timber.i("LifecycleSupport constructed"); @@ -103,7 +102,7 @@ public class MediaPlayerLifecycleSupport downloadQueueSerializer.serializeDownloadQueueNow(downloader.downloadList, downloader.getCurrentPlayingIndex(), mediaPlayerController.getPlayerPosition()); mediaPlayerController.clear(false); - context.unregisterReceiver(headsetEventReceiver); + UApp.Companion.applicationContext().unregisterReceiver(headsetEventReceiver); mediaPlayerController.onDestroy(); created = false; Timber.i("LifecycleSupport destroyed"); @@ -139,6 +138,7 @@ public class MediaPlayerLifecycleSupport */ private void registerHeadsetReceiver() { final SharedPreferences sp = Util.getPreferences(); + final Context context = UApp.Companion.applicationContext(); final String spKey = context .getString(R.string.settings_playback_resume_play_on_headphones_plug); @@ -177,7 +177,7 @@ public class MediaPlayerLifecycleSupport { headsetIntentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG); } - context.registerReceiver(headsetEventReceiver, headsetIntentFilter); + UApp.Companion.applicationContext().registerReceiver(headsetEventReceiver, headsetIntentFilter); } public void handleKeyEvent(KeyEvent event) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt index 1811a053..227465fa 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt @@ -16,10 +16,8 @@ import org.moire.ultrasonic.util.ShufflePlayBuffer * This Koin module contains the registration of classes related to the media player */ val mediaPlayerModule = module { - single { MediaPlayerController(androidContext(), get(), get(), get(), get(), get()) } - single { JukeboxMediaPlayer(androidContext(), get()) } - single { MediaPlayerLifecycleSupport(androidContext(), get(), get(), get()) } + single { MediaPlayerLifecycleSupport(get(), get(), get()) } single { DownloadQueueSerializer(androidContext()) } single { ExternalStorageMonitor(androidContext()) } single { ShufflePlayBuffer(androidContext()) } From 05cb82921d907edf69f4358c74e103186f0f0cd5 Mon Sep 17 00:00:00 2001 From: tzugen Date: Fri, 21 May 2021 19:20:21 +0200 Subject: [PATCH 05/17] Fix remaining warnings in MediaPlayerLifecycleSupport --- .../service/MediaPlayerLifecycleSupport.java | 160 +++++++++--------- 1 file changed, 77 insertions(+), 83 deletions(-) diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.java index 5cf4aa70..8713c7e7 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.java @@ -44,7 +44,7 @@ import org.moire.ultrasonic.util.Util; public class MediaPlayerLifecycleSupport { private boolean created = false; - private DownloadQueueSerializer downloadQueueSerializer; // From DI + private final DownloadQueueSerializer downloadQueueSerializer; // From DI private final MediaPlayerController mediaPlayerController; // From DI private final Downloader downloader; // From DI @@ -205,58 +205,55 @@ public class MediaPlayerLifecycleSupport keyCode == KeyEvent.KEYCODE_MEDIA_NEXT); // We can receive intents (e.g. MediaButton) when everything is stopped, so we need to start - onCreate(autoStart, new Runnable() { - @Override - public void run() { - switch (keyCode) - { - case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: - case KeyEvent.KEYCODE_HEADSETHOOK: - mediaPlayerController.togglePlayPause(); - break; - case KeyEvent.KEYCODE_MEDIA_PREVIOUS: - mediaPlayerController.previous(); - break; - case KeyEvent.KEYCODE_MEDIA_NEXT: - mediaPlayerController.next(); - break; - case KeyEvent.KEYCODE_MEDIA_STOP: - mediaPlayerController.stop(); - break; - case KeyEvent.KEYCODE_MEDIA_PLAY: - if (mediaPlayerController.getPlayerState() == PlayerState.IDLE) - { - mediaPlayerController.play(); - } - else if (mediaPlayerController.getPlayerState() != PlayerState.STARTED) - { - mediaPlayerController.start(); - } - break; - case KeyEvent.KEYCODE_MEDIA_PAUSE: - mediaPlayerController.pause(); - break; - case KeyEvent.KEYCODE_1: - mediaPlayerController.setSongRating(1); - break; - case KeyEvent.KEYCODE_2: - mediaPlayerController.setSongRating(2); - break; - case KeyEvent.KEYCODE_3: - mediaPlayerController.setSongRating(3); - break; - case KeyEvent.KEYCODE_4: - mediaPlayerController.setSongRating(4); - break; - case KeyEvent.KEYCODE_5: - mediaPlayerController.setSongRating(5); - break; - case KeyEvent.KEYCODE_STAR: - mediaPlayerController.toggleSongStarred(); - break; - default: - break; - } + onCreate(autoStart, () -> { + switch (keyCode) + { + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + case KeyEvent.KEYCODE_HEADSETHOOK: + mediaPlayerController.togglePlayPause(); + break; + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + mediaPlayerController.previous(); + break; + case KeyEvent.KEYCODE_MEDIA_NEXT: + mediaPlayerController.next(); + break; + case KeyEvent.KEYCODE_MEDIA_STOP: + mediaPlayerController.stop(); + break; + case KeyEvent.KEYCODE_MEDIA_PLAY: + if (mediaPlayerController.getPlayerState() == PlayerState.IDLE) + { + mediaPlayerController.play(); + } + else if (mediaPlayerController.getPlayerState() != PlayerState.STARTED) + { + mediaPlayerController.start(); + } + break; + case KeyEvent.KEYCODE_MEDIA_PAUSE: + mediaPlayerController.pause(); + break; + case KeyEvent.KEYCODE_1: + mediaPlayerController.setSongRating(1); + break; + case KeyEvent.KEYCODE_2: + mediaPlayerController.setSongRating(2); + break; + case KeyEvent.KEYCODE_3: + mediaPlayerController.setSongRating(3); + break; + case KeyEvent.KEYCODE_4: + mediaPlayerController.setSongRating(4); + break; + case KeyEvent.KEYCODE_5: + mediaPlayerController.setSongRating(5); + break; + case KeyEvent.KEYCODE_STAR: + mediaPlayerController.toggleSongStarred(); + break; + default: + break; } }); } @@ -278,36 +275,33 @@ public class MediaPlayerLifecycleSupport intentAction.equals(Constants.CMD_NEXT)); // We can receive intents when everything is stopped, so we need to start - onCreate(autoStart, new Runnable() { - @Override - public void run() { - switch(intentAction) - { - case Constants.CMD_PLAY: - mediaPlayerController.play(); - break; - case Constants.CMD_RESUME_OR_PLAY: - // If Ultrasonic wasn't running, the autoStart is enough to resume, no need to call anything - if (isRunning) mediaPlayerController.resumeOrPlay(); - break; - case Constants.CMD_NEXT: - mediaPlayerController.next(); - break; - case Constants.CMD_PREVIOUS: - mediaPlayerController.previous(); - break; - case Constants.CMD_TOGGLEPAUSE: - mediaPlayerController.togglePlayPause(); - break; - case Constants.CMD_STOP: - // TODO: There is a stop() function, shouldn't we use that? - mediaPlayerController.pause(); - mediaPlayerController.seekTo(0); - break; - case Constants.CMD_PAUSE: - mediaPlayerController.pause(); - break; - } + onCreate(autoStart, () -> { + switch(intentAction) + { + case Constants.CMD_PLAY: + mediaPlayerController.play(); + break; + case Constants.CMD_RESUME_OR_PLAY: + // If Ultrasonic wasn't running, the autoStart is enough to resume, no need to call anything + if (isRunning) mediaPlayerController.resumeOrPlay(); + break; + case Constants.CMD_NEXT: + mediaPlayerController.next(); + break; + case Constants.CMD_PREVIOUS: + mediaPlayerController.previous(); + break; + case Constants.CMD_TOGGLEPAUSE: + mediaPlayerController.togglePlayPause(); + break; + case Constants.CMD_STOP: + // TODO: There is a stop() function, shouldn't we use that? + mediaPlayerController.pause(); + mediaPlayerController.seekTo(0); + break; + case Constants.CMD_PAUSE: + mediaPlayerController.pause(); + break; } }); } From a801e276eef3296da154b0ecdbe6ad291fbf1695 Mon Sep 17 00:00:00 2001 From: tzugen Date: Fri, 21 May 2021 22:35:21 +0200 Subject: [PATCH 06/17] Rename .java to .kt --- .../org/moire/ultrasonic/service/MediaPlayerController.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ultrasonic/src/main/{java/org/moire/ultrasonic/service/MediaPlayerController.java => kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt} (100%) diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerController.java b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt similarity index 100% rename from ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerController.java rename to ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt From df047dd4634d39ea71b936a87b39ced9974b11e2 Mon Sep 17 00:00:00 2001 From: tzugen Date: Fri, 21 May 2021 22:35:22 +0200 Subject: [PATCH 07/17] Refactor MediaPlayerController to Kotlin and also make MediaPlayerService context-free --- detekt-config.yml | 3 +- .../moire/ultrasonic/di/MediaPlayerModule.kt | 2 +- .../service/MediaPlayerController.kt | 1108 ++++++++--------- .../ultrasonic/service/MediaPlayerService.kt | 14 +- 4 files changed, 517 insertions(+), 610 deletions(-) diff --git a/detekt-config.yml b/detekt-config.yml index 6b7db639..9301b0b7 100644 --- a/detekt-config.yml +++ b/detekt-config.yml @@ -63,7 +63,8 @@ style: excludePackageStatements: false excludeImportStatements: false MagicNumber: - ignoreNumbers: ['-1', '0', '1', '2', '100'] + # 100 common in percentage, 1000 in milliseconds + ignoreNumbers: ['-1', '0', '1', '2', '100', '1000'] ignoreEnums: true ignorePropertyDeclaration: true UnnecessaryAbstractClass: diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt index 227465fa..de18fda7 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt @@ -26,5 +26,5 @@ val mediaPlayerModule = module { single { AudioFocusHandler(get()) } // TODO Ideally this can be cleaned up when all circular references are removed. - single { MediaPlayerController(androidContext(), get(), get(), get(), get(), get()) } + single { MediaPlayerController(get(), get(), get(), get(), get()) } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt index 7ffd904a..1706cf7e 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt @@ -1,612 +1,514 @@ /* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - - Copyright 2009 (C) Sindre Mehus + * MediaPlayerController.kt + * Copyright (C) 2009-2021 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. */ -package org.moire.ultrasonic.service; +package org.moire.ultrasonic.service -import android.content.Context; -import android.content.Intent; -import timber.log.Timber; - -import org.koin.java.KoinJavaComponent; -import org.moire.ultrasonic.data.ActiveServerProvider; -import org.moire.ultrasonic.domain.MusicDirectory; -import org.moire.ultrasonic.domain.MusicDirectory.Entry; -import org.moire.ultrasonic.domain.PlayerState; -import org.moire.ultrasonic.domain.RepeatMode; -import org.moire.ultrasonic.domain.UserInfo; -import org.moire.ultrasonic.featureflags.Feature; -import org.moire.ultrasonic.featureflags.FeatureStorage; -import org.moire.ultrasonic.util.ShufflePlayBuffer; -import org.moire.ultrasonic.util.Util; - -import java.util.Iterator; -import java.util.List; - -import kotlin.Lazy; - -import static org.koin.java.KoinJavaComponent.inject; +import android.content.Intent +import org.koin.core.component.KoinApiExtension +import org.koin.java.KoinJavaComponent.get +import org.koin.java.KoinJavaComponent.inject +import org.moire.ultrasonic.app.UApp +import org.moire.ultrasonic.data.ActiveServerProvider +import org.moire.ultrasonic.domain.MusicDirectory +import org.moire.ultrasonic.domain.PlayerState +import org.moire.ultrasonic.domain.RepeatMode +import org.moire.ultrasonic.featureflags.Feature +import org.moire.ultrasonic.featureflags.FeatureStorage +import org.moire.ultrasonic.service.MediaPlayerService.Companion.executeOnStartedMediaPlayerService +import org.moire.ultrasonic.service.MediaPlayerService.Companion.getInstance +import org.moire.ultrasonic.service.MediaPlayerService.Companion.runningInstance +import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService +import org.moire.ultrasonic.util.ShufflePlayBuffer +import org.moire.ultrasonic.util.Util +import timber.log.Timber /** * The implementation of the Media Player Controller. * This class contains everything that is necessary for the Application UI * to control the Media Player implementation. - * - * @author Sindre Mehus, Joshua Bahnsen - * @version $Id$ */ -public class MediaPlayerController -{ - private boolean created = false; - private String suggestedPlaylistName; - private boolean keepScreenOn; - - private boolean showVisualization; - private boolean autoPlayStart; - - private final Context context; - private final Lazy jukeboxMediaPlayer = inject(JukeboxMediaPlayer.class); - private final Lazy activeServerProvider = inject(ActiveServerProvider.class); - - private final DownloadQueueSerializer downloadQueueSerializer; - private final ExternalStorageMonitor externalStorageMonitor; - private final Downloader downloader; - private final ShufflePlayBuffer shufflePlayBuffer; - private final LocalMediaPlayer localMediaPlayer; - - public MediaPlayerController(Context context, DownloadQueueSerializer downloadQueueSerializer, - ExternalStorageMonitor externalStorageMonitor, Downloader downloader, - ShufflePlayBuffer shufflePlayBuffer, LocalMediaPlayer localMediaPlayer) - { - this.context = context; - this.downloadQueueSerializer = downloadQueueSerializer; - this.externalStorageMonitor = externalStorageMonitor; - this.downloader = downloader; - this.shufflePlayBuffer = shufflePlayBuffer; - this.localMediaPlayer = localMediaPlayer; - - Timber.i("MediaPlayerController constructed"); - } - - public void onCreate() - { - if (created) return; - this.externalStorageMonitor.onCreate(this::reset); - - setJukeboxEnabled(activeServerProvider.getValue().getActiveServer().getJukeboxByDefault()); - created = true; - - Timber.i("MediaPlayerController created"); - } - - public void onDestroy() - { - if (!created) return; - externalStorageMonitor.onDestroy(); - context.stopService(new Intent(context, MediaPlayerService.class)); - downloader.onDestroy(); - created = false; - - Timber.i("MediaPlayerController destroyed"); - } - public synchronized void restore(List songs, final int currentPlayingIndex, final int currentPlayingPosition, final boolean autoPlay, boolean newPlaylist) - { - download(songs, false, false, false, false, newPlaylist); - - if (currentPlayingIndex != -1) - { - MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> - { - mediaPlayerService.play(currentPlayingIndex, autoPlayStart); - - if (localMediaPlayer.currentPlaying != null) - { - if (autoPlay && jukeboxMediaPlayer.getValue().isEnabled()) - { - jukeboxMediaPlayer.getValue().skip(downloader.getCurrentPlayingIndex(), currentPlayingPosition / 1000); - } - else - { - if (localMediaPlayer.currentPlaying.isCompleteFileAvailable()) - { - localMediaPlayer.play(localMediaPlayer.currentPlaying, currentPlayingPosition, autoPlay); - } - } - } - autoPlayStart = false; - return null; - } - ); - } - } - - public synchronized void preload() - { - MediaPlayerService.getInstance(context); - } - - public synchronized void play(final int index) - { - MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> { - mediaPlayerService.play(index, true); - return null; - } - ); - } - - public synchronized void play() - { - MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> { - - mediaPlayerService.play(); - return null; - } - ); - } - - public synchronized void resumeOrPlay() - { - MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> { - mediaPlayerService.resumeOrPlay(); - return null; - } - ); - } - - public synchronized void togglePlayPause() - { - if (localMediaPlayer.playerState == PlayerState.IDLE) autoPlayStart = true; - MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> { - mediaPlayerService.togglePlayPause(); - return null; - } - ); - } - - public synchronized void start() - { - MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> { - mediaPlayerService.start(); - return null; - } - ); - } - - public synchronized void seekTo(final int position) - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) mediaPlayerService.seekTo(position); - } - - public synchronized void pause() - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) mediaPlayerService.pause(); - } - - public synchronized void stop() - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) mediaPlayerService.stop(); - } - - public synchronized void download(List songs, boolean save, boolean autoPlay, boolean playNext, boolean shuffle, boolean newPlaylist) - { - downloader.download(songs, save, autoPlay, playNext, newPlaylist); - jukeboxMediaPlayer.getValue().updatePlaylist(); - - if (shuffle) shuffle(); - - if (!playNext && !autoPlay && (downloader.downloadList.size() - 1) == downloader.getCurrentPlayingIndex()) - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) mediaPlayerService.setNextPlaying(); - } - - if (autoPlay) - { - play(0); - } - else - { - if (localMediaPlayer.currentPlaying == null && downloader.downloadList.size() > 0) - { - localMediaPlayer.currentPlaying = downloader.downloadList.get(0); - localMediaPlayer.currentPlaying.setPlaying(true); - } - - downloader.checkDownloads(); - } - - downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition()); - } - - public synchronized void downloadBackground(List songs, boolean save) - { - downloader.downloadBackground(songs, save); - downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition()); - } - - public synchronized void setCurrentPlaying(DownloadFile currentPlaying) - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) localMediaPlayer.setCurrentPlaying(currentPlaying); - } - - public synchronized void setCurrentPlaying(int index) - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) mediaPlayerService.setCurrentPlaying(index); - } - - public synchronized void setPlayerState(PlayerState state) - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) localMediaPlayer.setPlayerState(state); - } - - public void stopJukeboxService() - { - jukeboxMediaPlayer.getValue().stopJukeboxService(); - } - - public synchronized void setShufflePlayEnabled(boolean enabled) - { - shufflePlayBuffer.isEnabled = enabled; - if (enabled) - { - clear(); - downloader.checkDownloads(); - } - } - - public boolean isShufflePlayEnabled() - { - return shufflePlayBuffer.isEnabled; - } - - public synchronized void shuffle() - { - downloader.shuffle(); - - downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition()); - jukeboxMediaPlayer.getValue().updatePlaylist(); - - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) mediaPlayerService.setNextPlaying(); - } - - public RepeatMode getRepeatMode() - { - return Util.getRepeatMode(); - } - - public synchronized void setRepeatMode(RepeatMode repeatMode) - { - Util.setRepeatMode(repeatMode); - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) mediaPlayerService.setNextPlaying(); - } - - public boolean getKeepScreenOn() - { - return keepScreenOn; - } - - public void setKeepScreenOn(boolean keepScreenOn) - { - this.keepScreenOn = keepScreenOn; - } - - public boolean getShowVisualization() - { - return showVisualization; - } - - public void setShowVisualization(boolean showVisualization) - { - this.showVisualization = showVisualization; - } - - public synchronized void clear() - { - clear(true); - } - - public synchronized void clear(boolean serialize) - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) { - mediaPlayerService.clear(serialize); - } else { - // If no MediaPlayerService is available, just empty the playlist - downloader.clear(); - if (serialize) { - downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, - downloader.getCurrentPlayingIndex(), getPlayerPosition()); - } - } - - jukeboxMediaPlayer.getValue().updatePlaylist(); - } - - public synchronized void clearIncomplete() - { - reset(); - Iterator iterator = downloader.downloadList.iterator(); - - while (iterator.hasNext()) - { - DownloadFile downloadFile = iterator.next(); - if (!downloadFile.isCompleteFileAvailable()) - { - iterator.remove(); - } - } - - downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition()); - jukeboxMediaPlayer.getValue().updatePlaylist(); - } - - public synchronized void remove(DownloadFile downloadFile) - { - if (downloadFile == localMediaPlayer.currentPlaying) - { - reset(); - setCurrentPlaying(null); - } - - downloader.removeDownloadFile(downloadFile); - - downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition()); - jukeboxMediaPlayer.getValue().updatePlaylist(); - - if (downloadFile == localMediaPlayer.nextPlaying) - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) mediaPlayerService.setNextPlaying(); - } - } - - public synchronized void delete(List songs) - { - for (MusicDirectory.Entry song : songs) - { - downloader.getDownloadFileForSong(song).delete(); - } - } - - public synchronized void unpin(List songs) - { - for (MusicDirectory.Entry song : songs) - { - downloader.getDownloadFileForSong(song).unpin(); - } - } - - public synchronized void previous() - { - int index = downloader.getCurrentPlayingIndex(); - if (index == -1) - { - return; - } - - // Restart song if played more than five seconds. - if (getPlayerPosition() > 5000 || index == 0) - { - play(index); - } - else - { - play(index - 1); - } - } - - public synchronized void next() - { - int index = downloader.getCurrentPlayingIndex(); - if (index != -1) - { - switch (getRepeatMode()) - { - case SINGLE: - case OFF: - if (index + 1 >= 0 && index + 1 < downloader.downloadList.size()) { - play(index + 1); - } - break; - case ALL: - play((index + 1) % downloader.downloadList.size()); - break; - default: - break; - } - } - } - - public synchronized void reset() - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) localMediaPlayer.reset(); - } - - public synchronized int getPlayerPosition() - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService == null) return 0; - return mediaPlayerService.getPlayerPosition(); - } - - public synchronized int getPlayerDuration() - { - if (localMediaPlayer.currentPlaying != null) - { - Integer duration = localMediaPlayer.currentPlaying.getSong().getDuration(); - if (duration != null) - { - return duration * 1000; - } - } - - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService == null) return 0; - return mediaPlayerService.getPlayerDuration(); - } - - public PlayerState getPlayerState() { return localMediaPlayer.playerState; } - - public void setSuggestedPlaylistName(String name) - { - this.suggestedPlaylistName = name; - } - - public String getSuggestedPlaylistName() - { - return suggestedPlaylistName; - } - - public boolean isJukeboxEnabled() - { - return jukeboxMediaPlayer.getValue().isEnabled(); - } - - public boolean isJukeboxAvailable() - { - try - { - String username = activeServerProvider.getValue().getActiveServer().getUserName(); - UserInfo user = MusicServiceFactory.getMusicService().getUser(username); - return user.getJukeboxRole(); - } - catch (Exception e) - { - Timber.w(e, "Error getting user information"); - } - - return false; - } - - public void setJukeboxEnabled(boolean jukeboxEnabled) - { - jukeboxMediaPlayer.getValue().setEnabled(jukeboxEnabled); - setPlayerState(PlayerState.IDLE); - - if (jukeboxEnabled) - { - jukeboxMediaPlayer.getValue().startJukeboxService(); - - reset(); - - // Cancel current download, if necessary. - if (downloader.currentDownloading != null) - { - downloader.currentDownloading.cancelDownload(); - } - } - else - { - jukeboxMediaPlayer.getValue().stopJukeboxService(); - } - } - - public void adjustJukeboxVolume(boolean up) - { - jukeboxMediaPlayer.getValue().adjustVolume(up); - } - - public void setVolume(float volume) - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) localMediaPlayer.setVolume(volume); - } - - public void updateNotification() - { - MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); - if (mediaPlayerService != null) mediaPlayerService.updateNotification(localMediaPlayer.playerState, localMediaPlayer.currentPlaying); - } - - public void toggleSongStarred() { - if (localMediaPlayer.currentPlaying == null) - return; - - final Entry song = localMediaPlayer.currentPlaying.getSong(); - - // Trigger an update - localMediaPlayer.setCurrentPlaying(localMediaPlayer.currentPlaying); - - song.setStarred(!song.getStarred()); - } - - public void setSongRating(final int rating) - { - if (!KoinJavaComponent.get(FeatureStorage.class).isFeatureEnabled(Feature.FIVE_STAR_RATING)) - return; - - if (localMediaPlayer.currentPlaying == null) - return; - - final Entry song = localMediaPlayer.currentPlaying.getSong(); - song.setUserRating(rating); - - new Thread(() -> { - try - { - MusicServiceFactory.getMusicService().setRating(song.getId(), rating); - } - catch (Exception e) - { - Timber.e(e); - } - }).start(); - - updateNotification(); - } - - public DownloadFile getCurrentPlaying() { - return localMediaPlayer.currentPlaying; - } - - public int getPlaylistSize() { - return downloader.downloadList.size(); - } - - public int getCurrentPlayingNumberOnPlaylist() { - return downloader.getCurrentPlayingIndex(); - } - - public DownloadFile getCurrentDownloading() { - return downloader.currentDownloading; - } - - public List getPlayList() { - return downloader.downloadList; - } - - public long getPlayListUpdateRevision() { - return downloader.getDownloadListUpdateRevision(); - } - - public long getPlayListDuration() { - return downloader.getDownloadListDuration(); - } - - public DownloadFile getDownloadFileForSong(Entry song) { - return downloader.getDownloadFileForSong(song); - } -} \ No newline at end of file +@KoinApiExtension +@Suppress("TooManyFunctions") +class MediaPlayerController( + private val downloadQueueSerializer: DownloadQueueSerializer, + private val externalStorageMonitor: ExternalStorageMonitor, + private val downloader: Downloader, + private val shufflePlayBuffer: ShufflePlayBuffer, + private val localMediaPlayer: LocalMediaPlayer +) { + + private var created = false + var suggestedPlaylistName: String? = null + var keepScreenOn = false + var showVisualization = false + private var autoPlayStart = false + + private val jukeboxMediaPlayer = inject(JukeboxMediaPlayer::class.java).value + private val activeServerProvider = inject(ActiveServerProvider::class.java).value + + fun onCreate() { + if (created) return + externalStorageMonitor.onCreate { reset() } + isJukeboxEnabled = activeServerProvider.getActiveServer().jukeboxByDefault + created = true + Timber.i("MediaPlayerController created") + } + + fun onDestroy() { + if (!created) return + val context = UApp.applicationContext() + externalStorageMonitor.onDestroy() + context.stopService(Intent(context, MediaPlayerService::class.java)) + downloader.onDestroy() + created = false + Timber.i("MediaPlayerController destroyed") + } + + @Synchronized + fun restore( + songs: List?, + currentPlayingIndex: Int, + currentPlayingPosition: Int, + autoPlay: Boolean, + newPlaylist: Boolean + ) { + download( + songs, + save = false, + autoPlay = false, + playNext = false, + shuffle = false, + newPlaylist = newPlaylist + ) + if (currentPlayingIndex != -1) { + executeOnStartedMediaPlayerService { mediaPlayerService: MediaPlayerService -> + mediaPlayerService.play(currentPlayingIndex, autoPlayStart) + if (localMediaPlayer.currentPlaying != null) { + if (autoPlay && jukeboxMediaPlayer.isEnabled) { + jukeboxMediaPlayer.skip( + downloader.currentPlayingIndex, + currentPlayingPosition / 1000 + ) + } else { + if (localMediaPlayer.currentPlaying!!.isCompleteFileAvailable) { + localMediaPlayer.play( + localMediaPlayer.currentPlaying, + currentPlayingPosition, + autoPlay + ) + } + } + } + autoPlayStart = false + } + } + } + + @Synchronized + fun preload() { + getInstance() + } + + @Synchronized + fun play(index: Int) { + executeOnStartedMediaPlayerService { service: MediaPlayerService -> + service.play(index, true) + } + } + + @Synchronized + fun play() { + executeOnStartedMediaPlayerService { service: MediaPlayerService -> + service.play() + } + } + + @Synchronized + fun resumeOrPlay() { + executeOnStartedMediaPlayerService { service: MediaPlayerService -> + service.resumeOrPlay() + } + } + + @Synchronized + fun togglePlayPause() { + if (localMediaPlayer.playerState === PlayerState.IDLE) autoPlayStart = true + executeOnStartedMediaPlayerService { service: MediaPlayerService -> + service.togglePlayPause() + } + } + + @Synchronized + fun start() { + executeOnStartedMediaPlayerService { service: MediaPlayerService -> + service.start() + } + } + + @Synchronized + fun seekTo(position: Int) { + val mediaPlayerService = runningInstance + mediaPlayerService?.seekTo(position) + } + + @Synchronized + fun pause() { + val mediaPlayerService = runningInstance + mediaPlayerService?.pause() + } + + @Synchronized + fun stop() { + val mediaPlayerService = runningInstance + mediaPlayerService?.stop() + } + + @Synchronized + @Suppress("LongParameterList") + fun download( + songs: List?, + save: Boolean, + autoPlay: Boolean, + playNext: Boolean, + shuffle: Boolean, + newPlaylist: Boolean + ) { + downloader.download(songs, save, autoPlay, playNext, newPlaylist) + jukeboxMediaPlayer.updatePlaylist() + if (shuffle) shuffle() + val isLastTrack = (downloader.downloadList.size - 1 == downloader.currentPlayingIndex) + + if (!playNext && !autoPlay && isLastTrack) { + val mediaPlayerService = runningInstance + mediaPlayerService?.setNextPlaying() + } + + if (autoPlay) { + play(0) + } else { + if (localMediaPlayer.currentPlaying == null && downloader.downloadList.size > 0) { + localMediaPlayer.currentPlaying = downloader.downloadList[0] + downloader.downloadList[0].setPlaying(true) + } + downloader.checkDownloads() + } + + downloadQueueSerializer.serializeDownloadQueue( + downloader.downloadList, + downloader.currentPlayingIndex, + playerPosition + ) + } + + @Synchronized + fun downloadBackground(songs: List?, save: Boolean) { + downloader.downloadBackground(songs, save) + downloadQueueSerializer.serializeDownloadQueue( + downloader.downloadList, + downloader.currentPlayingIndex, + playerPosition + ) + } + + @Synchronized + fun setCurrentPlaying(index: Int) { + val mediaPlayerService = runningInstance + mediaPlayerService?.setCurrentPlaying(index) + } + + fun stopJukeboxService() { + jukeboxMediaPlayer.stopJukeboxService() + } + + @set:Synchronized + var isShufflePlayEnabled: Boolean + get() = shufflePlayBuffer.isEnabled + set(enabled) { + shufflePlayBuffer.isEnabled = enabled + if (enabled) { + clear() + downloader.checkDownloads() + } + } + + @Synchronized + fun shuffle() { + downloader.shuffle() + downloadQueueSerializer.serializeDownloadQueue( + downloader.downloadList, + downloader.currentPlayingIndex, + playerPosition + ) + jukeboxMediaPlayer.updatePlaylist() + val mediaPlayerService = runningInstance + mediaPlayerService?.setNextPlaying() + } + + @set:Synchronized + var repeatMode: RepeatMode? + get() = Util.getRepeatMode() + set(repeatMode) { + Util.setRepeatMode(repeatMode) + val mediaPlayerService = runningInstance + mediaPlayerService?.setNextPlaying() + } + + @Synchronized + fun clear() { + clear(true) + } + + @Synchronized + fun clear(serialize: Boolean) { + val mediaPlayerService = runningInstance + if (mediaPlayerService != null) { + mediaPlayerService.clear(serialize) + } else { + // If no MediaPlayerService is available, just empty the playlist + downloader.clear() + if (serialize) { + downloadQueueSerializer.serializeDownloadQueue( + downloader.downloadList, + downloader.currentPlayingIndex, playerPosition + ) + } + } + jukeboxMediaPlayer.updatePlaylist() + } + + @Synchronized + fun clearIncomplete() { + reset() + val iterator = downloader.downloadList.iterator() + while (iterator.hasNext()) { + val downloadFile = iterator.next() + if (!downloadFile.isCompleteFileAvailable) { + iterator.remove() + } + } + + downloadQueueSerializer.serializeDownloadQueue( + downloader.downloadList, + downloader.currentPlayingIndex, + playerPosition + ) + + jukeboxMediaPlayer.updatePlaylist() + } + + @Synchronized + fun remove(downloadFile: DownloadFile) { + if (downloadFile == localMediaPlayer.currentPlaying) { + reset() + currentPlaying = null + } + downloader.removeDownloadFile(downloadFile) + + downloadQueueSerializer.serializeDownloadQueue( + downloader.downloadList, + downloader.currentPlayingIndex, + playerPosition + ) + + jukeboxMediaPlayer.updatePlaylist() + + if (downloadFile == localMediaPlayer.nextPlaying) { + val mediaPlayerService = runningInstance + mediaPlayerService?.setNextPlaying() + } + } + + @Synchronized + fun delete(songs: List) { + for (song in songs) { + downloader.getDownloadFileForSong(song).delete() + } + } + + @Synchronized + fun unpin(songs: List) { + for (song in songs) { + downloader.getDownloadFileForSong(song).unpin() + } + } + + @Synchronized + fun previous() { + val index = downloader.currentPlayingIndex + if (index == -1) { + return + } + + // Restart song if played more than five seconds. + @Suppress("MagicNumber") + if (playerPosition > 5000 || index == 0) { + play(index) + } else { + play(index - 1) + } + } + + @Synchronized + operator fun next() { + val index = downloader.currentPlayingIndex + if (index != -1) { + when (repeatMode) { + RepeatMode.SINGLE, RepeatMode.OFF -> { + // Play next if exists + if (index + 1 >= 0 && index + 1 < downloader.downloadList.size) { + play(index + 1) + } + } + RepeatMode.ALL -> { + play((index + 1) % downloader.downloadList.size) + } + else -> { + } + } + } + } + + @Synchronized + fun reset() { + val mediaPlayerService = runningInstance + if (mediaPlayerService != null) localMediaPlayer.reset() + } + + @get:Synchronized + val playerPosition: Int + get() { + val mediaPlayerService = runningInstance ?: return 0 + return mediaPlayerService.playerPosition + } + + @get:Synchronized + val playerDuration: Int + get() { + if (localMediaPlayer.currentPlaying != null) { + val duration = localMediaPlayer.currentPlaying!!.song.duration + if (duration != null) { + return duration * 1000 + } + } + val mediaPlayerService = runningInstance ?: return 0 + return mediaPlayerService.playerDuration + } + + @set:Synchronized + var playerState: PlayerState + get() = localMediaPlayer.playerState + set(state) { + val mediaPlayerService = runningInstance + if (mediaPlayerService != null) localMediaPlayer.setPlayerState(state) + } + + @set:Synchronized + var isJukeboxEnabled: Boolean + get() = jukeboxMediaPlayer.isEnabled + set(jukeboxEnabled) { + jukeboxMediaPlayer.isEnabled = jukeboxEnabled + playerState = PlayerState.IDLE + if (jukeboxEnabled) { + jukeboxMediaPlayer.startJukeboxService() + reset() + + // Cancel current download, if necessary. + if (downloader.currentDownloading != null) { + downloader.currentDownloading.cancelDownload() + } + } else { + jukeboxMediaPlayer.stopJukeboxService() + } + } + + @Suppress("TooGenericExceptionCaught") // The interface throws only generic exceptions + val isJukeboxAvailable: Boolean + get() { + try { + val username = activeServerProvider.getActiveServer().userName + val (_, _, _, _, _, _, _, _, _, _, _, _, jukeboxRole) = getMusicService().getUser( + username + ) + return jukeboxRole + } catch (e: Exception) { + Timber.w(e, "Error getting user information") + } + return false + } + + fun adjustJukeboxVolume(up: Boolean) { + jukeboxMediaPlayer.adjustVolume(up) + } + + fun setVolume(volume: Float) { + if (runningInstance != null) localMediaPlayer.setVolume(volume) + } + + private fun updateNotification() { + runningInstance?.updateNotification( + localMediaPlayer.playerState, + localMediaPlayer.currentPlaying + ) + } + + fun toggleSongStarred() { + if (localMediaPlayer.currentPlaying == null) return + val song = localMediaPlayer.currentPlaying!!.song + + // Trigger an update + localMediaPlayer.setCurrentPlaying(localMediaPlayer.currentPlaying) + song.starred = !song.starred + } + + @Suppress("TooGenericExceptionCaught") // The interface throws only generic exceptions + fun setSongRating(rating: Int) { + if (!get(FeatureStorage::class.java).isFeatureEnabled(Feature.FIVE_STAR_RATING)) return + if (localMediaPlayer.currentPlaying == null) return + val song = localMediaPlayer.currentPlaying!!.song + song.userRating = rating + Thread { + try { + getMusicService().setRating(song.id, rating) + } catch (e: Exception) { + Timber.e(e) + } + }.start() + updateNotification() + } + + @set:Synchronized + var currentPlaying: DownloadFile? + get() = localMediaPlayer.currentPlaying + set(currentPlaying) { + if (runningInstance != null) localMediaPlayer.setCurrentPlaying(currentPlaying) + } + + val playlistSize: Int + get() = downloader.downloadList.size + + val currentPlayingNumberOnPlaylist: Int + get() = downloader.currentPlayingIndex + + val currentDownloading: DownloadFile + get() = downloader.currentDownloading + + val playList: List + get() = downloader.downloadList + + val playListUpdateRevision: Long + get() = downloader.downloadListUpdateRevision + + val playListDuration: Long + get() = downloader.downloadListDuration + + fun getDownloadFileForSong(song: MusicDirectory.Entry?): DownloadFile { + return downloader.getDownloadFileForSong(song) + } + + init { + Timber.i("MediaPlayerController constructed") + } +} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt index 325ecf0a..ea0b9c25 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt @@ -24,8 +24,10 @@ import android.view.KeyEvent import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import org.koin.android.ext.android.inject +import org.koin.core.component.KoinApiExtension import org.moire.ultrasonic.R import org.moire.ultrasonic.activity.NavigationActivity +import org.moire.ultrasonic.app.UApp import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.domain.PlayerState import org.moire.ultrasonic.domain.RepeatMode @@ -47,6 +49,7 @@ import timber.log.Timber * Android Foreground Service for playing music * while the rest of the Ultrasonic App is in the background. */ +@KoinApiExtension @Suppress("LargeClass") class MediaPlayerService : Service() { private val binder: IBinder = SimpleServiceBinder(this) @@ -906,7 +909,8 @@ class MediaPlayerService : Service() { private val instanceLock = Any() @JvmStatic - fun getInstance(context: Context): MediaPlayerService? { + fun getInstance(): MediaPlayerService? { + val context = UApp.applicationContext() synchronized(instanceLock) { for (i in 0..19) { if (instance != null) return instance @@ -931,18 +935,18 @@ class MediaPlayerService : Service() { @JvmStatic fun executeOnStartedMediaPlayerService( - context: Context, - taskToExecute: (MediaPlayerService?) -> Unit + taskToExecute: (MediaPlayerService) -> Unit ) { val t: Thread = object : Thread() { override fun run() { - val instance = getInstance(context) + val instance = getInstance() if (instance == null) { Timber.e("ExecuteOnStarted.. failed to get a MediaPlayerService instance!") return + } else { + taskToExecute(instance) } - taskToExecute(instance) } } t.start() From 1b5db9da1f5273ff09ee791e709a74127189b317 Mon Sep 17 00:00:00 2001 From: tzugen Date: Fri, 21 May 2021 22:37:43 +0200 Subject: [PATCH 08/17] Make ExternalStorageMonitor context-free --- .../ultrasonic/service/ExternalStorageMonitor.java | 13 +++++-------- .../org/moire/ultrasonic/di/MediaPlayerModule.kt | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/ExternalStorageMonitor.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/ExternalStorageMonitor.java index f1a7256a..a2813758 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/ExternalStorageMonitor.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/ExternalStorageMonitor.java @@ -4,6 +4,9 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; + +import org.moire.ultrasonic.app.UApp; + import timber.log.Timber; /** @@ -11,15 +14,9 @@ import timber.log.Timber; */ public class ExternalStorageMonitor { - private Context context; private BroadcastReceiver ejectEventReceiver; private boolean externalStorageAvailable = true; - public ExternalStorageMonitor(Context context) - { - this.context = context; - } - public void onCreate(final Runnable ejectedCallback) { // Stop when SD card is ejected. @@ -44,12 +41,12 @@ public class ExternalStorageMonitor IntentFilter ejectFilter = new IntentFilter(Intent.ACTION_MEDIA_EJECT); ejectFilter.addAction(Intent.ACTION_MEDIA_MOUNTED); ejectFilter.addDataScheme("file"); - context.registerReceiver(ejectEventReceiver, ejectFilter); + UApp.Companion.applicationContext().registerReceiver(ejectEventReceiver, ejectFilter); } public void onDestroy() { - context.unregisterReceiver(ejectEventReceiver); + UApp.Companion.applicationContext().unregisterReceiver(ejectEventReceiver); } public boolean isExternalStorageAvailable() { return externalStorageAvailable; } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt index de18fda7..54cd70ee 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt @@ -19,7 +19,7 @@ val mediaPlayerModule = module { single { JukeboxMediaPlayer(androidContext(), get()) } single { MediaPlayerLifecycleSupport(get(), get(), get()) } single { DownloadQueueSerializer(androidContext()) } - single { ExternalStorageMonitor(androidContext()) } + single { ExternalStorageMonitor() } single { ShufflePlayBuffer(androidContext()) } single { Downloader(androidContext(), get(), get(), get()) } single { LocalMediaPlayer(get(), androidContext()) } From 00e64cdddc07a28c6ac75445743ac69e3015e887 Mon Sep 17 00:00:00 2001 From: tzugen Date: Fri, 21 May 2021 22:39:21 +0200 Subject: [PATCH 09/17] Use lambdas in OfflineMusicService --- .../service/OfflineMusicService.java | 126 ++++++++---------- 1 file changed, 53 insertions(+), 73 deletions(-) diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/OfflineMusicService.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/OfflineMusicService.java index 4a3be836..6bff4eb4 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/OfflineMusicService.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/OfflineMusicService.java @@ -21,9 +21,6 @@ package org.moire.ultrasonic.service; import android.graphics.Bitmap; import android.media.MediaMetadataRetriever; -import kotlin.Pair; -import timber.log.Timber; - import org.moire.ultrasonic.data.ActiveServerProvider; import org.moire.ultrasonic.domain.Artist; import org.moire.ultrasonic.domain.Bookmark; @@ -54,7 +51,6 @@ import java.io.Reader; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.HashSet; import java.util.LinkedList; import java.util.List; @@ -64,6 +60,8 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import kotlin.Lazy; +import kotlin.Pair; +import timber.log.Timber; import static org.koin.java.KoinJavaComponent.inject; @@ -95,49 +93,44 @@ public class OfflineMusicService implements MusicService String ignoredArticlesString = "The El La Los Las Le Les"; final String[] ignoredArticles = COMPILE.split(ignoredArticlesString); - Collections.sort(artists, new Comparator() - { - @Override - public int compare(Artist lhsArtist, Artist rhsArtist) + Collections.sort(artists, (lhsArtist, rhsArtist) -> { + String lhs = lhsArtist.getName().toLowerCase(); + String rhs = rhsArtist.getName().toLowerCase(); + + char lhs1 = lhs.charAt(0); + char rhs1 = rhs.charAt(0); + + if (Character.isDigit(lhs1) && !Character.isDigit(rhs1)) { - String lhs = lhsArtist.getName().toLowerCase(); - String rhs = rhsArtist.getName().toLowerCase(); - - char lhs1 = lhs.charAt(0); - char rhs1 = rhs.charAt(0); - - if (Character.isDigit(lhs1) && !Character.isDigit(rhs1)) - { - return 1; - } - - if (Character.isDigit(rhs1) && !Character.isDigit(lhs1)) - { - return -1; - } - - for (String article : ignoredArticles) - { - int index = lhs.indexOf(String.format("%s ", article.toLowerCase())); - - if (index == 0) - { - lhs = lhs.substring(article.length() + 1); - } - - index = rhs.indexOf(String.format("%s ", article.toLowerCase())); - - if (index == 0) - { - rhs = rhs.substring(article.length() + 1); - } - } - - return lhs.compareTo(rhs); + return 1; } + + if (Character.isDigit(rhs1) && !Character.isDigit(lhs1)) + { + return -1; + } + + for (String article : ignoredArticles) + { + int index = lhs.indexOf(String.format("%s ", article.toLowerCase())); + + if (index == 0) + { + lhs = lhs.substring(article.length() + 1); + } + + index = rhs.indexOf(String.format("%s ", article.toLowerCase())); + + if (index == 0) + { + rhs = rhs.substring(article.length() + 1); + } + } + + return lhs.compareTo(rhs); }); - return new Indexes(0L, ignoredArticlesString, Collections.emptyList(), artists); + return new Indexes(0L, ignoredArticlesString, Collections.emptyList(), artists); } @Override @@ -387,44 +380,31 @@ public class OfflineMusicService implements MusicService } } - Collections.sort(artists, new Comparator() - { - @Override - public int compare(Artist lhs, Artist rhs) + Collections.sort(artists, (lhs, rhs) -> { + if (lhs.getCloseness() == rhs.getCloseness()) { - if (lhs.getCloseness() == rhs.getCloseness()) - { - return 0; - } - - else return lhs.getCloseness() > rhs.getCloseness() ? -1 : 1; + return 0; } + + else return lhs.getCloseness() > rhs.getCloseness() ? -1 : 1; }); - Collections.sort(albums, new Comparator() - { - @Override - public int compare(MusicDirectory.Entry lhs, MusicDirectory.Entry rhs) - { - if (lhs.getCloseness() == rhs.getCloseness()) - { - return 0; - } - else return lhs.getCloseness() > rhs.getCloseness() ? -1 : 1; + Collections.sort(albums, (lhs, rhs) -> { + if (lhs.getCloseness() == rhs.getCloseness()) + { + return 0; } + + else return lhs.getCloseness() > rhs.getCloseness() ? -1 : 1; }); - Collections.sort(songs, new Comparator() - { - @Override - public int compare(MusicDirectory.Entry lhs, MusicDirectory.Entry rhs) - { - if (lhs.getCloseness() == rhs.getCloseness()) - { - return 0; - } - else return lhs.getCloseness() > rhs.getCloseness() ? -1 : 1; + Collections.sort(songs, (lhs, rhs) -> { + if (lhs.getCloseness() == rhs.getCloseness()) + { + return 0; } + + else return lhs.getCloseness() > rhs.getCloseness() ? -1 : 1; }); return new SearchResult(artists, albums, songs); From 9b2e54b94e81593a77c373a396e10cdf3aee9470 Mon Sep 17 00:00:00 2001 From: tzugen Date: Fri, 21 May 2021 22:52:16 +0200 Subject: [PATCH 10/17] Remove context from JukeboxMediaPlayer and Scrobbler --- .../ultrasonic/service/JukeboxMediaPlayer.java | 17 +++++------------ .../org/moire/ultrasonic/service/Scrobbler.java | 4 +--- .../moire/ultrasonic/di/MediaPlayerModule.kt | 2 +- .../ultrasonic/service/MediaPlayerService.kt | 4 ++-- 4 files changed, 9 insertions(+), 18 deletions(-) diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/JukeboxMediaPlayer.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/JukeboxMediaPlayer.java index 26ecdc30..f7c24cd8 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/JukeboxMediaPlayer.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/JukeboxMediaPlayer.java @@ -30,6 +30,7 @@ import android.widget.Toast; import org.jetbrains.annotations.NotNull; import org.moire.ultrasonic.R; import org.moire.ultrasonic.api.subsonic.ApiNotSupportedException; +import org.moire.ultrasonic.app.UApp; import org.moire.ultrasonic.data.ActiveServerProvider; import org.moire.ultrasonic.domain.JukeboxStatus; import org.moire.ultrasonic.domain.PlayerState; @@ -70,7 +71,6 @@ public class JukeboxMediaPlayer private final AtomicBoolean running = new AtomicBoolean(); private Thread serviceThread; private boolean enabled = false; - private final Context context; // TODO: These create circular references, try to refactor private final Lazy mediaPlayerControllerLazy = inject(MediaPlayerController.class); @@ -82,9 +82,8 @@ public class JukeboxMediaPlayer // TODO: Persist RC state? // TODO: Minimize status updates. - public JukeboxMediaPlayer(Context context, Downloader downloader) + public JukeboxMediaPlayer(Downloader downloader) { - this.context = context; this.downloader = downloader; } @@ -217,15 +216,8 @@ public class JukeboxMediaPlayer private void disableJukeboxOnError(Throwable x, final int resourceId) { Timber.w(x.toString()); - - new Handler().post(new Runnable() - { - @Override - public void run() - { - Util.toast(context, resourceId, false); - } - }); + Context context = UApp.Companion.applicationContext(); + new Handler().post(() -> Util.toast(context, resourceId, false)); mediaPlayerControllerLazy.getValue().setJukeboxEnabled(false); } @@ -293,6 +285,7 @@ public class JukeboxMediaPlayer tasks.remove(SetGain.class); tasks.add(new SetGain(gain)); + Context context = UApp.Companion.applicationContext(); if (volumeToast == null) volumeToast = new VolumeToast(context); volumeToast.setVolume(gain); diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/Scrobbler.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/Scrobbler.java index 60a5bbe4..83627a08 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/Scrobbler.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/Scrobbler.java @@ -1,8 +1,6 @@ package org.moire.ultrasonic.service; -import android.content.Context; import timber.log.Timber; - import org.moire.ultrasonic.data.ActiveServerProvider; /** @@ -16,7 +14,7 @@ public class Scrobbler private String lastSubmission; private String lastNowPlaying; - public void scrobble(final Context context, final DownloadFile song, final boolean submission) + public void scrobble(final DownloadFile song, final boolean submission) { if (song == null || !ActiveServerProvider.Companion.isScrobblingEnabled()) return; diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt index 54cd70ee..5c16e916 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt @@ -16,7 +16,7 @@ import org.moire.ultrasonic.util.ShufflePlayBuffer * This Koin module contains the registration of classes related to the media player */ val mediaPlayerModule = module { - single { JukeboxMediaPlayer(androidContext(), get()) } + single { JukeboxMediaPlayer(get()) } single { MediaPlayerLifecycleSupport(get(), get(), get()) } single { DownloadQueueSerializer(androidContext()) } single { ExternalStorageMonitor() } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt index ea0b9c25..63533b10 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt @@ -409,9 +409,9 @@ class MediaPlayerService : Service() { } if (playerState === PlayerState.STARTED) { - scrobbler.scrobble(context, currentPlaying, false) + scrobbler.scrobble(currentPlaying, false) } else if (playerState === PlayerState.COMPLETED) { - scrobbler.scrobble(context, currentPlaying, true) + scrobbler.scrobble(currentPlaying, true) } null From b8fbbd8c498649ad8e015d3cf26bb6f8594b325c Mon Sep 17 00:00:00 2001 From: tzugen Date: Fri, 21 May 2021 23:03:42 +0200 Subject: [PATCH 11/17] Remove Context from Downloader & DownloadFile --- detekt-baseline.xml | 2 +- .../ultrasonic/fragment/SettingsFragment.java | 2 +- .../moire/ultrasonic/service/Downloader.java | 24 ++++++------- .../ultrasonic/util/LegacyImageLoader.java | 2 +- .../ultrasonic/util/ShufflePlayBuffer.java | 2 +- .../java/org/moire/ultrasonic/util/Util.java | 36 +++++++++++-------- .../moire/ultrasonic/di/MediaPlayerModule.kt | 2 +- .../service/CommunicationErrorHandler.kt | 2 +- .../moire/ultrasonic/service/DownloadFile.kt | 15 ++++---- .../ultrasonic/service/LocalMediaPlayer.kt | 2 +- .../ultrasonic/service/MediaPlayerService.kt | 3 +- .../subsonic/NetworkAndStorageChecker.kt | 2 +- .../moire/ultrasonic/subsonic/ShareHandler.kt | 2 +- .../moire/ultrasonic/subsonic/VideoPlayer.kt | 2 +- 14 files changed, 51 insertions(+), 47 deletions(-) diff --git a/detekt-baseline.xml b/detekt-baseline.xml index 8c0868eb..cecd8495 100644 --- a/detekt-baseline.xml +++ b/detekt-baseline.xml @@ -25,7 +25,7 @@ ImplicitDefaultLocale:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$String.format("BufferTask (%s)", downloadFile) ImplicitDefaultLocale:LocalMediaPlayer.kt$LocalMediaPlayer.CheckCompletionTask$String.format("CheckCompletionTask (%s)", downloadFile) ImplicitDefaultLocale:ShareHandler.kt$ShareHandler$String.format("%d:%s", timeSpanAmount, timeSpanType) - ImplicitDefaultLocale:ShareHandler.kt$ShareHandler.<no name provided>$String.format("%s\n\n%s", Util.getShareGreeting(context), result.url) + ImplicitDefaultLocale:ShareHandler.kt$ShareHandler.<no name provided>$String.format("%s\n\n%s", Util.getShareGreeting(), result.url) ImplicitDefaultLocale:SongView.kt$SongView$String.format("%02d.", trackNumber) ImplicitDefaultLocale:SongView.kt$SongView$String.format("%s ", bitRate) ImplicitDefaultLocale:SongView.kt$SongView$String.format("%s > %s", suffix, transcodedSuffix) diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SettingsFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SettingsFragment.java index 240749ed..fcc81219 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SettingsFragment.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SettingsFragment.java @@ -139,7 +139,7 @@ public class SettingsFragment extends PreferenceFragmentCompat showArtistPicture = findPreference(Constants.PREFERENCES_KEY_SHOW_ARTIST_PICTURE); setupServersCategory(); - sharingDefaultGreeting.setText(Util.getShareGreeting(getActivity())); + sharingDefaultGreeting.setText(Util.getShareGreeting()); setupClearSearchPreference(); setupGaplessControlSettingsV14(); setupFeatureFlagsPreferences(); diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.java index 7874803e..1926e8ca 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.java @@ -35,7 +35,6 @@ public class Downloader private final ShufflePlayBuffer shufflePlayBuffer; private final ExternalStorageMonitor externalStorageMonitor; private final LocalMediaPlayer localMediaPlayer; - private final Context context; // TODO: This is a circular reference, try to remove private Lazy jukeboxMediaPlayer = inject(JukeboxMediaPlayer.class); @@ -45,10 +44,9 @@ public class Downloader private ScheduledExecutorService executorService; private long revision; - public Downloader(Context context, ShufflePlayBuffer shufflePlayBuffer, ExternalStorageMonitor externalStorageMonitor, + public Downloader(ShufflePlayBuffer shufflePlayBuffer, ExternalStorageMonitor externalStorageMonitor, LocalMediaPlayer localMediaPlayer) { - this.context = context; this.shufflePlayBuffer = shufflePlayBuffer; this.externalStorageMonitor = externalStorageMonitor; this.localMediaPlayer = localMediaPlayer; @@ -100,10 +98,10 @@ public class Downloader if (shufflePlayBuffer.isEnabled) { - checkShufflePlay(context); + checkShufflePlay(); } - if (jukeboxMediaPlayer.getValue().isEnabled() || !Util.isNetworkConnected(context)) + if (jukeboxMediaPlayer.getValue().isEnabled() || !Util.isNetworkConnected()) { return; } @@ -188,7 +186,7 @@ public class Downloader DownloadFile downloadFile = backgroundDownloadList.get(i); if (downloadFile.isWorkDone() && (!downloadFile.shouldSave() || downloadFile.isSaved())) { - Util.scanMedia(context, downloadFile.getCompleteFile()); + Util.scanMedia(downloadFile.getCompleteFile()); // Don't need to keep list like active song list backgroundDownloadList.remove(i); @@ -316,7 +314,7 @@ public class Downloader for (MusicDirectory.Entry song : songs) { - DownloadFile downloadFile = new DownloadFile(context, song, save); + DownloadFile downloadFile = new DownloadFile(song, save); downloadList.add(getCurrentPlayingIndex() + offset, downloadFile); offset++; } @@ -325,7 +323,7 @@ public class Downloader { for (MusicDirectory.Entry song : songs) { - DownloadFile downloadFile = new DownloadFile(context, song, save); + DownloadFile downloadFile = new DownloadFile(song, save); downloadList.add(downloadFile); } } @@ -336,7 +334,7 @@ public class Downloader { for (MusicDirectory.Entry song : songs) { - DownloadFile downloadFile = new DownloadFile(context, song, save); + DownloadFile downloadFile = new DownloadFile(song, save); backgroundDownloadList.add(downloadFile); } @@ -376,7 +374,7 @@ public class Downloader DownloadFile downloadFile = downloadFileCache.get(song); if (downloadFile == null) { - downloadFile = new DownloadFile(context, song, false); + downloadFile = new DownloadFile(song, false); downloadFileCache.put(song, downloadFile); } return downloadFile; @@ -398,7 +396,7 @@ public class Downloader } } - private synchronized void checkShufflePlay(Context context) + private synchronized void checkShufflePlay() { // Get users desired random playlist size int listSize = Util.getMaxSongs(); @@ -412,7 +410,7 @@ public class Downloader { for (MusicDirectory.Entry song : shufflePlayBuffer.get(listSize - size)) { - DownloadFile downloadFile = new DownloadFile(context, song, false); + DownloadFile downloadFile = new DownloadFile(song, false); downloadList.add(downloadFile); revision++; } @@ -426,7 +424,7 @@ public class Downloader int songsToShift = currIndex - 2; for (MusicDirectory.Entry song : shufflePlayBuffer.get(songsToShift)) { - downloadList.add(new DownloadFile(context, song, false)); + downloadList.add(new DownloadFile(song, false)); downloadList.get(0).cancelDownload(); downloadList.remove(0); revision++; diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/LegacyImageLoader.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/LegacyImageLoader.java index 28e203fa..47654e98 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/LegacyImageLoader.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/LegacyImageLoader.java @@ -79,7 +79,7 @@ public class LegacyImageLoader implements Runnable, ImageLoader { imageSizeDefault = drawable.getIntrinsicHeight(); } - imageSizeLarge = Util.getMaxDisplayMetric(context); + imageSizeLarge = Util.getMaxDisplayMetric(); createLargeUnknownImage(context); createUnknownAvatarImage(context); } diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/ShufflePlayBuffer.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/ShufflePlayBuffer.java index e13f1ae2..203e6d98 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/ShufflePlayBuffer.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/ShufflePlayBuffer.java @@ -97,7 +97,7 @@ public class ShufflePlayBuffer // Check if active server has changed. clearBufferIfNecessary(); - if (buffer.size() > REFILL_THRESHOLD || (!Util.isNetworkConnected(context) && !ActiveServerProvider.Companion.isOffline())) + if (buffer.size() > REFILL_THRESHOLD || (!Util.isNetworkConnected() && !ActiveServerProvider.Companion.isOffline())) { return; } diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/Util.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/Util.java index dc4ef45e..f89ed103 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/Util.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/Util.java @@ -167,10 +167,14 @@ public class Util } } - public static int getMaxBitRate(Context context) - { - ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + public static ConnectivityManager getConnectivityManager() { + Context context = appContext(); + return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + } + public static int getMaxBitRate() + { + ConnectivityManager manager = getConnectivityManager(); NetworkInfo networkInfo = manager.getActiveNetworkInfo(); if (networkInfo == null) @@ -548,9 +552,9 @@ public class Util return null; } - public static boolean isNetworkConnected(Context context) + public static boolean isNetworkConnected() { - ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + ConnectivityManager manager = getConnectivityManager(); NetworkInfo networkInfo = manager.getActiveNetworkInfo(); boolean connected = networkInfo != null && networkInfo.isConnected(); @@ -648,9 +652,9 @@ public class Util return bitmap; } - public static WifiManager.WifiLock createWifiLock(Context context, String tag) + public static WifiManager.WifiLock createWifiLock(String tag) { - WifiManager wm = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE); + WifiManager wm = (WifiManager) appContext().getApplicationContext().getSystemService(Context.WIFI_SERVICE); return wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, tag); } @@ -945,15 +949,15 @@ public class Util return size; } - public static int getMinDisplayMetric(Context context) + public static int getMinDisplayMetric() { - DisplayMetrics metrics = context.getResources().getDisplayMetrics(); + DisplayMetrics metrics = appContext().getResources().getDisplayMetrics(); return Math.min(metrics.widthPixels, metrics.heightPixels); } - public static int getMaxDisplayMetric(Context context) + public static int getMaxDisplayMetric() { - DisplayMetrics metrics = context.getResources().getDisplayMetrics(); + DisplayMetrics metrics = appContext().getResources().getDisplayMetrics(); return Math.max(metrics.widthPixels, metrics.heightPixels); } @@ -1251,10 +1255,12 @@ public class Util return preferences.getString(Constants.PREFERENCES_KEY_DEFAULT_SHARE_DESCRIPTION, ""); } - public static String getShareGreeting(Context context) + public static String getShareGreeting() { SharedPreferences preferences = getPreferences(); - return preferences.getString(Constants.PREFERENCES_KEY_DEFAULT_SHARE_GREETING, String.format(context.getResources().getString(R.string.share_default_greeting), context.getResources().getString(R.string.common_appname))); + Context context = appContext(); + String defaultVal = String.format(context.getResources().getString(R.string.share_default_greeting), context.getResources().getString(R.string.common_appname)); + return preferences.getString(Constants.PREFERENCES_KEY_DEFAULT_SHARE_GREETING, defaultVal); } public static String getDefaultShareExpiration() @@ -1313,11 +1319,11 @@ public class Util return preferences.getBoolean(Constants.PREFERENCES_KEY_SHOW_ALL_SONGS_BY_ARTIST, false); } - public static void scanMedia(Context context, File file) + public static void scanMedia(File file) { Uri uri = Uri.fromFile(file); Intent scanFileIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri); - context.sendBroadcast(scanFileIntent); + appContext().sendBroadcast(scanFileIntent); } public static int getImageLoaderConcurrency() diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt index 5c16e916..bd887e48 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt @@ -21,7 +21,7 @@ val mediaPlayerModule = module { single { DownloadQueueSerializer(androidContext()) } single { ExternalStorageMonitor() } single { ShufflePlayBuffer(androidContext()) } - single { Downloader(androidContext(), get(), get(), get()) } + single { Downloader(get(), get(), get()) } single { LocalMediaPlayer(get(), androidContext()) } single { AudioFocusHandler(get()) } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/CommunicationErrorHandler.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/CommunicationErrorHandler.kt index 39a72dcf..588ac266 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/CommunicationErrorHandler.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/CommunicationErrorHandler.kt @@ -51,7 +51,7 @@ class CommunicationErrorHandler { } fun getErrorMessage(error: Throwable, context: Context): String { - if (error is IOException && !Util.isNetworkConnected(context)) { + if (error is IOException && !Util.isNetworkConnected()) { return context.resources.getString(R.string.background_task_no_network) } else if (error is FileNotFoundException) { return context.resources.getString(R.string.background_task_not_found) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadFile.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadFile.kt index 6e773940..6c75e62d 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadFile.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadFile.kt @@ -21,6 +21,7 @@ import java.io.OutputStream import java.io.RandomAccessFile import org.koin.core.component.KoinApiExtension import org.koin.java.KoinJavaComponent.inject +import org.moire.ultrasonic.app.UApp import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService import org.moire.ultrasonic.util.CacheCleaner @@ -37,7 +38,6 @@ import timber.log.Timber */ @KoinApiExtension class DownloadFile( - private val context: Context, val song: MusicDirectory.Entry, private val save: Boolean ) { @@ -48,7 +48,7 @@ class DownloadFile( var isFailed = false private var retryCount = MAX_RETRIES - private val desiredBitRate: Int = Util.getMaxBitRate(context) + private val desiredBitRate: Int = Util.getMaxBitRate() @Volatile private var isPlaying = false @@ -138,7 +138,7 @@ class DownloadFile( Util.delete(completeFile) Util.delete(saveFile) - Util.scanMedia(context, saveFile) + Util.scanMedia(saveFile) } fun unpin() { @@ -186,7 +186,7 @@ class DownloadFile( } else if (completeWhenDone) { if (save) { Util.renameFile(partialFile, saveFile) - Util.scanMedia(context, saveFile) + Util.scanMedia(saveFile) } else { Util.renameFile(partialFile, completeFile) } @@ -211,7 +211,7 @@ class DownloadFile( var wifiLock: WifiLock? = null try { wakeLock = acquireWakeLock(wakeLock) - wifiLock = Util.createWifiLock(context, toString()) + wifiLock = Util.createWifiLock(toString()) wifiLock.acquire() if (saveFile.exists()) { @@ -285,7 +285,7 @@ class DownloadFile( } else { if (save) { Util.renameFile(partialFile, saveFile) - Util.scanMedia(context, saveFile) + Util.scanMedia(saveFile) } else { Util.renameFile(partialFile, completeFile) } @@ -317,6 +317,7 @@ class DownloadFile( private fun acquireWakeLock(wakeLock: WakeLock?): WakeLock? { var wakeLock1 = wakeLock if (Util.isScreenLitOnDownload()) { + val context = UApp.applicationContext() val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager val flags = PowerManager.SCREEN_DIM_WAKE_LOCK or PowerManager.ON_AFTER_RELEASE wakeLock1 = pm.newWakeLock(flags, toString()) @@ -333,7 +334,7 @@ class DownloadFile( private fun downloadAndSaveCoverArt(musicService: MusicService) { try { if (!TextUtils.isEmpty(song.coverArt)) { - val size = Util.getMinDisplayMetric(context) + val size = Util.getMinDisplayMetric() musicService.getCoverArt(song, size, true, true) } } catch (e: Exception) { diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/LocalMediaPlayer.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/LocalMediaPlayer.kt index 66c94cfb..0b48f666 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/LocalMediaPlayer.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/LocalMediaPlayer.kt @@ -397,7 +397,7 @@ class LocalMediaPlayer( secondaryProgress = (percent.toDouble() / 100.toDouble() * progressBar.max).toInt() - if (song.transcodedContentType == null && Util.getMaxBitRate(context) == 0) { + if (song.transcodedContentType == null && Util.getMaxBitRate() == 0) { progressBar?.secondaryProgress = secondaryProgress } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt index 63533b10..de935eef 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt @@ -478,12 +478,11 @@ class MediaPlayerService : Service() { // Set Metadata val metadata = MediaMetadataCompat.Builder() - val context = applicationContext if (currentPlaying != null) { try { val song = currentPlaying.song val cover = FileUtil.getAlbumArtBitmap( - song, Util.getMinDisplayMetric(context), + song, Util.getMinDisplayMetric(), true ) metadata.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, -1L) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/NetworkAndStorageChecker.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/NetworkAndStorageChecker.kt index aa508821..1bab7f17 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/NetworkAndStorageChecker.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/NetworkAndStorageChecker.kt @@ -12,7 +12,7 @@ class NetworkAndStorageChecker(val context: Context) { fun warnIfNetworkOrStorageUnavailable() { if (!Util.isExternalStoragePresent()) { Util.toast(context, R.string.select_album_no_sdcard) - } else if (!isOffline() && !Util.isNetworkConnected(context)) { + } else if (!isOffline() && !Util.isNetworkConnected()) { Util.toast(context, R.string.select_album_no_network) } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ShareHandler.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ShareHandler.kt index 99551418..e52e98d9 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ShareHandler.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ShareHandler.kt @@ -91,7 +91,7 @@ class ShareHandler(val context: Context) { intent.type = "text/plain" intent.putExtra( Intent.EXTRA_TEXT, - String.format("%s\n\n%s", Util.getShareGreeting(context), result.url) + String.format("%s\n\n%s", Util.getShareGreeting(), result.url) ) fragment.activity?.startActivity( Intent.createChooser( diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/VideoPlayer.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/VideoPlayer.kt index f8afd6a9..fb03a627 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/VideoPlayer.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/VideoPlayer.kt @@ -10,7 +10,7 @@ import org.moire.ultrasonic.util.Util */ class VideoPlayer() { fun playVideo(context: Context, entry: MusicDirectory.Entry?) { - if (!Util.isNetworkConnected(context)) { + if (!Util.isNetworkConnected()) { Util.toast(context, R.string.select_album_no_network) return } From ed6c6c90571581fb83b96cad1154567b8a839392 Mon Sep 17 00:00:00 2001 From: tzugen Date: Fri, 21 May 2021 23:04:35 +0200 Subject: [PATCH 12/17] Remove two warnings in Downloader --- .../moire/ultrasonic/service/Downloader.java | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.java index 1926e8ca..b47929d9 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.java @@ -1,6 +1,5 @@ package org.moire.ultrasonic.service; -import android.content.Context; import timber.log.Timber; import org.moire.ultrasonic.domain.MusicDirectory; @@ -37,7 +36,7 @@ public class Downloader private final LocalMediaPlayer localMediaPlayer; // TODO: This is a circular reference, try to remove - private Lazy jukeboxMediaPlayer = inject(JukeboxMediaPlayer.class); + private final Lazy jukeboxMediaPlayer = inject(JukeboxMediaPlayer.class); private final List cleanupCandidates = new ArrayList<>(); private final LRUCache downloadFileCache = new LRUCache<>(100); @@ -54,19 +53,14 @@ public class Downloader public void onCreate() { - Runnable downloadChecker = new Runnable() - { - @Override - public void run() + Runnable downloadChecker = () -> { + try { - try - { - checkDownloads(); - } - catch (Throwable x) - { - Timber.e(x,"checkDownloads() failed."); - } + checkDownloads(); + } + catch (Throwable x) + { + Timber.e(x,"checkDownloads() failed."); } }; From 3d99269c414afe18a904076405852b055b9a5f1d Mon Sep 17 00:00:00 2001 From: tzugen Date: Fri, 21 May 2021 23:08:28 +0200 Subject: [PATCH 13/17] Remove context from ShufflePlayBuffer --- .../ultrasonic/util/ShufflePlayBuffer.java | 18 ++++-------------- .../moire/ultrasonic/di/MediaPlayerModule.kt | 2 +- .../ultrasonic/service/LocalMediaPlayer.kt | 2 ++ 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/ShufflePlayBuffer.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/ShufflePlayBuffer.java index 203e6d98..d918a4e0 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/ShufflePlayBuffer.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/ShufflePlayBuffer.java @@ -18,9 +18,6 @@ */ package org.moire.ultrasonic.util; -import android.content.Context; -import timber.log.Timber; - import org.moire.ultrasonic.data.ActiveServerProvider; import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.service.MusicService; @@ -32,6 +29,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import timber.log.Timber; + /** * @author Sindre Mehus * @version $Id$ @@ -42,28 +41,19 @@ public class ShufflePlayBuffer private static final int REFILL_THRESHOLD = 40; private final List buffer = new ArrayList<>(); - private final Context context; private ScheduledExecutorService executorService; private int currentServer; public boolean isEnabled = false; - public ShufflePlayBuffer(Context context) + public ShufflePlayBuffer() { - this.context = context; } public void onCreate() { executorService = Executors.newSingleThreadScheduledExecutor(); - Runnable runnable = new Runnable() - { - @Override - public void run() - { - refill(); - } - }; + Runnable runnable = this::refill; executorService.scheduleWithFixedDelay(runnable, 1, 10, TimeUnit.SECONDS); Timber.i("ShufflePlayBuffer created"); } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt index bd887e48..6f7a751d 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt @@ -20,7 +20,7 @@ val mediaPlayerModule = module { single { MediaPlayerLifecycleSupport(get(), get(), get()) } single { DownloadQueueSerializer(androidContext()) } single { ExternalStorageMonitor() } - single { ShufflePlayBuffer(androidContext()) } + single { ShufflePlayBuffer() } single { Downloader(get(), get(), get()) } single { LocalMediaPlayer(get(), androidContext()) } single { AudioFocusHandler(get()) } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/LocalMediaPlayer.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/LocalMediaPlayer.kt index 0b48f666..11f22a44 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/LocalMediaPlayer.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/LocalMediaPlayer.kt @@ -20,6 +20,7 @@ import android.os.Looper import android.os.PowerManager import android.os.PowerManager.PARTIAL_WAKE_LOCK import android.os.PowerManager.WakeLock +import org.koin.core.component.KoinApiExtension import java.io.File import java.net.URLEncoder import java.util.Locale @@ -39,6 +40,7 @@ import timber.log.Timber /** * Represents a Media Player which uses the mobile's resources for playback */ +@KoinApiExtension class LocalMediaPlayer( private val audioFocusHandler: AudioFocusHandler, private val context: Context From 005c1cb8f74c5708437f268f624d50421f1f59ba Mon Sep 17 00:00:00 2001 From: tzugen Date: Fri, 21 May 2021 23:25:44 +0200 Subject: [PATCH 14/17] Fix an String resource int that wasn't converted to a String --- .../java/org/moire/ultrasonic/fragment/MainFragment.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/MainFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/MainFragment.java index 144d012f..91631658 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/MainFragment.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/MainFragment.java @@ -235,10 +235,11 @@ public class MainFragment extends Fragment { Navigation.findNavController(getView()).navigate(R.id.mainToArtistList, bundle); } - private void showAlbumList(final String type, final int title) { + private void showAlbumList(final String type, final int titleIndex) { Bundle bundle = new Bundle(); + String title = getContext().getResources().getString(titleIndex, ""); bundle.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, type); - bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TITLE, title); + bundle.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TITLE, title); bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, Util.getMaxAlbums()); bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0); Navigation.findNavController(getView()).navigate(R.id.mainToAlbumList, bundle); From 0500bd5e88f2cc5b04a184d851598e881d65e9a0 Mon Sep 17 00:00:00 2001 From: tzugen Date: Fri, 21 May 2021 23:26:13 +0200 Subject: [PATCH 15/17] Handle also UnknownHostException in GenericListModel --- .../moire/ultrasonic/fragment/GenericListModel.kt | 13 ++++++++++--- .../moire/ultrasonic/service/LocalMediaPlayer.kt | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/GenericListModel.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/GenericListModel.kt index 1fe45f3f..810e90f5 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/GenericListModel.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/GenericListModel.kt @@ -11,6 +11,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import java.net.ConnectException +import java.net.UnknownHostException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -91,12 +92,18 @@ open class GenericListModel(application: Application) : try { load(isOffline, useId3Tags, musicService, refresh, bundle) } catch (exception: ConnectException) { - Handler(Looper.getMainLooper()).post { - CommunicationErrorHandler.handleError(exception, swipe.context) - } + handleException(exception, swipe.context) + } catch (exception: UnknownHostException) { + handleException(exception, swipe.context) } } + private fun handleException(exception: Exception, context: Context) { + Handler(Looper.getMainLooper()).post { + CommunicationErrorHandler.handleError(exception, context) + } + } + /** * This is the central function you need to implement if you want to extend this class */ diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/LocalMediaPlayer.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/LocalMediaPlayer.kt index 11f22a44..26b96e36 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/LocalMediaPlayer.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/LocalMediaPlayer.kt @@ -20,12 +20,12 @@ import android.os.Looper import android.os.PowerManager import android.os.PowerManager.PARTIAL_WAKE_LOCK import android.os.PowerManager.WakeLock -import org.koin.core.component.KoinApiExtension import java.io.File import java.net.URLEncoder import java.util.Locale import kotlin.math.abs import kotlin.math.max +import org.koin.core.component.KoinApiExtension import org.moire.ultrasonic.audiofx.EqualizerController import org.moire.ultrasonic.audiofx.VisualizerController import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline From 79fd1ac84f7f30387402e160d902e7695ce4bd66 Mon Sep 17 00:00:00 2001 From: tzugen Date: Fri, 21 May 2021 23:33:52 +0200 Subject: [PATCH 16/17] Remove resolved issues from baselines --- detekt-baseline.xml | 5 --- ultrasonic/lint-baseline.xml | 80 +++++++++--------------------------- 2 files changed, 20 insertions(+), 65 deletions(-) diff --git a/detekt-baseline.xml b/detekt-baseline.xml index cecd8495..6423c4f4 100644 --- a/detekt-baseline.xml +++ b/detekt-baseline.xml @@ -46,20 +46,15 @@ LongParameterList:ServerRowAdapter.kt$ServerRowAdapter$( private var context: Context, private var data: Array<ServerSetting>, private val model: ServerSettingsModel, private val activeServerProvider: ActiveServerProvider, private val manageMode: Boolean, private val serverDeletedCallback: ((Int) -> Unit), private val serverEditRequestedCallback: ((Int) -> Unit) ) MagicNumber:ActiveServerProvider.kt$ActiveServerProvider$8192 MagicNumber:DownloadFile.kt$DownloadFile.DownloadTask$10 - MagicNumber:DownloadFile.kt$DownloadFile.DownloadTask$1000L MagicNumber:DownloadFile.kt$DownloadFile.DownloadTask$60 - MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer$1000 - MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.<no name provided>$1000 MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.<no name provided>$60000 MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$100000 - MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$1000L MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$1024L MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$8 MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$86400L MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$8L MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.CheckCompletionTask$5000L MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.PositionCache$50L - MagicNumber:MediaPlayerService.kt$MediaPlayerService$1000 MagicNumber:MediaPlayerService.kt$MediaPlayerService$256 MagicNumber:MediaPlayerService.kt$MediaPlayerService$3 MagicNumber:MediaPlayerService.kt$MediaPlayerService$4 diff --git a/ultrasonic/lint-baseline.xml b/ultrasonic/lint-baseline.xml index c80cca17..c34a884d 100644 --- a/ultrasonic/lint-baseline.xml +++ b/ultrasonic/lint-baseline.xml @@ -26,45 +26,45 @@ + errorLine1=" String lhs = lhsArtist.getName().toLowerCase();" + errorLine2=" ~~~~~~~~~~~"> + line="97" + column="37"/> + errorLine1=" String rhs = rhsArtist.getName().toLowerCase();" + errorLine2=" ~~~~~~~~~~~"> + line="98" + column="37"/> + errorLine1=" int index = lhs.indexOf(String.format("%s ", article.toLowerCase()));" + errorLine2=" ~~~~~~~~~~~"> + line="115" + column="58"/> + errorLine1=" index = rhs.indexOf(String.format("%s ", article.toLowerCase()));" + errorLine2=" ~~~~~~~~~~~"> + line="122" + column="54"/> @@ -85,7 +85,7 @@ errorLine2=" ~~~~~~~~~~~"> @@ -238,7 +238,7 @@ errorLine2=" ~~~~"> @@ -491,7 +491,7 @@ errorLine2=" ~~~~~~~~~~~~"> @@ -579,39 +579,6 @@ file="src/main/res/drawable-xhdpi-v14"/> - - - - - - - - - - - - - - - - Date: Sat, 22 May 2021 11:01:12 +0200 Subject: [PATCH 17/17] Hide cluttering koin warning, which was added in 2.2.0 and will be removed again in 2.3.0 See https://github.com/InsertKoinIO/koin/issues/939 for more details --- ultrasonic/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/ultrasonic/build.gradle b/ultrasonic/build.gradle index 01f5c64f..f8530d95 100644 --- a/ultrasonic/build.gradle +++ b/ultrasonic/build.gradle @@ -56,6 +56,7 @@ android { kotlinOptions { jvmTarget = "1.8" + freeCompilerArgs += "-Xopt-in=org.koin.core.component.KoinApiExtension" } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8