diff --git a/play-services-base/core/src/main/AndroidManifest.xml b/play-services-base/core/src/main/AndroidManifest.xml
index aebc21a461..5f539647cc 100644
--- a/play-services-base/core/src/main/AndroidManifest.xml
+++ b/play-services-base/core/src/main/AndroidManifest.xml
@@ -5,10 +5,17 @@
-->
+
+
+
+ android:exported="true"
+ android:readPermission="${applicationId}.permission.READ_SETTINGS"
+ android:writePermission="${applicationId}.permission.WRITE_SETTINGS" />
diff --git a/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt b/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt
index 2d59c173b2..56aa4bd61f 100644
--- a/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt
+++ b/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt
@@ -171,6 +171,18 @@ object SettingsContract {
)
}
+ object Vending {
+ private const val id = "vending"
+ fun getContentUri(context: Context) = Uri.withAppendedPath(getAuthorityUri(context), id)
+ fun getContentType(context: Context) = "vnd.android.cursor.item/vnd.${getAuthority(context)}.$id"
+
+ const val LICENSING = "vending_licensing"
+
+ val PROJECTION = arrayOf(
+ LICENSING
+ )
+ }
+
private fun withoutCallingIdentity(f: () -> T): T {
val identity = Binder.clearCallingIdentity()
try {
diff --git a/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt b/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt
index 01d51a31a0..8bcfd1ecf5 100644
--- a/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt
+++ b/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt
@@ -22,6 +22,7 @@ import org.microg.gms.settings.SettingsContract.DroidGuard
import org.microg.gms.settings.SettingsContract.Exposure
import org.microg.gms.settings.SettingsContract.Gcm
import org.microg.gms.settings.SettingsContract.Location
+import org.microg.gms.settings.SettingsContract.Vending
import org.microg.gms.settings.SettingsContract.Profile
import org.microg.gms.settings.SettingsContract.SafetyNet
import org.microg.gms.settings.SettingsContract.getAuthority
@@ -78,6 +79,7 @@ class SettingsProvider : ContentProvider() {
DroidGuard.getContentUri(context!!) -> queryDroidGuard(projection ?: DroidGuard.PROJECTION)
Profile.getContentUri(context!!) -> queryProfile(projection ?: Profile.PROJECTION)
Location.getContentUri(context!!) -> queryLocation(projection ?: Location.PROJECTION)
+ Vending.getContentUri(context!!) -> queryVending(projection ?: Vending.PROJECTION)
else -> null
}
@@ -98,6 +100,7 @@ class SettingsProvider : ContentProvider() {
DroidGuard.getContentUri(context!!) -> updateDroidGuard(values)
Profile.getContentUri(context!!) -> updateProfile(values)
Location.getContentUri(context!!) -> updateLocation(values)
+ Vending.getContentUri(context!!) -> updateVending(values)
else -> return 0
}
return 1
@@ -335,6 +338,25 @@ class SettingsProvider : ContentProvider() {
editor.apply()
}
+ private fun queryVending(p: Array): Cursor = MatrixCursor(p).addRow(p) { key ->
+ when (key) {
+ Vending.LICENSING -> getSettingsBoolean(key, false)
+ else -> throw IllegalArgumentException("Unknown key: $key")
+ }
+ }
+
+ private fun updateVending(values: ContentValues) {
+ if (values.size() == 0) return
+ val editor = preferences.edit()
+ values.valueSet().forEach { (key, value) ->
+ when (key) {
+ Vending.LICENSING -> editor.putBoolean(key, value as Boolean)
+ else -> throw IllegalArgumentException("Unknown key: $key")
+ }
+ }
+ editor.apply()
+ }
+
private fun MatrixCursor.addRow(
p: Array,
valueGetter: (String) -> Any?
diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/SettingsFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/SettingsFragment.kt
index 6d3d6f7d23..703f80914a 100644
--- a/play-services-core/src/main/kotlin/org/microg/gms/ui/SettingsFragment.kt
+++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/SettingsFragment.kt
@@ -15,6 +15,7 @@ import com.google.android.gms.R
import org.microg.gms.checkin.CheckinPreferences
import org.microg.gms.gcm.GcmDatabase
import org.microg.gms.gcm.GcmPrefs
+import org.microg.gms.vending.VendingPreferences
import org.microg.gms.safetynet.SafetyNetPreferences
import org.microg.gms.ui.settings.SettingsProvider
import org.microg.gms.ui.settings.getAllSettingsProviders
@@ -42,11 +43,18 @@ class SettingsFragment : ResourceSettingsFragment() {
findNavController().navigate(requireContext(), R.id.openLocationSettings)
true
}
- findPreference(PREF_ABOUT)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
- findNavController().navigate(requireContext(), R.id.openAbout)
+ findPreference(PREF_VENDING)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
+ findNavController().navigate(requireContext(), R.id.openVendingSettings)
true
}
- findPreference(PREF_ABOUT)!!.summary = getString(org.microg.tools.ui.R.string.about_version_str, AboutFragment.getSelfVersion(context))
+
+ findPreference(PREF_ABOUT)!!.apply {
+ onPreferenceClickListener = Preference.OnPreferenceClickListener {
+ findNavController().navigate(requireContext(), R.id.openAbout)
+ true
+ }
+ summary = getString(org.microg.tools.ui.R.string.about_version_str, AboutFragment.getSelfVersion(context))
+ }
for (entry in getAllSettingsProviders(requireContext()).flatMap { it.getEntriesStatic(requireContext()) }) {
entry.createPreference()
@@ -101,6 +109,7 @@ class SettingsFragment : ResourceSettingsFragment() {
findPreference(PREF_CHECKIN)!!.setSummary(if (CheckinPreferences.isEnabled(requireContext())) org.microg.gms.base.core.R.string.service_status_enabled_short else org.microg.gms.base.core.R.string.service_status_disabled_short)
findPreference(PREF_SNET)!!.setSummary(if (SafetyNetPreferences.isEnabled(requireContext())) org.microg.gms.base.core.R.string.service_status_enabled_short else org.microg.gms.base.core.R.string.service_status_disabled_short)
+ findPreference(PREF_VENDING)!!.setSummary(if (VendingPreferences.isLicensingEnabled(requireContext())) R.string.pref_vending_summary_licensing_on else R.string.pref_vending_summary_licensing_off)
lifecycleScope.launchWhenResumed {
val entries = getAllSettingsProviders(requireContext()).flatMap { it.getEntriesDynamic(requireContext()) }
@@ -121,6 +130,7 @@ class SettingsFragment : ResourceSettingsFragment() {
const val PREF_SNET = "pref_snet"
const val PREF_LOCATION = "pref_location"
const val PREF_CHECKIN = "pref_checkin"
+ const val PREF_VENDING = "pref_vending"
}
init {
diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/VendingFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/VendingFragment.kt
new file mode 100644
index 0000000000..72750dcc4b
--- /dev/null
+++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/VendingFragment.kt
@@ -0,0 +1,54 @@
+/*
+ * SPDX-FileCopyrightText: 2023, e Foundation
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.microg.gms.ui
+
+import android.annotation.SuppressLint
+import android.os.Bundle
+import androidx.lifecycle.lifecycleScope
+import androidx.preference.Preference
+import androidx.preference.PreferenceFragmentCompat
+import androidx.preference.TwoStatePreference
+import com.google.android.gms.R
+import org.microg.gms.vending.VendingPreferences
+
+class VendingFragment : PreferenceFragmentCompat() {
+ private lateinit var licensingEnabled: TwoStatePreference
+
+ override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+ addPreferencesFromResource(R.xml.preferences_vending)
+ }
+
+ @SuppressLint("RestrictedApi")
+ override fun onBindPreferences() {
+ licensingEnabled = preferenceScreen.findPreference(PREF_LICENSING_ENABLED) ?: licensingEnabled
+ licensingEnabled.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
+ val appContext = requireContext().applicationContext
+ lifecycleScope.launchWhenResumed {
+ if (newValue is Boolean) {
+ VendingPreferences.setLicensingEnabled(appContext, newValue)
+ }
+ updateContent()
+ }
+ true
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ updateContent()
+ }
+
+ private fun updateContent() {
+ val appContext = requireContext().applicationContext
+ lifecycleScope.launchWhenResumed {
+ licensingEnabled.isChecked = VendingPreferences.isLicensingEnabled(appContext)
+ }
+ }
+
+ companion object {
+ const val PREF_LICENSING_ENABLED = "vending_licensing"
+ }
+}
\ No newline at end of file
diff --git a/play-services-core/src/main/kotlin/org/microg/gms/vending/VendingPreferences.kt b/play-services-core/src/main/kotlin/org/microg/gms/vending/VendingPreferences.kt
new file mode 100644
index 0000000000..a3d631d3c1
--- /dev/null
+++ b/play-services-core/src/main/kotlin/org/microg/gms/vending/VendingPreferences.kt
@@ -0,0 +1,26 @@
+/*
+ * SPDX-FileCopyrightText: 2023, e Foundation
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.microg.gms.vending
+
+import android.content.Context
+import org.microg.gms.settings.SettingsContract
+
+object VendingPreferences {
+ @JvmStatic
+ fun isLicensingEnabled(context: Context): Boolean {
+ val projection = arrayOf(SettingsContract.Vending.LICENSING)
+ return SettingsContract.getSettings(context, SettingsContract.Vending.getContentUri(context), projection) { c ->
+ c.getInt(0) != 0
+ }
+ }
+
+ @JvmStatic
+ fun setLicensingEnabled(context: Context, enabled: Boolean) {
+ SettingsContract.setSettings(context, SettingsContract.Vending.getContentUri(context)) {
+ put(SettingsContract.Vending.LICENSING, enabled)
+ }
+ }
+}
\ No newline at end of file
diff --git a/play-services-core/src/main/res/drawable/ic_shop.xml b/play-services-core/src/main/res/drawable/ic_shop.xml
new file mode 100644
index 0000000000..cf3c794285
--- /dev/null
+++ b/play-services-core/src/main/res/drawable/ic_shop.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/play-services-core/src/main/res/navigation/nav_settings.xml b/play-services-core/src/main/res/navigation/nav_settings.xml
index 7effe7c323..d9a7fc3938 100644
--- a/play-services-core/src/main/res/navigation/nav_settings.xml
+++ b/play-services-core/src/main/res/navigation/nav_settings.xml
@@ -23,6 +23,9 @@
+
@@ -134,6 +137,13 @@
app:argType="string" />
+
+
+
+
Google device registration
Cloud Messaging
Google SafetyNet
+ Play Store services
Google Play Games
%1$s would like to use Play Games
@@ -231,4 +232,9 @@ This can take a couple of minutes."
ON / Manual: %s
%s seconds
%s minutes
+
+ Licensing off
+ Licensing on
+ Answer license verification requests
+ Some apps require verification that you have purchased them on Google Play. When requested by an app, microG can download a proof of purchase from Google. If disabled, or if no Google account is added, requests for license verification are ignored.
diff --git a/play-services-core/src/main/res/xml/preferences_start.xml b/play-services-core/src/main/res/xml/preferences_start.xml
index 2336cf9aac..c2b618367f 100644
--- a/play-services-core/src/main/res/xml/preferences_start.xml
+++ b/play-services-core/src/main/res/xml/preferences_start.xml
@@ -47,6 +47,10 @@
android:icon="@drawable/ic_certificate"
android:key="pref_snet"
android:title="@string/service_name_snet" />
+
+
+
+
+
+
+
+
+
+
+