initial commit
							
								
								
									
										1
									
								
								app/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1 @@
 | 
			
		||||
/build
 | 
			
		||||
							
								
								
									
										66
									
								
								app/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,66 @@
 | 
			
		||||
plugins {
 | 
			
		||||
    id 'com.android.application'
 | 
			
		||||
    id 'kotlin-android'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
android {
 | 
			
		||||
    compileSdk 31
 | 
			
		||||
 | 
			
		||||
    defaultConfig {
 | 
			
		||||
        applicationId "net.blumia.pcmdroid"
 | 
			
		||||
        minSdk 21
 | 
			
		||||
        targetSdk 31
 | 
			
		||||
        versionCode 1
 | 
			
		||||
        versionName "1.0"
 | 
			
		||||
 | 
			
		||||
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
 | 
			
		||||
        vectorDrawables {
 | 
			
		||||
            useSupportLibrary true
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    buildTypes {
 | 
			
		||||
        release {
 | 
			
		||||
            minifyEnabled false
 | 
			
		||||
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    compileOptions {
 | 
			
		||||
        sourceCompatibility JavaVersion.VERSION_1_8
 | 
			
		||||
        targetCompatibility JavaVersion.VERSION_1_8
 | 
			
		||||
    }
 | 
			
		||||
    kotlinOptions {
 | 
			
		||||
        jvmTarget = '1.8'
 | 
			
		||||
        useIR = true
 | 
			
		||||
    }
 | 
			
		||||
    buildFeatures {
 | 
			
		||||
        compose true
 | 
			
		||||
    }
 | 
			
		||||
    composeOptions {
 | 
			
		||||
        kotlinCompilerExtensionVersion compose_version
 | 
			
		||||
    }
 | 
			
		||||
    packagingOptions {
 | 
			
		||||
        resources {
 | 
			
		||||
            excludes += '/META-INF/{AL2.0,LGPL2.1}'
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
 | 
			
		||||
    implementation 'androidx.core:core-ktx:1.7.0'
 | 
			
		||||
    implementation 'androidx.appcompat:appcompat:1.3.1'
 | 
			
		||||
    implementation 'com.google.android.material:material:1.4.0'
 | 
			
		||||
    implementation "androidx.compose.ui:ui:$compose_version"
 | 
			
		||||
    implementation "androidx.compose.material:material:$compose_version"
 | 
			
		||||
    implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
 | 
			
		||||
    implementation "androidx.navigation:navigation-compose:$nav_compose_version"
 | 
			
		||||
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
 | 
			
		||||
    implementation 'androidx.activity:activity-compose:1.4.0'
 | 
			
		||||
    implementation "androidx.datastore:datastore-preferences:1.0.0"
 | 
			
		||||
    testImplementation 'junit:junit:4.+'
 | 
			
		||||
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
 | 
			
		||||
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
 | 
			
		||||
    androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
 | 
			
		||||
    debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								app/proguard-rules.pro
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,21 @@
 | 
			
		||||
# Add project specific ProGuard rules here.
 | 
			
		||||
# You can control the set of applied configuration files using the
 | 
			
		||||
# proguardFiles setting in build.gradle.
 | 
			
		||||
#
 | 
			
		||||
# For more details, see
 | 
			
		||||
#   http://developer.android.com/guide/developing/tools/proguard.html
 | 
			
		||||
 | 
			
		||||
# If your project uses WebView with JS, uncomment the following
 | 
			
		||||
# and specify the fully qualified class name to the JavaScript interface
 | 
			
		||||
# class:
 | 
			
		||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
 | 
			
		||||
#   public *;
 | 
			
		||||
#}
 | 
			
		||||
 | 
			
		||||
# Uncomment this to preserve the line number information for
 | 
			
		||||
# debugging stack traces.
 | 
			
		||||
#-keepattributes SourceFile,LineNumberTable
 | 
			
		||||
 | 
			
		||||
# If you keep the line number information, uncomment this to
 | 
			
		||||
# hide the original source file name.
 | 
			
		||||
#-renamesourcefileattribute SourceFile
 | 
			
		||||
@ -0,0 +1,24 @@
 | 
			
		||||
package net.blumia.pcmdroid
 | 
			
		||||
 | 
			
		||||
import androidx.test.platform.app.InstrumentationRegistry
 | 
			
		||||
import androidx.test.ext.junit.runners.AndroidJUnit4
 | 
			
		||||
 | 
			
		||||
import org.junit.Test
 | 
			
		||||
import org.junit.runner.RunWith
 | 
			
		||||
 | 
			
		||||
import org.junit.Assert.*
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Instrumented test, which will execute on an Android device.
 | 
			
		||||
 *
 | 
			
		||||
 * See [testing documentation](http://d.android.com/tools/testing).
 | 
			
		||||
 */
 | 
			
		||||
@RunWith(AndroidJUnit4::class)
 | 
			
		||||
class ExampleInstrumentedTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    fun useAppContext() {
 | 
			
		||||
        // Context of the app under test.
 | 
			
		||||
        val appContext = InstrumentationRegistry.getInstrumentation().targetContext
 | 
			
		||||
        assertEquals("net.blumia.pcmdroid", appContext.packageName)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								app/src/main/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,25 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    package="net.blumia.pcmdroid">
 | 
			
		||||
 | 
			
		||||
    <application
 | 
			
		||||
        android:allowBackup="true"
 | 
			
		||||
        android:icon="@mipmap/ic_launcher"
 | 
			
		||||
        android:label="@string/app_name"
 | 
			
		||||
        android:roundIcon="@mipmap/ic_launcher_round"
 | 
			
		||||
        android:supportsRtl="true"
 | 
			
		||||
        android:theme="@style/Theme.PrivateCloudMusic">
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".MainActivity"
 | 
			
		||||
            android:exported="true"
 | 
			
		||||
            android:label="@string/app_name"
 | 
			
		||||
            android:theme="@style/Theme.PrivateCloudMusic.NoActionBar">
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
                <action android:name="android.intent.action.MAIN" />
 | 
			
		||||
 | 
			
		||||
                <category android:name="android.intent.category.LAUNCHER" />
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
        </activity>
 | 
			
		||||
    </application>
 | 
			
		||||
 | 
			
		||||
</manifest>
 | 
			
		||||
							
								
								
									
										40
									
								
								app/src/main/java/net/blumia/pcmdroid/MainActivity.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,40 @@
 | 
			
		||||
package net.blumia.pcmdroid
 | 
			
		||||
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import androidx.activity.ComponentActivity
 | 
			
		||||
import androidx.activity.compose.setContent
 | 
			
		||||
import androidx.compose.material.MaterialTheme
 | 
			
		||||
import androidx.compose.material.Surface
 | 
			
		||||
import androidx.compose.material.Text
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.ui.tooling.preview.Preview
 | 
			
		||||
import net.blumia.pcmdroid.ui.NavGraph
 | 
			
		||||
import net.blumia.pcmdroid.ui.screen.main.MainPlayer
 | 
			
		||||
import net.blumia.pcmdroid.ui.theme.PrivateCloudMusicTheme
 | 
			
		||||
 | 
			
		||||
class MainActivity : ComponentActivity() {
 | 
			
		||||
    override fun onCreate(savedInstanceState: Bundle?) {
 | 
			
		||||
        super.onCreate(savedInstanceState)
 | 
			
		||||
        setContent {
 | 
			
		||||
            PrivateCloudMusicTheme {
 | 
			
		||||
                // A surface container using the 'background' color from the theme
 | 
			
		||||
                Surface(color = MaterialTheme.colors.background) {
 | 
			
		||||
                    NavGraph()
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun Greeting(name: String) {
 | 
			
		||||
    Text(text = "Hello $name!")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Preview(showBackground = true)
 | 
			
		||||
@Composable
 | 
			
		||||
fun DefaultPreview() {
 | 
			
		||||
    PrivateCloudMusicTheme {
 | 
			
		||||
        Greeting("Android")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								app/src/main/java/net/blumia/pcmdroid/model/Server.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,9 @@
 | 
			
		||||
package net.blumia.pcmdroid.model
 | 
			
		||||
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
 | 
			
		||||
data class Server (
 | 
			
		||||
    val url: Uri,
 | 
			
		||||
    val name: String,
 | 
			
		||||
    val baseFolderName: String,
 | 
			
		||||
)
 | 
			
		||||
@ -0,0 +1,28 @@
 | 
			
		||||
package net.blumia.pcmdroid.preferences
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import androidx.datastore.core.DataStore
 | 
			
		||||
import androidx.datastore.preferences.core.Preferences
 | 
			
		||||
import androidx.datastore.preferences.core.edit
 | 
			
		||||
import androidx.datastore.preferences.core.stringPreferencesKey
 | 
			
		||||
import androidx.datastore.preferences.preferencesDataStore
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
import kotlinx.coroutines.flow.map
 | 
			
		||||
 | 
			
		||||
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
 | 
			
		||||
 | 
			
		||||
val P_LOCAL_STORAGE_PATH = stringPreferencesKey("local_storage_path_url")
 | 
			
		||||
 | 
			
		||||
fun Context.stringPreference(key: Preferences.Key<String>) : Flow<String> {
 | 
			
		||||
    return dataStore.data
 | 
			
		||||
        .map { preferences ->
 | 
			
		||||
            // No type safety.
 | 
			
		||||
            preferences[key] ?: ""
 | 
			
		||||
        }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
suspend fun Context.setStringPreference(key: Preferences.Key<String>, value: String) {
 | 
			
		||||
    dataStore.edit { settings ->
 | 
			
		||||
        settings[key] = value
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										55
									
								
								app/src/main/java/net/blumia/pcmdroid/ui/NavGraph.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,55 @@
 | 
			
		||||
package net.blumia.pcmdroid.ui
 | 
			
		||||
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.navigation.NavHostController
 | 
			
		||||
import androidx.navigation.compose.NavHost
 | 
			
		||||
import androidx.navigation.compose.composable
 | 
			
		||||
import androidx.navigation.compose.rememberNavController
 | 
			
		||||
import net.blumia.pcmdroid.ui.screen.addserver.AddServerScreen
 | 
			
		||||
import net.blumia.pcmdroid.ui.screen.main.MainPlayer
 | 
			
		||||
import net.blumia.pcmdroid.ui.screen.settings.SettingsScreen
 | 
			
		||||
 | 
			
		||||
object MainDestinations {
 | 
			
		||||
    const val MAIN_ROUTE = "main"
 | 
			
		||||
    const val ADD_SERVER_ROUTE = "add_server"
 | 
			
		||||
    const val SETTINGS_ROUTE = "settings"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun NavGraph(
 | 
			
		||||
    navController: NavHostController = rememberNavController(),
 | 
			
		||||
    startDestination: String = MainDestinations.MAIN_ROUTE,
 | 
			
		||||
) {
 | 
			
		||||
    NavHost(
 | 
			
		||||
        navController = navController,
 | 
			
		||||
        startDestination = startDestination
 | 
			
		||||
    ) {
 | 
			
		||||
        composable(MainDestinations.MAIN_ROUTE) {
 | 
			
		||||
            MainPlayer(
 | 
			
		||||
                addServerActionTriggered = {
 | 
			
		||||
                    navController.navigate(MainDestinations.ADD_SERVER_ROUTE)
 | 
			
		||||
                },
 | 
			
		||||
                settingsActionTriggered = {
 | 
			
		||||
                    navController.navigate(MainDestinations.SETTINGS_ROUTE)
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        composable(MainDestinations.ADD_SERVER_ROUTE) {
 | 
			
		||||
            AddServerScreen(
 | 
			
		||||
                onCloseActionTriggered = {
 | 
			
		||||
                    navController.navigateUp()
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        composable(MainDestinations.SETTINGS_ROUTE) {
 | 
			
		||||
            SettingsScreen(
 | 
			
		||||
                onBackActionTriggered = {
 | 
			
		||||
                    navController.navigateUp()
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,95 @@
 | 
			
		||||
package net.blumia.pcmdroid.ui.screen.addserver
 | 
			
		||||
 | 
			
		||||
import androidx.compose.foundation.layout.*
 | 
			
		||||
import androidx.compose.foundation.shape.RoundedCornerShape
 | 
			
		||||
import androidx.compose.material.*
 | 
			
		||||
import androidx.compose.material.icons.Icons
 | 
			
		||||
import androidx.compose.material.icons.filled.AddCircle
 | 
			
		||||
import androidx.compose.material.icons.filled.Close
 | 
			
		||||
import androidx.compose.material.icons.rounded.AddCircle
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.ui.Alignment
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.graphics.Color
 | 
			
		||||
import androidx.compose.ui.text.style.TextAlign
 | 
			
		||||
import androidx.compose.ui.tooling.preview.Preview
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
 | 
			
		||||
@Preview(showBackground = true)
 | 
			
		||||
@Composable
 | 
			
		||||
fun AddServerScreen(
 | 
			
		||||
    onCloseActionTriggered: () -> Unit = {},
 | 
			
		||||
) {
 | 
			
		||||
    Scaffold(
 | 
			
		||||
        topBar = {
 | 
			
		||||
            TopAppBar(
 | 
			
		||||
                backgroundColor = Color.Transparent,
 | 
			
		||||
                elevation = 0.dp,
 | 
			
		||||
                title = {},
 | 
			
		||||
                navigationIcon = {
 | 
			
		||||
                    IconButton(onClick = onCloseActionTriggered) {
 | 
			
		||||
                        Icon(imageVector = Icons.Filled.Close, contentDescription = "Close")
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    ) {
 | 
			
		||||
        Column(
 | 
			
		||||
            modifier = Modifier
 | 
			
		||||
                .fillMaxSize()
 | 
			
		||||
                .padding(20.dp),
 | 
			
		||||
            horizontalAlignment = Alignment.CenterHorizontally
 | 
			
		||||
        ) {
 | 
			
		||||
            Icon(
 | 
			
		||||
                modifier = Modifier.size(50.dp),
 | 
			
		||||
                imageVector = Icons.Filled.AddCircle,
 | 
			
		||||
                contentDescription = null,
 | 
			
		||||
            )
 | 
			
		||||
            Text(
 | 
			
		||||
                modifier = Modifier
 | 
			
		||||
                    .fillMaxWidth()
 | 
			
		||||
                    .padding(vertical = 10.dp),
 | 
			
		||||
                text = "Add Server",
 | 
			
		||||
                textAlign = TextAlign.Center,
 | 
			
		||||
                style = MaterialTheme.typography.h5,
 | 
			
		||||
            )
 | 
			
		||||
            Text(
 | 
			
		||||
                modifier = Modifier
 | 
			
		||||
                    .fillMaxWidth()
 | 
			
		||||
                    .padding(bottom = 10.dp),
 | 
			
		||||
                text = "Add Private Cloud Music server",
 | 
			
		||||
                textAlign = TextAlign.Center,
 | 
			
		||||
                style = MaterialTheme.typography.subtitle2,
 | 
			
		||||
            )
 | 
			
		||||
            OutlinedTextField(
 | 
			
		||||
                modifier = Modifier
 | 
			
		||||
                    .fillMaxWidth()
 | 
			
		||||
                    .padding(vertical = 10.dp),
 | 
			
		||||
                value = "",
 | 
			
		||||
                onValueChange = {},
 | 
			
		||||
                label = { Text("Server API Url") },
 | 
			
		||||
            )
 | 
			
		||||
            Spacer(modifier = Modifier.weight(1f))
 | 
			
		||||
            Row(
 | 
			
		||||
                modifier = Modifier.fillMaxWidth(),
 | 
			
		||||
                horizontalArrangement = Arrangement.SpaceBetween
 | 
			
		||||
            ) {
 | 
			
		||||
                TextButton(
 | 
			
		||||
                    onClick = { /*TODO*/ },
 | 
			
		||||
                    enabled = false
 | 
			
		||||
                ) {
 | 
			
		||||
                    Text("Back")
 | 
			
		||||
                }
 | 
			
		||||
                Button(
 | 
			
		||||
                    onClick = { /*TODO*/ },
 | 
			
		||||
                    shape = RoundedCornerShape(50)
 | 
			
		||||
                ) {
 | 
			
		||||
                    Text(
 | 
			
		||||
                        text = "Next",
 | 
			
		||||
                        modifier = Modifier.padding(horizontal = 5.dp)
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,164 @@
 | 
			
		||||
package net.blumia.pcmdroid.ui.screen.main
 | 
			
		||||
 | 
			
		||||
import androidx.compose.foundation.Image
 | 
			
		||||
import androidx.compose.foundation.layout.*
 | 
			
		||||
import androidx.compose.material.*
 | 
			
		||||
import androidx.compose.material.icons.Icons
 | 
			
		||||
import androidx.compose.material.icons.filled.*
 | 
			
		||||
import androidx.compose.material.icons.rounded.PlayArrow
 | 
			
		||||
import androidx.compose.runtime.*
 | 
			
		||||
import androidx.compose.ui.Alignment
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.graphics.vector.ImageVector
 | 
			
		||||
import androidx.compose.ui.res.painterResource
 | 
			
		||||
import androidx.compose.ui.tooling.preview.Preview
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import net.blumia.pcmdroid.R
 | 
			
		||||
 | 
			
		||||
@Preview(showBackground = true)
 | 
			
		||||
@OptIn(ExperimentalMaterialApi::class)
 | 
			
		||||
@Composable
 | 
			
		||||
fun NowPlaying() {
 | 
			
		||||
    Row(
 | 
			
		||||
        modifier = Modifier
 | 
			
		||||
            .fillMaxWidth()
 | 
			
		||||
            .height(56.dp),
 | 
			
		||||
        verticalAlignment = Alignment.CenterVertically,
 | 
			
		||||
    ) {
 | 
			
		||||
        Image(
 | 
			
		||||
            painter = painterResource(id = R.drawable.ic_launcher_background),
 | 
			
		||||
            contentDescription = "Album cover",
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        ListItem(
 | 
			
		||||
            text = {
 | 
			
		||||
                Text(
 | 
			
		||||
                    text = "Not playing at all.",
 | 
			
		||||
                )
 | 
			
		||||
            },
 | 
			
		||||
            trailing = {
 | 
			
		||||
                IconButton(onClick = { /*TODO*/ }) {
 | 
			
		||||
                    Icon(imageVector = Icons.Rounded.PlayArrow, contentDescription = "Menu")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Preview(showBackground = true)
 | 
			
		||||
@OptIn(ExperimentalMaterialApi::class)
 | 
			
		||||
@Composable
 | 
			
		||||
fun DrawerContent(
 | 
			
		||||
    addServerActionTriggered: () -> Unit = {},
 | 
			
		||||
    settingsActionTriggered: () -> Unit = {},
 | 
			
		||||
) {
 | 
			
		||||
    Row(modifier = Modifier.fillMaxSize()) {
 | 
			
		||||
        var selectedItem by remember { mutableStateOf<Int?>(null) }
 | 
			
		||||
        val items = listOf<String>()
 | 
			
		||||
        val icons = listOf<ImageVector>()
 | 
			
		||||
        NavigationRail {
 | 
			
		||||
            items.forEachIndexed { index, item ->
 | 
			
		||||
                NavigationRailItem(
 | 
			
		||||
                    icon = {
 | 
			
		||||
                        Icon(
 | 
			
		||||
                            icons[index], item
 | 
			
		||||
                        )
 | 
			
		||||
                    },
 | 
			
		||||
                    label = {
 | 
			
		||||
                        Text(item)
 | 
			
		||||
                    },
 | 
			
		||||
                    selected = selectedItem == index,
 | 
			
		||||
                    onClick = { /*TODO*/ }
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            NavigationRailItem(
 | 
			
		||||
                icon = { Icon(Icons.Filled.AddCircle, null) },
 | 
			
		||||
                label = { Text("Add") },
 | 
			
		||||
                selected = false,
 | 
			
		||||
                onClick = addServerActionTriggered,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            Spacer(modifier = Modifier.weight(1f))
 | 
			
		||||
 | 
			
		||||
            NavigationRailItem(
 | 
			
		||||
                icon = { Icon(Icons.Filled.Settings, null) },
 | 
			
		||||
                label = { Text("Settings") },
 | 
			
		||||
                selected = false,
 | 
			
		||||
                onClick = settingsActionTriggered,
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Column(modifier = Modifier.weight(1f)) {
 | 
			
		||||
            ListItem(
 | 
			
		||||
                text = {
 | 
			
		||||
                    Text(
 | 
			
		||||
                        text = "No server selected",
 | 
			
		||||
                        style = MaterialTheme.typography.subtitle1
 | 
			
		||||
                    )
 | 
			
		||||
                },
 | 
			
		||||
                trailing = {
 | 
			
		||||
                    IconButton(onClick = { /*TODO*/ }) {
 | 
			
		||||
                        Icon(imageVector = Icons.Filled.MoreVert, contentDescription = "Menu")
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            Divider()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Preview(showBackground = true)
 | 
			
		||||
@Composable
 | 
			
		||||
fun MainPlayer(
 | 
			
		||||
    addServerActionTriggered: () -> Unit = {},
 | 
			
		||||
    settingsActionTriggered: () -> Unit = {},
 | 
			
		||||
) {
 | 
			
		||||
    val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
 | 
			
		||||
    val drawerScope = rememberCoroutineScope()
 | 
			
		||||
    ModalDrawer(
 | 
			
		||||
        drawerState = drawerState,
 | 
			
		||||
        drawerContent = {
 | 
			
		||||
            DrawerContent(
 | 
			
		||||
                addServerActionTriggered = addServerActionTriggered,
 | 
			
		||||
                settingsActionTriggered = settingsActionTriggered,
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    ) {
 | 
			
		||||
        Scaffold(
 | 
			
		||||
            topBar = {
 | 
			
		||||
                TopAppBar(
 | 
			
		||||
                    title = { Text("Private Cloud Music") },
 | 
			
		||||
                    navigationIcon = {
 | 
			
		||||
                        IconButton(onClick = {
 | 
			
		||||
                            drawerScope.launch {
 | 
			
		||||
                                drawerState.open()
 | 
			
		||||
                            }
 | 
			
		||||
                        }) {
 | 
			
		||||
                            Icon(imageVector = Icons.Filled.Menu, contentDescription = "Open drawer")
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    actions = {
 | 
			
		||||
                        IconButton(onClick = { /*TODO*/ }) {
 | 
			
		||||
                            Icon(imageVector = Icons.Filled.MoreVert, contentDescription = "Menu")
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        ) {
 | 
			
		||||
            Column(
 | 
			
		||||
                modifier = Modifier.fillMaxSize()
 | 
			
		||||
            ) {
 | 
			
		||||
                Column(modifier = Modifier.weight(1f, fill = true)) {
 | 
			
		||||
                    //
 | 
			
		||||
                }
 | 
			
		||||
                NowPlaying()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,86 @@
 | 
			
		||||
package net.blumia.pcmdroid.ui.screen.settings
 | 
			
		||||
 | 
			
		||||
import android.app.Activity
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import android.os.Build
 | 
			
		||||
import android.provider.DocumentsContract
 | 
			
		||||
import androidx.annotation.RequiresApi
 | 
			
		||||
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.material.*
 | 
			
		||||
import androidx.compose.material.icons.Icons
 | 
			
		||||
import androidx.compose.material.icons.filled.ArrowBack
 | 
			
		||||
import androidx.compose.material.icons.filled.Close
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.graphics.Color
 | 
			
		||||
import androidx.compose.ui.platform.LocalContext
 | 
			
		||||
import androidx.compose.ui.tooling.preview.Preview
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
 | 
			
		||||
fun openDirectory(context: Context) {
 | 
			
		||||
    // Choose a directory using the system's file picker.
 | 
			
		||||
    val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
 | 
			
		||||
        // Provide read access to files and sub-directories in the user-selected
 | 
			
		||||
        // directory.
 | 
			
		||||
        flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
 | 
			
		||||
 | 
			
		||||
        // Optionally, specify a URI for the directory that should be opened in
 | 
			
		||||
        // the system file picker when it loads.
 | 
			
		||||
        // , pickerInitialUri: Uri
 | 
			
		||||
//        putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri)
 | 
			
		||||
    }
 | 
			
		||||
    context as Activity
 | 
			
		||||
 | 
			
		||||
    context.startActivityForResult(intent, 1919810)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@OptIn(ExperimentalMaterialApi::class)
 | 
			
		||||
@Preview(showBackground = true)
 | 
			
		||||
@Composable
 | 
			
		||||
fun SettingsScreen(
 | 
			
		||||
    onBackActionTriggered: () -> Unit = {},
 | 
			
		||||
) {
 | 
			
		||||
    Scaffold(
 | 
			
		||||
        topBar = {
 | 
			
		||||
            TopAppBar(
 | 
			
		||||
                backgroundColor = Color.Transparent,
 | 
			
		||||
                elevation = 0.dp,
 | 
			
		||||
                title = { Text("Settings") },
 | 
			
		||||
                navigationIcon = {
 | 
			
		||||
                    IconButton(onClick = onBackActionTriggered) {
 | 
			
		||||
                        Icon(imageVector = Icons.Filled.ArrowBack, contentDescription = "Close")
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    ) {
 | 
			
		||||
        Column(
 | 
			
		||||
            modifier = Modifier.fillMaxSize()
 | 
			
		||||
        ) {
 | 
			
		||||
            ListItem(
 | 
			
		||||
                text = {
 | 
			
		||||
                    Text(
 | 
			
		||||
                        text = "Local storage",
 | 
			
		||||
                        style = MaterialTheme.typography.overline,
 | 
			
		||||
                        color = MaterialTheme.colors.primary,
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            // cannot do the cast here or it will break the preview.
 | 
			
		||||
            val currentActivity = LocalContext.current
 | 
			
		||||
            ListItem(
 | 
			
		||||
                modifier = Modifier.clickable { ->
 | 
			
		||||
                    openDirectory(currentActivity)
 | 
			
		||||
                },
 | 
			
		||||
                text = { Text("Location") },
 | 
			
		||||
                secondaryText = { Text("Click to select storage location.") },
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								app/src/main/java/net/blumia/pcmdroid/ui/theme/Color.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,8 @@
 | 
			
		||||
package net.blumia.pcmdroid.ui.theme
 | 
			
		||||
 | 
			
		||||
import androidx.compose.ui.graphics.Color
 | 
			
		||||
 | 
			
		||||
val Purple200 = Color(0xFFBB86FC)
 | 
			
		||||
val Purple500 = Color(0xFF6200EE)
 | 
			
		||||
val Purple700 = Color(0xFF3700B3)
 | 
			
		||||
val Teal200 = Color(0xFF03DAC5)
 | 
			
		||||
							
								
								
									
										11
									
								
								app/src/main/java/net/blumia/pcmdroid/ui/theme/Shape.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,11 @@
 | 
			
		||||
package net.blumia.pcmdroid.ui.theme
 | 
			
		||||
 | 
			
		||||
import androidx.compose.foundation.shape.RoundedCornerShape
 | 
			
		||||
import androidx.compose.material.Shapes
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
 | 
			
		||||
val Shapes = Shapes(
 | 
			
		||||
    small = RoundedCornerShape(4.dp),
 | 
			
		||||
    medium = RoundedCornerShape(4.dp),
 | 
			
		||||
    large = RoundedCornerShape(0.dp)
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										47
									
								
								app/src/main/java/net/blumia/pcmdroid/ui/theme/Theme.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,47 @@
 | 
			
		||||
package net.blumia.pcmdroid.ui.theme
 | 
			
		||||
 | 
			
		||||
import androidx.compose.foundation.isSystemInDarkTheme
 | 
			
		||||
import androidx.compose.material.MaterialTheme
 | 
			
		||||
import androidx.compose.material.darkColors
 | 
			
		||||
import androidx.compose.material.lightColors
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
 | 
			
		||||
private val DarkColorPalette = darkColors(
 | 
			
		||||
    primary = Purple200,
 | 
			
		||||
    primaryVariant = Purple700,
 | 
			
		||||
    secondary = Teal200
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
private val LightColorPalette = lightColors(
 | 
			
		||||
    primary = Purple500,
 | 
			
		||||
    primaryVariant = Purple700,
 | 
			
		||||
    secondary = Teal200
 | 
			
		||||
 | 
			
		||||
    /* Other default colors to override
 | 
			
		||||
    background = Color.White,
 | 
			
		||||
    surface = Color.White,
 | 
			
		||||
    onPrimary = Color.White,
 | 
			
		||||
    onSecondary = Color.Black,
 | 
			
		||||
    onBackground = Color.Black,
 | 
			
		||||
    onSurface = Color.Black,
 | 
			
		||||
    */
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun PrivateCloudMusicTheme(
 | 
			
		||||
    darkTheme: Boolean = isSystemInDarkTheme(),
 | 
			
		||||
    content: @Composable() () -> Unit
 | 
			
		||||
) {
 | 
			
		||||
    val colors = if (darkTheme) {
 | 
			
		||||
        DarkColorPalette
 | 
			
		||||
    } else {
 | 
			
		||||
        LightColorPalette
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MaterialTheme(
 | 
			
		||||
        colors = colors,
 | 
			
		||||
        typography = Typography,
 | 
			
		||||
        shapes = Shapes,
 | 
			
		||||
        content = content
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								app/src/main/java/net/blumia/pcmdroid/ui/theme/Type.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,28 @@
 | 
			
		||||
package net.blumia.pcmdroid.ui.theme
 | 
			
		||||
 | 
			
		||||
import androidx.compose.material.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(
 | 
			
		||||
    body1 = TextStyle(
 | 
			
		||||
        fontFamily = FontFamily.Default,
 | 
			
		||||
        fontWeight = FontWeight.Normal,
 | 
			
		||||
        fontSize = 16.sp
 | 
			
		||||
    )
 | 
			
		||||
    /* Other default text styles to override
 | 
			
		||||
    button = TextStyle(
 | 
			
		||||
        fontFamily = FontFamily.Default,
 | 
			
		||||
        fontWeight = FontWeight.W500,
 | 
			
		||||
        fontSize = 14.sp
 | 
			
		||||
    ),
 | 
			
		||||
    caption = TextStyle(
 | 
			
		||||
        fontFamily = FontFamily.Default,
 | 
			
		||||
        fontWeight = FontWeight.Normal,
 | 
			
		||||
        fontSize = 12.sp
 | 
			
		||||
    )
 | 
			
		||||
    */
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										30
									
								
								app/src/main/res/drawable-v24/ic_launcher_foreground.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,30 @@
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    xmlns:aapt="http://schemas.android.com/aapt"
 | 
			
		||||
    android:width="108dp"
 | 
			
		||||
    android:height="108dp"
 | 
			
		||||
    android:viewportWidth="108"
 | 
			
		||||
    android:viewportHeight="108">
 | 
			
		||||
    <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
 | 
			
		||||
        <aapt:attr name="android:fillColor">
 | 
			
		||||
            <gradient
 | 
			
		||||
                android:endX="85.84757"
 | 
			
		||||
                android:endY="92.4963"
 | 
			
		||||
                android:startX="42.9492"
 | 
			
		||||
                android:startY="49.59793"
 | 
			
		||||
                android:type="linear">
 | 
			
		||||
                <item
 | 
			
		||||
                    android:color="#44000000"
 | 
			
		||||
                    android:offset="0.0" />
 | 
			
		||||
                <item
 | 
			
		||||
                    android:color="#00000000"
 | 
			
		||||
                    android:offset="1.0" />
 | 
			
		||||
            </gradient>
 | 
			
		||||
        </aapt:attr>
 | 
			
		||||
    </path>
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#FFFFFF"
 | 
			
		||||
        android:fillType="nonZero"
 | 
			
		||||
        android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
 | 
			
		||||
        android:strokeWidth="1"
 | 
			
		||||
        android:strokeColor="#00000000" />
 | 
			
		||||
</vector>
 | 
			
		||||
							
								
								
									
										170
									
								
								app/src/main/res/drawable/ic_launcher_background.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,170 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="108dp"
 | 
			
		||||
    android:height="108dp"
 | 
			
		||||
    android:viewportWidth="108"
 | 
			
		||||
    android:viewportHeight="108">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#3DDC84"
 | 
			
		||||
        android:pathData="M0,0h108v108h-108z" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M9,0L9,108"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M19,0L19,108"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M29,0L29,108"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M39,0L39,108"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M49,0L49,108"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M59,0L59,108"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M69,0L69,108"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M79,0L79,108"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M89,0L89,108"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M99,0L99,108"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M0,9L108,9"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M0,19L108,19"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M0,29L108,29"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M0,39L108,39"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M0,49L108,49"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M0,59L108,59"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M0,69L108,69"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M0,79L108,79"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M0,89L108,89"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M0,99L108,99"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M19,29L89,29"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M19,39L89,39"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M19,49L89,49"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M19,59L89,59"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M19,69L89,69"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M19,79L89,79"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M29,19L29,89"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M39,19L39,89"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M49,19L49,89"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M59,19L59,89"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M69,19L69,89"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#00000000"
 | 
			
		||||
        android:pathData="M79,19L79,89"
 | 
			
		||||
        android:strokeWidth="0.8"
 | 
			
		||||
        android:strokeColor="#33FFFFFF" />
 | 
			
		||||
</vector>
 | 
			
		||||
							
								
								
									
										5
									
								
								app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
 | 
			
		||||
    <background android:drawable="@drawable/ic_launcher_background" />
 | 
			
		||||
    <foreground android:drawable="@drawable/ic_launcher_foreground" />
 | 
			
		||||
</adaptive-icon>
 | 
			
		||||
							
								
								
									
										5
									
								
								app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
 | 
			
		||||
    <background android:drawable="@drawable/ic_launcher_background" />
 | 
			
		||||
    <foreground android:drawable="@drawable/ic_launcher_foreground" />
 | 
			
		||||
</adaptive-icon>
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-hdpi/ic_launcher.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.4 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 2.8 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-mdpi/ic_launcher.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 982 B  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.7 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xhdpi/ic_launcher.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.9 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 3.8 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 2.8 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 5.8 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 3.8 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 7.6 KiB  | 
							
								
								
									
										16
									
								
								app/src/main/res/values-night/themes.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,16 @@
 | 
			
		||||
<resources xmlns:tools="http://schemas.android.com/tools">
 | 
			
		||||
    <!-- Base application theme. -->
 | 
			
		||||
    <style name="Theme.PrivateCloudMusic" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
 | 
			
		||||
        <!-- Primary brand color. -->
 | 
			
		||||
        <item name="colorPrimary">@color/purple_200</item>
 | 
			
		||||
        <item name="colorPrimaryVariant">@color/purple_700</item>
 | 
			
		||||
        <item name="colorOnPrimary">@color/black</item>
 | 
			
		||||
        <!-- Secondary brand color. -->
 | 
			
		||||
        <item name="colorSecondary">@color/teal_200</item>
 | 
			
		||||
        <item name="colorSecondaryVariant">@color/teal_200</item>
 | 
			
		||||
        <item name="colorOnSecondary">@color/black</item>
 | 
			
		||||
        <!-- Status bar color. -->
 | 
			
		||||
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
 | 
			
		||||
        <!-- Customize your theme here. -->
 | 
			
		||||
    </style>
 | 
			
		||||
</resources>
 | 
			
		||||
							
								
								
									
										10
									
								
								app/src/main/res/values/colors.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,10 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<resources>
 | 
			
		||||
    <color name="purple_200">#FFBB86FC</color>
 | 
			
		||||
    <color name="purple_500">#FF6200EE</color>
 | 
			
		||||
    <color name="purple_700">#FF3700B3</color>
 | 
			
		||||
    <color name="teal_200">#FF03DAC5</color>
 | 
			
		||||
    <color name="teal_700">#FF018786</color>
 | 
			
		||||
    <color name="black">#FF000000</color>
 | 
			
		||||
    <color name="white">#FFFFFFFF</color>
 | 
			
		||||
</resources>
 | 
			
		||||
							
								
								
									
										3
									
								
								app/src/main/res/values/strings.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,3 @@
 | 
			
		||||
<resources>
 | 
			
		||||
    <string name="app_name">Private Cloud Music</string>
 | 
			
		||||
</resources>
 | 
			
		||||
							
								
								
									
										25
									
								
								app/src/main/res/values/themes.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,25 @@
 | 
			
		||||
<resources xmlns:tools="http://schemas.android.com/tools">
 | 
			
		||||
    <!-- Base application theme. -->
 | 
			
		||||
    <style name="Theme.PrivateCloudMusic" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
 | 
			
		||||
        <!-- Primary brand color. -->
 | 
			
		||||
        <item name="colorPrimary">@color/purple_500</item>
 | 
			
		||||
        <item name="colorPrimaryVariant">@color/purple_700</item>
 | 
			
		||||
        <item name="colorOnPrimary">@color/white</item>
 | 
			
		||||
        <!-- Secondary brand color. -->
 | 
			
		||||
        <item name="colorSecondary">@color/teal_200</item>
 | 
			
		||||
        <item name="colorSecondaryVariant">@color/teal_700</item>
 | 
			
		||||
        <item name="colorOnSecondary">@color/black</item>
 | 
			
		||||
        <!-- Status bar color. -->
 | 
			
		||||
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
 | 
			
		||||
        <!-- Customize your theme here. -->
 | 
			
		||||
    </style>
 | 
			
		||||
 | 
			
		||||
    <style name="Theme.PrivateCloudMusic.NoActionBar">
 | 
			
		||||
        <item name="windowActionBar">false</item>
 | 
			
		||||
        <item name="windowNoTitle">true</item>
 | 
			
		||||
    </style>
 | 
			
		||||
 | 
			
		||||
    <style name="Theme.PrivateCloudMusic.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
 | 
			
		||||
 | 
			
		||||
    <style name="Theme.PrivateCloudMusic.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
 | 
			
		||||
</resources>
 | 
			
		||||
							
								
								
									
										17
									
								
								app/src/test/java/net/blumia/pcmdroid/ExampleUnitTest.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,17 @@
 | 
			
		||||
package net.blumia.pcmdroid
 | 
			
		||||
 | 
			
		||||
import org.junit.Test
 | 
			
		||||
 | 
			
		||||
import org.junit.Assert.*
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Example local unit test, which will execute on the development machine (host).
 | 
			
		||||
 *
 | 
			
		||||
 * See [testing documentation](http://d.android.com/tools/testing).
 | 
			
		||||
 */
 | 
			
		||||
class ExampleUnitTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    fun addition_isCorrect() {
 | 
			
		||||
        assertEquals(4, 2 + 2)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||