diff --git a/build.gradle.kts b/build.gradle.kts index ecf7a23..a81b269 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,4 +3,5 @@ plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.android.library) apply false + alias(libs.plugins.compose.compiler) apply false } \ No newline at end of file diff --git a/demo/build.gradle.kts b/demo/build.gradle.kts index f35d50f..1ff3e47 100644 --- a/demo/build.gradle.kts +++ b/demo/build.gradle.kts @@ -1,6 +1,7 @@ plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) + alias(libs.plugins.compose.compiler) } android { @@ -24,6 +25,12 @@ android { ) } } + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = libs.versions.compose.get() as String? + } compileOptions { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 @@ -36,7 +43,20 @@ android { dependencies { implementation(libs.androidx.core.ktx) implementation(libs.androidx.appcompat) - implementation(libs.material) - implementation(libs.coil) + implementation(libs.androidx.material) + + // Compose UI + implementation(libs.compose.ui) + implementation(libs.compose.ui.tooling) + implementation(libs.compose.ui.tooling.preview) + implementation(libs.compose.foundation) + implementation(libs.compose.runtime) + implementation(libs.compose.material3) + implementation(libs.compose.activity) + + // Coil for Compose + implementation(libs.coil.compose) + + // Library implementation(project(":library")) } \ No newline at end of file diff --git a/demo/src/main/java/dev/oxmc/androiddeviceinfo_demo/MainActivity.kt b/demo/src/main/java/dev/oxmc/androiddeviceinfo_demo/MainActivity.kt index e25784d..9758c26 100644 --- a/demo/src/main/java/dev/oxmc/androiddeviceinfo_demo/MainActivity.kt +++ b/demo/src/main/java/dev/oxmc/androiddeviceinfo_demo/MainActivity.kt @@ -1,101 +1,201 @@ package dev.oxmc.androiddeviceinfo_demo import android.annotation.SuppressLint +import android.os.Build import android.os.Bundle -import android.view.Gravity -import android.widget.ImageView -import android.widget.LinearLayout -import android.widget.TextView -import androidx.appcompat.app.AppCompatActivity -import androidx.lifecycle.lifecycleScope -import coil.load +import android.provider.Settings +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage import dev.oxmc.androiddevicenames.DeviceInfo import dev.oxmc.androiddevicenames.AndroidInfo +import dev.oxmc.androiddeviceinfo_demo.ui.theme.AndroidDeviceInfoTheme import kotlinx.coroutines.launch -class MainActivity : AppCompatActivity() { - - private lateinit var layout: LinearLayout - private lateinit var infoView: TextView - private lateinit var imageView: ImageView - - @SuppressLint("SetTextI18n") +class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - - supportActionBar?.title = "Android Device Info Demo" - - // Main container layout - val rootLayout = LinearLayout(this).apply { - orientation = LinearLayout.VERTICAL - layoutParams = LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.MATCH_PARENT - ) - // Add top padding to avoid action bar (56dp is standard action bar height) - setPadding(0, 56, 0, 0) - } - - // Content layout that will be centered - layout = LinearLayout(this).apply { - orientation = LinearLayout.VERTICAL - layoutParams = LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT - ).apply { - // This centers the content vertically - gravity = Gravity.CENTER - } - // Internal padding for the content - setPadding(32, 32, 32, 32) - } - - infoView = TextView(this).apply { - textSize = 16f - setPadding(0, 16, 0, 16) - } - - imageView = ImageView(this).apply { - layoutParams = LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - 600 - ).apply { - // Add margins around the image - setMargins(0, 16, 0, 16) - } - scaleType = ImageView.ScaleType.FIT_CENTER - adjustViewBounds = true - } - - layout.addView(imageView) - layout.addView(infoView) - rootLayout.addView(layout) - setContentView(rootLayout) - - lifecycleScope.launch { - try { - val (deviceName, manufacturer, model, codename, imageUrl) = - DeviceInfo.getDeviceInfo(this@MainActivity) - val deviceType = AndroidInfo.getType(this@MainActivity) - - infoView.text = """ - Device Name: $deviceName - Manufacturer: $manufacturer - Model: $model - Codename: $codename - Device Type: $deviceType - Android Version: ${AndroidInfo.Version.release} (SDK ${AndroidInfo.Version.sdkInt}) - Image URL: ${imageUrl ?: "N/A"} - """.trimIndent() - - imageUrl?.let { url -> - imageView.load(url) { - crossfade(true) - } + setContent { + AndroidDeviceInfoTheme { + Surface(modifier = Modifier.fillMaxSize()) { + DeviceInfoScreen() } - } catch (e: Exception) { - infoView.text = "Error loading device info: ${e.message}" } } } +} + +@SuppressLint("HardwareIds", "MissingPermission") +@Composable +fun DeviceInfoScreen() { + val context = LocalContext.current + val coroutineScope = rememberCoroutineScope() + + var deviceName: String? by remember { mutableStateOf("Unknown") } + var manufacturer: String? by remember { mutableStateOf("Unknown") } + var model: String? by remember { mutableStateOf("Unknown") } + var codename: String? by remember { mutableStateOf("Unknown") } + var imageUrl by remember { mutableStateOf(null) } + var deviceType by remember { mutableStateOf("Unknown") } + var androidVersion by remember { mutableStateOf("Unknown") } + var serial by remember { mutableStateOf("Unknown") } + var isLoading by remember { mutableStateOf(true) } + + LaunchedEffect(Unit) { + coroutineScope.launch { + try { + val (name, manu, mod, code, url) = DeviceInfo.getDeviceInfo(context) + deviceName = name + manufacturer = manu + model = mod + codename = code + imageUrl = url + deviceType = AndroidInfo.getType(context).toString() + androidVersion = "${AndroidInfo.Version.release} (SDK ${AndroidInfo.Version.sdkInt})" + + serial = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + Build.SERIAL.takeIf { it != Build.UNKNOWN } + ?: Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID) + } else { + try { + Build.getSerial() + } catch (e: SecurityException) { + Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID) + } + } + } catch (_: Exception) { } + isLoading = false + } + } + + if (isLoading) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally + ) { + CircularProgressIndicator() + Text( + text = "Loading device info...", + style = MaterialTheme.typography.bodyLarge, + modifier = Modifier.padding(top = 25.dp), + textAlign = TextAlign.Center + ) + } + } + } else { + Box( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + contentAlignment = Alignment.Center + ) { + Card( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp), + shape = MaterialTheme.shapes.extraLarge, + elevation = CardDefaults.cardElevation(defaultElevation = 8.dp), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surface, + contentColor = MaterialTheme.colorScheme.onSurface + ) + ) { + Column( + modifier = Modifier + .padding(24.dp) + .fillMaxWidth() + .verticalScroll(rememberScrollState()), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text("Device Info", style = MaterialTheme.typography.headlineSmall) + Spacer(modifier = Modifier.height(16.dp)) + + imageUrl?.let { + AsyncImage( + model = it, + contentDescription = "Device Image", + modifier = Modifier + .size(180.dp) + .padding(8.dp) + ) + } ?: run { + Image( + painter = painterResource(id = R.drawable.unknown_device), + contentDescription = "Unknown device", + modifier = Modifier + .size(180.dp) + .padding(8.dp) + ) + } + + Spacer(modifier = Modifier.height(16.dp)) + + InfoText("Name", deviceName) + InfoText("Manufacturer", manufacturer) + InfoText("Model", model) + InfoText("Codename", codename) + InfoText("Serial", serial) + InfoText("Type", deviceType) + InfoText("Android", androidVersion) + + // If the device has no image or the information is unknown, show a message + if (imageUrl == null || deviceName == "Unknown") { + Text( + text = "Unable to determine device details from the model. If you know the model or would like to upload this information to improve detection, please use the button below:", + style = MaterialTheme.typography.bodyMedium, + textAlign = TextAlign.Center + ) + Spacer(modifier = Modifier.height(8.dp)) + Button( + onClick = { + coroutineScope.launch { + DeviceInfo.reportDeviceInfo( + context, + deviceName ?: "Unknown", + manufacturer ?: "Unknown", + model ?: "Unknown", + codename ?: "Unknown" + ) + } + }, + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Text( + text = "Upload Phone Data", + style = MaterialTheme.typography.bodyLarge, + textAlign = TextAlign.Center + ) + } + } + } + } + } + } +} + +@Composable +fun InfoText(label: String, value: String?) { + Text( + text = "$label: $value", + style = MaterialTheme.typography.bodyLarge, + modifier = Modifier.padding(vertical = 2.dp), + textAlign = TextAlign.Center + ) } \ No newline at end of file diff --git a/demo/src/main/java/dev/oxmc/androiddeviceinfo_demo/ui/theme/AndroidDeviceInfoTheme.kt b/demo/src/main/java/dev/oxmc/androiddeviceinfo_demo/ui/theme/AndroidDeviceInfoTheme.kt new file mode 100644 index 0000000..144bcb3 --- /dev/null +++ b/demo/src/main/java/dev/oxmc/androiddeviceinfo_demo/ui/theme/AndroidDeviceInfoTheme.kt @@ -0,0 +1,42 @@ +package dev.oxmc.androiddeviceinfo_demo.ui.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color + +private val LightColors = lightColorScheme( + primary = Color(0xFFB388FF), // Lavender + onPrimary = Color.White, + secondary = Color(0xFF7C4DFF), // Purple + onSecondary = Color.White, + background = Color(0xFFD2AEDC), // Light Lavender + onBackground = Color(0xFF2A003F), // Dark Purple + surface = Color.White, + onSurface = Color(0xFF2A003F) // Dark Purple +) + +private val DarkColors = darkColorScheme( + primary = Color(0xFFD1B3FF), // Light Lavender (for contrast) + onPrimary = Color.Black, + secondary = Color(0xFFB388FF), + onSecondary = Color.Black, + background = Color(0xFF1B0030), + onBackground = Color.White, + surface = Color(0xFF2A003F), + onSurface = Color.White +) + +@Composable +fun AndroidDeviceInfoTheme( + useDarkTheme: Boolean = isSystemInDarkTheme(), + content: @Composable () -> Unit +) { + val colorScheme = if (useDarkTheme) DarkColors else LightColors + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography(), + content = content + ) +} \ No newline at end of file diff --git a/demo/src/main/res/drawable/unknown_device.xml b/demo/src/main/res/drawable/unknown_device.xml new file mode 100644 index 0000000..f4318af --- /dev/null +++ b/demo/src/main/res/drawable/unknown_device.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/demo/src/main/res/values-night/themes.xml b/demo/src/main/res/values-night/themes.xml deleted file mode 100644 index 7c79dfa..0000000 --- a/demo/src/main/res/values-night/themes.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - \ No newline at end of file diff --git a/demo/src/main/res/values-v23/themes.xml b/demo/src/main/res/values-v23/themes.xml new file mode 100644 index 0000000..07a4211 --- /dev/null +++ b/demo/src/main/res/values-v23/themes.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/demo/src/main/res/values/colors.xml b/demo/src/main/res/values/colors.xml index f8c6127..4315ae8 100644 --- a/demo/src/main/res/values/colors.xml +++ b/demo/src/main/res/values/colors.xml @@ -1,10 +1,10 @@ - #FFBB86FC - #FF6200EE - #FF3700B3 - #FF03DAC5 - #FF018786 - #FF000000 - #FFFFFFFF + #B388FF + #7C4DFF + #FFFFFF + #7C4DFF + #FFFFFF + #F3E5F5 + #2A003F \ No newline at end of file diff --git a/demo/src/main/res/values/themes.xml b/demo/src/main/res/values/themes.xml index b632bbf..335179b 100644 --- a/demo/src/main/res/values/themes.xml +++ b/demo/src/main/res/values/themes.xml @@ -1,14 +1,16 @@ - - diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0fbb5df..e80c7c9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,17 +3,30 @@ agp = "8.12.0" kotlin = "2.0.21" coreKtx = "1.10.1" appcompat = "1.6.1" -material = "1.12.0" +material = "1.9.0" +material3 = "1.2.1" +compose = "1.6.0" coil = "2.5.0" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } -material = { group = "com.google.android.material", name = "material", version.ref = "material" } -coil = { group = "io.coil-kt", name = "coil", version.ref = "coil" } +androidx-material = { group = "com.google.android.material", name = "material", version.ref = "material" } + +# Compose core libraries +compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "compose" } +compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "compose" } +compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview", version.ref = "compose" } +compose-foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "compose" } +compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" } +compose-runtime = { group = "androidx.compose.runtime", name = "runtime", version.ref = "compose" } +compose-activity = { group = "androidx.activity", name = "activity-compose", version = "1.8.2" } + +# Coil for Compose +coil-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } android-library = { id = "com.android.library", version.ref = "agp" } - +compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } diff --git a/library/build.gradle.kts b/library/build.gradle.kts index 204564b..d47d40e 100644 --- a/library/build.gradle.kts +++ b/library/build.gradle.kts @@ -33,5 +33,4 @@ android { dependencies { implementation(libs.androidx.core.ktx) implementation(libs.androidx.appcompat) - implementation(libs.material) } \ No newline at end of file diff --git a/library/src/main/java/dev/oxmc/androiddevicenames/AltDeviceNames.kt b/library/src/main/java/dev/oxmc/androiddevicenames/AltDeviceNames.kt index ffa1942..61786a9 100644 --- a/library/src/main/java/dev/oxmc/androiddevicenames/AltDeviceNames.kt +++ b/library/src/main/java/dev/oxmc/androiddevicenames/AltDeviceNames.kt @@ -41,6 +41,12 @@ object AltDeviceNames { Triple(listOf("CPH2195"), listOf("Oppo"), listOf("")) to "OPPO A54 5G", Triple(listOf("OP595DL1", "CPH2583"), listOf("Oppo"), listOf("")) to "OnePlus 12", + // Emulators/sdk + Triple(listOf("sdk_gphone_x86"), listOf("Google"), listOf("")) to "Android SDK (x86)", + Triple(listOf("sdk_gphone_x86_64"), listOf("Google"), listOf("")) to "Android SDK (x86_64)", + Triple(listOf("sdk_gphone_arm64"), listOf("Google"), listOf("")) to "Android SDK (arm64)", + Triple(listOf("sdk_gphone_arm"), listOf("Google"), listOf("")) to "Android SDK (arm)", + // Google Triple(listOf("Pixel 6"), listOf("Google"), listOf("")) to "Pixel 6", diff --git a/settings.gradle.kts b/settings.gradle.kts index 302f565..220019f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,6 +19,6 @@ dependencyResolutionManagement { } } -rootProject.name = "AndroidDeviceNames" +rootProject.name = "AndroidDeviceInfo" include(":demo") include(":library")