Fix bugs and add AOSP Android.bp
- gen-db.py: fix DB output path (assets/ not res/raw/) - DeviceName: use Build.DEVICE for DB lookup, Build.PRODUCT for AltDeviceNames - DeviceName: fix capitalize() for API 36 (replaceFirstChar) - DBHelper: add 24h rate-limit on update checks via SharedPreferences - DBHelper: make DB download atomic (write to .tmp then rename) - DeviceInfo: wrap getCurrentDeviceInfo() in withContext(Dispatchers.IO) - DeviceInfo: add 30-day SharedPreferences cache for device image URLs - AltDeviceNames: rewrite with Entry class + byModel index for O(1) lookup - Android.bp: add android_library module for AOSP source build
This commit is contained in:
+14
@@ -0,0 +1,14 @@
|
||||
android_library {
|
||||
name: "android-device-info",
|
||||
srcs: ["library/src/main/java/**/*.kt"],
|
||||
asset_dirs: ["library/src/main/assets"],
|
||||
sdk_version: "current",
|
||||
min_sdk_version: "36",
|
||||
kotlincflags: ["-Xjvm-default=all"],
|
||||
static_libs: [
|
||||
"kotlinx-coroutines-android",
|
||||
"androidx.annotation_annotation",
|
||||
"androidx.core_core-ktx",
|
||||
],
|
||||
visibility: ["//visibility:public"],
|
||||
}
|
||||
@@ -7,7 +7,7 @@ from io import StringIO
|
||||
|
||||
# Constants
|
||||
CSV_URL = "https://storage.googleapis.com/play_public/supported_devices.csv"
|
||||
DB_PATH = "library/src/main/res/raw/android-devices.db"
|
||||
DB_PATH = "library/src/main/assets/android_devices.db"
|
||||
ZIP_PATH = "database/android-devices.zip"
|
||||
|
||||
# Ensure output directory exists
|
||||
|
||||
@@ -19,135 +19,101 @@ package dev.oxmc.androiddeviceinfo
|
||||
|
||||
object AltDeviceNames {
|
||||
/**
|
||||
* A map of device identifiers to their alternate names.
|
||||
* The key is a Triple containing:
|
||||
* - List of model names
|
||||
* - List of manufacturer names
|
||||
* - List of codenames (can be empty)
|
||||
*
|
||||
* The value is the alternate name for the device.
|
||||
* Represents a consumer device product, which may have multiple model numbers, manufacturer
|
||||
* names (e.g. rebrands), and codenames (Build.PRODUCT values). Empty codenames means the
|
||||
* entry matches regardless of codename.
|
||||
*/
|
||||
private val deviceNames: Map<Triple<List<String>, List<String>, List<String>>, String> = mapOf(
|
||||
private class Entry(
|
||||
models: List<String>,
|
||||
manufacturers: List<String>,
|
||||
codenames: List<String> = emptyList(),
|
||||
val name: String
|
||||
) {
|
||||
private val modelSet = models.mapTo(HashSet()) { it.uppercase() }
|
||||
private val mfrSet = manufacturers.mapTo(HashSet()) { it.uppercase() }
|
||||
// empty = match any codename; non-empty = must match one of these
|
||||
private val codenameSet = codenames.filterNot { it.isEmpty() }.mapTo(HashSet()) { it.uppercase() }
|
||||
val models: List<String> = models
|
||||
|
||||
fun matches(model: String, manufacturer: String, codename: String): Boolean =
|
||||
model.uppercase() in modelSet &&
|
||||
manufacturer.uppercase() in mfrSet &&
|
||||
(codenameSet.isEmpty() || codename.uppercase() in codenameSet)
|
||||
}
|
||||
|
||||
private val entries = listOf(
|
||||
// Meta / Oculus / Facebook
|
||||
Triple(listOf("Meta Quest 3"), listOf("Meta", "oculus"), listOf("")) to "Meta Quest 3",
|
||||
Triple(listOf("Meta Quest Pro"), listOf("Meta", "oculus"), listOf("")) to "Meta Quest Pro",
|
||||
Triple(listOf("Meta Quest 2"), listOf("Meta", "oculus"), listOf("")) to "Meta Quest 2",
|
||||
Triple(listOf("Meta Quest"), listOf("Meta", "oculus"), listOf("")) to "Meta Quest",
|
||||
Entry(listOf("Meta Quest 3"), listOf("Meta", "oculus"), name = "Meta Quest 3"),
|
||||
Entry(listOf("Meta Quest Pro"), listOf("Meta", "oculus"), name = "Meta Quest Pro"),
|
||||
Entry(listOf("Meta Quest 2"), listOf("Meta", "oculus"), name = "Meta Quest 2"),
|
||||
Entry(listOf("Meta Quest"), listOf("Meta", "oculus"), name = "Meta Quest"),
|
||||
|
||||
// ZTE
|
||||
Triple(listOf("Z6251"), listOf("ZTE"), listOf("Z6251")) to "ZMax 11",
|
||||
Entry(listOf("Z6251"), listOf("ZTE"), listOf("Z6251"), "ZMax 11"),
|
||||
|
||||
// OnePlus
|
||||
Triple(listOf("CPH2195"), listOf("Oppo"), listOf("")) to "OPPO A54 5G",
|
||||
Triple(listOf("OP595DL1", "CPH2583"), listOf("Oppo"), listOf("")) to "OnePlus 12",
|
||||
// OnePlus / Oppo
|
||||
Entry(listOf("CPH2195"), listOf("Oppo"), name = "OPPO A54 5G"),
|
||||
Entry(listOf("OP595DL1", "CPH2583"), listOf("Oppo"), name = "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)",
|
||||
// Emulators
|
||||
Entry(listOf("sdk_gphone_x86"), listOf("Google"), name = "Android SDK (x86)"),
|
||||
Entry(listOf("sdk_gphone_x86_64"), listOf("Google"), name = "Android SDK (x86_64)"),
|
||||
Entry(listOf("sdk_gphone_arm64"), listOf("Google"), name = "Android SDK (arm64)"),
|
||||
Entry(listOf("sdk_gphone_arm"), listOf("Google"), name = "Android SDK (arm)"),
|
||||
|
||||
// Google
|
||||
Triple(listOf("Pixel 6"), listOf("Google"), listOf("")) to "Pixel 6",
|
||||
Entry(listOf("Pixel 6"), listOf("Google"), name = "Pixel 6"),
|
||||
|
||||
// Motorola
|
||||
Triple(listOf("moto g power (2021)"), listOf("Motorola"), listOf("")) to "Moto G Power (2021)",
|
||||
Triple(listOf("XT2163-4"), listOf("Motorola"), listOf("")) to "Moto G Pure",
|
||||
Entry(listOf("moto g power (2021)"), listOf("Motorola"), name = "Moto G Power (2021)"),
|
||||
Entry(listOf("XT2163-4"), listOf("Motorola"), name = "Moto G Pure"),
|
||||
|
||||
// LG
|
||||
Triple(listOf("LGLS675", "LS675"), listOf("LG"), listOf("LG M1", "")) to "LG Tribute 5",
|
||||
Entry(listOf("LGLS675", "LS675"), listOf("LG"), listOf("LG M1"), "LG Tribute 5"),
|
||||
|
||||
// Samsung (some with empty codenames)
|
||||
Triple(listOf("SM-S928U"), listOf("SAMSUNG"), listOf("e3qsqw")) to "Galaxy S24 Ultra",
|
||||
Triple(listOf("SM-S901U"), listOf("SAMSUNG"), listOf("r0qsqw")) to "Samsung Galaxy S22",
|
||||
Triple(listOf("SM-A526U"), listOf("SAMSUNG"), listOf("")) to "Galaxy A52 5G (US)",
|
||||
Triple(listOf("SM-A326U"), listOf("SAMSUNG"), listOf("")) to "Galaxy A32 5G (US)",
|
||||
Triple(listOf("SM-G998U"), listOf("SAMSUNG"), listOf("")) to "Galaxy S21 Ultra 5G (US)",
|
||||
Triple(listOf("SM-G998W"), listOf("SAMSUNG"), listOf("p3qcsx")) to "Galaxy S21 Ultra 5G (CA)",
|
||||
Triple(listOf("SM-G991U"), listOf("SAMSUNG"), listOf("")) to "Galaxy S21 5G",
|
||||
Triple(listOf("SM-A125F"), listOf("SAMSUNG"), listOf("")) to "Galaxy A12",
|
||||
Triple(listOf("SM-T290"), listOf("SAMSUNG"), listOf("gtowifixx")) to "Galaxy Tab A8",
|
||||
Triple(listOf("SM-G780F"), listOf("SAMSUNG"), listOf("")) to "Galaxy S20 FE",
|
||||
Triple(listOf("SM-N986U"), listOf("SAMSUNG"), listOf("")) to "Galaxy Note 20 Ultra 5G",
|
||||
Triple(listOf("SM-N981U"), listOf("SAMSUNG"), listOf("")) to "Galaxy Note 20 5G",
|
||||
Triple(listOf("A013G"), listOf("SAMSUNG"), listOf("")) to "Galaxy A01 Core",
|
||||
Triple(listOf("SM-A715F"), listOf("SAMSUNG"), listOf("")) to "Galaxy A71",
|
||||
Triple(listOf("SM-A515F"), listOf("SAMSUNG"), listOf("")) to "Galaxy A51",
|
||||
Triple(listOf("SM-G975F"), listOf("SAMSUNG"), listOf("")) to "Galaxy S10+",
|
||||
Triple(listOf("SM-G973F"), listOf("SAMSUNG"), listOf("")) to "Galaxy S10",
|
||||
Triple(listOf("SM-G970F"), listOf("SAMSUNG"), listOf("")) to "Galaxy S10e",
|
||||
Triple(listOf("SM-N960F"), listOf("SAMSUNG"), listOf("")) to "Galaxy Note 9",
|
||||
Triple(listOf("SM-G965F"), listOf("SAMSUNG"), listOf("")) to "Galaxy S9+",
|
||||
Triple(listOf("SM-G960F"), listOf("SAMSUNG"), listOf("")) to "Galaxy S9",
|
||||
Triple(listOf("SM-N950F"), listOf("SAMSUNG"), listOf("")) to "Galaxy Note 8",
|
||||
Triple(listOf("SM-J730F"), listOf("SAMSUNG"), listOf("")) to "Galaxy J7 (2017)",
|
||||
Triple(listOf("SM-J530F"), listOf("SAMSUNG"), listOf("")) to "Galaxy J5 (2017)",
|
||||
Triple(listOf("SM-J330F"), listOf("SAMSUNG"), listOf("")) to "Galaxy J3 (2017)",
|
||||
Triple(listOf("SM-G955F"), listOf("SAMSUNG"), listOf("")) to "Galaxy S8+",
|
||||
Triple(listOf("SM-G950F"), listOf("SAMSUNG"), listOf("")) to "Galaxy S8"
|
||||
// Samsung
|
||||
Entry(listOf("SM-S928U"), listOf("SAMSUNG"), listOf("e3qsqw"), "Galaxy S24 Ultra"),
|
||||
Entry(listOf("SM-S901U"), listOf("SAMSUNG"), listOf("r0qsqw"), "Samsung Galaxy S22"),
|
||||
Entry(listOf("SM-A526U"), listOf("SAMSUNG"), name = "Galaxy A52 5G (US)"),
|
||||
Entry(listOf("SM-A326U"), listOf("SAMSUNG"), name = "Galaxy A32 5G (US)"),
|
||||
Entry(listOf("SM-G998U"), listOf("SAMSUNG"), name = "Galaxy S21 Ultra 5G (US)"),
|
||||
Entry(listOf("SM-G998W"), listOf("SAMSUNG"), listOf("p3qcsx"), "Galaxy S21 Ultra 5G (CA)"),
|
||||
Entry(listOf("SM-G991U"), listOf("SAMSUNG"), name = "Galaxy S21 5G"),
|
||||
Entry(listOf("SM-A125F"), listOf("SAMSUNG"), name = "Galaxy A12"),
|
||||
Entry(listOf("SM-T290"), listOf("SAMSUNG"), listOf("gtowifixx"), "Galaxy Tab A8"),
|
||||
Entry(listOf("SM-G780F"), listOf("SAMSUNG"), name = "Galaxy S20 FE"),
|
||||
Entry(listOf("SM-N986U"), listOf("SAMSUNG"), name = "Galaxy Note 20 Ultra 5G"),
|
||||
Entry(listOf("SM-N981U"), listOf("SAMSUNG"), name = "Galaxy Note 20 5G"),
|
||||
Entry(listOf("A013G"), listOf("SAMSUNG"), name = "Galaxy A01 Core"),
|
||||
Entry(listOf("SM-A715F"), listOf("SAMSUNG"), name = "Galaxy A71"),
|
||||
Entry(listOf("SM-A515F"), listOf("SAMSUNG"), name = "Galaxy A51"),
|
||||
Entry(listOf("SM-G975F"), listOf("SAMSUNG"), name = "Galaxy S10+"),
|
||||
Entry(listOf("SM-G973F"), listOf("SAMSUNG"), name = "Galaxy S10"),
|
||||
Entry(listOf("SM-G970F"), listOf("SAMSUNG"), name = "Galaxy S10e"),
|
||||
Entry(listOf("SM-N960F"), listOf("SAMSUNG"), name = "Galaxy Note 9"),
|
||||
Entry(listOf("SM-G965F"), listOf("SAMSUNG"), name = "Galaxy S9+"),
|
||||
Entry(listOf("SM-G960F"), listOf("SAMSUNG"), name = "Galaxy S9"),
|
||||
Entry(listOf("SM-N950F"), listOf("SAMSUNG"), name = "Galaxy Note 8"),
|
||||
Entry(listOf("SM-J730F"), listOf("SAMSUNG"), name = "Galaxy J7 (2017)"),
|
||||
Entry(listOf("SM-J530F"), listOf("SAMSUNG"), name = "Galaxy J5 (2017)"),
|
||||
Entry(listOf("SM-J330F"), listOf("SAMSUNG"), name = "Galaxy J3 (2017)"),
|
||||
Entry(listOf("SM-G955F"), listOf("SAMSUNG"), name = "Galaxy S8+"),
|
||||
Entry(listOf("SM-G950F"), listOf("SAMSUNG"), name = "Galaxy S8"),
|
||||
)
|
||||
|
||||
/**
|
||||
* Returns an alternate name for the device based on its model, manufacturer, and codename.
|
||||
* If no alternate name is found, returns null.
|
||||
*
|
||||
* @param model The model of the device.
|
||||
* @param manufacturer The manufacturer of the device.
|
||||
* @param codename The codename of the device (can be empty).
|
||||
* @return The alternate name or null if not found.
|
||||
*/
|
||||
private fun getAlternateName(model: String, manufacturer: String, codename: String): String? {
|
||||
return deviceNames.entries.find { (key, _) ->
|
||||
model in key.first &&
|
||||
manufacturer in key.second &&
|
||||
((key.third.isEmpty() && codename.isBlank()) || (key.third.isNotEmpty() && codename in key.third))
|
||||
}?.value
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an alternate name for the device based on its model and manufacturer.
|
||||
* If no alternate name is found, returns null.
|
||||
*
|
||||
* @param model The model of the device.
|
||||
* @param manufacturer The manufacturer of the device.
|
||||
* @param codename Optional codename of the device (can be null).
|
||||
* @return The alternate name or null if not found.
|
||||
*/
|
||||
private fun getAlternateNameFlexible(model: String, manufacturer: String, codename: String? = null): String? {
|
||||
return deviceNames.entries.find { (key, _) ->
|
||||
model in key.first &&
|
||||
manufacturer in key.second &&
|
||||
((key.third.isEmpty() && codename.isNullOrBlank()) || (key.third.isNotEmpty() && codename != null && codename in key.third))
|
||||
}?.value
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an alternate name for the device based on its model and manufacturer.
|
||||
* If no alternate name is found, returns null.
|
||||
*
|
||||
* @param model The model of the device.
|
||||
* @param manufacturer The manufacturer of the device.
|
||||
* @return The alternate name or null if not found.
|
||||
*/
|
||||
fun getAlternateNameOrDefault(model: String, manufacturer: String, codename: String, default: String): String {
|
||||
return if (codename.isBlank()) {
|
||||
getAlternateNameFlexible(model, manufacturer)
|
||||
} else {
|
||||
getAlternateName(model, manufacturer, codename)
|
||||
} ?: default
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the device identifiers are present in the alternate names map.
|
||||
*
|
||||
* @param model The model of the device.
|
||||
* @param manufacturer The manufacturer of the device.
|
||||
* @param codename The codename of the device (can be empty).
|
||||
* @return True if the identifiers are found, false otherwise.
|
||||
*/
|
||||
fun containsIdentifier(model: String, manufacturer: String, codename: String): Boolean {
|
||||
return deviceNames.entries.any { (key, _) ->
|
||||
model in key.first && manufacturer in key.second && codename in key.third
|
||||
// Index built at class init: model (uppercase) → entries that contain that model.
|
||||
// O(1) first-level lookup; secondary filter is O(small) since model collisions are rare.
|
||||
private val byModel: Map<String, List<Entry>> = buildMap<String, MutableList<Entry>> {
|
||||
for (entry in entries) {
|
||||
for (model in entry.models) {
|
||||
getOrPut(model.uppercase()) { mutableListOf() }.add(entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getAlternateNameOrDefault(model: String, manufacturer: String, codename: String, default: String): String =
|
||||
byModel[model.uppercase()]?.find { it.matches(model, manufacturer, codename) }?.name ?: default
|
||||
|
||||
fun containsIdentifier(model: String, manufacturer: String, codename: String): Boolean =
|
||||
byModel[model.uppercase()]?.any { it.matches(model, manufacturer, codename) } ?: false
|
||||
}
|
||||
|
||||
@@ -180,6 +180,10 @@ class DeviceDatabase(context: Context, private val autoUpdateDB: Boolean = true)
|
||||
}
|
||||
|
||||
private fun checkForUpdates() {
|
||||
val prefs = context.getSharedPreferences("device_db_prefs", Context.MODE_PRIVATE)
|
||||
val lastCheck = prefs.getLong("last_update_check", 0L)
|
||||
if (System.currentTimeMillis() - lastCheck < UPDATE_CHECK_INTERVAL_MS) return
|
||||
prefs.edit().putLong("last_update_check", System.currentTimeMillis()).apply()
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val url = URL(UPDATE_URL)
|
||||
@@ -203,22 +207,21 @@ class DeviceDatabase(context: Context, private val autoUpdateDB: Boolean = true)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Handle error during update check, e.g., log it
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun downloadAndUpdateDatabase(downloadUrl: String) = withContext(Dispatchers.IO) {
|
||||
val tempFile = File(file.parent, "$NAME.tmp")
|
||||
try {
|
||||
val url = URL(downloadUrl)
|
||||
val connection = url.openConnection() as HttpURLConnection
|
||||
val connection = URL(downloadUrl).openConnection() as HttpURLConnection
|
||||
connection.connect()
|
||||
context.deleteDatabase(NAME) // Delete old database
|
||||
val outputStream = FileOutputStream(file)
|
||||
connection.inputStream.copyTo(outputStream)
|
||||
outputStream.close()
|
||||
FileOutputStream(tempFile).use { connection.inputStream.copyTo(it) }
|
||||
context.deleteDatabase(NAME)
|
||||
tempFile.renameTo(file)
|
||||
} catch (e: IOException) {
|
||||
tempFile.delete()
|
||||
throw SQLException("Error downloading or updating $NAME database", e)
|
||||
}
|
||||
}
|
||||
@@ -260,8 +263,9 @@ class DeviceDatabase(context: Context, private val autoUpdateDB: Boolean = true)
|
||||
private const val COLUMN_CODENAME = "codename"
|
||||
private const val COLUMN_MODEL = "model"
|
||||
private const val NAME = "android_devices.db"
|
||||
private const val VERSION_CODE = 2 // Integer, increment for schema changes
|
||||
private const val VERSION_NAME = "1.0.1" // SemVer for content updates
|
||||
private const val VERSION_CODE = 2
|
||||
private const val VERSION_NAME = "1.0.1"
|
||||
private const val UPDATE_URL = "https://cdn.oxmc.me/android-devicenames/latest.json"
|
||||
private const val UPDATE_CHECK_INTERVAL_MS = 86_400_000L // 24 hours
|
||||
}
|
||||
}
|
||||
@@ -29,12 +29,12 @@ import java.net.URL
|
||||
import java.net.URLEncoder
|
||||
|
||||
object DeviceInfo {
|
||||
// API base URL and endpoints
|
||||
var api_url_base = "https://cdn.oxmc.me/api";
|
||||
var api_device = "device-info";
|
||||
var api_deviceSearch = "${api_device}/search-device";
|
||||
var api_reportDevice = "${api_device}/report-device-info";
|
||||
var api_version = "v2";
|
||||
var api_url_base = "https://cdn.oxmc.me/api"
|
||||
var api_device = "device-info"
|
||||
var api_deviceSearch = "${api_device}/search-device"
|
||||
var api_reportDevice = "${api_device}/report-device-info"
|
||||
var api_version = "v2"
|
||||
private const val IMAGE_CACHE_MS = 2_592_000_000L // 30 days
|
||||
|
||||
/**
|
||||
* Get device information including name, manufacturer, model, codename, and image URL.
|
||||
@@ -43,8 +43,9 @@ object DeviceInfo {
|
||||
* @return An array containing device name, manufacturer, model, codename, and image URL.
|
||||
*/
|
||||
suspend fun getDeviceInfo(context: Context): Array<String?> {
|
||||
// Get basic device information
|
||||
val deviceInfo = AndroidDeviceInfo.getCurrentDeviceInfo(context)
|
||||
val deviceInfo = withContext(Dispatchers.IO) {
|
||||
AndroidDeviceInfo.getCurrentDeviceInfo(context)
|
||||
}
|
||||
|
||||
// Set all the device information
|
||||
val deviceName = deviceInfo.name ?: "Unknown"
|
||||
@@ -101,34 +102,35 @@ object DeviceInfo {
|
||||
* @return The URL of the device image or null if not found.
|
||||
*/
|
||||
suspend fun getDeviceImage(context: Context, codename: String, model: String, manufacturer: String): String? {
|
||||
val prefs = context.getSharedPreferences("device_images", Context.MODE_PRIVATE)
|
||||
val cacheKey = "$codename:$model"
|
||||
prefs.getString(cacheKey, null)?.let { cached ->
|
||||
try {
|
||||
val json = JSONObject(cached)
|
||||
if (System.currentTimeMillis() - json.getLong("ts") < IMAGE_CACHE_MS) {
|
||||
val url = json.optString("url", "")
|
||||
return if (url.isEmpty()) null else url
|
||||
}
|
||||
} catch (_: Exception) {}
|
||||
}
|
||||
|
||||
val baseUrl = "${api_url_base}/${api_deviceSearch}/${api_version}"
|
||||
val queryParams = mutableListOf<String>()
|
||||
val fullUrl = "$baseUrl?codename=${URLEncoder.encode(codename, "UTF-8")}" +
|
||||
"&model=${URLEncoder.encode(model, "UTF-8")}" +
|
||||
"&manufacturer=${URLEncoder.encode(manufacturer, "UTF-8")}"
|
||||
|
||||
// Encode parameters to ensure they are URL-safe
|
||||
queryParams.add("codename=${URLEncoder.encode(codename, "UTF-8")}")
|
||||
queryParams.add("model=${URLEncoder.encode(model, "UTF-8")}")
|
||||
queryParams.add("manufacturer=${URLEncoder.encode(manufacturer, "UTF-8")}")
|
||||
|
||||
// Construct the full URL with query parameters
|
||||
val fullUrl = "$baseUrl?${queryParams.joinToString("&")}"
|
||||
|
||||
// Get the device image from the API
|
||||
var deviceImage: String? = null
|
||||
withContext(Dispatchers.IO) {
|
||||
var connection: HttpURLConnection? = null
|
||||
try {
|
||||
val url = URL(fullUrl)
|
||||
connection = url.openConnection() as HttpURLConnection
|
||||
connection = URL(fullUrl).openConnection() as HttpURLConnection
|
||||
connection.requestMethod = "GET"
|
||||
connection.connectTimeout = 5000
|
||||
connection.readTimeout = 5000
|
||||
|
||||
// Check the response code
|
||||
if (connection.responseCode == HttpURLConnection.HTTP_OK) {
|
||||
val response = connection.inputStream.bufferedReader().use(BufferedReader::readText)
|
||||
val jsonObject = JSONObject(response)
|
||||
deviceImage = jsonObject.optString("image")
|
||||
if (deviceImage.isEmpty()) deviceImage = null
|
||||
deviceImage = jsonObject.optString("image").ifEmpty { null }
|
||||
} else {
|
||||
println("HTTP Error: ${connection.responseCode} ${connection.responseMessage} for URL: $fullUrl")
|
||||
}
|
||||
@@ -138,7 +140,12 @@ object DeviceInfo {
|
||||
connection?.disconnect()
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
val cacheJson = JSONObject()
|
||||
cacheJson.put("url", deviceImage ?: "")
|
||||
cacheJson.put("ts", System.currentTimeMillis())
|
||||
prefs.edit().putString(cacheKey, cacheJson.toString()).apply()
|
||||
} catch (_: Exception) {}
|
||||
return deviceImage
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ object AndroidDeviceInfo {
|
||||
|
||||
@WorkerThread
|
||||
fun getCurrentDeviceInfo(context: Context?): DeviceInfo {
|
||||
val codename = Build.PRODUCT
|
||||
val codename = Build.DEVICE
|
||||
val model = Build.MODEL
|
||||
|
||||
val cachedInfo = getDeviceInfoFromCache(context, codename, model)
|
||||
@@ -67,8 +67,8 @@ object AndroidDeviceInfo {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
// Attempt to get the alternate name of the device
|
||||
val altName = AltDeviceNames.getAlternateNameOrDefault(capitalize(model), capitalize(manufacturer), codename, codename)
|
||||
val product = Build.PRODUCT
|
||||
val altName = AltDeviceNames.getAlternateNameOrDefault(capitalize(model), capitalize(manufacturer), product, product)
|
||||
val finalDeviceName: String = if (!TextUtils.isEmpty(altName)) {
|
||||
altName
|
||||
} else {
|
||||
@@ -125,7 +125,7 @@ object AndroidDeviceInfo {
|
||||
if (TextUtils.isEmpty(str)) {
|
||||
return str
|
||||
}
|
||||
return str.uppercase()
|
||||
return str.replaceFirstChar { it.uppercaseChar() }
|
||||
}
|
||||
|
||||
class DeviceInfo @JvmOverloads constructor(
|
||||
|
||||
Reference in New Issue
Block a user