mirror of
https://github.com/emptyynes/EmptyPlayer.git
synced 2025-10-29 21:58:56 +03:00
iu improvements
This commit is contained in:
parent
4057af1c55
commit
fdfb0aa679
13 changed files with 298 additions and 64 deletions
|
|
@ -1,6 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<manifest xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||
tools:ignore="ScopedStorage" />
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
|
|
@ -9,8 +11,7 @@
|
|||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Player"
|
||||
>
|
||||
android:theme="@style/Theme.Player">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
|
|
@ -21,6 +22,6 @@
|
|||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<service android:name=".PlayerService"/>
|
||||
<service android:name=".PlayerService" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
@ -1,29 +1,59 @@
|
|||
package usr.empty.player
|
||||
|
||||
import android.content.Intent
|
||||
import android.media.MediaMetadataRetriever
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import usr.empty.player.ui.theme.PlayerTheme
|
||||
|
||||
|
||||
inline fun <T> nullifyException(block: () -> T) = try {
|
||||
block()
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if (!Environment.isExternalStorageManager()) {
|
||||
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
|
||||
val uri = Uri.fromParts("package", packageName, null)
|
||||
intent.setData(uri)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
startService(Intent(this, PlayerService::class.java))
|
||||
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
PlayerTheme {
|
||||
|
|
@ -35,22 +65,74 @@ class MainActivity : ComponentActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
fun uriToPath(uri: Uri): String {
|
||||
val result = uri.pathSegments!![1].replace(Regex("^primary"), "emulated/0").replace(":", "/")
|
||||
return "/storage/" + result.replace("primary:", "/storage/emulated/0/")
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MainLayout(modifier: Modifier = Modifier) {
|
||||
val notas = remember { mutableStateListOf<NotaDescriptor>() }
|
||||
|
||||
val pickAudioLauncher = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.GetContent()
|
||||
) { audioUri ->
|
||||
audioUri?.run {
|
||||
path?.let {
|
||||
Log.d("meow", it)
|
||||
uriToPath(this).let {
|
||||
MediaMetadataRetriever().apply {
|
||||
setDataSource(it)
|
||||
notas.add(NotaDescriptor(name = nullifyException {
|
||||
extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE)
|
||||
} ?: it.split('/').last().split('.').first(), artist = nullifyException {
|
||||
extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST)
|
||||
} ?: "Unknown Artist", sourceType = NotaDescriptor.Source.LOCAL, source = it))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Column (modifier.fillMaxSize()) {
|
||||
Button(modifier = Modifier.align(Alignment.CenterHorizontally), onClick = {
|
||||
|
||||
Column(modifier.fillMaxSize()) {
|
||||
Button(shape = RectangleShape, colors = ButtonDefaults.buttonColors(
|
||||
containerColor = MaterialTheme.colorScheme.secondaryContainer, contentColor = MaterialTheme.colorScheme.secondary
|
||||
), modifier = Modifier.align(Alignment.CenterHorizontally), onClick = {
|
||||
pickAudioLauncher.launch("audio/*")
|
||||
}) {
|
||||
Text("add track")
|
||||
}
|
||||
NotaList(notas)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NotaList(notas: List<NotaDescriptor>, modifier: Modifier = Modifier) {
|
||||
LazyColumn(
|
||||
modifier = modifier,
|
||||
) {
|
||||
items(notas) { nota ->
|
||||
Column(modifier = Modifier
|
||||
.fillParentMaxWidth()
|
||||
.clickable { }
|
||||
.padding(4.dp)
|
||||
.padding(start = 16.dp)) {
|
||||
Text(
|
||||
nota.name, fontSize = 16.sp
|
||||
)
|
||||
Text(
|
||||
nota.artist, fontSize = 12.sp
|
||||
)
|
||||
}
|
||||
Log.d("nya", nota.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun Preview() {
|
||||
NotaList(
|
||||
listOf(
|
||||
NotaDescriptor(name = "hello", artist = "there", sourceType = NotaDescriptor.Source.LOCAL, source = ""),
|
||||
NotaDescriptor(name = "preview", artist = "me", sourceType = NotaDescriptor.Source.LOCAL, source = ""),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package usr.empty.player
|
||||
|
||||
interface Nota {
|
||||
fun play()
|
||||
fun pause()
|
||||
fun stop()
|
||||
fun seek()
|
||||
}
|
||||
16
app/src/main/java/usr/empty/player/NotaDescriptor.kt
Normal file
16
app/src/main/java/usr/empty/player/NotaDescriptor.kt
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package usr.empty.player
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
||||
@Serializable
|
||||
data class NotaDescriptor(
|
||||
val name: String,
|
||||
val artist: String,
|
||||
val sourceType: Source,
|
||||
val source: String,
|
||||
) {
|
||||
enum class Source {
|
||||
LOCAL
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package usr.empty.player
|
||||
|
||||
|
||||
class NotaPlayer {
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package usr.empty.player
|
||||
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.exoplayer.ExoPlayer
|
||||
|
|
@ -8,19 +9,31 @@ import androidx.media3.session.MediaSessionService
|
|||
import com.google.common.util.concurrent.Futures
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
|
||||
|
||||
class PlayerService : MediaSessionService() {
|
||||
private var mediaSession: MediaSession? = null
|
||||
var player: ExoPlayer? = null
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
player = ExoPlayer.Builder(this).build()
|
||||
mediaSession = MediaSession.Builder(this, player!!)
|
||||
.setCallback(MediaSessionCallback())
|
||||
.build()
|
||||
with(ExoPlayer.Builder(this).build()) {
|
||||
player = this
|
||||
mediaSession = MediaSession.Builder(this@PlayerService, this).setCallback(MediaSessionCallback()).build()
|
||||
}
|
||||
Log.d("meow", "memememe")
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
intent?.run {
|
||||
|
||||
}
|
||||
return super.onStartCommand(intent, flags, startId)
|
||||
}
|
||||
|
||||
private fun start() {
|
||||
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
player?.release()
|
||||
mediaSession?.release()
|
||||
|
|
@ -32,9 +45,7 @@ class PlayerService : MediaSessionService() {
|
|||
|
||||
private inner class MediaSessionCallback : MediaSession.Callback {
|
||||
override fun onAddMediaItems(
|
||||
mediaSession: MediaSession,
|
||||
controller: MediaSession.ControllerInfo,
|
||||
mediaItems: MutableList<MediaItem>
|
||||
mediaSession: MediaSession, controller: MediaSession.ControllerInfo, mediaItems: MutableList<MediaItem>
|
||||
): ListenableFuture<MutableList<MediaItem>> {
|
||||
val updatedMediaItems = mediaItems.map {
|
||||
it.buildUpon().setUri(it.mediaId).build()
|
||||
|
|
|
|||
|
|
@ -7,49 +7,31 @@ import androidx.compose.material3.lightColorScheme
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
background = Color.Black,
|
||||
primary = Purple80,
|
||||
secondary = PurpleGrey80,
|
||||
tertiary = Pink80
|
||||
)
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
background = Color.White,
|
||||
primary = Purple40,
|
||||
secondary = PurpleGrey40,
|
||||
tertiary = Pink40
|
||||
|
||||
/* Other default colors to override
|
||||
background = Color(0xFFFFFBFE),
|
||||
surface = Color(0xFFFFFBFE),
|
||||
onPrimary = Color.White,
|
||||
onSecondary = Color.White,
|
||||
onTertiary = Color.White,
|
||||
onBackground = Color(0xFF1C1B1F),
|
||||
onSurface = Color(0xFF1C1B1F),
|
||||
*/
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun PlayerTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
// dynamicColor: Boolean = true,
|
||||
content: @Composable () -> Unit
|
||||
darkTheme: Boolean = isSystemInDarkTheme(), // Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = true, content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
// dynamicColor -> {
|
||||
// val context = LocalContext.current
|
||||
// if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
// }
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
dynamicColor -> {
|
||||
if (darkTheme) darkColorScheme(
|
||||
background = Color.Black
|
||||
) else lightColorScheme(
|
||||
background = Color.White
|
||||
)
|
||||
}
|
||||
|
||||
darkTheme -> darkColorScheme(
|
||||
background = Color.Black
|
||||
)
|
||||
|
||||
else -> lightColorScheme(
|
||||
background = Color.White
|
||||
)
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
content = content
|
||||
colorScheme = colorScheme, typography = Typography, content = content
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue