From 8be19a1309f009a260f967f07fc99486385667c5 Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Sun, 19 Dec 2021 18:29:52 +0100 Subject: [PATCH] Refactor music widget --- app/build.gradle.kts | 2 +- .../java/de/mm20/launcher2/music/Module.kt | 1 - .../mm20/launcher2/music/MusicRepository.kt | 31 ++++++------ .../de/mm20/launcher2/music/MusicViewModel.kt | 48 ------------------- .../ui/launcher/search/SearchViewModel.kt | 17 +++++++ .../widgets/music}/MusicWidget.kt | 21 ++++---- .../launcher/widgets/music/MusicWidgetVM.kt | 47 ++++++++++++++++++ .../launcher2/ui/legacy/widget/MusicWidget.kt | 16 ------- 8 files changed, 88 insertions(+), 95 deletions(-) delete mode 100644 music/src/main/java/de/mm20/launcher2/music/MusicViewModel.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchViewModel.kt rename ui/src/main/java/de/mm20/launcher2/ui/{widget => launcher/widgets/music}/MusicWidget.kt (89%) create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/music/MusicWidgetVM.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index db9721d1..144b3d26 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -74,7 +74,7 @@ fun buildTime(): String { } fun versionCodeDate(): Int { - val df = SimpleDateFormat("yyyyMMdd00") + val df = SimpleDateFormat("yyyyMMdd01") return df.format(Date()).toInt() } diff --git a/music/src/main/java/de/mm20/launcher2/music/Module.kt b/music/src/main/java/de/mm20/launcher2/music/Module.kt index 99180f40..5bf012ec 100644 --- a/music/src/main/java/de/mm20/launcher2/music/Module.kt +++ b/music/src/main/java/de/mm20/launcher2/music/Module.kt @@ -6,5 +6,4 @@ import org.koin.dsl.module val musicModule = module { single { MusicRepository(androidContext()) } - viewModel { MusicViewModel(get()) } } \ No newline at end of file diff --git a/music/src/main/java/de/mm20/launcher2/music/MusicRepository.kt b/music/src/main/java/de/mm20/launcher2/music/MusicRepository.kt index 0f7beacf..55006085 100644 --- a/music/src/main/java/de/mm20/launcher2/music/MusicRepository.kt +++ b/music/src/main/java/de/mm20/launcher2/music/MusicRepository.kt @@ -6,33 +6,30 @@ import android.content.Intent import android.graphics.Bitmap import android.graphics.BitmapFactory import android.media.AudioManager -import android.net.Uri import android.support.v4.media.session.MediaSessionCompat import android.util.Log import android.view.KeyEvent import androidx.core.content.edit import androidx.core.graphics.scale -import androidx.lifecycle.MutableLiveData import androidx.media2.common.MediaItem import androidx.media2.common.MediaMetadata import androidx.media2.common.SessionPlayer import androidx.media2.session.MediaController import androidx.media2.session.SessionCommandGroup -import de.mm20.launcher2.ktx.asBitmap import kotlinx.coroutines.* +import kotlinx.coroutines.flow.MutableStateFlow import java.io.File -import java.io.FileNotFoundException import java.util.concurrent.Executors class MusicRepository(val context: Context) { private val scope = CoroutineScope(Job() + Dispatchers.Main) - val playbackState = MutableLiveData() - val title = MutableLiveData() - val artist = MutableLiveData() - val album = MutableLiveData() - val albumArt = MutableLiveData() + val playbackState = MutableStateFlow(PlaybackState.Stopped) + val title = MutableStateFlow(null) + val artist = MutableStateFlow(null) + val album = MutableStateFlow(null) + val albumArt = MutableStateFlow(null) private var lastPlayer: String? = null set(value) { @@ -57,7 +54,7 @@ class MusicRepository(val context: Context) { private var mediaController: MediaController? = null set(value) { if (value == null) { - playbackState.postValue(PlaybackState.Stopped) + playbackState.value = PlaybackState.Stopped } field = value } @@ -120,9 +117,9 @@ class MusicRepository(val context: Context) { val album = metadata.getString(MediaMetadata.METADATA_KEY_ALBUM) lastPlayer = mediaController?.connectedToken?.packageName ?: lastPlayer - this@MusicRepository.title.postValue(title) - this@MusicRepository.artist.postValue(artist) - this@MusicRepository.album.postValue(album) + this@MusicRepository.title.value = title + this@MusicRepository.artist.value = artist + this@MusicRepository.album.value = album scope.launch { withContext(Dispatchers.IO) { @@ -131,7 +128,7 @@ class MusicRepository(val context: Context) { putString(PREFS_KEY_ALBUM_ART, if (albumArt == null) "null" else "notnull") } if (albumArt == null) { - this@MusicRepository.albumArt.postValue(null) + this@MusicRepository.albumArt.value = null return@withContext } val size = context.resources.getDimensionPixelSize(R.dimen.album_art_size) @@ -140,7 +137,7 @@ class MusicRepository(val context: Context) { val outStream = file.outputStream() scaledBitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream) outStream.close() - this@MusicRepository.albumArt.postValue(scaledBitmap) + this@MusicRepository.albumArt.value = scaledBitmap } } @@ -157,7 +154,7 @@ class MusicRepository(val context: Context) { SessionPlayer.PLAYER_STATE_PAUSED -> PlaybackState.Paused else -> PlaybackState.Stopped } - this@MusicRepository.playbackState.postValue(playbackState) + this.playbackState.value = playbackState } val hasActiveSession: Boolean = mediaController?.isConnected != null @@ -228,7 +225,7 @@ class MusicRepository(val context: Context) { if (playbackState.value != PlaybackState.Playing) play() else pause() } - fun getLaunchIntent(context: Context): PendingIntent { + fun getLaunchIntent(): PendingIntent { mediaController?.sessionActivity?.let { return it } diff --git a/music/src/main/java/de/mm20/launcher2/music/MusicViewModel.kt b/music/src/main/java/de/mm20/launcher2/music/MusicViewModel.kt deleted file mode 100644 index 3285c1d9..00000000 --- a/music/src/main/java/de/mm20/launcher2/music/MusicViewModel.kt +++ /dev/null @@ -1,48 +0,0 @@ -package de.mm20.launcher2.music - -import android.app.PendingIntent -import android.content.Context -import android.graphics.Bitmap -import androidx.lifecycle.LiveData -import androidx.lifecycle.ViewModel - -class MusicViewModel( - val musicRepository: MusicRepository -) : ViewModel() { - - val title: LiveData = musicRepository.title - val artist: LiveData = musicRepository.artist - val album: LiveData = musicRepository.album - val albumArt: LiveData = musicRepository.albumArt - val playbackState: LiveData = musicRepository.playbackState - - val hasActiveSession : Boolean = musicRepository.hasActiveSession - - fun previous() { - musicRepository.previous() - } - - fun next() { - musicRepository.next() - } - - fun play() { - musicRepository.play() - } - - fun pause() { - musicRepository.pause() - } - - fun togglePause() { - musicRepository.togglePause() - } - - fun getLaunchIntent(context: Context): PendingIntent { - return musicRepository.getLaunchIntent(context) - } - - fun openPlayerChooser(context: Context) { - musicRepository.openPlayerChooser(context) - } -} \ No newline at end of file diff --git a/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchViewModel.kt b/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchViewModel.kt new file mode 100644 index 00000000..20db0b7d --- /dev/null +++ b/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchViewModel.kt @@ -0,0 +1,17 @@ +package de.mm20.launcher2.ui.launcher.search + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import de.mm20.launcher2.search.data.File +import org.koin.core.component.KoinComponent + +class SearchViewModel: ViewModel(), KoinComponent { + + fun search(query: String) { + + } + + private val _files = MutableLiveData>() + val files: LiveData> = _files +} \ No newline at end of file diff --git a/ui/src/main/java/de/mm20/launcher2/ui/widget/MusicWidget.kt b/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/music/MusicWidget.kt similarity index 89% rename from ui/src/main/java/de/mm20/launcher2/ui/widget/MusicWidget.kt rename to ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/music/MusicWidget.kt index ed48cfbb..d7b2a511 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/widget/MusicWidget.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/music/MusicWidget.kt @@ -21,26 +21,25 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import de.mm20.launcher2.music.MusicViewModel +import androidx.lifecycle.viewmodel.compose.viewModel import de.mm20.launcher2.music.PlaybackState import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.ktx.conditional -import org.koin.androidx.compose.getViewModel +import de.mm20.launcher2.ui.launcher.widgets.music.MusicWidgetVM @OptIn( - ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class, + ExperimentalFoundationApi::class, ExperimentalAnimationGraphicsApi::class ) @Composable fun MusicWidget() { - val viewModel: MusicViewModel = getViewModel() + val viewModel: MusicWidgetVM = viewModel() val albumArt by viewModel.albumArt.observeAsState() val title by viewModel.title.observeAsState() @@ -91,7 +90,7 @@ fun MusicWidget() { ) { IconButton( onClick = { - viewModel.previous() + viewModel.onPreviousClick() }) { Icon( imageVector = Icons.Rounded.SkipPrevious, @@ -99,14 +98,14 @@ fun MusicWidget() { ) } val playPauseIcon = AnimatedImageVector.animatedVectorResource(R.drawable.anim_ic_play_pause) - IconButton(onClick = { viewModel.togglePause() }) { + IconButton(onClick = { viewModel.onPlayClick() }) { Icon( painter = rememberAnimatedVectorPainter(playPauseIcon, atEnd = playbackState == PlaybackState.Playing), contentDescription = "" ) } IconButton(onClick = { - viewModel.next() + viewModel.onNextClick() }) { Icon( imageVector = Icons.Rounded.SkipNext, @@ -120,12 +119,10 @@ fun MusicWidget() { .size(144.dp) .combinedClickable( onClick = { - viewModel - .getLaunchIntent(context) - .send() + viewModel.onAlbumArtClick() }, onLongClick = { - viewModel.openPlayerChooser(context) + viewModel.onAlbumArtLongClick(context) } ) .conditional( diff --git a/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/music/MusicWidgetVM.kt b/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/music/MusicWidgetVM.kt new file mode 100644 index 00000000..565bac20 --- /dev/null +++ b/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/music/MusicWidgetVM.kt @@ -0,0 +1,47 @@ +package de.mm20.launcher2.ui.launcher.widgets.music + +import android.app.PendingIntent +import android.content.Context +import android.graphics.Bitmap +import androidx.lifecycle.LiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.asLiveData +import de.mm20.launcher2.crashreporter.CrashReporter +import de.mm20.launcher2.music.MusicRepository +import de.mm20.launcher2.music.PlaybackState +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +class MusicWidgetVM: ViewModel(), KoinComponent { + private val musicRepository: MusicRepository by inject() + + val title: LiveData = musicRepository.title.asLiveData() + val artist: LiveData = musicRepository.artist.asLiveData() + val album: LiveData = musicRepository.album.asLiveData() + val albumArt: LiveData = musicRepository.albumArt.asLiveData() + val playbackState: LiveData = musicRepository.playbackState.asLiveData() + + fun onPreviousClick() { + musicRepository.previous() + } + + fun onNextClick() { + musicRepository.next() + } + + fun onPlayClick() { + musicRepository.togglePause() + } + + fun onAlbumArtClick() { + try { + musicRepository.getLaunchIntent().send() + } catch (e: PendingIntent.CanceledException) { + CrashReporter.logException(e) + } + } + + fun onAlbumArtLongClick(context: Context) { + musicRepository.openPlayerChooser(context) + } +} \ No newline at end of file diff --git a/ui/src/main/java/de/mm20/launcher2/ui/legacy/widget/MusicWidget.kt b/ui/src/main/java/de/mm20/launcher2/ui/legacy/widget/MusicWidget.kt index d231e818..9ed562f1 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/legacy/widget/MusicWidget.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/legacy/widget/MusicWidget.kt @@ -1,30 +1,16 @@ package de.mm20.launcher2.ui.legacy.widget import android.content.Context -import android.content.res.ColorStateList -import android.graphics.Color -import android.graphics.drawable.AnimatedVectorDrawable import android.util.AttributeSet -import android.view.LayoutInflater import android.widget.FrameLayout -import androidx.appcompat.app.AppCompatActivity import androidx.compose.foundation.layout.Column import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.platform.ComposeView -import androidx.core.content.ContextCompat -import androidx.core.graphics.alpha -import androidx.lifecycle.Observer -import de.mm20.launcher2.ktx.dp -import de.mm20.launcher2.legacy.helper.ActivityStarter -import de.mm20.launcher2.music.MusicViewModel -import de.mm20.launcher2.music.PlaybackState import de.mm20.launcher2.ui.LegacyLauncherTheme import de.mm20.launcher2.ui.R -import de.mm20.launcher2.ui.databinding.CompactMusicBinding import de.mm20.launcher2.ui.widget.MusicWidget -import org.koin.androidx.viewmodel.ext.android.viewModel class MusicWidget : LauncherWidget { @@ -33,8 +19,6 @@ class MusicWidget : LauncherWidget { override val name: String get() = context.getString(R.string.widget_name_music) - private val viewModel: MusicViewModel by (context as AppCompatActivity).viewModel() - constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) constructor(context: Context, attrs: AttributeSet?, defStyleRes: Int) : super(context, attrs, defStyleRes)