Skip to content

Commit

Permalink
build: initial commit for shortcuts.
Browse files Browse the repository at this point in the history
  • Loading branch information
oxyroid committed Dec 30, 2023
1 parent 7846ab5 commit 014c9d4
Show file tree
Hide file tree
Showing 14 changed files with 77 additions and 34 deletions.
6 changes: 6 additions & 0 deletions core/src/main/java/com/m3u/core/Contracts.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.m3u.core

object Contracts {
const val PLAYER_ACTIVITY = "com.m3u.features.stream.PlayerActivity"
const val PLAYER_SHORTCUT_STREAM_URL = "shortcut:stream-url"
}
2 changes: 1 addition & 1 deletion data/src/main/java/com/m3u/data/database/dao/StreamDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ interface StreamDao {
suspend fun setFavourite(id: Int, target: Boolean)

@Query("UPDATE streams SET banned = :target WHERE id = :id")
suspend fun setBanned(id: Int, target: Boolean)
suspend fun ban(id: Int, target: Boolean)

@Query("UPDATE streams SET seen = :target WHERE id = :id")
suspend fun updateSeen(id: Int, target: Long)
Expand Down
9 changes: 0 additions & 9 deletions data/src/main/java/com/m3u/data/io/CrashFilePathCacher.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import android.content.Context
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import com.m3u.core.architecture.FilePath
import com.m3u.core.architecture.FilePathCacher
import com.m3u.core.util.collections.forEachNotNull
Expand Down Expand Up @@ -110,12 +109,4 @@ class CrashFilePathCacher @Inject constructor(
}
file.writeText(text)
}

