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" /> +