Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MainActivity: Add Javascript interactive content #2654

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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(
Expand All @@ -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(
Expand All @@ -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

Expand All @@ -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>("account")?.name ?: getSelectedAccountName()
accounts.find { it.name.equals(accountName) }?.name
Expand Down Expand Up @@ -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)
Expand All @@ -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<String, String>? {
val hashMap = HashMap<String, String>()
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
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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())
}"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading