Refactor music widget
This commit is contained in:
parent
71652ffc71
commit
8be19a1309
@ -74,7 +74,7 @@ fun buildTime(): String {
|
||||
}
|
||||
|
||||
fun versionCodeDate(): Int {
|
||||
val df = SimpleDateFormat("yyyyMMdd00")
|
||||
val df = SimpleDateFormat("yyyyMMdd01")
|
||||
return df.format(Date()).toInt()
|
||||
}
|
||||
|
||||
|
||||
@ -6,5 +6,4 @@ import org.koin.dsl.module
|
||||
|
||||
val musicModule = module {
|
||||
single { MusicRepository(androidContext()) }
|
||||
viewModel { MusicViewModel(get()) }
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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(
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user