iu improvements

master
Isaac Iwasaki 3 months ago
parent 4057af1c55
commit fdfb0aa679

@ -0,0 +1,117 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</code_scheme>
</component>

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

@ -4,6 +4,14 @@
<selectionStates> <selectionStates>
<SelectionState runConfigName="app"> <SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2024-09-25T08:00:24.944510800Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=3128450" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState> </SelectionState>
</selectionStates> </selectionStates>
</component> </component>

@ -3,15 +3,19 @@
<option name="myName" value="Project Default" /> <option name="myName" value="Project Default" />
<inspection_tool class="ComposePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true"> <inspection_tool class="ComposePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="GlancePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true"> <inspection_tool class="GlancePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
@ -27,18 +31,23 @@
</inspection_tool> </inspection_tool>
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
</profile> </profile>
</component> </component>

@ -12,7 +12,7 @@ android {
minSdk = 34 minSdk = 34
targetSdk = 34 targetSdk = 34
versionCode = 1 versionCode = 1
versionName = "1.0" versionName = "0.0.1α"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables { vectorDrawables {
@ -24,8 +24,7 @@ android {
release { release {
isMinifyEnabled = false isMinifyEnabled = false
proguardFiles( proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"), getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
"proguard-rules.pro"
) )
} }
} }
@ -50,6 +49,7 @@ android {
} }
dependencies { dependencies {
implementation(libs.kotlinx.serialization.json)
implementation(libs.androidx.core.ktx) implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose) implementation(libs.androidx.activity.compose)
@ -62,11 +62,12 @@ dependencies {
implementation(libs.androidx.media3.ui) implementation(libs.androidx.media3.ui)
implementation(libs.androidx.media3.common) implementation(libs.androidx.media3.common)
implementation(libs.androidx.media3.session) implementation(libs.androidx.media3.session)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core) androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(platform(libs.androidx.compose.bom)) androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.ui.test.junit4) androidTestImplementation(libs.androidx.ui.test.junit4)
debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest) debugImplementation(libs.androidx.ui.test.manifest)
} }

@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-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 <application
android:allowBackup="true" android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
@ -9,8 +11,7 @@
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.Player" android:theme="@style/Theme.Player">
>
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
@ -21,6 +22,6 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<service android:name=".PlayerService"/> <service android:name=".PlayerService" />
</application> </application>
</manifest> </manifest>

