diff --git a/play-services-base/core/src/main/AndroidManifest.xml b/play-services-base/core/src/main/AndroidManifest.xml
index aebc21a461a95c69027e427f4efbca7f94ab97de..83a39c3a4cd75e565f5c900ef39f1580452610b3 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="org.microg.gms.permission.READ_SETTINGS"
+ android:writePermission="org.microg.gms.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 2d59c173b23d85d31c9c7e0738cd4b58b0a89621..2c6e0012458967769b2b7677d90873dae31a6a23 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 Play {
+ private const val id = "play"
+ fun getContentUri(context: Context) = Uri.withAppendedPath(getAuthorityUri(context), id)
+ fun getContentType(context: Context) = "vnd.android.cursor.item/vnd.${getAuthority(context)}.$id"
+
+ const val LICENSING = "play_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 01d51a31a0d3a3039f8a404844bbade0e55ad408..ed2ccf987dedea272c5451f76fe30d54b7632ee1 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.Play
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)
+ Play.getContentUri(context!!) -> queryPlay(projection ?: Play.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)
+ Play.getContentUri(context!!) -> updatePlay(values)
else -> return 0
}
return 1
@@ -335,6 +338,25 @@ class SettingsProvider : ContentProvider() {
editor.apply()
}
+ private fun queryPlay(p: Array): Cursor = MatrixCursor(p).addRow(p) { key ->
+ when (key) {
+ Play.LICENSING -> getSettingsBoolean(key, false)
+ else -> throw IllegalArgumentException("Unknown key: $key")
+ }
+ }
+
+ private fun updatePlay(values: ContentValues) {
+ if (values.size() == 0) return
+ val editor = preferences.edit()
+ values.valueSet().forEach { (key, value) ->
+ when (key) {
+ Play.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/play/PlayPreferences.kt b/play-services-core/src/main/kotlin/org/microg/gms/play/PlayPreferences.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b11944e60b945122707e5dc141de37517e7b268c
--- /dev/null
+++ b/play-services-core/src/main/kotlin/org/microg/gms/play/PlayPreferences.kt
@@ -0,0 +1,21 @@
+package org.microg.gms.play
+
+import android.content.Context
+import org.microg.gms.settings.SettingsContract
+
+object PlayPreferences {
+ @JvmStatic
+ fun isLicensingEnabled(context: Context): Boolean {
+ val projection = arrayOf(SettingsContract.Play.LICENSING)
+ return SettingsContract.getSettings(context, SettingsContract.Play.getContentUri(context), projection) { c ->
+ c.getInt(0) != 0
+ }
+ }
+
+ @JvmStatic
+ fun setLicensingEnabled(context: Context, enabled: Boolean) {
+ SettingsContract.setSettings(context, SettingsContract.Play.getContentUri(context)) {
+ put(SettingsContract.Play.LICENSING, enabled)
+ }
+ }
+}
\ No newline at end of file
diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/PlayFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/PlayFragment.kt
new file mode 100644
index 0000000000000000000000000000000000000000..401fd9ee5ec2350fed53ce5e199e8c549d21f4cb
--- /dev/null
+++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/PlayFragment.kt
@@ -0,0 +1,49 @@
+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.play.PlayPreferences
+
+class PlayFragment : PreferenceFragmentCompat() {
+ private lateinit var licensingEnabled: TwoStatePreference
+
+ override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+ addPreferencesFromResource(R.xml.preferences_play)
+ }
+
+ @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) {
+ PlayPreferences.setLicensingEnabled(appContext, newValue)
+ }
+ updateContent()
+ }
+ true
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ updateContent()
+ }
+
+ private fun updateContent() {
+ val appContext = requireContext().applicationContext
+ lifecycleScope.launchWhenResumed {
+ licensingEnabled.isChecked = PlayPreferences.isLicensingEnabled(appContext)
+ }
+ }
+
+ companion object {
+ const val PREF_LICENSING_ENABLED = "play_licensing"
+ }
+}
\ No newline at end of file
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 6d3d6f7d232f970d89b7e9c5cbf7f4126ad198cb..cb6e6e55b7fc72615fb28c9a756b5848ef0c7cc6 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.play.PlayPreferences
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_PLAY)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
+ findNavController().navigate(requireContext(), R.id.openPlaySettings)
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_PLAY)!!.setSummary(if (PlayPreferences.isLicensingEnabled(requireContext())) R.string.pref_play_summary_licensing_on else R.string.pref_play_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_PLAY = "pref_play"
}
init {
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 0000000000000000000000000000000000000000..6234575510aea17f363eaf4d3eca3a1a154ed7bb
--- /dev/null
+++ b/play-services-core/src/main/res/drawable/ic_shop.xml
@@ -0,0 +1,10 @@
+
+
+
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 7effe7c323f62b0e32d0ba278c6c07f9af3cac61..2c4cf23c4c3e668b8dcd55f2ed1f7a837125bd25 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_play.xml b/play-services-core/src/main/res/xml/preferences_play.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a373abda04fd9b49126365c82ff7c1dbc6dba7b4
--- /dev/null
+++ b/play-services-core/src/main/res/xml/preferences_play.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
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 2336cf9aacacb24b3509aad2c34e2edb70340834..c0b61ee16d0109f2b02bb59f309e10b98cc37907 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" />
+