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

Check WebView Version For Login Functionality #12108

Merged
Merged
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 @@ -125,6 +125,7 @@
import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.ErrorMessageAdapter;
import com.owncloud.android.utils.PermissionUtil;
import com.owncloud.android.utils.WebViewUtil;
import com.owncloud.android.utils.theme.CapabilityUtils;
import com.owncloud.android.utils.theme.ViewThemeUtils;

Expand Down Expand Up @@ -268,6 +269,7 @@ protected void onCreate(Bundle savedInstanceState) {
viewThemeUtils = viewThemeUtilsFactory.withPrimaryAsBackground();
viewThemeUtils.platform.themeStatusBar(this, ColorRole.PRIMARY);

WebViewUtil webViewUtil = new WebViewUtil(this);

Uri data = getIntent().getData();
boolean directLogin = data != null && data.toString().startsWith(getString(R.string.login_data_own_scheme));
Expand Down Expand Up @@ -337,6 +339,8 @@ protected void onCreate(Bundle savedInstanceState) {
}

initServerPreFragment(savedInstanceState);

webViewUtil.checkWebViewVersion();
}

private void deleteCookies() {
Expand Down
110 changes: 110 additions & 0 deletions app/src/main/java/com/owncloud/android/utils/WebViewUtil.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Nextcloud Android client application
*
* @author Alper Ozturk
* Copyright (C) 2023 Alper Ozturk
* Copyright (C) 2023 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.owncloud.android.utils

import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.owncloud.android.R

class WebViewUtil(private val context: Context) {

private val packageName = "com.google.android.webview"

fun checkWebViewVersion() {
if (!isWebViewVersionValid()) {
showUpdateDialog()
}
}

private fun isWebViewVersionValid(): Boolean {
val currentWebViewVersion = getCurrentWebViewMajorVersion() ?: return true
val minSupportedWebViewVersion: String = getMinimumSupportedMajorWebViewVersion()
return currentWebViewVersion.toInt() >= minSupportedWebViewVersion.toInt()
}

private fun showUpdateDialog() {
val builder = MaterialAlertDialogBuilder(context)
.setTitle(context.getString(R.string.webview_version_check_alert_dialog_title))
.setMessage(context.getString(R.string.webview_version_check_alert_dialog_message))
.setCancelable(false)
.setPositiveButton(
context.getString(R.string.webview_version_check_alert_dialog_positive_button_title)
) { _, _ ->
redirectToAndroidSystemWebViewStorePage()
}

val dialog = builder.create()
dialog.show()
}

private fun redirectToAndroidSystemWebViewStorePage() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A very similar function already exists in DrawerActivity.java, called openAppStore(). Currently its visibility is set to private. Would it be possible to consolidate this functionality?

val uri = Uri.parse("market://details?id=$packageName")
val intent = Intent(Intent.ACTION_VIEW, uri)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK

try {
context.startActivity(intent)
} catch (e: android.content.ActivityNotFoundException) {
redirectToPlayStoreWebsiteForAndroidSystemWebView()
}
}

private fun redirectToPlayStoreWebsiteForAndroidSystemWebView() {
val playStoreWebUri = Uri.parse("https://play.google.com/store/apps/details?id=$packageName")
val webIntent = Intent(Intent.ACTION_VIEW, playStoreWebUri)
context.startActivity(webIntent)
}

private fun getCurrentWebViewMajorVersion(): String? {
val pm: PackageManager = context.packageManager

return try {
val pi = pm.getPackageInfo("com.google.android.webview", 0)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

During my tests, this line consistently throws PackageManager.NameNotFoundException. This doesn't work as expected on devices running AOSP. Maybe the package name differs on AOSP images? It's called com.android.webview on my phone running LineageOS.

I've been able to reproduce this using the following configurations:

  • Pixel 2 (Android Studio Emulator) x86_64 API 26 AOSP
  • Pixel 2 (Android Studio Emulator) x86_64 API 24 AOSP
  • Pixel 2 (Android Studio Emulator) x86_64 API 30 AOSP
  • OnePlus 6T API 33 LineageOS 20

In that case, one would also have to consider third party SystemWebView implementations, such as Bromite, for example.

However, because the calling functions returns true, if this function returns null, this could possibly be ignored?

val fullVersion = pi.versionName

// Split the version string by "." and get the first part
val versionParts = fullVersion.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }
.toTypedArray()

if (versionParts.isNotEmpty()) {
versionParts[0]
} else {
null
}
} catch (e: PackageManager.NameNotFoundException) {
null
}
}

/**
* Ideally we should fetch from database, reading actual value
* from PlayStore not feasible due to frequently api changes made by
* Google
*
*/
private fun getMinimumSupportedMajorWebViewVersion(): String {
return "118"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AndyScherzinger @tobiasKaminsky Can we get that value from backend?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Server doesn't have/know this. So we could for the moment hard-code this on client-side and bump it per major release of the server.

}
}
4 changes: 4 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,10 @@
<string name="dialog_close">Close</string>
<string name="direct_login_text">Login with %1$s to %2$s</string>
<string name="direct_login_failed">Login via direct link failed!</string>
<string name="webview_version_check_alert_dialog_title">Update Android System WebView</string>
<string name="webview_version_check_alert_dialog_message">Please update the Android System WebView app for a login</string>
<string name="webview_version_check_alert_dialog_positive_button_title">Update</string>

<string name="login_url_helper_text">The link to your %1$s web interface when you open it in the browser.</string>
<string name="brute_force_delay">Delayed due to too many wrong attempts</string>
<string name="create">Create</string>
Expand Down
Loading