Skip to content

Commit

Permalink
Add experimental support for in app billing
Browse files Browse the repository at this point in the history
Co-Authored-by: DaVinci9196 <150454414+DaVinci9196@users.noreply.github.com>
  • Loading branch information
mar-v-in and DaVinci9196 committed Mar 21, 2024
1 parent 5528a0f commit d4d7713
Show file tree
Hide file tree
Showing 84 changed files with 7,432 additions and 229 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,10 @@ class IdentityToolkitClient(context: Context, private val apiKey: String, privat

suspend fun getTokenByRefreshToken(refreshToken: String): JSONObject = suspendCoroutine { continuation ->
queue.add(object : JsonRequest<JSONObject>(POST, buildStsUrl("token"), "grant_type=refresh_token&refresh_token=$refreshToken", { continuation.resume(it) }, { continuation.resumeWithException(RuntimeException(it)) }) {
override fun parseNetworkResponse(response: NetworkResponse?): Response<JSONObject> {
override fun parseNetworkResponse(response: NetworkResponse): Response<JSONObject> {
return try {
val jsonString = String(response!!.data, Charset.forName(HttpHeaderParser.parseCharset(response!!.headers, PROTOCOL_CHARSET)))
Response.success(JSONObject(jsonString), HttpHeaderParser.parseCacheHeaders(response))
val jsonString = String(response.data, Charset.forName(HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET)))
Response.success(JSONObject(jsonString), null)
} catch (e: UnsupportedEncodingException) {
Response.error(ParseError(e))
} catch (je: JSONException) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ package org.microg.gms.profile

import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.PackageManager
import android.content.res.XmlResourceParser
import android.net.Uri
import android.os.Bundle
import android.util.Log
import org.microg.gms.settings.SettingsContract
import org.microg.gms.settings.SettingsContract.Profile
Expand All @@ -19,11 +22,13 @@ import kotlin.random.Random

object ProfileManager {
private const val TAG = "ProfileManager"
const val META_DATA_KEY_SOURCE_PACKAGE = "org.microg.gms.profile:source-package"
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"
const val PROFILE_REMOTE = "remote"

private var activeProfile: String? = null

Expand Down Expand Up @@ -130,6 +135,17 @@ object ProfileManager {
}
}

private fun getRemoteProfileData(context: Context, packageName: String): Map<String, String> {
val data = mutableMapOf<String, String>()
val cursor = context.contentResolver.query(Uri.parse("content://${packageName}.microg.profile"), null, null, null, null)
cursor?.use {
while (cursor.moveToNext()) {
data[cursor.getString(0)] = cursor.getString(1)
}
}
return data
}

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) }
Expand Down Expand Up @@ -302,6 +318,17 @@ object ProfileManager {
activeProfile = profile
}

private fun applyRemoteProfile(context: Context, packageName: String) {
val profileData = getRemoteProfileData(context, packageName)
if (Log.isLoggable(TAG, Log.VERBOSE)) {
for ((key, value) in profileData) {
Log.v(TAG, "<data key=\"$key\" value=\"$value\" />")
}
}
applyProfileData(profileData)
activeProfile = PROFILE_REMOTE
}

fun getProfileName(context: Context, profile: String): String? = getProfileName { getProfileXml(context, profile) }

private fun getProfileName(parserCreator: () -> XmlResourceParser?): String? {
Expand Down Expand Up @@ -352,14 +379,23 @@ object ProfileManager {

@JvmStatic
fun ensureInitialized(context: Context) {
val metaData = runCatching { context.packageManager.getApplicationInfo(context.packageName, PackageManager.GET_META_DATA).metaData }.getOrNull() ?: Bundle.EMPTY
synchronized(this) {
try {
val profile = getProfile(context)
if (activeProfile == profile) return
applyProfile(context, profile)
if (metaData.containsKey(META_DATA_KEY_SOURCE_PACKAGE)) {
if (activeProfile != PROFILE_REMOTE) {
val packageName = metaData.getString(META_DATA_KEY_SOURCE_PACKAGE)!!
applyRemoteProfile(context, packageName)
}
} else {
val profile = getProfile(context)
if (activeProfile == profile) return
applyProfile(context, profile)
}
} catch (e: Exception) {
Log.w(TAG, e)
}
Unit
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,19 @@ package org.microg.gms.settings

import android.content.ContentValues
import android.content.Context
import android.content.pm.PackageManager
import android.database.Cursor
import android.net.Uri
import android.os.Binder
import android.os.Bundle

object SettingsContract {
fun getAuthority(context: Context) = "${context.packageName}.microg.settings"
const val META_DATA_KEY_SOURCE_PACKAGE = "org.microg.gms.settings:source-package"
fun getAuthority(context: Context): String {
val metaData = runCatching { context.packageManager.getApplicationInfo(context.packageName, PackageManager.GET_META_DATA).metaData }.getOrNull() ?: Bundle.EMPTY
val sourcePackage = metaData.getString(META_DATA_KEY_SOURCE_PACKAGE, context.packageName)
return "${sourcePackage}.microg.settings"
}
fun getAuthorityUri(context: Context): Uri = Uri.parse("content://${getAuthority(context)}")

object CheckIn {
Expand Down Expand Up @@ -179,9 +186,11 @@ object SettingsContract {
fun getContentType(context: Context) = "vnd.android.cursor.item/vnd.${getAuthority(context)}.$id"

const val LICENSING = "vending_licensing"
const val BILLING = "vending_billing"

val PROJECTION = arrayOf(
LICENSING
LICENSING,
BILLING,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ class SettingsProvider : ContentProvider() {
private fun queryVending(p: Array<out String>): Cursor = MatrixCursor(p).addRow(p) { key ->
when (key) {
Vending.LICENSING -> getSettingsBoolean(key, false)
Vending.BILLING -> getSettingsBoolean(key, false)
else -> throw IllegalArgumentException("Unknown key: $key")
}
}
Expand All @@ -353,6 +354,7 @@ class SettingsProvider : ContentProvider() {
values.valueSet().forEach { (key, value) ->
when (key) {
Vending.LICENSING -> editor.putBoolean(key, value as Boolean)
Vending.BILLING -> editor.putBoolean(key, value as Boolean)
else -> throw IllegalArgumentException("Unknown key: $key")
}
}
Expand Down
6 changes: 6 additions & 0 deletions play-services-core/src/huawei/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,11 @@
<meta-data
android:name="org.microg.gms.settings.safetynet_enabled"
android:value="true" />
<meta-data
android:name="org.microg.gms.settings.vending_billing"
android:value="true" />
<meta-data
android:name="org.microg.gms.settings.vending_licensing"
android:value="true" />
</application>
</manifest>
6 changes: 6 additions & 0 deletions play-services-core/src/huaweilh/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@
<meta-data
android:name="org.microg.gms.settings.safetynet_enabled"
android:value="true" />
<meta-data
android:name="org.microg.gms.settings.vending_billing"
android:value="true" />
<meta-data
android:name="org.microg.gms.settings.vending_licensing"
android:value="true" />

<activity-alias
android:name="org.microg.gms.ui.SettingsActivity"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ class SettingsFragment : ResourceSettingsFragment() {

findPreference<Preference>(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<Preference>(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<Preference>(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()) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import org.microg.gms.vending.VendingPreferences

class VendingFragment : PreferenceFragmentCompat() {
private lateinit var licensingEnabled: TwoStatePreference
private lateinit var iapEnable: TwoStatePreference

override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.preferences_vending)
Expand All @@ -34,6 +35,18 @@ class VendingFragment : PreferenceFragmentCompat() {
}
true
}

iapEnable = preferenceScreen.findPreference(PREF_IAP_ENABLED) ?: iapEnable
iapEnable.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
val appContext = requireContext().applicationContext
lifecycleScope.launchWhenResumed {
if (newValue is Boolean) {
VendingPreferences.setBillingEnabled(appContext, newValue)
}
updateContent()
}
true
}
}

override fun onResume() {
Expand All @@ -45,10 +58,12 @@ class VendingFragment : PreferenceFragmentCompat() {
val appContext = requireContext().applicationContext
lifecycleScope.launchWhenResumed {
licensingEnabled.isChecked = VendingPreferences.isLicensingEnabled(appContext)
iapEnable.isChecked = VendingPreferences.isBillingEnabled(appContext)
}
}

companion object {
const val PREF_LICENSING_ENABLED = "vending_licensing"
const val PREF_IAP_ENABLED = "vending_iap"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,19 @@ object VendingPreferences {
put(SettingsContract.Vending.LICENSING, enabled)
}
}

@JvmStatic
fun isBillingEnabled(context: Context): Boolean {
val projection = arrayOf(SettingsContract.Vending.BILLING)
return SettingsContract.getSettings(context, SettingsContract.Vending.getContentUri(context), projection) { c ->
c.getInt(0) != 0
}
}

@JvmStatic
fun setBillingEnabled(context: Context, enabled: Boolean) {
SettingsContract.setSettings(context, SettingsContract.Vending.getContentUri(context)) {
put(SettingsContract.Vending.BILLING, enabled)
}
}
}
2 changes: 2 additions & 0 deletions play-services-core/src/main/res/values-zh-rCN/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -196,4 +196,6 @@ microG GmsCore 内置一套自由的 SafetyNet 实现,但是官方服务器要
<string name="signin_confirm_button_text">允许并共享</string>
<string name="feedback_disabled">反馈功能不可用</string>
<string name="backup_disabled">备份功能不可用</string>
<string name="pref_vending_billing_enable_switch">使用 Google 应用内购买服务</string>
<string name="pref_vending_billing_enable_summary">启用后一些应用可以通过Google 应用内购买服务完成应用内服务购买订阅</string>
</resources>
3 changes: 2 additions & 1 deletion play-services-core/src/main/res/values-zh-rTW/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,5 @@

microG GmsCore包含了SafetyNet的自由執行,但官方伺服器需要SafetyNet請求經非自由軟體性質的DroidGuard系統簽名。</string>
<string name="pref_safetynet_test_title">測試SafetyNet驗證</string>
</resources>
<string name="pref_vending_billing_enable_switch">啟用後一些應用程式可以透過Google 應用程式內購買服務完成應用程式內服務購買訂閱</string>
</resources>
7 changes: 7 additions & 0 deletions play-services-core/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -256,10 +256,17 @@ This can take a couple of minutes."</string>

<string name="pref_vending_summary_licensing_off">Licensing off</string>
<string name="pref_vending_summary_licensing_on">Licensing on</string>
<string name="pref_vending_licensing_category">Google Play Licensing</string>
<string name="pref_vending_licensing_enable_switch">Answer license verification requests</string>
<string name="pref_vending_license_enable_summary">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.</string>

<string name="feedback_disabled">Feedback currently not possible</string>
<string name="backup_disabled">Backup currently not possible</string>

<string name="pref_vending_billing_category">Google Play Billing</string>
<string name="pref_vending_billing_enable_switch">Handle billing requests</string>
<string name="pref_vending_billing_enable_summary">Once enabled, some apps can complete purchases or start subscriptions through Google\'s Play Billing service.</string>
<string name="pref_vending_billing_note_experimental">This feature is experimental and may lead to loss of money. You have been warned.</string>
<string name="pref_vending_billing_note_licensing">Some apps may require you to also enable license verification to verify your purchases.</string>

</resources>
31 changes: 23 additions & 8 deletions play-services-core/src/main/res/xml/preferences_vending.xml
Original file line number Diff line number Diff line change
@@ -1,20 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
<?xml version="1.0" encoding="utf-8"?><!--
~ SPDX-FileCopyrightText: 2023, e Foundation
~ SPDX-License-Identifier: Apache-2.0
-->

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">

<SwitchPreferenceCompat
android:title="@string/pref_vending_licensing_enable_switch"
android:key="vending_licensing"
android:persistent="false" />
<PreferenceCategory android:title="@string/pref_vending_licensing_category">
<org.microg.gms.ui.TextPreference
android:selectable="false"
android:summary="@string/pref_vending_license_enable_summary" />
<SwitchPreferenceCompat
android:title="@string/pref_vending_licensing_enable_switch"
android:key="vending_licensing"
android:persistent="false" />
</PreferenceCategory>

<PreferenceCategory android:layout="@layout/preference_category_no_label">
<PreferenceCategory android:title="@string/pref_vending_billing_category">
<org.microg.gms.ui.TextPreference
android:selectable="false"
android:summary="@string/pref_vending_billing_enable_summary" />
<SwitchPreferenceCompat
android:title="@string/pref_vending_billing_enable_switch"
android:key="vending_iap"
android:persistent="false" />
<org.microg.gms.ui.TextPreference
android:icon="@drawable/ic_circle_warn"
android:selectable="false"
android:summary="@string/pref_vending_billing_note_experimental" />
<org.microg.gms.ui.TextPreference
android:icon="@drawable/ic_info_outline"
android:selectable="false"
android:summary="@string/pref_vending_license_enable_summary" />
android:summary="@string/pref_vending_billing_note_licensing" />
</PreferenceCategory>
</PreferenceScreen>
30 changes: 30 additions & 0 deletions vending-app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ android {

buildFeatures {
aidl = true
buildConfig = true
compose true
}

lintOptions {
Expand All @@ -80,6 +82,9 @@ android {
kotlinOptions {
jvmTarget = 1.8
}
composeOptions {
kotlinCompilerExtensionVersion '1.5.10'
}
}

dependencies {
Expand All @@ -89,6 +94,31 @@ dependencies {

implementation "com.squareup.wire:wire-runtime:$wireVersion"
implementation "com.android.volley:volley:$volleyVersion"

implementation "androidx.webkit:webkit:$webkitVersion"

implementation "com.squareup.wire:wire-grpc-client:$wireVersion"

//compose
implementation platform('androidx.compose:compose-bom:2022.10.00')
implementation 'androidx.compose.ui:ui'
implementation 'androidx.compose.material3:material3'
implementation 'androidx.compose.animation:animation-graphics'
implementation 'androidx.activity:activity-compose:1.7.2'
implementation("io.coil-kt:coil-compose:2.4.0")
implementation("io.coil-kt:coil-svg:2.2.2")
implementation "com.google.android.material:material:$materialVersion"
implementation "com.google.accompanist:accompanist-systemuicontroller:0.28.0"

//droidguard
implementation project(':play-services-droidguard')

//androidx
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion"
implementation "androidx.core:core-ktx:$coreVersion"
implementation "androidx.appcompat:appcompat:$appcompatVersion"
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.2'
implementation "androidx.preference:preference-ktx:$preferenceVersion"
}

wire {
Expand Down
2 changes: 1 addition & 1 deletion vending-app/src/huaweilh/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<application>
<application tools:overrideLibrary="coil.svg">
<activity-alias
android:name="org.lighthouseex.MainActivity"
android:exported="true"
Expand Down
Loading

0 comments on commit d4d7713

Please sign in to comment.