private fun writeInfoToFile(text: String) {
val file = File(dir.path, "info.txt")
if (!file.exists()) {
file.createNewFile()
}
file.appendText("[${System.currentTimeMillis()}] $text")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface StreamRepository : ReadOnlyRepository<Stream, Int> {
suspend fun getByUrl(url: String): Stream?
suspend fun getByPlaylistUrl(playlistUrl: String): List<Stream>
suspend fun setFavourite(id: Int, target: Boolean)
suspend fun setBanned(id: Int, target: Boolean)
suspend fun updateSeen(id: Int)
suspend fun ban(id: Int, target: Boolean)
suspend fun reportPlayed(id: Int)
fun observeAllUnseenFavourites(limit: Duration): Flow<List<Stream>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import kotlin.time.Duration

class StreamRepositoryImpl @Inject constructor(
private val streamDao: StreamDao,
private val logger: Logger
private val logger: Logger,
) : StreamRepository {
override fun observe(id: Int): Flow<Stream?> = logger.execute {
streamDao.observeById(id)
Expand All @@ -40,11 +40,11 @@ class StreamRepositoryImpl @Inject constructor(
streamDao.setFavourite(id, target)
}

override suspend fun setBanned(id: Int, target: Boolean) = logger.sandBox {
streamDao.setBanned(id, target)
override suspend fun ban(id: Int, target: Boolean) = logger.sandBox {
streamDao.ban(id, target)
}

override suspend fun updateSeen(id: Int) = logger.sandBox {
override suspend fun reportPlayed(id: Int) = logger.sandBox {
val current = Clock.System.now().toEpochMilliseconds()
streamDao.updateSeen(id, current)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.m3u.features.playlist

import android.content.Context

sealed interface PlaylistEvent {
data class Observe(val playlistUrl: String) : PlaylistEvent
data object Refresh : PlaylistEvent
data class Favourite(val id: Int, val target: Boolean) : PlaylistEvent
data class Mute(val id: Int, val target: Boolean) : PlaylistEvent
data class Ban(val id: Int, val target: Boolean) : PlaylistEvent
data class SavePicture(val id: Int) : PlaylistEvent
data object ScrollUp : PlaylistEvent
data class Query(val text: String) : PlaylistEvent
data class CreateShortcut(val context: Context, val id: Int) : PlaylistEvent
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
Expand Down Expand Up @@ -107,6 +108,7 @@ internal fun PlaylistRoute(
modifier: Modifier = Modifier,
viewModel: PlaylistViewModel = hiltViewModel()
) {
val context = LocalContext.current
val helper = LocalHelper.current
val pref = LocalPref.current

Expand Down Expand Up @@ -187,13 +189,16 @@ internal fun PlaylistRoute(
status = dialogStatus,
onUpdate = { dialogStatus = it },
onFavorite = { id, target -> viewModel.onEvent(PlaylistEvent.Favourite(id, target)) },
onBanned = { id, target -> viewModel.onEvent(PlaylistEvent.Mute(id, target)) },
ban = { id, target -> viewModel.onEvent(PlaylistEvent.Ban(id, target)) },
onSavePicture = { id ->
if (writeExternalPermissionState.status is PermissionStatus.Denied) {
writeExternalPermissionState.launchPermissionRequest()
return@PlaylistDialog
}
viewModel.onEvent(PlaylistEvent.SavePicture(id))
},
createShortcut = { id ->
viewModel.onEvent(PlaylistEvent.CreateShortcut(context, id))
}
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
package com.m3u.features.playlist

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.IconCompat
import androidx.lifecycle.viewModelScope
import com.m3u.core.Contracts
import com.m3u.core.architecture.logger.Logger
import com.m3u.core.architecture.pref.Pref
import com.m3u.core.architecture.pref.observeAsFlow
Expand Down Expand Up @@ -47,9 +54,10 @@ class PlaylistViewModel @Inject constructor(
PlaylistEvent.Refresh -> refresh()
is PlaylistEvent.Favourite -> favourite(event)
PlaylistEvent.ScrollUp -> scrollUp()
is PlaylistEvent.Mute -> mute(event)
is PlaylistEvent.Ban -> ban(event)
is PlaylistEvent.SavePicture -> savePicture(event)
is PlaylistEvent.Query -> query(event)
is PlaylistEvent.CreateShortcut -> createShortcut(event.context, event.id)
}
}

Expand Down Expand Up @@ -158,19 +166,41 @@ class PlaylistViewModel @Inject constructor(
}
}

private fun mute(event: PlaylistEvent.Mute) {
private fun ban(event: PlaylistEvent.Ban) {
viewModelScope.launch {
val id = event.id
val target = event.target
val stream = streamRepository.get(id)
if (stream == null) {
onMessage(PlaylistMessage.StreamNotFound)
} else {
streamRepository.setBanned(stream.id, target)
streamRepository.ban(stream.id, target)
}
}
}

private fun createShortcut(context: Context, id: Int) {
val shortcutId = "stream_$id"
viewModelScope.launch {
val stream = streamRepository.get(id) ?: return@launch
val shortcutInfo = ShortcutInfoCompat.Builder(context, shortcutId)
.setShortLabel(stream.title)
.setLongLabel(stream.url)
.setIcon(IconCompat.createWithResource(context, android.R.drawable.ic_media_play))
.setIntent(
Intent(Intent.ACTION_VIEW).apply {
component = ComponentName.createRelative(
context,
Contracts.PLAYER_ACTIVITY
)
putExtra(Contracts.PLAYER_SHORTCUT_STREAM_URL, stream.url)
}
)
.build()
ShortcutManagerCompat.pushDynamicShortcut(context, shortcutInfo)
}
}

private val _query = MutableStateFlow("")
val query = _query.asStateFlow()
private fun query(event: PlaylistEvent.Query) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,14 @@ import com.m3u.material.components.DialogItem
import com.m3u.material.components.DialogTextField
import com.m3u.material.model.LocalSpacing

internal typealias OnUpdateDialogStatus = (DialogStatus) -> Unit
internal typealias OnFavoriteStream = (streamId: Int, target: Boolean) -> Unit
internal typealias OnBannedStream = (streamId: Int, target: Boolean) -> Unit
internal typealias OnSavePicture = (streamId: Int) -> Unit

@Composable
internal fun PlaylistDialog(
status: DialogStatus,
onUpdate: OnUpdateDialogStatus,
onFavorite: OnFavoriteStream,
onBanned: OnBannedStream,
onSavePicture: OnSavePicture,
onUpdate: (DialogStatus) -> Unit,
onFavorite: (streamId: Int, target: Boolean) -> Unit,
ban: (streamId: Int, target: Boolean) -> Unit,
onSavePicture: (streamId: Int) -> Unit,
createShortcut: (streamId: Int) -> Unit,
modifier: Modifier = Modifier
) {
AppDialog(
Expand All @@ -57,14 +53,18 @@ internal fun PlaylistDialog(
}
DialogItem(string.feat_playlist_dialog_mute_title) {
onUpdate(DialogStatus.Idle)
onBanned(status.stream.id, true)
ban(status.stream.id, true)
}
if (!status.stream.cover.isNullOrEmpty()) {
DialogItem(string.feat_playlist_dialog_save_picture_title) {
onUpdate(DialogStatus.Idle)
onSavePicture(status.stream.id)
}
}
DialogItem(string.feat_playlist_dialog_create_shortcut_title) {
onUpdate(DialogStatus.Idle)
createShortcut(status.stream.id)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class SettingViewModel @Inject constructor(
val banned = readable.banneds.find { it.id == streamId }
if (banned != null) {
viewModelScope.launch {
streamRepository.setBanned(streamId, false)
streamRepository.ban(streamId, false)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import androidx.compose.runtime.Composable
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.lifecycle.lifecycleScope
import com.m3u.core.Contracts
import com.m3u.core.architecture.logger.Logger
import com.m3u.core.architecture.pref.Pref
import com.m3u.core.unspecified.UBoolean
Expand Down Expand Up @@ -70,6 +71,7 @@ class PlayerActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
val shortcutStreamUrl = intent.getStringExtra(Contracts.PLAYER_SHORTCUT_STREAM_URL)
setContent {
M3ULocalProvider(
helper = helper,
Expand All @@ -80,6 +82,9 @@ class PlayerActivity : ComponentActivity() {
)
}
}
if (!shortcutStreamUrl.isNullOrEmpty()) {
helper.play(shortcutStreamUrl)
}
}

private fun helper(): Helper = object : Helper {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class StreamViewModel @Inject constructor(
.onEach { url ->
url?: return@onEach
val stream = streamRepository.getByUrl(url)?: return@onEach
streamRepository.updateSeen(stream.id)
streamRepository.reportPlayed(stream.id)
}
.launchIn(viewModelScope)
}
Expand Down
2 changes: 2 additions & 0 deletions i18n/src/main/res/values-zh-rCN/feat_playlist.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
<string name="feat_playlist_dialog_favourite_cancel_title">取消喜欢</string>
<string name="feat_playlist_dialog_mute_title">屏蔽</string>
<string name="feat_playlist_dialog_save_picture_title">保存封面</string>
<string name="feat_playlist_dialog_create_shortcut_title">创建快捷方式</string>

<string name="feat_playlist_query_placeholder">输入关键字</string>

<string name="feat_playlist_error_playlist_not_found">订阅不存在(%s)</string>
Expand Down
3 changes: 2 additions & 1 deletion i18n/src/main/res/values/feat_playlist.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
<string name="feat_playlist_dialog_favourite_title">like</string>
<string name="feat_playlist_dialog_favourite_cancel_title">cancel like</string>
<string name="feat_playlist_dialog_mute_title">ban</string>
<string name="feat_playlist_error_playlist_not_found">playlist is not existed (%s)</string>
<string name="feat_playlist_dialog_save_picture_title">save to gallery</string>
<string name="feat_playlist_dialog_create_shortcut_title">create shortcut</string>
<string name="feat_playlist_error_playlist_not_found">playlist is not existed (%s)</string>
<string name="feat_playlist_query_placeholder">enter key word</string>
<string name="feat_playlist_error_playlist_url_not_found">playlist url is not existed</string>
<string name="feat_playlist_error_stream_cover_not_found">cover is not existed</string>
Expand Down

0 comments on commit 014c9d4

Please sign in to comment.