Skip to content

Commit f239cf1

Browse files
authored
Games: Added game account management (#2918)
1 parent 9b091ab commit f239cf1

40 files changed

+1974
-109
lines changed

play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,20 @@ object SettingsContract {
298298
)
299299
}
300300

301+
object GameProfile {
302+
const val ID = "gameprofile"
303+
fun getContentUri(context: Context) = Uri.withAppendedPath(getCrossProfileSharedAuthorityUri(context), ID)
304+
fun getContentType(context: Context) = "vnd.android.cursor.item/vnd.${getAuthority(context)}.$ID"
305+
306+
const val ALLOW_CREATE_PLAYER = "game_allow_create_player"
307+
const val ALLOW_UPLOAD_GAME_PLAYED = "allow_upload_game_played"
308+
309+
val PROJECTION = arrayOf(
310+
ALLOW_CREATE_PLAYER,
311+
ALLOW_UPLOAD_GAME_PLAYED
312+
)
313+
}
314+
301315
private fun <T> withoutCallingIdentity(f: () -> T): T {
302316
val identity = Binder.clearCallingIdentity()
303317
try {

play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import org.microg.gms.settings.SettingsContract.Auth
2121
import org.microg.gms.settings.SettingsContract.CheckIn
2222
import org.microg.gms.settings.SettingsContract.DroidGuard
2323
import org.microg.gms.settings.SettingsContract.Exposure
24+
import org.microg.gms.settings.SettingsContract.GameProfile
2425
import org.microg.gms.settings.SettingsContract.Gcm
2526
import org.microg.gms.settings.SettingsContract.Location
2627
import org.microg.gms.settings.SettingsContract.Profile
@@ -84,6 +85,7 @@ class SettingsProvider : ContentProvider() {
8485
Location.ID -> queryLocation(projection ?: Location.PROJECTION)
8586
Vending.ID -> queryVending(projection ?: Vending.PROJECTION)
8687
WorkProfile.ID -> queryWorkProfile(projection ?: WorkProfile.PROJECTION)
88+
GameProfile.ID -> queryGameProfile(projection ?: GameProfile.PROJECTION)
8789
else -> null
8890
}
8991

@@ -106,6 +108,7 @@ class SettingsProvider : ContentProvider() {
106108
Location.ID -> updateLocation(values)
107109
Vending.ID -> updateVending(values)
108110
WorkProfile.ID -> updateWorkProfile(values)
111+
GameProfile.ID -> updateGameProfile(values)
109112
else -> return 0
110113
}
111114
return 1
@@ -403,6 +406,27 @@ class SettingsProvider : ContentProvider() {
403406
editor.apply()
404407
}
405408

409+
private fun queryGameProfile(p: Array<out String>): Cursor = MatrixCursor(p).addRow(p) { key ->
410+
when (key) {
411+
GameProfile.ALLOW_CREATE_PLAYER -> getSettingsBoolean(key, false)
412+
GameProfile.ALLOW_UPLOAD_GAME_PLAYED -> getSettingsBoolean(key, false)
413+
else -> throw IllegalArgumentException("Unknown key: $key")
414+
}
415+
}
416+
417+
private fun updateGameProfile(values: ContentValues) {
418+
if (values.size() == 0) return
419+
val editor = preferences.edit()
420+
values.valueSet().forEach { (key, value) ->
421+
when (key) {
422+
GameProfile.ALLOW_CREATE_PLAYER -> editor.putBoolean(key, value as Boolean)
423+
GameProfile.ALLOW_UPLOAD_GAME_PLAYED -> editor.putBoolean(key, value as Boolean)
424+
else -> throw IllegalArgumentException("Unknown key: $key")
425+
}
426+
}
427+
editor.apply()
428+
}
429+
406430
private fun MatrixCursor.addRow(
407431
p: Array<out String>,
408432
valueGetter: (String) -> Any?

play-services-base/core/src/main/res/values-zh-rCN/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<string name="foreground_service_notification_text"><xliff:g example="Exposure Notification">%1$s</xliff:g> 正在后台运行。</string>
55
<string name="foreground_service_notification_big_text">对 <xliff:g example="microG Services">%1$s</xliff:g> 忽略电池优化,或者修改通知设置以隐藏此通知。</string>
66
<string name="menu_advanced">高级</string>
7+
<string name="menu_game_managed">管理游戏账号</string>
78
<string name="list_no_item_none">无</string>
89
<string name="list_item_see_all">全部显示</string>
910
<string name="open_app">打开</string>

play-services-base/core/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<string name="foreground_service_notification_big_text">Exclude <xliff:g example="microG Services">%1$s</xliff:g> from battery optimizations or change notification settings to hide this notification.</string>
1111

1212
<string name="menu_advanced">Advanced</string>
13+
<string name="menu_game_managed">Game Accounts Managed</string>
1314

1415
<string name="list_no_item_none">None</string>
1516
<string name="list_item_see_all">See all</string>
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 microG Project Team
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package google.play.games.whitelisted.v1whitelisted;
7+
8+
option java_outer_classname = "GamesPlayersProto";
9+
10+
option java_package = "org.microg.gms.games";
11+
option java_multiple_files = true;
12+
13+
service PlayersFirstParty {
14+
rpc DeleteApplicationDataFirstParty (DeleteApplicationDataRequest) returns (DeleteApplicationDataResponse);
15+
rpc DeletePlayerFirstParty (DeletePlayerRequest) returns (DeletePlayerResponse);
16+
}
17+
18+
service ApplicationsFirstParty {
19+
rpc ListApplicationsWithUserDataFirstParty (ListApplicationsWithUserDataRequest) returns (ListApplicationsWithUserDataResponse);
20+
}
21+
22+
message ListApplicationsWithUserDataRequest {
23+
optional string locale = 1;
24+
optional string androidSdk = 2;
25+
}
26+
27+
message ListApplicationsWithUserDataResponse {
28+
optional string tag = 1;
29+
optional int32 code = 2;
30+
repeated FirstPartyApplication firstPartyApplication = 3;
31+
}
32+
33+
message FirstPartyApplication {
34+
optional string tag = 1;
35+
optional Application application = 2;
36+
optional int32 unlockAchievementsNum = 6;
37+
optional int32 played = 9;
38+
}
39+
40+
message Application {
41+
optional string tag = 1;
42+
optional string gameId = 2;
43+
optional string gameName = 3;
44+
optional ApplicationIcon gameIcon = 7;
45+
optional int32 achievementsNum = 10;
46+
}
47+
48+
message ApplicationIcon {
49+
optional string type = 1;
50+
optional int32 width = 2;
51+
optional int32 height = 3;
52+
optional string url = 4;
53+
optional string tag = 5;
54+
}
55+
56+
message DeletePlayerRequest {}
57+
58+
message DeletePlayerResponse {}
59+
60+
message DeleteApplicationDataRequest {
61+
optional string gameId = 1;
62+
optional int32 status = 2;
63+
}
64+
65+
message DeleteApplicationDataResponse {}

play-services-core/src/huawei/AndroidManifest.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,11 @@
4646
<meta-data
4747
android:name="org.microg.gms.settings.vending_split_install"
4848
android:value="false" />
49+
<meta-data
50+
android:name="org.microg.gms.settings.game_allow_create_player"
51+
android:value="true" />
52+
<meta-data
53+
android:name="org.microg.gms.settings.allow_upload_game_played"
54+
android:value="true" />
4955
</application>
5056
</manifest>

play-services-core/src/main/AndroidManifest.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,12 @@
605605

606606
<!-- Games -->
607607

608+
<activity
609+
android:name="org.microg.gms.games.ui.GamePlayDataActivity"
610+
android:process=":ui"
611+
android:exported="false"
612+
android:theme="@style/Theme.AppCompat.Light.NoActionBar"/>
613+
608614
<activity
609615
android:name="org.microg.gms.games.ui.InGameUiActivity"
610616
android:process=":ui"

play-services-core/src/main/kotlin/org/microg/gms/auth/consent/ConsentSignInActivity.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ package org.microg.gms.auth.consent
77

88
import android.annotation.SuppressLint
99
import android.app.Activity
10+
import android.content.res.Configuration
1011
import android.graphics.Bitmap
1112
import android.os.Build.VERSION.SDK_INT
1213
import android.os.Bundle
1314
import android.os.Message
1415
import android.os.Messenger
1516
import android.util.Log
1617
import android.view.View
18+
import android.view.WindowManager
1719
import android.webkit.CookieManager
1820
import android.webkit.JavascriptInterface
1921
import android.webkit.WebView
@@ -58,11 +60,18 @@ class ConsentSignInActivity : Activity() {
5860
finish()
5961
return
6062
}
61-
63+
initLayout()
6264
initWebView()
6365
initCookieManager()
6466
}
6567

68+
private fun initLayout() {
69+
val layoutParams = window.attributes as WindowManager.LayoutParams
70+
layoutParams.width = (resources.displayMetrics.widthPixels * 0.8).toInt()
71+
layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT
72+
window.attributes = layoutParams
73+
}
74+
6675
private fun initWebView() {
6776
webView?.settings?.apply {
6877
userAgentString = generateWebViewUserAgentString(userAgentString)

play-services-core/src/main/kotlin/org/microg/gms/auth/signin/AuthSignInActivity.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ class AuthSignInActivity : AppCompatActivity() {
6262
if (packageName == null || (packageName != callingActivity?.packageName && callingActivity?.packageName != this.packageName))
6363
return finishResult(CommonStatusCodes.DEVELOPER_ERROR, "package name mismatch")
6464

65-
initView()
65+
initView(packageName)
6666
}
6767

68-
private fun initView() {
68+
private fun initView(packageName: String) {
6969
val accountManager = getSystemService<AccountManager>() ?: return finishResult(CommonStatusCodes.INTERNAL_ERROR, "No account manager")
7070
val accounts = accountManager.getAccountsByType(DEFAULT_ACCOUNT_TYPE)
7171
if (accounts.isNotEmpty()) {
@@ -234,6 +234,6 @@ class AuthSignInActivity : AppCompatActivity() {
234234

235235
override fun onConfigurationChanged(newConfig: Configuration) {
236236
super.onConfigurationChanged(newConfig)
237-
initView()
237+
config?.packageName?.let { initView(it) }
238238
}
239239
}

play-services-core/src/main/kotlin/org/microg/gms/auth/signin/AuthSignInService.kt

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import com.google.android.gms.auth.api.signin.GoogleSignInOptions
3131
import com.google.android.gms.auth.api.signin.internal.ISignInCallbacks
3232
import com.google.android.gms.auth.api.signin.internal.ISignInService
3333
import com.google.android.gms.common.Feature
34+
import com.google.android.gms.common.Scopes
3435
import com.google.android.gms.common.api.CommonStatusCodes
3536
import com.google.android.gms.common.api.Scope
3637
import com.google.android.gms.common.api.Status
@@ -42,6 +43,8 @@ import org.microg.gms.auth.AuthConstants
4243
import org.microg.gms.auth.AuthPrefs
4344
import org.microg.gms.common.GmsService
4445
import org.microg.gms.common.PackageUtils
46+
import org.microg.gms.games.GAMES_PACKAGE_NAME
47+
import org.microg.gms.games.GamesConfigurationService
4548
import org.microg.gms.utils.singleInstanceOf
4649
import org.microg.gms.utils.warnOnTransactionIssues
4750
import kotlin.coroutines.resume
@@ -79,10 +82,18 @@ class AuthSignInServiceImpl(
7982
}
8083
lifecycleScope.launchWhenStarted {
8184
try {
82-
val account = account
83-
?: options?.account
84-
?: SignInConfigurationService.getDefaultAccount(context, packageName)
85-
?: AccountManager.get(context).getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE).firstOrNull()
85+
var currentAccount = account ?: options?.account
86+
if (options?.scopes?.any { it.scopeUri.contains(Scopes.GAMES) } == true) {
87+
currentAccount = currentAccount ?: GamesConfigurationService.getDefaultAccount(context, packageName)
88+
if (currentAccount == null && GamesConfigurationService.loadPlayedGames(context)?.any { it == packageName } == false) {
89+
currentAccount = GamesConfigurationService.getDefaultAccount(context, GAMES_PACKAGE_NAME)
90+
}
91+
if (currentAccount == null) {
92+
sendResult(null, Status(CommonStatusCodes.SIGN_IN_REQUIRED))
93+
return@launchWhenStarted
94+
}
95+
}
96+
val account = currentAccount ?: SignInConfigurationService.getDefaultAccount(context, packageName)
8697
Log.d(TAG, "silentSignIn: account -> ${account?.name}")
8798
if (account != null && options?.isForceCodeForRefreshToken != true) {
8899
if (getOAuthManager(context, packageName, options, account).isPermitted || AuthPrefs.isTrustGooglePermitted(context)) {
@@ -115,6 +126,9 @@ class AuthSignInServiceImpl(
115126
Log.d(TAG, "$packageName:signOut defaultOptions:($defaultOptions)")
116127
performSignOut(context, packageName, defaultOptions ?: options, account)
117128
}
129+
if (options?.scopes?.any { it.scopeUri.contains(Scopes.GAMES) } == true) {
130+
GamesConfigurationService.setDefaultAccount(context, packageName, null)
131+
}
118132
SignInConfigurationService.setDefaultSignInInfo(context, packageName, null, null)
119133
runCatching { callbacks.onSignOut(Status.SUCCESS) }
120134
} catch (e: Exception) {

0 commit comments

Comments
 (0)