@ -1,29 +1,59 @@
package usr.empty.player package usr.empty.player
import android.content.Intent import android.content.Intent
import android.media.MediaMetadataRetriever
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Environment
import android.provider.Settings
import android.util.Log import android.util.Log
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding 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.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier 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 import usr.empty.player.ui.theme.PlayerTheme
inline fun <T> nullifyException(block: () -> T) = try {
block()
} catch (_: Exception) {
null
}
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) 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)) startService(Intent(this, PlayerService::class.java))
enableEdgeToEdge() enableEdgeToEdge()
setContent { setContent {
PlayerTheme { 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 @Composable
fun MainLayout(modifier: Modifier = Modifier) { fun MainLayout(modifier: Modifier = Modifier) {
val notas = remember { mutableStateListOf<NotaDescriptor>() }
val pickAudioLauncher = rememberLauncherForActivityResult( val pickAudioLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.GetContent() ActivityResultContracts.GetContent()
) { audioUri -> ) { audioUri ->
audioUri?.run { audioUri?.run {
path?.let { uriToPath(this).let {
Log.d("meow", it) 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/*") pickAudioLauncher.launch("audio/*")
}) { }) {
Text("add track") 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()
}

@ -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 package usr.empty.player
class NotaPlayer { class NotaPlayer {
} }

@ -1,5 +1,6 @@
package usr.empty.player package usr.empty.player
import android.content.Intent
import android.util.Log import android.util.Log
import androidx.media3.common.MediaItem import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer 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.Futures
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
class PlayerService : MediaSessionService() { class PlayerService : MediaSessionService() {
private var mediaSession: MediaSession? = null private var mediaSession: MediaSession? = null
var player: ExoPlayer? = null var player: ExoPlayer? = null
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
player = ExoPlayer.Builder(this).build() with(ExoPlayer.Builder(this).build()) {
mediaSession = MediaSession.Builder(this, player!!) player = this
.setCallback(MediaSessionCallback()) mediaSession = MediaSession.Builder(this@PlayerService, this).setCallback(MediaSessionCallback()).build()
.build() }
Log.d("meow", "memememe") 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() { override fun onDestroy() {
player?.release() player?.release()
mediaSession?.release() mediaSession?.release()
@ -32,9 +45,7 @@ class PlayerService : MediaSessionService() {
private inner class MediaSessionCallback : MediaSession.Callback { private inner class MediaSessionCallback : MediaSession.Callback {
override fun onAddMediaItems( override fun onAddMediaItems(
mediaSession: MediaSession, mediaSession: MediaSession, controller: MediaSession.ControllerInfo, mediaItems: MutableList<MediaItem>
controller: MediaSession.ControllerInfo,
mediaItems: MutableList<MediaItem>
): ListenableFuture<MutableList<MediaItem>> { ): ListenableFuture<MutableList<MediaItem>> {
val updatedMediaItems = mediaItems.map { val updatedMediaItems = mediaItems.map {
it.buildUpon().setUri(it.mediaId).build() it.buildUpon().setUri(it.mediaId).build()

@ -7,49 +7,31 @@ import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color 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 @Composable
fun PlayerTheme( fun PlayerTheme(
darkTheme: Boolean = isSystemInDarkTheme(), darkTheme: Boolean = isSystemInDarkTheme(), // Dynamic color is available on Android 12+
// Dynamic color is available on Android 12+ dynamicColor: Boolean = true, content: @Composable () -> Unit
// dynamicColor: Boolean = true,
content: @Composable () -> Unit
) { ) {
val colorScheme = when { val colorScheme = when {
// dynamicColor -> { dynamicColor -> {
// val context = LocalContext.current if (darkTheme) darkColorScheme(
// if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) background = Color.Black
// } ) else lightColorScheme(
darkTheme -> DarkColorScheme background = Color.White
else -> LightColorScheme )
}
darkTheme -> darkColorScheme(
background = Color.Black
)
else -> lightColorScheme(
background = Color.White
)
} }
MaterialTheme( MaterialTheme(
colorScheme = colorScheme, colorScheme = colorScheme, typography = Typography, content = content
typography = Typography,
content = content
) )
} }

@ -1,16 +1,20 @@
[versions] [versions]
agp = "8.6.0" agp = "8.6.0"
core = "1.6.1"
kotlin = "1.9.0" kotlin = "1.9.0"
coreKtx = "1.13.1" coreKtx = "1.13.1"
junit = "4.13.2" junit = "4.13.2"
junitVersion = "1.2.1" junitVersion = "1.2.1"
espressoCore = "3.6.1" espressoCore = "3.6.1"
kotlinxSerializationJson = "1.7.1"
lifecycleRuntimeKtx = "2.6.1" lifecycleRuntimeKtx = "2.6.1"
activityCompose = "1.9.2" activityCompose = "1.9.2"
composeBom = "2024.04.01" composeBom = "2024.04.01"
media3Exoplayer = "1.4.1" media3Exoplayer = "1.4.1"
junitJupiter = "5.8.1"
[libraries] [libraries]
androidx-core = { module = "androidx.test:core", version.ref = "core" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-media3-common = { module = "androidx.media3:media3-common", version.ref = "media3Exoplayer" } androidx-media3-common = { module = "androidx.media3:media3-common", version.ref = "media3Exoplayer" }
androidx-media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3Exoplayer" } androidx-media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3Exoplayer" }
@ -29,6 +33,11 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" } androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version.ref = "junitJupiter" }
mockito-core = { module = "org.mockito:mockito-core", version = "5.10.0" }
mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version = "5.4.0" }
mockk = { module = "io.mockk:mockk", version = "1.13.10" }
[plugins] [plugins]
android-application = { id = "com.android.application", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" }

Loading…
Cancel
Save