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

Improve the Help link #2536

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
53 changes: 53 additions & 0 deletions play-services-core-proto/src/main/proto/help.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/

option java_package = "org.microg.gms.googlehelp";

option java_multiple_files = true;

message RequestContent {
optional CallerAppInfo appInfo = 1;
optional DeviceInfo deviceInfo = 2;
optional RequestBody body = 3;
optional string host = 4;
}

message ResponseContentWarp {
optional ResponseContent content = 1;
}

message ResponseContent {
optional AnswerInfo info = 1;
optional uint32 theme = 2;
}

message AnswerInfo {
optional string answerId = 1;
optional string answerTitle = 2;
optional string answerUrl = 3;
optional uint32 type = 5;
}

message CallerAppInfo {
optional string packageName = 1;
optional string version = 2;
}

message DeviceInfo {
optional string language = 1;
optional string name = 2;
optional string version = 3;
optional string code = 7;
optional string timeZone = 9;
}

message RequestBody {
optional string appContext = 3;
optional string session = 4;
optional uint32 gmsVersionCode = 12;
optional string gmsVersionName = 13;
optional uint32 type = 26;
optional string ap = 28;
}
4 changes: 2 additions & 2 deletions play-services-core/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,7 @@
<!-- microG Settings activity -->
<activity
android:name="org.microg.gms.ui.MainSettingsActivity"
android:exported="true"
android:icon="@mipmap/ic_app_settings"
android:label="@string/gms_settings_name"
android:process=":ui"
Expand Down Expand Up @@ -776,8 +777,7 @@

<activity
android:name="org.microg.gms.googlehelp.ui.GoogleHelpRedirectActivity"
android:process=":ui"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:theme="@style/Theme.App.Translucent"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.googlehelp.HELP" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,118 @@
package org.microg.gms.googlehelp.ui

import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.os.Parcel
import android.os.Parcelable.Creator
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import com.android.volley.NetworkResponse
import com.android.volley.Request
import com.android.volley.Response
import com.android.volley.VolleyError
import com.android.volley.toolbox.Volley
import com.google.android.gms.googlehelp.GoogleHelp
import com.google.android.gms.googlehelp.InProductHelp
import org.microg.gms.googlehelp.CallerAppInfo
import org.microg.gms.googlehelp.DeviceInfo
import org.microg.gms.googlehelp.RequestBody
import org.microg.gms.googlehelp.RequestContent
import org.microg.gms.googlehelp.ResponseContentWarp
import java.util.Locale
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine

private const val TAG = "GoogleHelpRedirect"
private const val GOOGLE_HELP_KEY = "EXTRA_GOOGLE_HELP"
private const val PRODUCT_HELP_KEY = "EXTRA_IN_PRODUCT_HELP"

private const val HELP_URL = "https://www.google.com/tools/feedback/mobile/help-suggestions"

class GoogleHelpRedirectActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val callingPackage = callingActivity?.packageName ?: return finish()
val intent = intent ?: return finish()
val googleHelp = intent.getParcelableExtra<GoogleHelp>(EXTRA_GOOGLE_HELP) ?: return finish()
Log.d(TAG, "Using GoogleHelp: $googleHelp")
val uri = googleHelp.uri ?: return finish()
// TODO: Not all Google apps send proper URI values, as these are in fact not used by Google's original implementation.
// As a work-around we should get the proper URL by retrieving top_level_topic_url:$callingPackage
// from https://www.google.com/tools/feedback/mobile/get-configurations endpoint.
// Long-term best is to not redirect to web but instead implement the thing properly, allowing us also to show
// option items, do proper theming for better integration, etc.
Log.d(TAG, "Open $uri for $callingPackage/${googleHelp.appContext} in Browser")
// noinspection UnsafeImplicitIntentLaunch
startActivity(Intent(Intent.ACTION_VIEW, uri))
finish()
Log.d(TAG, "onCreate begin")
if (intent == null) {
Log.d(TAG, "onCreate intent is null")
finish()
return
}
val callingPackage = callingPackage ?: callingActivity?.packageName ?: return finish()
Log.d(TAG, "onCreate callingPackage: $callingPackage")
val googleHelp = intent.getParcelableExtra<GoogleHelp>(GOOGLE_HELP_KEY)
var inProductHelp: InProductHelp? = null
if (googleHelp == null) {
inProductHelp = getParcelableFromIntent<InProductHelp>(intent, PRODUCT_HELP_KEY, InProductHelp.CREATOR)
}

lifecycleScope.launchWhenCreated {
Log.d(TAG, "onCreate: googleHelp: ${googleHelp ?: inProductHelp?.googleHelp}")
val searchId = googleHelp?.appContext ?: inProductHelp?.googleHelp?.appContext
val answerUrl = runCatching { requestHelpLink(callingPackage, searchId).content?.info?.answerUrl }.getOrNull()
Log.d(TAG, "requestHelpLink answerUrl: $answerUrl")
val url = answerUrl ?: googleHelp?.uri?.toString() ?: inProductHelp?.googleHelp?.uri?.toString() ?: return@launchWhenCreated finish()
Log.d(TAG, "Open $url for $callingPackage in Browser")
val targetIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
val resolveInfoList = packageManager.queryIntentActivities(targetIntent, 0)
Log.d(TAG, "resolveInfoList: $resolveInfoList")
if (resolveInfoList.isNotEmpty()) {
startActivity(targetIntent)
}
finish()
}
}

private fun <T> getParcelableFromIntent(intent: Intent, key: String?, creator: Creator<T>): T? {
try {
val data = intent.getByteArrayExtra(key)
if (data != null) {
val parcel = Parcel.obtain()
parcel.unmarshall(data, 0, data.size)
parcel.setDataPosition(0)
val result = creator.createFromParcel(parcel)
parcel.recycle()
return result
}
} catch (e: Exception) {
Log.e(TAG, "Error deserializing InProductHelp", e)
}
return null
}

companion object {
const val EXTRA_GOOGLE_HELP = "EXTRA_GOOGLE_HELP"
private suspend fun requestHelpLink(callingPackage: String, searchId: String?) = suspendCoroutine { sus ->
Volley.newRequestQueue(this.applicationContext).add(object : Request<ResponseContentWarp>(Method.POST, HELP_URL, {
Log.d(TAG, "requestHelpLink: ", it)
sus.resumeWithException(it)
}) {

override fun deliverResponse(response: ResponseContentWarp) {
Log.d(TAG, "requestHelpLink response: $response")
sus.resume(response)
}

override fun getBody(): ByteArray {
return RequestContent.Builder().apply {
appInfo = CallerAppInfo.Builder().apply { packageName = callingPackage }.build()
deviceInfo = DeviceInfo.Builder().apply { language = Locale.getDefault().language }.build()
body = RequestBody.Builder().apply { appContext = searchId }.build()
}.build().also {
Log.d(TAG, "requestBody: $it")
}.encode()
}

override fun getBodyContentType(): String = "application/x-protobuf"

override fun parseNetworkResponse(response: NetworkResponse): Response<ResponseContentWarp> {
return try {
Response.success(ResponseContentWarp.ADAPTER.decode(response.data), null)
} catch (e: Exception) {
Response.error(VolleyError(e))
}
}
})
}
}