Refactor music widget

This commit is contained in:
MM20 2021-12-19 18:29:52 +01:00
parent 71652ffc71
commit 8be19a1309
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
8 changed files with 88 additions and 95 deletions

View File

@ -74,7 +74,7 @@ fun buildTime(): String {
}
fun versionCodeDate(): Int {
val df = SimpleDateFormat("yyyyMMdd00")
val df = SimpleDateFormat("yyyyMMdd01")
return df.format(Date()).toInt()
}

View File

@ -6,5 +6,4 @@ import org.koin.dsl.module
val musicModule = module {
single { MusicRepository(androidContext()) }
viewModel { MusicViewModel(get()) }
}

View File

@ -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<PlaybackState>()
val title = MutableLiveData<String?>()
val artist = MutableLiveData<String?>()
val album = MutableLiveData<String?>()
val albumArt = MutableLiveData<Bitmap?>()
val playbackState = MutableStateFlow(PlaybackState.Stopped)
val title = MutableStateFlow<String?>(null)
val artist = MutableStateFlow<String?>(null)
val album = MutableStateFlow<String?>(null)
val albumArt = MutableStateFlow<Bitmap?>(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
}

View File

@ -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<String?> = musicRepository.title
val artist: LiveData<String?> = musicRepository.artist
val album: LiveData<String?> = musicRepository.album
val albumArt: LiveData<Bitmap?> = musicRepository.albumArt
val playbackState: LiveData<PlaybackState> = 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)
}
}

View File

@ -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<List<File>>()
val files: LiveData<List<File>> = _files
}

View File

@ -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(

View File

@ -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<String?> = musicRepository.title.asLiveData()
val artist: LiveData<String?> = musicRepository.artist.asLiveData()
val album: LiveData<String?> = musicRepository.album.asLiveData()
val albumArt: LiveData<Bitmap?> = musicRepository.albumArt.asLiveData()
val playbackState: LiveData<PlaybackState> = 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)
}
}

View File

@ -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)