Skip to content

Commit 2f9043f

Browse files
committed
[feature|optimize|fix] Support picture-in-picture mode in the player; optimize the code of file picker and player; fix an issue that cannot play another new media while playing a media
1 parent e068d03 commit 2f9043f

File tree

20 files changed

+460
-80
lines changed

20 files changed

+460
-80
lines changed

app/build.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ android {
2222
minSdk = 24
2323
targetSdk = 34
2424
versionCode = 18
25-
versionName = "1.1-beta55"
25+
versionName = "1.1-beta56"
2626

2727
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2828

@@ -159,6 +159,7 @@ dependencies {
159159

160160
implementation("androidx.core:core-ktx:1.13.1")
161161
implementation("androidx.appcompat:appcompat:1.7.0")
162+
implementation("androidx.activity:activity-ktx:1.9.0")
162163
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
163164
implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha13")
164165
implementation("androidx.navigation:navigation-fragment-ktx:2.7.7")

app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,13 @@
6767

6868
<activity
6969
android:name=".ui.activity.PlayActivity"
70+
android:autoRemoveFromRecents="true"
7071
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize"
7172
android:exported="true"
7273
android:label="@string/play_activity_label"
7374
android:launchMode="singleTask"
7475
android:screenOrientation="userLandscape"
76+
android:supportsPictureInPicture="true"
7577
android:theme="@style/Theme.AniVu.Player">
7678
<intent-filter>
7779
<action android:name="android.intent.action.VIEW" />

app/src/main/java/com/skyd/anivu/ext/PreferenceExt.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleFrequenc
3232
import com.skyd.anivu.model.preference.data.autodelete.UseAutoDeletePreference
3333
import com.skyd.anivu.model.preference.data.medialib.MediaLibLocationPreference
3434
import com.skyd.anivu.model.preference.player.HardwareDecodePreference
35+
import com.skyd.anivu.model.preference.player.PlayerAutoPipPreference
3536
import com.skyd.anivu.model.preference.player.PlayerDoubleTapPreference
3637
import com.skyd.anivu.model.preference.player.PlayerShow85sButtonPreference
3738
import com.skyd.anivu.model.preference.player.PlayerShowScreenshotButtonPreference
@@ -74,6 +75,7 @@ fun Preferences.toSettings(): Settings {
7475
playerShow85sButton = PlayerShow85sButtonPreference.fromPreferences(this),
7576
playerShowScreenshotButton = PlayerShowScreenshotButtonPreference.fromPreferences(this),
7677
hardwareDecode = HardwareDecodePreference.fromPreferences(this),
78+
playerAutoPip = PlayerAutoPipPreference.fromPreferences(this),
7779

7880
// Data
7981
useAutoDelete = UseAutoDeletePreference.fromPreferences(this),

app/src/main/java/com/skyd/anivu/model/preference/Settings.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import com.skyd.anivu.model.preference.data.autodelete.AutoDeleteArticleFrequenc
3737
import com.skyd.anivu.model.preference.data.autodelete.UseAutoDeletePreference
3838
import com.skyd.anivu.model.preference.data.medialib.MediaLibLocationPreference
3939
import com.skyd.anivu.model.preference.player.HardwareDecodePreference
40+
import com.skyd.anivu.model.preference.player.PlayerAutoPipPreference
4041
import com.skyd.anivu.model.preference.player.PlayerDoubleTapPreference
4142
import com.skyd.anivu.model.preference.player.PlayerShow85sButtonPreference
4243
import com.skyd.anivu.model.preference.player.PlayerShowScreenshotButtonPreference
@@ -63,6 +64,7 @@ import com.skyd.anivu.ui.local.LocalMediaLibLocation
6364
import com.skyd.anivu.ui.local.LocalNavigationBarLabel
6465
import com.skyd.anivu.ui.local.LocalOpmlExportDir
6566
import com.skyd.anivu.ui.local.LocalPickImageMethod
67+
import com.skyd.anivu.ui.local.LocalPlayerAutoPip
6668
import com.skyd.anivu.ui.local.LocalPlayerDoubleTap
6769
import com.skyd.anivu.ui.local.LocalPlayerShow85sButton
6870
import com.skyd.anivu.ui.local.LocalPlayerShowScreenshotButton
@@ -111,6 +113,7 @@ data class Settings(
111113
val playerShow85sButton: Boolean = PlayerShow85sButtonPreference.default,
112114
val playerShowScreenshotButton: Boolean = PlayerShowScreenshotButtonPreference.default,
113115
val hardwareDecode: Boolean = HardwareDecodePreference.default,
116+
val playerAutoPip: Boolean = PlayerAutoPipPreference.default,
114117
// Data
115118
val useAutoDelete: Boolean = UseAutoDeletePreference.default,
116119
val autoDeleteArticleFrequency: Long = AutoDeleteArticleFrequencyPreference.default,
@@ -162,6 +165,7 @@ fun SettingsProvider(
162165
LocalPlayerShow85sButton provides settings.playerShow85sButton,
163166
LocalPlayerShowScreenshotButton provides settings.playerShowScreenshotButton,
164167
LocalHardwareDecode provides settings.hardwareDecode,
168+
LocalPlayerAutoPip provides settings.playerAutoPip,
165169
// Data
166170
LocalUseAutoDelete provides settings.useAutoDelete,
167171
LocalAutoDeleteArticleFrequency provides settings.autoDeleteArticleFrequency,

app/src/main/java/com/skyd/anivu/model/preference/data/FilePickerLocationPreference.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package com.skyd.anivu.model.preference.data
22

33
import android.content.Context
4-
import android.os.Environment
54
import androidx.datastore.preferences.core.Preferences
65
import androidx.datastore.preferences.core.stringPreferencesKey
76
import com.skyd.anivu.base.BasePreference
7+
import com.skyd.anivu.config.Const
88
import com.skyd.anivu.ext.dataStore
99
import com.skyd.anivu.ext.put
1010
import kotlinx.coroutines.CoroutineScope
@@ -14,7 +14,7 @@ import kotlinx.coroutines.launch
1414
object FilePickerLocationPreference : BasePreference<String> {
1515
private const val FILE_PICKER_LOCATION = "filePickerLocation"
1616

17-
override val default: String = Environment.getExternalStorageDirectory().absolutePath
17+
override val default: String = Const.INTERNAL_STORAGE
1818

1919
val key = stringPreferencesKey(FILE_PICKER_LOCATION)
2020

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.skyd.anivu.model.preference.player
2+
3+
import android.content.Context
4+
import androidx.datastore.preferences.core.Preferences
5+
import androidx.datastore.preferences.core.booleanPreferencesKey
6+
import com.skyd.anivu.base.BasePreference
7+
import com.skyd.anivu.ext.dataStore
8+
import com.skyd.anivu.ext.put
9+
import kotlinx.coroutines.CoroutineScope
10+
import kotlinx.coroutines.Dispatchers
11+
import kotlinx.coroutines.launch
12+
13+
object PlayerAutoPipPreference : BasePreference<Boolean> {
14+
private const val PLAYER_AUTO_PIP = "playerAutoPip"
15+
override val default = false
16+
17+
val key = booleanPreferencesKey(PLAYER_AUTO_PIP)
18+
19+
fun put(context: Context, scope: CoroutineScope, value: Boolean) {
20+
scope.launch(Dispatchers.IO) {
21+
context.dataStore.put(key, value)
22+
}
23+
}
24+
25+
override fun fromPreferences(preferences: Preferences): Boolean = preferences[key] ?: default
26+
}

app/src/main/java/com/skyd/anivu/ui/activity/PlayActivity.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import androidx.activity.result.contract.ActivityResultContracts
1111
import androidx.compose.runtime.DisposableEffect
1212
import androidx.compose.runtime.getValue
1313
import androidx.compose.runtime.mutableStateOf
14-
import androidx.compose.runtime.remember
14+
import androidx.compose.runtime.saveable.rememberSaveable
1515
import androidx.compose.runtime.setValue
1616
import androidx.core.content.IntentCompat
1717
import androidx.core.util.Consumer
@@ -58,7 +58,7 @@ class PlayActivity : BaseComposeActivity() {
5858
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
5959

6060
setContentBase {
61-
var videoUri by remember { mutableStateOf(handleIntent(intent)) }
61+
var videoUri by rememberSaveable { mutableStateOf(handleIntent(intent)) }
6262

6363
DisposableEffect(Unit) {
6464
val listener = Consumer<Intent> { newIntent ->

app/src/main/java/com/skyd/anivu/ui/fragment/filepicker/FilePickerFragment.kt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import androidx.compose.ui.res.stringResource
4646
import androidx.compose.ui.unit.dp
4747
import androidx.hilt.navigation.compose.hiltViewModel
4848
import androidx.lifecycle.compose.collectAsStateWithLifecycle
49+
import androidx.navigation.NavController
4950
import com.skyd.anivu.R
5051
import com.skyd.anivu.base.BaseComposeFragment
5152
import com.skyd.anivu.base.mvi.getDispatcher
@@ -54,12 +55,17 @@ import com.skyd.anivu.ext.findMainNavController
5455
import com.skyd.anivu.ext.getMimeType
5556
import com.skyd.anivu.ext.popBackStackWithLifecycle
5657
import com.skyd.anivu.ext.showSnackbarWithLaunchedEffect
58+
import com.skyd.anivu.model.preference.data.medialib.MediaLibLocationPreference
5759
import com.skyd.anivu.ui.component.AniVuIconButton
5860
import com.skyd.anivu.ui.component.AniVuTopBar
5961
import com.skyd.anivu.ui.component.AniVuTopBarStyle
6062
import com.skyd.anivu.ui.local.LocalNavController
6163
import com.skyd.anivu.util.fileicon.getFileIcon
6264
import dagger.hilt.android.AndroidEntryPoint
65+
import kotlinx.coroutines.CoroutineScope
66+
import kotlinx.coroutines.flow.collect
67+
import kotlinx.coroutines.flow.filterNotNull
68+
import kotlinx.coroutines.flow.onEach
6369
import java.io.File
6470

6571

@@ -90,6 +96,40 @@ const val PICK_FOLDER_KEY = "pickFolder"
9096
const val EXTENSION_NAME_KEY = "extensionName"
9197
const val FILE_PICKER_NEW_PATH_KEY = "newPath"
9298

99+
@Composable
100+
fun ListenToFilePicker(onNewPath: CoroutineScope.(String) -> Unit) {
101+
val navController = LocalNavController.current
102+
LaunchedEffect(Unit) {
103+
navController.currentBackStackEntry?.savedStateHandle
104+
?.getStateFlow<String?>(FILE_PICKER_NEW_PATH_KEY, null)
105+
?.filterNotNull()
106+
?.onEach { this.onNewPath(it) }
107+
?.collect()
108+
}
109+
}
110+
111+
fun navigateToFilePicker(
112+
navController: NavController,
113+
path: String,
114+
pickFolder: Boolean = true,
115+
extensionName: String = "",
116+
) {
117+
navController.navigate(
118+
R.id.action_to_file_picker_fragment,
119+
Bundle().apply {
120+
putBoolean(PICK_FOLDER_KEY, pickFolder)
121+
putString(
122+
PATH_KEY,
123+
if (path == MediaLibLocationPreference.default) {
124+
Const.INTERNAL_STORAGE
125+
} else path
126+
)
127+
putString(EXTENSION_NAME_KEY, extensionName)
128+
}
129+
)
130+
}
131+
132+
93133
@Composable
94134
fun FilePickerScreen(
95135
path: String,

app/src/main/java/com/skyd/anivu/ui/fragment/media/MediaScreen.kt

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import androidx.compose.foundation.pager.rememberPagerState
1616
import androidx.compose.material.icons.Icons
1717
import androidx.compose.material.icons.outlined.Download
1818
import androidx.compose.material.icons.outlined.Edit
19+
import androidx.compose.material.icons.outlined.MyLocation
1920
import androidx.compose.material.icons.outlined.Refresh
2021
import androidx.compose.material.icons.outlined.Workspaces
2122
import androidx.compose.material3.HorizontalDivider
@@ -38,6 +39,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
3839
import androidx.compose.runtime.setValue
3940
import androidx.compose.ui.Modifier
4041
import androidx.compose.ui.input.nestedscroll.nestedScroll
42+
import androidx.compose.ui.platform.LocalContext
4143
import androidx.compose.ui.res.stringResource
4244
import androidx.compose.ui.unit.dp
4345
import androidx.hilt.navigation.compose.hiltViewModel
@@ -47,12 +49,15 @@ import com.skyd.anivu.base.mvi.getDispatcher
4749
import com.skyd.anivu.ext.isCompact
4850
import com.skyd.anivu.ext.showSnackbarWithLaunchedEffect
4951
import com.skyd.anivu.model.bean.MediaGroupBean
52+
import com.skyd.anivu.model.preference.data.medialib.MediaLibLocationPreference
5053
import com.skyd.anivu.ui.component.AniVuFloatingActionButton
5154
import com.skyd.anivu.ui.component.AniVuIconButton
5255
import com.skyd.anivu.ui.component.AniVuTopBar
5356
import com.skyd.anivu.ui.component.AniVuTopBarStyle
5457
import com.skyd.anivu.ui.component.dialog.TextFieldDialog
5558
import com.skyd.anivu.ui.component.dialog.WaitingDialog
59+
import com.skyd.anivu.ui.fragment.filepicker.ListenToFilePicker
60+
import com.skyd.anivu.ui.fragment.filepicker.navigateToFilePicker
5661
import com.skyd.anivu.ui.fragment.media.list.GroupInfo
5762
import com.skyd.anivu.ui.fragment.media.list.MediaList
5863
import com.skyd.anivu.ui.local.LocalNavController
@@ -67,6 +72,7 @@ fun MediaScreen(path: String, viewModel: MediaViewModel = hiltViewModel()) {
6772
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
6873
val snackbarHostState = remember { SnackbarHostState() }
6974
val navController = LocalNavController.current
75+
val context = LocalContext.current
7076
val windowSizeClass = LocalWindowSizeClass.current
7177
val scope = rememberCoroutineScope()
7278

@@ -80,13 +86,28 @@ fun MediaScreen(path: String, viewModel: MediaViewModel = hiltViewModel()) {
8086
val pagerState = rememberPagerState(pageCount = { uiState.groups.size })
8187
var openEditGroupDialog by rememberSaveable { mutableStateOf<MediaGroupBean?>(value = null) }
8288

89+
ListenToFilePicker { newPath ->
90+
MediaLibLocationPreference.put(context, this, newPath)
91+
}
92+
8393
Scaffold(
8494
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
8595
topBar = {
8696
AniVuTopBar(
8797
style = AniVuTopBarStyle.CenterAligned,
8898
title = { Text(text = stringResource(R.string.media_screen_name)) },
89-
navigationIcon = { },
99+
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
100+
navigationIconContentColor = TopAppBarDefaults.centerAlignedTopAppBarColors().actionIconContentColor
101+
),
102+
navigationIcon = {
103+
AniVuIconButton(
104+
onClick = {
105+
navigateToFilePicker(navController = navController, path = path)
106+
},
107+
imageVector = Icons.Outlined.MyLocation,
108+
contentDescription = stringResource(id = R.string.data_screen_media_lib_location),
109+
)
110+
},
90111
scrollBehavior = scrollBehavior,
91112
windowInsets = WindowInsets.safeDrawing.run {
92113
var sides = WindowInsetsSides.Top + WindowInsetsSides.Right
@@ -97,6 +118,7 @@ fun MediaScreen(path: String, viewModel: MediaViewModel = hiltViewModel()) {
97118
AniVuIconButton(
98119
onClick = { dispatch(MediaIntent.Refresh(path)) },
99120
imageVector = Icons.Outlined.Refresh,
121+
contentDescription = stringResource(id = R.string.refresh),
100122
)
101123
AniVuIconButton(
102124
onClick = { navController.navigate(R.id.action_to_download_fragment) },

app/src/main/java/com/skyd/anivu/ui/fragment/settings/data/DataFragment.kt

Lines changed: 7 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.skyd.anivu.ui.fragment.settings.data
22

33
import android.os.Bundle
4-
import android.os.Environment
54
import android.view.LayoutInflater
65
import android.view.View
76
import android.view.ViewGroup
@@ -24,7 +23,6 @@ import androidx.compose.material3.TextButton
2423
import androidx.compose.material3.TopAppBarDefaults
2524
import androidx.compose.material3.rememberDatePickerState
2625
import androidx.compose.runtime.Composable
27-
import androidx.compose.runtime.LaunchedEffect
2826
import androidx.compose.runtime.derivedStateOf
2927
import androidx.compose.runtime.getValue
3028
import androidx.compose.runtime.mutableStateOf
@@ -51,16 +49,11 @@ import com.skyd.anivu.ui.component.BaseSettingsItem
5149
import com.skyd.anivu.ui.component.CategorySettingsItem
5250
import com.skyd.anivu.ui.component.dialog.DeleteWarningDialog
5351
import com.skyd.anivu.ui.component.dialog.WaitingDialog
54-
import com.skyd.anivu.ui.fragment.filepicker.EXTENSION_NAME_KEY
55-
import com.skyd.anivu.ui.fragment.filepicker.FILE_PICKER_NEW_PATH_KEY
56-
import com.skyd.anivu.ui.fragment.filepicker.PATH_KEY
57-
import com.skyd.anivu.ui.fragment.filepicker.PICK_FOLDER_KEY
52+
import com.skyd.anivu.ui.fragment.filepicker.ListenToFilePicker
53+
import com.skyd.anivu.ui.fragment.filepicker.navigateToFilePicker
5854
import com.skyd.anivu.ui.local.LocalMediaLibLocation
5955
import com.skyd.anivu.ui.local.LocalNavController
6056
import dagger.hilt.android.AndroidEntryPoint
61-
import kotlinx.coroutines.flow.collect
62-
import kotlinx.coroutines.flow.filterNotNull
63-
import kotlinx.coroutines.flow.onEach
6457

6558

6659
@AndroidEntryPoint
@@ -84,17 +77,8 @@ fun DataScreen(viewModel: DataViewModel = hiltViewModel()) {
8477
val uiEvent by viewModel.singleEvent.collectAsStateWithLifecycle(initialValue = null)
8578
val dispatch = viewModel.getDispatcher(startWith = DataIntent.Init)
8679

87-
LaunchedEffect(Unit) {
88-
navController.currentBackStackEntry?.savedStateHandle
89-
?.getStateFlow<String?>(FILE_PICKER_NEW_PATH_KEY, null)
90-
?.filterNotNull()
91-
?.onEach { newPath ->
92-
MediaLibLocationPreference.put(
93-
context,
94-
this,
95-
newPath,
96-
)
97-
}?.collect()
80+
ListenToFilePicker { newPath ->
81+
MediaLibLocationPreference.put(context, this, newPath)
9882
}
9983

10084
Scaffold(
@@ -128,18 +112,9 @@ fun DataScreen(viewModel: DataViewModel = hiltViewModel()) {
128112
text = stringResource(id = R.string.data_screen_media_lib_location),
129113
descriptionText = localMediaLibLocation,
130114
onClick = {
131-
navController.navigate(
132-
R.id.action_to_file_picker_fragment,
133-
Bundle().apply {
134-
putBoolean(PICK_FOLDER_KEY, true)
135-
putString(
136-
PATH_KEY,
137-
if (localMediaLibLocation == MediaLibLocationPreference.default) {
138-
Environment.getExternalStorageDirectory().absolutePath
139-
} else localMediaLibLocation
140-
)
141-
putString(EXTENSION_NAME_KEY, "")
142-
}
115+
navigateToFilePicker(
116+
navController = navController,
117+
path = localMediaLibLocation,
143118
)
144119
}
145120
) {

0 commit comments

Comments
 (0)