mirror of
https://github.com/TeamVanced/VancedMicroG
synced 2025-01-30 04:27:32 +01:00
Profile Manager: Add configuration features
This commit is contained in:
parent
3bbae67fda
commit
6e21b52bfe
@ -7,10 +7,13 @@ package org.microg.gms.profile
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.res.XmlResourceParser
|
||||
import android.util.Log
|
||||
import org.microg.gms.settings.SettingsContract
|
||||
import org.microg.gms.settings.SettingsContract.Profile
|
||||
import org.microg.gms.utils.FileXmlResourceParser
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import kotlin.random.Random
|
||||
|
||||
@ -19,18 +22,67 @@ object ProfileManager {
|
||||
const val PROFILE_REAL = "real"
|
||||
const val PROFILE_AUTO = "auto"
|
||||
const val PROFILE_NATIVE = "native"
|
||||
const val PROFILE_USER = "user"
|
||||
const val PROFILE_SYSTEM = "system"
|
||||
|
||||
private var initialized = false
|
||||
private var activeProfile: String? = null
|
||||
|
||||
private fun getProfileFromSettings(context: Context) = SettingsContract.getSettings(context, Profile.getContentUri(context), arrayOf(Profile.PROFILE)) { it.getString(0) }
|
||||
private fun getAutoProfile(context: Context): String {
|
||||
private fun getUserProfileFile(context: Context): File = File(context.filesDir, "device_profile.xml")
|
||||
private fun getSystemProfileFile(context: Context): File = File("/system/etc/microg_device_profile.xml")
|
||||
private fun getProfileResId(context: Context, profile: String) = context.resources.getIdentifier("${context.packageName}:xml/profile_$profile".toLowerCase(Locale.US), null, null)
|
||||
|
||||
fun getConfiguredProfile(context: Context): String = SettingsContract.getSettings(context, Profile.getContentUri(context), arrayOf(Profile.PROFILE)) { it.getString(0) } ?: PROFILE_AUTO
|
||||
|
||||
fun getAutoProfile(context: Context): String {
|
||||
if (hasProfile(context, PROFILE_SYSTEM) && isAutoProfile(context, PROFILE_SYSTEM)) return PROFILE_SYSTEM
|
||||
val profile = "${android.os.Build.PRODUCT}_${android.os.Build.VERSION.SDK_INT}"
|
||||
if (hasProfile(context, profile)) return profile
|
||||
if (hasProfile(context, profile) && isAutoProfile(context, profile)) return profile
|
||||
return PROFILE_NATIVE
|
||||
}
|
||||
|
||||
private fun getProfileResId(context: Context, profile: String) = context.resources.getIdentifier("${context.packageName}:xml/profile_$profile".toLowerCase(Locale.US), null, null)
|
||||
private fun hasProfile(context: Context, profile: String): Boolean = getProfileResId(context, profile) != 0
|
||||
fun hasProfile(context: Context, profile: String): Boolean = when (profile) {
|
||||
PROFILE_AUTO -> hasProfile(context, getAutoProfile(context))
|
||||
PROFILE_NATIVE, PROFILE_REAL -> true
|
||||
PROFILE_USER -> getUserProfileFile(context).exists()
|
||||
PROFILE_SYSTEM -> getSystemProfileFile(context).exists()
|
||||
else -> getProfileResId(context, profile) != 0
|
||||
}
|
||||
|
||||
private fun getProfileXml(context: Context, profile: String): XmlResourceParser? = kotlin.runCatching {
|
||||
when (profile) {
|
||||
PROFILE_AUTO -> getProfileXml(context, getAutoProfile(context))
|
||||
PROFILE_NATIVE, PROFILE_REAL -> null
|
||||
PROFILE_USER -> FileXmlResourceParser(getUserProfileFile(context))
|
||||
PROFILE_SYSTEM -> FileXmlResourceParser(getSystemProfileFile(context))
|
||||
else -> {
|
||||
val profileResId = getProfileResId(context, profile)
|
||||
if (profileResId == 0) return@runCatching null
|
||||
context.resources.getXml(profileResId)
|
||||
}
|
||||
}
|
||||
}.getOrNull()
|
||||
|
||||
fun isAutoProfile(context: Context, profile: String): Boolean = kotlin.runCatching {
|
||||
when (profile) {
|
||||
PROFILE_AUTO -> false
|
||||
PROFILE_REAL -> false
|
||||
PROFILE_NATIVE -> true
|
||||
else -> getProfileXml(context, profile)?.use {
|
||||
var next = it.next()
|
||||
while (next != XmlPullParser.END_DOCUMENT) {
|
||||
when (next) {
|
||||
XmlPullParser.START_TAG -> when (it.name) {
|
||||
"profile" -> {
|
||||
return@use it.getAttributeBooleanValue(null, "auto", false)
|
||||
}
|
||||
}
|
||||
}
|
||||
next = it.next()
|
||||
}
|
||||
} == true
|
||||
}
|
||||
}.getOrDefault(false)
|
||||
|
||||
private fun getProfileData(context: Context, profile: String, realData: Map<String, String>): Map<String, String> {
|
||||
try {
|
||||
if (profile in listOf(PROFILE_REAL, PROFILE_NATIVE)) return realData
|
||||
@ -38,7 +90,7 @@ object ProfileManager {
|
||||
if (profileResId == 0) return realData
|
||||
val resultData = mutableMapOf<String, String>()
|
||||
resultData.putAll(realData)
|
||||
context.resources.getXml(profileResId).use {
|
||||
getProfileXml(context, profile)?.use {
|
||||
var next = it.next()
|
||||
while (next != XmlPullParser.END_DOCUMENT) {
|
||||
when (next) {
|
||||
@ -61,7 +113,7 @@ object ProfileManager {
|
||||
}
|
||||
}
|
||||
|
||||
private fun getActiveProfile(context: Context) = getProfileFromSettings(context).let { if (it != PROFILE_AUTO) it else getAutoProfile(context) }
|
||||
private fun getProfile(context: Context) = getConfiguredProfile(context).let { if (it != PROFILE_AUTO) it else getAutoProfile(context) }
|
||||
private fun getSerialFromSettings(context: Context): String? = SettingsContract.getSettings(context, Profile.getContentUri(context), arrayOf(Profile.SERIAL)) { it.getString(0) }
|
||||
private fun saveSerial(context: Context, serial: String) = SettingsContract.setSettings(context, Profile.getContentUri(context)) { put(Profile.SERIAL, serial) }
|
||||
|
||||
@ -99,18 +151,15 @@ object ProfileManager {
|
||||
|
||||
// From profile
|
||||
try {
|
||||
val profileResId = getProfileResId(context, profile)
|
||||
if (profileResId != 0) {
|
||||
context.resources.getXml(profileResId).use {
|
||||
var next = it.next()
|
||||
while (next != XmlPullParser.END_DOCUMENT) {
|
||||
when (next) {
|
||||
XmlPullParser.START_TAG -> when (it.name) {
|
||||
"serial" -> return it.getAttributeValue(null, "template")
|
||||
}
|
||||
getProfileXml(context, profile)?.use {
|
||||
var next = it.next()
|
||||
while (next != XmlPullParser.END_DOCUMENT) {
|
||||
when (next) {
|
||||
XmlPullParser.START_TAG -> when (it.name) {
|
||||
"serial" -> return it.getAttributeValue(null, "template")
|
||||
}
|
||||
next = it.next()
|
||||
}
|
||||
next = it.next()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
@ -118,18 +167,18 @@ object ProfileManager {
|
||||
}
|
||||
|
||||
// Fallback
|
||||
return "008741A0B2C4D6E8"
|
||||
return randomSerial("008741A0B2C4D6E8")
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
private fun getEffectiveProfileSerial(context: Context, profile: String): String {
|
||||
getSerialFromSettings(context)?.let { return it }
|
||||
fun getSerial(context: Context, profile: String = getProfile(context), local: Boolean = false): String {
|
||||
if (!local) getSerialFromSettings(context)?.let { return it }
|
||||
val serialTemplate = getProfileSerialTemplate(context, profile)
|
||||
val serial = when {
|
||||
profile == PROFILE_REAL && serialTemplate != android.os.Build.UNKNOWN -> serialTemplate
|
||||
else -> randomSerial(serialTemplate)
|
||||
}
|
||||
saveSerial(context, serial)
|
||||
if (!local) saveSerial(context, serial)
|
||||
return serial
|
||||
}
|
||||
|
||||
@ -210,29 +259,67 @@ object ProfileManager {
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyProfile(context: Context, profile: String) {
|
||||
val profileData = getProfileData(context, profile, getRealData()) ?: getRealData()
|
||||
private fun applyProfile(context: Context, profile: String, serial: String = getSerial(context, profile)) {
|
||||
val profileData = getProfileData(context, profile, getRealData())
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
for ((key, value) in profileData) {
|
||||
Log.v(TAG, "<data key=\"$key\" value=\"$value\" />")
|
||||
}
|
||||
}
|
||||
applyProfileData(profileData)
|
||||
Build.SERIAL = getEffectiveProfileSerial(context, profile)
|
||||
Build.SERIAL = serial
|
||||
Log.d(TAG, "Using Serial ${Build.SERIAL}")
|
||||
activeProfile = profile
|
||||
}
|
||||
|
||||
fun getProfileName(context: Context, profile: String): String? = getProfileName { getProfileXml(context, profile) }
|
||||
|
||||
private fun getProfileName(parserCreator: () -> XmlResourceParser?): String? = parserCreator()?.use {
|
||||
var next = it.next()
|
||||
while (next != XmlPullParser.END_DOCUMENT) {
|
||||
when (next) {
|
||||
XmlPullParser.START_TAG -> when (it.name) {
|
||||
"profile" -> {
|
||||
return@use it.getAttributeValue(null, "name")
|
||||
}
|
||||
}
|
||||
}
|
||||
next = it.next()
|
||||
}
|
||||
null
|
||||
}
|
||||
|
||||
fun setProfile(context: Context, profile: String?) {
|
||||
val changed = getProfile(context) != profile
|
||||
val newProfile = profile ?: PROFILE_AUTO
|
||||
val newSerial = if (changed) getSerial(context, newProfile, true) else getSerial(context)
|
||||
SettingsContract.setSettings(context, Profile.getContentUri(context)) {
|
||||
put(Profile.PROFILE, profile)
|
||||
put(Profile.SERIAL, null as String?)
|
||||
put(Profile.PROFILE, newProfile)
|
||||
if (changed) put(Profile.SERIAL, newSerial)
|
||||
}
|
||||
if (changed && activeProfile != null) applyProfile(context, newProfile, newSerial)
|
||||
}
|
||||
|
||||
fun importUserProfile(context: Context, file: File): Boolean {
|
||||
val profileName = getProfileName { FileXmlResourceParser(file) } ?: return false
|
||||
try {
|
||||
Log.d(TAG, "Importing user profile '$profileName'")
|
||||
file.copyTo(getUserProfileFile(context))
|
||||
if (activeProfile == PROFILE_USER) applyProfile(context, PROFILE_USER)
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, e)
|
||||
return false
|
||||
}
|
||||
applyProfile(context, profile ?: PROFILE_AUTO)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun ensureInitialized(context: Context) {
|
||||
synchronized(this) {
|
||||
if (initialized) return
|
||||
try {
|
||||
val profile = getActiveProfile(context)
|
||||
val profile = getProfile(context)
|
||||
if (activeProfile == profile) return
|
||||
applyProfile(context, profile)
|
||||
initialized = true
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, e)
|
||||
}
|
||||
|
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.utils
|
||||
|
||||
import android.content.res.XmlResourceParser
|
||||
import android.util.Xml
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import java.io.Closeable
|
||||
import java.io.File
|
||||
import java.io.FileReader
|
||||
import java.io.Reader
|
||||
|
||||
class FileXmlResourceParser(private val reader: Reader, private val parser: XmlPullParser = Xml.newPullParser()) :
|
||||
XmlResourceParser,
|
||||
XmlPullParser by parser,
|
||||
Closeable by reader {
|
||||
constructor(file: File) : this(FileReader(file))
|
||||
|
||||
init {
|
||||
parser.setInput(reader)
|
||||
}
|
||||
|
||||
override fun getAttributeNameResource(index: Int): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun getAttributeListValue(
|
||||
namespace: String?, attribute: String?,
|
||||
options: Array<String?>?, defaultValue: Int
|
||||
): Int {
|
||||
val s = getAttributeValue(namespace, attribute)
|
||||
return s?.toInt() ?: defaultValue
|
||||
}
|
||||
|
||||
override fun getAttributeBooleanValue(
|
||||
namespace: String?, attribute: String?,
|
||||
defaultValue: Boolean
|
||||
): Boolean {
|
||||
|
||||
val s = getAttributeValue(namespace, attribute)
|
||||
return s?.toBooleanStrictOrNull() ?: defaultValue
|
||||
}
|
||||
|
||||
override fun getAttributeResourceValue(
|
||||
namespace: String?, attribute: String?,
|
||||
defaultValue: Int
|
||||
): Int {
|
||||
val s = getAttributeValue(namespace, attribute)
|
||||
return s?.toInt() ?: defaultValue
|
||||
}
|
||||
|
||||
override fun getAttributeIntValue(
|
||||
namespace: String?, attribute: String?,
|
||||
defaultValue: Int
|
||||
): Int {
|
||||
val s = getAttributeValue(namespace, attribute)
|
||||
return s?.toInt() ?: defaultValue
|
||||
}
|
||||
|
||||
override fun getAttributeUnsignedIntValue(
|
||||
namespace: String?, attribute: String?,
|
||||
defaultValue: Int
|
||||
): Int {
|
||||
val s = getAttributeValue(namespace, attribute)
|
||||
return s?.toInt() ?: defaultValue
|
||||
}
|
||||
|
||||
override fun getAttributeFloatValue(
|
||||
namespace: String?, attribute: String?,
|
||||
defaultValue: Float
|
||||
): Float {
|
||||
val s = getAttributeValue(namespace, attribute)
|
||||
return s?.toFloat() ?: defaultValue
|
||||
}
|
||||
|
||||
override fun getAttributeListValue(
|
||||
index: Int,
|
||||
options: Array<String?>?, defaultValue: Int
|
||||
): Int {
|
||||
val s = getAttributeValue(index)
|
||||
return s?.toInt() ?: defaultValue
|
||||
}
|
||||
|
||||
override fun getAttributeBooleanValue(index: Int, defaultValue: Boolean): Boolean {
|
||||
val s = getAttributeValue(index)
|
||||
return s?.toBooleanStrictOrNull() ?: defaultValue
|
||||
}
|
||||
|
||||
override fun getAttributeResourceValue(index: Int, defaultValue: Int): Int {
|
||||
val s = getAttributeValue(index)
|
||||
return s?.toInt() ?: defaultValue
|
||||
}
|
||||
|
||||
override fun getAttributeIntValue(index: Int, defaultValue: Int): Int {
|
||||
val s = getAttributeValue(index)
|
||||
return s?.toInt() ?: defaultValue
|
||||
}
|
||||
|
||||
override fun getAttributeUnsignedIntValue(index: Int, defaultValue: Int): Int {
|
||||
val s = getAttributeValue(index)
|
||||
return s?.toInt() ?: defaultValue
|
||||
}
|
||||
|
||||
override fun getAttributeFloatValue(index: Int, defaultValue: Float): Float {
|
||||
val s = getAttributeValue(index)
|
||||
return s?.toFloat() ?: defaultValue
|
||||
}
|
||||
|
||||
override fun getIdAttribute(): String? {
|
||||
return getAttributeValue(null, "id")
|
||||
}
|
||||
|
||||
override fun getClassAttribute(): String? {
|
||||
return getAttributeValue(null, "class")
|
||||
}
|
||||
|
||||
override fun getIdAttributeResourceValue(defaultValue: Int): Int {
|
||||
return getAttributeResourceValue(null, "id", defaultValue)
|
||||
}
|
||||
|
||||
override fun getStyleAttribute(): Int {
|
||||
return getAttributeResourceValue(null, "style", 0)
|
||||
}
|
||||
}
|
@ -5,31 +5,122 @@
|
||||
|
||||
package org.microg.gms.ui
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.text.format.DateUtils
|
||||
import android.util.Log
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceCategory
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import com.google.android.gms.R
|
||||
import org.microg.gms.checkin.getCheckinServiceInfo
|
||||
import org.microg.gms.profile.ProfileManager
|
||||
import org.microg.gms.profile.ProfileManager.PROFILE_AUTO
|
||||
import org.microg.gms.profile.ProfileManager.PROFILE_NATIVE
|
||||
import org.microg.gms.profile.ProfileManager.PROFILE_REAL
|
||||
import org.microg.gms.profile.ProfileManager.PROFILE_SYSTEM
|
||||
import org.microg.gms.profile.ProfileManager.PROFILE_USER
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
|
||||
class DeviceRegistrationPreferencesFragment : PreferenceFragmentCompat() {
|
||||
private lateinit var deviceProfile: ListPreference
|
||||
private lateinit var importProfile: Preference
|
||||
private lateinit var serial: Preference
|
||||
private lateinit var statusCategory: PreferenceCategory
|
||||
private lateinit var status: Preference
|
||||
private lateinit var androidId: Preference
|
||||
private val handler = Handler()
|
||||
private val updateRunnable = Runnable { updateStatus() }
|
||||
private lateinit var profileFileImport: ActivityResultLauncher<String>
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
profileFileImport = registerForActivityResult(ActivityResultContracts.GetContent(), this::onFileSelected)
|
||||
}
|
||||
|
||||
private fun onFileSelected(uri: Uri?) {
|
||||
if (uri == null) return
|
||||
try {
|
||||
val context = requireContext()
|
||||
val file = File.createTempFile("profile_", ".xml", context.cacheDir)
|
||||
context.contentResolver.openInputStream(uri)?.use { inputStream ->
|
||||
FileOutputStream(file).use { inputStream.copyTo(it) }
|
||||
}
|
||||
val success = ProfileManager.importUserProfile(context, file)
|
||||
file.delete()
|
||||
if (success && ProfileManager.isAutoProfile(context, PROFILE_USER)) {
|
||||
ProfileManager.setProfile(context, PROFILE_USER)
|
||||
}
|
||||
updateStatus()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.preferences_device_registration)
|
||||
}
|
||||
|
||||
override fun onBindPreferences() {
|
||||
deviceProfile = preferenceScreen.findPreference("pref_device_profile") ?: deviceProfile
|
||||
importProfile = preferenceScreen.findPreference("pref_device_profile_import") ?: importProfile
|
||||
serial = preferenceScreen.findPreference("pref_device_serial") ?: serial
|
||||
statusCategory = preferenceScreen.findPreference("prefcat_device_registration_status") ?: statusCategory
|
||||
status = preferenceScreen.findPreference("pref_device_registration_status") ?: status
|
||||
androidId = preferenceScreen.findPreference("pref_device_registration_android_id") ?: androidId
|
||||
|
||||
deviceProfile.setOnPreferenceChangeListener { _, newValue ->
|
||||
ProfileManager.setProfile(requireContext(), newValue as String? ?: PROFILE_AUTO)
|
||||
updateStatus()
|
||||
true
|
||||
}
|
||||
importProfile.setOnPreferenceClickListener {
|
||||
profileFileImport.launch("text/xml")
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun configureProfilePreference() {
|
||||
val context = requireContext()
|
||||
val configuredProfile = ProfileManager.getConfiguredProfile(context)
|
||||
val autoProfile = ProfileManager.getAutoProfile(context)
|
||||
val autoProfileName = when (autoProfile) {
|
||||
PROFILE_NATIVE -> "Native"
|
||||
PROFILE_REAL -> "Real"
|
||||
else -> ProfileManager.getProfileName(context, autoProfile)
|
||||
}
|
||||
val profiles =
|
||||
mutableListOf(PROFILE_AUTO, PROFILE_NATIVE, PROFILE_REAL)
|
||||
val profileNames = mutableListOf("Automatic: $autoProfileName", "Native", "Real")
|
||||
if (ProfileManager.hasProfile(context, PROFILE_SYSTEM)) {
|
||||
profiles.add(PROFILE_SYSTEM)
|
||||
profileNames.add("System: ${ProfileManager.getProfileName(context, PROFILE_SYSTEM)}")
|
||||
}
|
||||
if (ProfileManager.hasProfile(context, PROFILE_USER)) {
|
||||
profiles.add(PROFILE_USER)
|
||||
profileNames.add("Custom: ${ProfileManager.getProfileName(context, PROFILE_USER)}")
|
||||
}
|
||||
for (profile in R.xml::class.java.declaredFields.map { it.name }
|
||||
.filter { it.startsWith("profile_") }
|
||||
.map { it.substring(8) }
|
||||
.sorted()) {
|
||||
val profileName = ProfileManager.getProfileName(context, profile)
|
||||
if (profileName != null) {
|
||||
profiles.add(profile)
|
||||
profileNames.add(profileName)
|
||||
}
|
||||
}
|
||||
deviceProfile.entryValues = profiles.toTypedArray()
|
||||
deviceProfile.entries = profileNames.toTypedArray()
|
||||
deviceProfile.value = configuredProfile
|
||||
deviceProfile.summary =
|
||||
profiles.indexOf(configuredProfile).takeIf { it >= 0 }?.let { profileNames[it] } ?: "Unknown"
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
@ -43,13 +134,19 @@ class DeviceRegistrationPreferencesFragment : PreferenceFragmentCompat() {
|
||||
}
|
||||
|
||||
private fun updateStatus() {
|
||||
handler.removeCallbacks(updateRunnable)
|
||||
handler.postDelayed(updateRunnable, UPDATE_INTERVAL)
|
||||
val appContext = requireContext().applicationContext
|
||||
lifecycleScope.launchWhenResumed {
|
||||
configureProfilePreference()
|
||||
serial.summary = ProfileManager.getSerial(appContext)
|
||||
val serviceInfo = getCheckinServiceInfo(appContext)
|
||||
statusCategory.isVisible = serviceInfo.configuration.enabled
|
||||
if (serviceInfo.lastCheckin > 0) {
|
||||
status.summary = getString(R.string.checkin_last_registration, DateUtils.getRelativeTimeSpanString(serviceInfo.lastCheckin, System.currentTimeMillis(), 0))
|
||||
status.summary = getString(
|
||||
R.string.checkin_last_registration,
|
||||
DateUtils.getRelativeTimeSpanString(serviceInfo.lastCheckin, System.currentTimeMillis(), 0)
|
||||
)
|
||||
androidId.isVisible = true
|
||||
androidId.summary = serviceInfo.androidId.toString(16)
|
||||
} else {
|
||||
|
@ -11,12 +11,18 @@
|
||||
android:title="Device profile">
|
||||
<ListPreference
|
||||
android:key="pref_device_profile"
|
||||
android:persistent="false"
|
||||
android:title="Select profile"
|
||||
tools:summary="Automatic (Google Pixel 3, Android 11)" />
|
||||
<Preference
|
||||
android:key="pref_device_profile_import"
|
||||
android:summary="Import device profile from file"
|
||||
android:title="Import profile" />
|
||||
android:title="Import custom profile" />
|
||||
<Preference
|
||||
android:enabled="false"
|
||||
android:key="pref_device_serial"
|
||||
android:title="Serial"
|
||||
tools:summary="123456" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
android:key="prefcat_device_registration_status"
|
||||
|
@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ SPDX-FileCopyrightText: 2021, microG Project Team
|
||||
~ SPDX-FileCopyrightText: 2021 microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<profile name="Google Nexus 5X (Android 8.1.0)" product="bullhead" sdk="27" id="bullhead_27">
|
||||
<profile name="Google Nexus 5X (Android 8.1.0)" product="bullhead" sdk="27" id="bullhead_27" auto="true">
|
||||
<!-- Data from OPM3.171019.016, Mar 2018 -->
|
||||
<data key="Build.BOARD" value="bullhead" />
|
||||
<data key="Build.BOOTLOADER" value="BHZ31b" />
|
||||
@ -27,6 +27,7 @@
|
||||
<data key="Build.VERSION.CODENAME" value="REL" />
|
||||
<data key="Build.VERSION.INCREMENTAL" value="6d95f5a143" />
|
||||
<data key="Build.VERSION.RELEASE" value="8.1.0" />
|
||||
<data key="Build.VERSION.SECURITY_PATCH" value="2021-10-05" />
|
||||
<data key="Build.VERSION.SDK" value="27" />
|
||||
<data key="Build.VERSION.SDK_INT" value="27" />
|
||||
<data key="Build.SUPPORTED_ABIS" value="arm64-v8a,armeabi-v7a,armeabi" />
|
||||
|
@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ SPDX-FileCopyrightText: 2022 microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<profile name="Motorola Moto G (LineageOS 14.1)" product="lineage_falcon" sdk="25" id="lineage_falcon_25" auto="true">
|
||||
<!-- Data from LineageOS 14.1-20190207-NIGHTLY-falcon, Feb 2019 -->
|
||||
<data key="Build.BOARD" value="MSM8226" />
|
||||
<data key="Build.BOOTLOADER" value="0x4118" />
|
||||
<data key="Build.BRAND" value="motorola" />
|
||||
<data key="Build.CPU_ABI" value="armeabi-v7a" />
|
||||
<data key="Build.CPU_ABI2" value="armeabi" />
|
||||
<data key="Build.DEVICE" value="falcon_umts" />
|
||||
<data key="Build.DISPLAY" value="lineage_falcon-userdebug 7.1.2 NJH47F f4535aec29" />
|
||||
<data key="Build.FINGERPRINT" value="motorola/falcon_retuglb/falcon_umts:5.1/LPB23.13-58/58:user/release-keys" />
|
||||
<data key="Build.HARDWARE" value="qcom" />
|
||||
<data key="Build.HOST" value="lineage-runner" />
|
||||
<data key="Build.ID" value="NJH47F" />
|
||||
<data key="Build.MANUFACTURER" value="motorola" />
|
||||
<data key="Build.MODEL" value="Moto G" />
|
||||
<data key="Build.PRODUCT" value="lineage_falcon" />
|
||||
<data key="Build.RADIO" value="unknown" />
|
||||
<data key="Build.TAGS" value="release-keys" />
|
||||
<data key="Build.TIME" value="1549548437000" />
|
||||
<data key="Build.TYPE" value="user" />
|
||||
<data key="Build.USER" value="gitlab-runner" />
|
||||
<data key="Build.VERSION.CODENAME" value="REL" />
|
||||
<data key="Build.VERSION.INCREMENTAL" value="f4535aec29" />
|
||||
<data key="Build.VERSION.RELEASE" value="7.1.2" />
|
||||
<data key="Build.VERSION.SECURITY_PATCH" value="2019-01-05" />
|
||||
<data key="Build.VERSION.SDK" value="25" />
|
||||
<data key="Build.VERSION.SDK_INT" value="25" />
|
||||
<data key="Build.SUPPORTED_ABIS" value="armeabi-v7a,armeabi" />
|
||||
|
||||
<serial template="TA9290XXXX" />
|
||||
</profile>
|
Loading…
x
Reference in New Issue
Block a user