diff --git a/app/build.gradle b/app/build.gradle
index 50de0c9..766d631 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,6 +1,7 @@
plugins {
id 'com.android.application'
id 'com.google.devtools.ksp'
+ id 'org.jetbrains.kotlin.plugin.compose'
}
apply plugin: 'kotlin-android'
@@ -37,6 +38,9 @@ android {
jvmTarget = '21'
}
namespace 'phone.vishnu.dialogmusicplayer'
+ buildFeatures {
+ compose true
+ }
}
dependencies {
@@ -51,6 +55,17 @@ dependencies {
implementation 'androidx.core:core-ktx:1.13.1'
implementation 'androidx.media:media:1.7.0'
+ implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.7'
+ implementation 'androidx.activity:activity-compose:1.9.3'
+ implementation platform('androidx.compose:compose-bom:2024.04.01')
+ implementation 'androidx.compose.ui:ui'
+ implementation 'androidx.compose.ui:ui-graphics'
+ implementation 'androidx.compose.ui:ui-tooling-preview'
+ implementation 'androidx.compose.material3:material3'
+ androidTestImplementation platform('androidx.compose:compose-bom:2024.04.01')
+ androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
+ debugImplementation 'androidx.compose.ui:ui-tooling'
+ debugImplementation 'androidx.compose.ui:ui-test-manifest'
def room_version = "2.6.1"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 75f4c34..a167f99 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,8 +1,9 @@
-
+
@@ -12,16 +13,16 @@
+ android:icon="@drawable/ic_icon"
+ android:label="@string/app_name"
+ android:requestLegacyExternalStorage="true"
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme">
+ android:name=".MainActivity"
+ android:exported="true"
+ android:launchMode="singleTask">
@@ -46,15 +47,27 @@
+
+
+
+
+
+
+
+
+
+ android:name=".MediaPlaybackService"
+ android:foregroundServiceType="mediaPlayback"
+ android:exported="false">
-
\ No newline at end of file
diff --git a/app/src/main/java/phone/vishnu/dialogmusicplayer/AudioUtils.java b/app/src/main/java/phone/vishnu/dialogmusicplayer/AudioUtils.java
index 12cdfda..07cd0d9 100644
--- a/app/src/main/java/phone/vishnu/dialogmusicplayer/AudioUtils.java
+++ b/app/src/main/java/phone/vishnu/dialogmusicplayer/AudioUtils.java
@@ -299,4 +299,18 @@ private static String getUriToDrawable(@NonNull Context context, @AnyRes int dra
+ '/'
+ context.getResources().getResourceEntryName(drawableId);
}
+
+ static String getFormattedTime(long millis, long totalDuration, boolean isTimeReversed) {
+
+ long minutes = (millis / 1000) / 60;
+ long seconds = (millis / 1000) % 60;
+
+ String secondsStr = Long.toString(seconds);
+
+ String secs = (secondsStr.length() >= 2) ? secondsStr.substring(0, 2) : "0" + secondsStr;
+
+ if (!isTimeReversed) return minutes + ":" + secs;
+
+ return "-" + getFormattedTime(totalDuration - millis, totalDuration, false);
+ }
}
diff --git a/app/src/main/java/phone/vishnu/dialogmusicplayer/ComposeActivity.kt b/app/src/main/java/phone/vishnu/dialogmusicplayer/ComposeActivity.kt
new file mode 100644
index 0000000..57688c1
--- /dev/null
+++ b/app/src/main/java/phone/vishnu/dialogmusicplayer/ComposeActivity.kt
@@ -0,0 +1,306 @@
+package phone.vishnu.dialogmusicplayer
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.media.MediaMetadata
+import android.net.Uri
+import android.os.Bundle
+import android.support.v4.media.MediaMetadataCompat
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Slider
+import androidx.compose.material3.SliderDefaults
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableLongStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.font.Font
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import phone.vishnu.dialogmusicplayer.ui.theme.DialogMusicPlayerTheme
+
+private val poppinsFont = FontFamily(Font(R.font.poppins))
+
+class ComposeActivity : ComponentActivity() {
+
+ @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ enableEdgeToEdge()
+
+ setContent {
+ DialogMusicPlayerTheme {
+ Scaffold(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(horizontal = 16.dp),
+ containerColor = Color.Transparent,
+ ) { _ ->
+
+ PlayerUI(
+ applicationContext,
+ Audio(
+ -1,
+ MediaMetadataCompat.Builder().putString(
+ MediaMetadata.METADATA_KEY_MEDIA_ID,
+ "-1",
+ ).putString(
+ MediaMetadata.METADATA_KEY_DISPLAY_TITLE,
+ "Dreaming On",
+ ).putString(
+ MediaMetadata.METADATA_KEY_TITLE,
+ "Dreaming On",
+ ).putString(
+ MediaMetadata.METADATA_KEY_ARTIST,
+ "NEFEX",
+ ).putLong(
+ MediaMetadata.METADATA_KEY_DURATION,
+ 100000,
+ ).putBitmap(
+ MediaMetadata.METADATA_KEY_ALBUM_ART,
+ null,
+ ).build(),
+ 100000,
+ Uri.EMPTY,
+ ),
+ )
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun PlayerUI(context: Context, audio: Audio) {
+ var sliderState by remember {
+ mutableFloatStateOf(
+ 0f,
+ )
+ }
+ var elapsed by remember {
+ mutableLongStateOf(
+ 0,
+ )
+ }
+ val totalDuration = audio.mediaMetadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION)
+
+ Column(
+ verticalArrangement = Arrangement.Bottom,
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Spacer(Modifier.weight(1f))
+
+ Image(
+ modifier = Modifier
+ .align(alignment = Alignment.CenterHorizontally)
+ .padding(12.dp)
+ .size(64.dp)
+ .aspectRatio(1f),
+ alignment = Alignment.Center,
+ painter = painterResource(R.drawable.ic_music_note),
+ contentDescription = "",
+ )
+
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .fillMaxHeight(0.35f)
+ .clip(RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp))
+ .background(MaterialTheme.colorScheme.background)
+ .padding(8.dp),
+ ) {
+ Text(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(16.dp),
+ text = context.getString(R.string.app_name),
+ textAlign = TextAlign.Center,
+ letterSpacing = 1.1.sp,
+ color = MaterialTheme.colorScheme.onBackground,
+ fontSize = 20.sp,
+ fontWeight = FontWeight.SemiBold,
+ )
+
+ Text(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(8.dp, 4.dp, 8.dp, 1.dp),
+ fontFamily = poppinsFont,
+ text = audio.mediaMetadata.getText(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE)
+ .toString(),
+ maxLines = 1,
+ color = MaterialTheme.colorScheme.onBackground,
+ fontSize = 16.sp,
+ fontWeight = FontWeight.SemiBold,
+ )
+
+ Text(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(8.dp, 1.dp, 8.dp, 2.dp),
+ fontFamily = poppinsFont,
+ text = audio.mediaMetadata.getText(MediaMetadataCompat.METADATA_KEY_ARTIST)
+ .toString(),
+ letterSpacing = 1.08.sp,
+ maxLines = 1,
+ color = MaterialTheme.colorScheme.onBackground,
+ fontSize = 14.sp,
+ fontWeight = FontWeight.SemiBold,
+ )
+
+ Slider(
+ modifier = Modifier.padding(
+ vertical = 16.dp,
+ horizontal = 8.dp,
+ ),
+ value = sliderState,
+ onValueChange = { sliderState = it },
+ colors = SliderDefaults.colors(
+ thumbColor = MaterialTheme.colorScheme.secondary,
+ activeTrackColor = MaterialTheme.colorScheme.secondary,
+ inactiveTrackColor = MaterialTheme.colorScheme.secondaryContainer,
+ ),
+ valueRange = 0f..totalDuration.toFloat(),
+ )
+
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween,
+ ) {
+ Text(
+ modifier = Modifier
+ .padding(start = 12.dp, end = 4.dp),
+ text = AudioUtils.getFormattedTime(elapsed, totalDuration, false),
+ color = MaterialTheme.colorScheme.onSurface,
+ fontSize = 12.sp,
+ )
+
+ Spacer(Modifier.weight(1f))
+
+ Text(
+ modifier = Modifier
+ .padding(start = 4.dp, end = 12.dp),
+ text = AudioUtils.getFormattedTime(totalDuration, totalDuration, false),
+ color = MaterialTheme.colorScheme.onSurface,
+ fontSize = 12.sp,
+ )
+ }
+
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center,
+ ) {
+ TextButton(
+ modifier = Modifier.padding(8.dp, 4.dp, 4.dp, 4.dp),
+ onClick = {},
+ content = {
+ Text(
+ text = context.getString(R.string.one_x),
+ color = MaterialTheme.colorScheme.onSurface,
+ fontSize = 16.sp,
+ )
+ },
+ )
+
+ Spacer(Modifier.weight(1f))
+
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center,
+ ) {
+ IconButton(
+ modifier = Modifier
+ .padding(8.dp, 4.dp, 8.dp, 4.dp)
+ .size(36.dp),
+ onClick = {},
+ ) {
+ Icon(
+ modifier = Modifier
+ .size(36.dp),
+ painter = painterResource(R.drawable.ic_rewind),
+ tint = MaterialTheme.colorScheme.onSurface,
+ contentDescription = "rewind icon",
+ )
+ }
+
+ IconButton(
+ modifier = Modifier
+ .padding(4.dp, 2.dp, 4.dp, 4.dp)
+ .size(64.dp),
+ onClick = {},
+ ) {
+ Icon(
+ modifier = Modifier
+ .size(64.dp),
+ painter = painterResource(R.drawable.ic_play),
+ tint = MaterialTheme.colorScheme.onSurface,
+ contentDescription = "play pause icon",
+ )
+ }
+
+ IconButton(
+ modifier = Modifier
+ .padding(8.dp, 4.dp, 8.dp, 4.dp)
+ .size(36.dp),
+ onClick = {},
+ ) {
+ Icon(
+ modifier = Modifier
+ .size(36.dp),
+ painter = painterResource(R.drawable.ic_seek),
+ tint = MaterialTheme.colorScheme.onSurface,
+ contentDescription = "seek icon",
+ )
+ }
+ }
+
+ Spacer(Modifier.weight(1f))
+
+ IconButton(
+ modifier = Modifier
+ .padding(4.dp, 4.dp, 8.dp, 4.dp)
+ .size(36.dp),
+ onClick = {},
+ ) {
+ Icon(
+ modifier = Modifier
+ .size(36.dp),
+ painter = painterResource(R.drawable.ic_repeat),
+ tint = MaterialTheme.colorScheme.onSurface,
+ contentDescription = "seek icon",
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/phone/vishnu/dialogmusicplayer/ui/theme/Color.kt b/app/src/main/java/phone/vishnu/dialogmusicplayer/ui/theme/Color.kt
new file mode 100644
index 0000000..569edc5
--- /dev/null
+++ b/app/src/main/java/phone/vishnu/dialogmusicplayer/ui/theme/Color.kt
@@ -0,0 +1,11 @@
+package phone.vishnu.dialogmusicplayer.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+val Purple80 = Color(0xFFD0BCFF)
+val PurpleGrey80 = Color(0xFFCCC2DC)
+val Pink80 = Color(0xFFEFB8C8)
+
+val Purple40 = Color(0xFF6650a4)
+val PurpleGrey40 = Color(0xFF625b71)
+val Pink40 = Color(0xFF7D5260)
diff --git a/app/src/main/java/phone/vishnu/dialogmusicplayer/ui/theme/Theme.kt b/app/src/main/java/phone/vishnu/dialogmusicplayer/ui/theme/Theme.kt
new file mode 100644
index 0000000..de11b30
--- /dev/null
+++ b/app/src/main/java/phone/vishnu/dialogmusicplayer/ui/theme/Theme.kt
@@ -0,0 +1,57 @@
+package phone.vishnu.dialogmusicplayer.ui.theme
+
+import android.os.Build
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.LocalContext
+
+private val DarkColorScheme = darkColorScheme(
+ primary = Purple80,
+ secondary = PurpleGrey80,
+ tertiary = Pink80,
+)
+
+private val LightColorScheme = lightColorScheme(
+ 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 DialogMusicPlayerTheme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
+ // Dynamic color is available on Android 12+
+ dynamicColor: Boolean = true,
+ content: @Composable () -> Unit,
+) {
+ val colorScheme = when {
+ dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
+ val context = LocalContext.current
+ if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
+ }
+
+ darkTheme -> DarkColorScheme
+ else -> LightColorScheme
+ }
+
+ MaterialTheme(
+ colorScheme = colorScheme,
+ typography = Typography,
+ content = content,
+ )
+}
diff --git a/app/src/main/java/phone/vishnu/dialogmusicplayer/ui/theme/Type.kt b/app/src/main/java/phone/vishnu/dialogmusicplayer/ui/theme/Type.kt
new file mode 100644
index 0000000..460bee6
--- /dev/null
+++ b/app/src/main/java/phone/vishnu/dialogmusicplayer/ui/theme/Type.kt
@@ -0,0 +1,34 @@
+package phone.vishnu.dialogmusicplayer.ui.theme
+
+import androidx.compose.material3.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+// Set of Material typography styles to start with
+val Typography = Typography(
+ bodyLarge = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.5.sp,
+ ),
+ /* Other default text styles to override
+ titleLarge = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 22.sp,
+ lineHeight = 28.sp,
+ letterSpacing = 0.sp
+ ),
+ labelSmall = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Medium,
+ fontSize = 11.sp,
+ lineHeight = 16.sp,
+ letterSpacing = 0.5.sp
+ )
+ */
+)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 2c936f8..5f18617 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -15,4 +15,6 @@
Repeat Track Toggle
Album Art IV
+ ComposeActivity
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index e8f19cd..c4bf026 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -6,7 +6,7 @@
- @color/white
- @null
- - true
+
- true
- true
- @android:color/transparent
diff --git a/build.gradle b/build.gradle
index 4ee3fba..a6f22c5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -17,6 +17,8 @@ buildscript {
plugins {
id "com.diffplug.spotless" version "6.19.0"
id 'com.google.devtools.ksp' version '2.0.0-1.0.23' apply false
+ id 'org.jetbrains.kotlin.android' version '2.0.0' apply false
+ id 'org.jetbrains.kotlin.plugin.compose' version '2.0.0' apply false
}
spotless {