diff --git a/play-services-core/src/main/java/org/microg/gms/people/PeopleManager.java b/play-services-core/src/main/java/org/microg/gms/people/PeopleManager.java index e6ea7dbf80..0323c81407 100644 --- a/play-services-core/src/main/java/org/microg/gms/people/PeopleManager.java +++ b/play-services-core/src/main/java/org/microg/gms/people/PeopleManager.java @@ -20,6 +20,7 @@ import android.content.ContentValues; import android.content.Context; import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.util.Log; @@ -114,6 +115,17 @@ public static Bitmap getOwnerAvatarBitmap(Context context, String accountName, b return BitmapFactory.decodeFile(avaterFile.getPath()); } + public static void updateOwnerAvatar(Context context, String accountName, String newAvatar) { + try (DatabaseHelper databaseHelper = new DatabaseHelper(context); SQLiteDatabase db = databaseHelper.getWritableDatabase()) { + ContentValues contentValues = new ContentValues(); + contentValues.put("avatar", newAvatar); + int rowsAffected = db.update(DatabaseHelper.OWNERS_TABLE, contentValues, "account_name = ?", new String[]{accountName}); + Log.d(TAG, "updateOwnerAvatar affected: " + rowsAffected); + } catch (Exception e) { + Log.e(TAG, "Error updating avatar: " + e.getMessage()); + } + } + public static String loadUserInfo(Context context, Account account) { try { URLConnection conn = new URL(USERINFO_URL).openConnection(); diff --git a/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/MainActivity.kt b/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/MainActivity.kt index 7040c9dbed..3509ab0b9f 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/MainActivity.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/MainActivity.kt @@ -7,20 +7,26 @@ package org.microg.gms.accountsettings.ui import android.accounts.Account import android.accounts.AccountManager +import android.content.Intent import android.os.Bundle import android.text.TextUtils import android.util.Log import android.view.View +import android.webkit.JavascriptInterface import android.webkit.WebView -import android.widget.FrameLayout import android.widget.ProgressBar import android.widget.RelativeLayout import android.widget.RelativeLayout.LayoutParams.MATCH_PARENT import android.widget.RelativeLayout.LayoutParams.WRAP_CONTENT import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.updateLayoutParams +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.json.JSONException +import org.json.JSONObject import org.microg.gms.auth.AuthConstants import org.microg.gms.common.Constants +import org.microg.gms.people.PeopleManager private const val TAG = "AccountSettings" @@ -95,6 +101,7 @@ private val SCREEN_ID_TO_URL = hashMapOf( 10729 to "https://myaccount.google.com/data-and-privacy/data-visibility", 10759 to "https://myaccount.google.com/address/home", 10760 to "https://myaccount.google.com/address/work", + 14500 to "https://profilewidgets.google.com/alternate-profile/edit?interop=o&opts=sb", ) private val ALLOWED_WEB_PREFIXES = setOf( @@ -114,7 +121,8 @@ private val ALLOWED_WEB_PREFIXES = setOf( "https://fit.google.com/privacy/settings", "https://maps.google.com/maps/timeline", "https://myadcenter.google.com/controls", - "https://families.google.com/kidonboarding" + "https://families.google.com/kidonboarding", + "https://profilewidgets.google.com/alternate-profile/edit", ) private val ACTION_TO_SCREEN_ID = hashMapOf( @@ -126,6 +134,8 @@ private val ACTION_TO_SCREEN_ID = hashMapOf( class MainActivity : AppCompatActivity() { private lateinit var webView: WebView + private var accountName: String? = null + private var resultBundle: Bundle? = null private fun getSelectedAccountName(): String? = null @@ -146,7 +156,7 @@ class MainActivity : AppCompatActivity() { val callingPackage = intent?.getStringExtra(EXTRA_CALLING_PACKAGE_NAME) ?: callingActivity?.packageName ?: Constants.GMS_PACKAGE_NAME val ignoreAccount = intent?.getBooleanExtra(EXTRA_IGNORE_ACCOUNT, false) ?: false - val accountName = if (ignoreAccount) null else { + accountName = if (ignoreAccount) null else { val accounts = AccountManager.get(this).getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE) val accountName = intent.getStringExtra(EXTRA_ACCOUNT_NAME) ?: intent.getParcelableExtra("account")?.name ?: getSelectedAccountName() accounts.find { it.name.equals(accountName) }?.name @@ -175,6 +185,7 @@ class MainActivity : AppCompatActivity() { webView = WebView(this).apply { layoutParams = RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT) visibility = View.INVISIBLE + addJavascriptInterface(UiBridge(), "ocUi") } layout.addView(webView) setContentView(layout) @@ -198,4 +209,126 @@ class MainActivity : AppCompatActivity() { super.onBackPressed() } } + + private fun updateLocalAccountAvatar(newAvatarUrl: String?) { + if (TextUtils.isEmpty(newAvatarUrl) || accountName == null) { + return + } + lifecycleScope.launchWhenCreated { + withContext(Dispatchers.IO) { + PeopleManager.updateOwnerAvatar(this@MainActivity, accountName, newAvatarUrl) + } + } + } + + private inner class UiBridge { + + @JavascriptInterface + fun close() { + Log.d(TAG, "close: ") + val intent = Intent() + if (resultBundle != null) { + intent.putExtras(resultBundle!!) + } + setResult(RESULT_OK, intent) + finish() + } + + @JavascriptInterface + fun closeWithResult(resultJsonStr: String?) { + Log.d(TAG, "closeWithResult: resultJsonStr -> $resultJsonStr") + setResult(resultJsonStr) + close() + } + + @JavascriptInterface + fun goBackOrClose() { + Log.d(TAG, "goBackOrClose: ") + onBackPressed() + } + + @JavascriptInterface + fun hideKeyboard() { + Log.d(TAG, "hideKeyboard: ") + } + + @JavascriptInterface + fun isCloseWithResultSupported(): Boolean { + return true + } + + @JavascriptInterface + fun isOpenHelpEnabled(): Boolean { + return true + } + + @JavascriptInterface + fun isOpenScreenEnabled(): Boolean { + return true + } + + @JavascriptInterface + fun isSetResultSupported(): Boolean { + return true + } + + @JavascriptInterface + fun open(str: String?) { + Log.d(TAG, "open: str -> $str") + } + + @JavascriptInterface + fun openHelp(str: String?) { + Log.d(TAG, "openHelp: str -> $str") + } + + @JavascriptInterface + fun openScreen(screenId: Int, str: String?) { + Log.d(TAG, "openScreen: screenId -> $screenId str -> $str accountName -> $accountName") + val intent = Intent(this@MainActivity, MainActivity::class.java).apply { + putExtra(EXTRA_SCREEN_ID, screenId) + putExtra(EXTRA_ACCOUNT_NAME, accountName) + } + startActivity(intent) + } + + @JavascriptInterface + fun setBackStop() { + Log.d(TAG, "setBackStop: ") + webView.clearHistory() + } + + @JavascriptInterface + fun setResult(resultJsonStr: String?) { + Log.d(TAG, "setResult: resultJsonStr -> $resultJsonStr") + val map = jsonToMap(resultJsonStr) ?: return + if (map.containsKey(KEY_UPDATED_PHOTO_URL)) { + updateLocalAccountAvatar(map[KEY_UPDATED_PHOTO_URL]) + } + resultBundle = Bundle().apply { + for ((key, value) in map) { + putString("result.$key", value) + } + } + } + + private fun jsonToMap(jsonStr: String?): Map? { + val hashMap = HashMap() + if (!jsonStr.isNullOrEmpty()) { + try { + val jSONObject = JSONObject(jsonStr) + val keys = jSONObject.keys() + while (keys.hasNext()) { + val next = keys.next() + val obj = jSONObject[next] + hashMap[next] = obj as String + } + } catch (e: JSONException) { + Log.d(TAG, "Unable to parse result JSON string", e) + return null + } + } + return hashMap + } + } } \ No newline at end of file diff --git a/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/WebViewHelper.kt b/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/WebViewHelper.kt index 71bebf9f90..ffbbf45367 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/WebViewHelper.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/WebViewHelper.kt @@ -20,6 +20,7 @@ import androidx.webkit.WebViewClientCompat import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import org.json.JSONObject import org.microg.gms.auth.AuthManager import org.microg.gms.common.Constants.GMS_PACKAGE_NAME import org.microg.gms.common.PackageUtils @@ -126,5 +127,10 @@ class WebViewHelper(private val activity: AppCompatActivity, private val webView settings.useWideViewPort = false settings.setSupportZoom(false) settings.javaScriptCanOpenWindowsAutomatically = false + settings.userAgentString = "${settings.userAgentString} ${ + String.format(Locale.getDefault(), "OcIdWebView (%s)", JSONObject().apply { + put("os", "Android") + }.toString()) + }" } } \ No newline at end of file diff --git a/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/extensions.kt b/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/extensions.kt index 30a3b01be9..d1850ea56e 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/extensions.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/extensions.kt @@ -22,4 +22,6 @@ const val EXTRA_THEME_CHOICE = "extra.themeChoice" const val EXTRA_SCREEN_MY_ACTIVITY_PRODUCT = "extra.screen.myactivityProduct" const val EXTRA_SCREEN_KID_ONBOARDING_PARAMS = "extra.screen.kidOnboardingParams" +const val KEY_UPDATED_PHOTO_URL = "updatedPhotoUrl" + const val OPTION_SCREEN_FLAVOR = "screenFlavor" \ No newline at end of file diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/AccountsFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/AccountsFragment.kt index e0039366b8..a5dc94efd5 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/ui/AccountsFragment.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/AccountsFragment.kt @@ -53,7 +53,9 @@ class AccountsFragment : PreferenceFragmentCompat() { } private fun getCircleBitmapDrawable(bitmap: Bitmap?) = - if (bitmap != null) RoundedBitmapDrawableFactory.create(resources, bitmap).also { it.isCircular = true } else null + if (bitmap != null) RoundedBitmapDrawableFactory.create(resources, bitmap.let { + Bitmap.createScaledBitmap(bitmap, 100, 100, true) + }).also { it.isCircular = true } else null override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.preferences_accounts)