From 59c13bf8c1b98b5ed0b9eb73743c82ebaea291ae Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Wed, 25 May 2022 12:35:43 +0200 Subject: [PATCH 01/13] Make widget web view request system permissions for camera and microphone Previously the widget web view prompted to grant the widget permissions but it didn't actually request those permissions from the system. So if the web view requested, e.g. the camera permission but the app hadn't previously been granted that permission, the web view wouldn't get camera access even when the widget permission request had been confirmed. With this commit, the app will also request camera and microphone permissions from the system when needed. Signed-off-by: Johannes Marbach --- .../webview/WebChromeEventListener.kt | 32 ++++++++++ .../app/features/widgets/WidgetFragment.kt | 20 +++++- .../widgets/webview/WebviewPermissionUtils.kt | 61 ++++++++++++++++++- .../features/widgets/webview/WidgetWebView.kt | 5 +- 4 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/webview/WebChromeEventListener.kt diff --git a/vector/src/main/java/im/vector/app/features/webview/WebChromeEventListener.kt b/vector/src/main/java/im/vector/app/features/webview/WebChromeEventListener.kt new file mode 100644 index 0000000000..8a12052def --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/webview/WebChromeEventListener.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.webview + +import android.webkit.PermissionRequest + +interface WebChromeEventListener { + + /** + * Triggered when the web view requests permissions. + * + * @param request The permission request. + */ + fun onPermissionRequest(request: PermissionRequest) { + // NO-OP + } + +} diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt index cd2a4dcdf4..c8a13d11cf 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt @@ -26,6 +26,8 @@ import android.view.Menu import android.view.MenuItem import android.view.View import android.view.ViewGroup +import android.webkit.PermissionRequest +import androidx.activity.result.contract.ActivityResultContracts import androidx.core.view.isInvisible import androidx.core.view.isVisible import com.airbnb.mvrx.Fail @@ -42,7 +44,9 @@ import im.vector.app.core.platform.OnBackPressed import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.openUrlInExternalBrowser import im.vector.app.databinding.FragmentRoomWidgetBinding +import im.vector.app.features.webview.WebChromeEventListener import im.vector.app.features.webview.WebViewEventListener +import im.vector.app.features.widgets.webview.WebviewPermissionUtils import im.vector.app.features.widgets.webview.clearAfterWidget import im.vector.app.features.widgets.webview.setupForWidget import kotlinx.parcelize.Parcelize @@ -63,6 +67,7 @@ data class WidgetArgs( class WidgetFragment @Inject constructor() : VectorBaseFragment(), WebViewEventListener, + WebChromeEventListener, OnBackPressed { private val fragmentArgs: WidgetArgs by args() @@ -75,7 +80,7 @@ class WidgetFragment @Inject constructor() : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setHasOptionsMenu(true) - views.widgetWebView.setupForWidget(this) + views.widgetWebView.setupForWidget(this, this) if (fragmentArgs.kind.isAdmin()) { viewModel.getPostAPIMediator().setWebView(views.widgetWebView) } @@ -271,6 +276,19 @@ class WidgetFragment @Inject constructor() : viewModel.handle(WidgetAction.OnWebViewLoadingError(url, true, errorCode, description)) } + private val permissionResultLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { result -> + WebviewPermissionUtils.onPermissionResult(result) + } + + override fun onPermissionRequest(request: PermissionRequest) { + WebviewPermissionUtils.promptForPermissions( + title = R.string.room_widget_resource_permission_title, + request = request, + context = requireContext(), + activity = requireActivity(), + activityResultLauncher = permissionResultLauncher) + } + private fun displayTerms(displayTerms: WidgetViewEvents.DisplayTerms) { navigator.openTerms( context = requireContext(), diff --git a/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt b/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt index 12b58cc208..f1111a4650 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt @@ -15,17 +15,30 @@ */ package im.vector.app.features.widgets.webview +import android.Manifest import android.annotation.SuppressLint import android.content.Context import android.webkit.PermissionRequest +import androidx.activity.result.ActivityResultLauncher import androidx.annotation.StringRes +import androidx.fragment.app.FragmentActivity import com.google.android.material.dialog.MaterialAlertDialogBuilder import im.vector.app.R +import im.vector.app.core.utils.checkPermissions object WebviewPermissionUtils { + private var permissionRequest: PermissionRequest? = null + private var selectedPermissions = listOf() + @SuppressLint("NewApi") - fun promptForPermissions(@StringRes title: Int, request: PermissionRequest, context: Context) { + fun promptForPermissions( + @StringRes title: Int, + request: PermissionRequest, + context: Context, + activity: FragmentActivity, + activityResultLauncher: ActivityResultLauncher> + ) { val allowedPermissions = request.resources.map { it to false }.toMutableList() @@ -37,9 +50,21 @@ object WebviewPermissionUtils { allowedPermissions[which] = allowedPermissions[which].first to isChecked } .setPositiveButton(R.string.room_widget_resource_grant_permission) { _, _ -> - request.grant(allowedPermissions.mapNotNull { perm -> + permissionRequest = request + selectedPermissions = allowedPermissions.mapNotNull { perm -> perm.first.takeIf { perm.second } - }.toTypedArray()) + } + + val requiredAndroidPermissions = selectedPermissions.mapNotNull { permission -> + webPermissionToAndroidPermission(permission) + } + + // When checkPermissions returns false, some of the required Android permissions will + // have to be requested and the flow completes asynchronously via onPermissionResult + if (checkPermissions(requiredAndroidPermissions, activity, activityResultLauncher)) { + request.grant(selectedPermissions.toTypedArray()) + reset() + } } .setNegativeButton(R.string.room_widget_resource_decline_permission) { _, _ -> request.deny() @@ -47,6 +72,28 @@ object WebviewPermissionUtils { .show() } + fun onPermissionResult(result: Map) { + permissionRequest?.let { request -> + val grantedPermissions = selectedPermissions.filter { webPermission -> + val androidPermission = webPermissionToAndroidPermission(webPermission) + ?: return@filter true // No corresponding Android permission exists + return@filter result[androidPermission] + ?: return@filter true // Android permission already granted before + } + if (grantedPermissions.isNotEmpty()) { + request.grant(grantedPermissions.toTypedArray()) + } else { + request.deny() + } + reset() + } + } + + private fun reset() { + permissionRequest = null + selectedPermissions = listOf() + } + private fun webPermissionToHumanReadable(permission: String, context: Context): String { return when (permission) { PermissionRequest.RESOURCE_AUDIO_CAPTURE -> context.getString(R.string.room_widget_webview_access_microphone) @@ -55,4 +102,12 @@ object WebviewPermissionUtils { else -> permission } } + + private fun webPermissionToAndroidPermission(permission: String): String? { + return when (permission) { + PermissionRequest.RESOURCE_AUDIO_CAPTURE -> Manifest.permission.RECORD_AUDIO + PermissionRequest.RESOURCE_VIDEO_CAPTURE -> Manifest.permission.CAMERA + else -> null + } + } } diff --git a/vector/src/main/java/im/vector/app/features/widgets/webview/WidgetWebView.kt b/vector/src/main/java/im/vector/app/features/widgets/webview/WidgetWebView.kt index 7147529e5f..a49eb802da 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/webview/WidgetWebView.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/webview/WidgetWebView.kt @@ -25,10 +25,11 @@ import android.webkit.WebView import im.vector.app.R import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.webview.VectorWebViewClient +import im.vector.app.features.webview.WebChromeEventListener import im.vector.app.features.webview.WebViewEventListener @SuppressLint("NewApi") -fun WebView.setupForWidget(webViewEventListener: WebViewEventListener) { +fun WebView.setupForWidget(webViewEventListener: WebViewEventListener, webChromeEventListener: WebChromeEventListener) { // xml value seems ignored setBackgroundColor(ThemeUtils.getColor(context, R.attr.colorSurface)) @@ -59,7 +60,7 @@ fun WebView.setupForWidget(webViewEventListener: WebViewEventListener) { // Permission requests webChromeClient = object : WebChromeClient() { override fun onPermissionRequest(request: PermissionRequest) { - WebviewPermissionUtils.promptForPermissions(R.string.room_widget_resource_permission_title, request, context) + webChromeEventListener.onPermissionRequest(request) } } webViewClient = VectorWebViewClient(webViewEventListener) From 946902719b3229e6b555d8843c296bd145cbf870 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Wed, 25 May 2022 12:53:47 +0200 Subject: [PATCH 02/13] Add changelog entry --- changelog.d/6149.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/6149.bugfix diff --git a/changelog.d/6149.bugfix b/changelog.d/6149.bugfix new file mode 100644 index 0000000000..c255a8daee --- /dev/null +++ b/changelog.d/6149.bugfix @@ -0,0 +1 @@ +Make widget web view request system permissions for camera and microphone (PSF-1061) From 580bbd60e73837a7887a8e4fb2679b23bb99dcd0 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Wed, 25 May 2022 13:01:42 +0200 Subject: [PATCH 03/13] Appease the linter --- .../im/vector/app/features/webview/WebChromeEventListener.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/webview/WebChromeEventListener.kt b/vector/src/main/java/im/vector/app/features/webview/WebChromeEventListener.kt index 8a12052def..cc9725abc4 100644 --- a/vector/src/main/java/im/vector/app/features/webview/WebChromeEventListener.kt +++ b/vector/src/main/java/im/vector/app/features/webview/WebChromeEventListener.kt @@ -28,5 +28,4 @@ interface WebChromeEventListener { fun onPermissionRequest(request: PermissionRequest) { // NO-OP } - } From 6ec6d41aa992b8d58b9bde03e4d2c727e3e3d237 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Wed, 25 May 2022 15:53:58 +0200 Subject: [PATCH 04/13] Make permission utils instantiable and throw when permission request is null --- .../app/features/widgets/WidgetFragment.kt | 8 +++-- .../widgets/webview/WebviewPermissionUtils.kt | 30 ++++++++++--------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt index c8a13d11cf..ed2c69c908 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt @@ -72,6 +72,7 @@ class WidgetFragment @Inject constructor() : private val fragmentArgs: WidgetArgs by args() private val viewModel: WidgetViewModel by activityViewModel() + private val permissionUtils = WebviewPermissionUtils() override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomWidgetBinding { return FragmentRoomWidgetBinding.inflate(inflater, container, false) @@ -277,16 +278,17 @@ class WidgetFragment @Inject constructor() : } private val permissionResultLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { result -> - WebviewPermissionUtils.onPermissionResult(result) + permissionUtils.onPermissionResult(result) } override fun onPermissionRequest(request: PermissionRequest) { - WebviewPermissionUtils.promptForPermissions( + permissionUtils.promptForPermissions( title = R.string.room_widget_resource_permission_title, request = request, context = requireContext(), activity = requireActivity(), - activityResultLauncher = permissionResultLauncher) + activityResultLauncher = permissionResultLauncher + ) } private fun displayTerms(displayTerms: WidgetViewEvents.DisplayTerms) { diff --git a/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt b/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt index f1111a4650..3a39153581 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt @@ -25,8 +25,9 @@ import androidx.fragment.app.FragmentActivity import com.google.android.material.dialog.MaterialAlertDialogBuilder import im.vector.app.R import im.vector.app.core.utils.checkPermissions +import java.lang.NullPointerException -object WebviewPermissionUtils { +class WebviewPermissionUtils { private var permissionRequest: PermissionRequest? = null private var selectedPermissions = listOf() @@ -73,20 +74,21 @@ object WebviewPermissionUtils { } fun onPermissionResult(result: Map) { - permissionRequest?.let { request -> - val grantedPermissions = selectedPermissions.filter { webPermission -> - val androidPermission = webPermissionToAndroidPermission(webPermission) - ?: return@filter true // No corresponding Android permission exists - return@filter result[androidPermission] - ?: return@filter true // Android permission already granted before - } - if (grantedPermissions.isNotEmpty()) { - request.grant(grantedPermissions.toTypedArray()) - } else { - request.deny() - } - reset() + if (permissionRequest == null) { + throw NullPointerException("permissionRequest was null! Make sure to call promptForPermissions first.") } + val grantedPermissions = selectedPermissions.filter { webPermission -> + val androidPermission = webPermissionToAndroidPermission(webPermission) + ?: return@filter true // No corresponding Android permission exists + return@filter result[androidPermission] + ?: return@filter true // Android permission already granted before + } + if (grantedPermissions.isNotEmpty()) { + permissionRequest?.grant(grantedPermissions.toTypedArray()) + } else { + permissionRequest?.deny() + } + reset() } private fun reset() { From 9e084ec372aa9de27bb431844c5b51590db16871 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Wed, 25 May 2022 16:22:16 +0200 Subject: [PATCH 05/13] Inject permission utils --- .../java/im/vector/app/features/widgets/WidgetFragment.kt | 5 +++-- .../app/features/widgets/webview/WebviewPermissionUtils.kt | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt index ed2c69c908..897f6d6279 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt @@ -64,7 +64,9 @@ data class WidgetArgs( val urlParams: Map = emptyMap() ) : Parcelable -class WidgetFragment @Inject constructor() : +class WidgetFragment @Inject constructor( + private val permissionUtils: WebviewPermissionUtils +) : VectorBaseFragment(), WebViewEventListener, WebChromeEventListener, @@ -72,7 +74,6 @@ class WidgetFragment @Inject constructor() : private val fragmentArgs: WidgetArgs by args() private val viewModel: WidgetViewModel by activityViewModel() - private val permissionUtils = WebviewPermissionUtils() override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomWidgetBinding { return FragmentRoomWidgetBinding.inflate(inflater, container, false) diff --git a/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt b/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt index 3a39153581..2bf12dff43 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt @@ -26,8 +26,9 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import im.vector.app.R import im.vector.app.core.utils.checkPermissions import java.lang.NullPointerException +import javax.inject.Inject -class WebviewPermissionUtils { +class WebviewPermissionUtils @Inject constructor() { private var permissionRequest: PermissionRequest? = null private var selectedPermissions = listOf() From 92a140b5041f9fc4423ccc9aa9348de41ab8a1c8 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Wed, 25 May 2022 20:50:58 +0200 Subject: [PATCH 06/13] Add unit tests for filter/map logic --- .../widgets/WebviewPermissionUtilsTest.kt | 83 +++++++++++++++++++ .../widgets/webview/WebviewPermissionUtils.kt | 18 ++-- 2 files changed, 95 insertions(+), 6 deletions(-) create mode 100644 vector/src/androidTest/java/im/vector/app/features/widgets/WebviewPermissionUtilsTest.kt diff --git a/vector/src/androidTest/java/im/vector/app/features/widgets/WebviewPermissionUtilsTest.kt b/vector/src/androidTest/java/im/vector/app/features/widgets/WebviewPermissionUtilsTest.kt new file mode 100644 index 0000000000..6ae17dbda4 --- /dev/null +++ b/vector/src/androidTest/java/im/vector/app/features/widgets/WebviewPermissionUtilsTest.kt @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.widgets + +import android.Manifest +import android.webkit.PermissionRequest +import im.vector.app.InstrumentedTest +import im.vector.app.features.widgets.webview.WebviewPermissionUtils +import org.amshove.kluent.shouldBeEqualTo +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.junit.runners.MethodSorters + +@RunWith(JUnit4::class) +@FixMethodOrder(MethodSorters.JVM) +class WebviewPermissionUtilsTest : InstrumentedTest { + + private val utils = WebviewPermissionUtils() + + @Test + fun filterPermissionsToBeGranted_selectedAndGrantedNothing() { + val permissions = utils.filterPermissionsToBeGranted( + selectedWebPermissions = listOf(), + androidPermissionResult = mapOf()) + permissions shouldBeEqualTo listOf() + } + + @Test + fun filterPermissionsToBeGranted_selectedNothingGrantedCamera() { + val permissions = utils.filterPermissionsToBeGranted( + selectedWebPermissions = listOf(), + androidPermissionResult = mapOf(Manifest.permission.CAMERA to true)) + permissions shouldBeEqualTo listOf() + } + + @Test + fun filterPermissionsToBeGranted_selectedAndPreviouslyGrantedCamera() { + val permissions = utils.filterPermissionsToBeGranted( + selectedWebPermissions = listOf(PermissionRequest.RESOURCE_VIDEO_CAPTURE), + androidPermissionResult = mapOf()) + permissions shouldBeEqualTo listOf(PermissionRequest.RESOURCE_VIDEO_CAPTURE) + } + + @Test + fun filterPermissionsToBeGranted_selectedAndGrantedCamera() { + val permissions = utils.filterPermissionsToBeGranted( + selectedWebPermissions = listOf(PermissionRequest.RESOURCE_VIDEO_CAPTURE), + androidPermissionResult = mapOf(Manifest.permission.CAMERA to true)) + permissions shouldBeEqualTo listOf(PermissionRequest.RESOURCE_VIDEO_CAPTURE) + } + + @Test + fun filterPermissionsToBeGranted_selectedAndDeniedCamera() { + val permissions = utils.filterPermissionsToBeGranted( + selectedWebPermissions = listOf(PermissionRequest.RESOURCE_VIDEO_CAPTURE), + androidPermissionResult = mapOf(Manifest.permission.CAMERA to false)) + permissions shouldBeEqualTo listOf() + } + + @Test + fun filterPermissionsToBeGranted_selectedProtectedMediaGrantedNothing() { + val permissions = utils.filterPermissionsToBeGranted( + selectedWebPermissions = listOf(PermissionRequest.RESOURCE_PROTECTED_MEDIA_ID), + androidPermissionResult = mapOf(Manifest.permission.CAMERA to false)) + permissions shouldBeEqualTo listOf(PermissionRequest.RESOURCE_PROTECTED_MEDIA_ID) + } +} diff --git a/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt b/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt index 2bf12dff43..92b6ad213c 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt @@ -21,6 +21,7 @@ import android.content.Context import android.webkit.PermissionRequest import androidx.activity.result.ActivityResultLauncher import androidx.annotation.StringRes +import androidx.annotation.VisibleForTesting import androidx.fragment.app.FragmentActivity import com.google.android.material.dialog.MaterialAlertDialogBuilder import im.vector.app.R @@ -78,12 +79,7 @@ class WebviewPermissionUtils @Inject constructor() { if (permissionRequest == null) { throw NullPointerException("permissionRequest was null! Make sure to call promptForPermissions first.") } - val grantedPermissions = selectedPermissions.filter { webPermission -> - val androidPermission = webPermissionToAndroidPermission(webPermission) - ?: return@filter true // No corresponding Android permission exists - return@filter result[androidPermission] - ?: return@filter true // Android permission already granted before - } + val grantedPermissions = filterPermissionsToBeGranted(selectedPermissions, result) if (grantedPermissions.isNotEmpty()) { permissionRequest?.grant(grantedPermissions.toTypedArray()) } else { @@ -92,6 +88,16 @@ class WebviewPermissionUtils @Inject constructor() { reset() } + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + fun filterPermissionsToBeGranted(selectedWebPermissions: List, androidPermissionResult: Map): List { + return selectedWebPermissions.filter { webPermission -> + val androidPermission = webPermissionToAndroidPermission(webPermission) + ?: return@filter true // No corresponding Android permission exists + return@filter androidPermissionResult[androidPermission] + ?: return@filter true // Android permission already granted before + } + } + private fun reset() { permissionRequest = null selectedPermissions = listOf() From 73d78646ea30db607d568800e6c51817826f90a7 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Mon, 30 May 2022 16:34:19 +0200 Subject: [PATCH 07/13] Turn test into normal unit test --- .../vector/app/features/widgets/WebviewPermissionUtilsTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/androidTest/java/im/vector/app/features/widgets/WebviewPermissionUtilsTest.kt b/vector/src/androidTest/java/im/vector/app/features/widgets/WebviewPermissionUtilsTest.kt index 6ae17dbda4..148bb9057c 100644 --- a/vector/src/androidTest/java/im/vector/app/features/widgets/WebviewPermissionUtilsTest.kt +++ b/vector/src/androidTest/java/im/vector/app/features/widgets/WebviewPermissionUtilsTest.kt @@ -29,7 +29,7 @@ import org.junit.runners.MethodSorters @RunWith(JUnit4::class) @FixMethodOrder(MethodSorters.JVM) -class WebviewPermissionUtilsTest : InstrumentedTest { +class WebviewPermissionUtilsTest { private val utils = WebviewPermissionUtils() From d757914225cc66b4aaae763ba9ee1f2a754743e9 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Mon, 30 May 2022 16:35:44 +0200 Subject: [PATCH 08/13] Unsuppress lint rule --- .../app/features/widgets/webview/WebviewPermissionUtils.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt b/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt index 92b6ad213c..3650b5b1f1 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt @@ -34,7 +34,6 @@ class WebviewPermissionUtils @Inject constructor() { private var permissionRequest: PermissionRequest? = null private var selectedPermissions = listOf() - @SuppressLint("NewApi") fun promptForPermissions( @StringRes title: Int, request: PermissionRequest, From 769b217c7eb74b140e4f9c821296932510f96db3 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Mon, 30 May 2022 16:53:59 +0200 Subject: [PATCH 09/13] Remove unused imports --- .../im/vector/app/features/widgets/WebviewPermissionUtilsTest.kt | 1 - .../app/features/widgets/webview/WebviewPermissionUtils.kt | 1 - 2 files changed, 2 deletions(-) diff --git a/vector/src/androidTest/java/im/vector/app/features/widgets/WebviewPermissionUtilsTest.kt b/vector/src/androidTest/java/im/vector/app/features/widgets/WebviewPermissionUtilsTest.kt index 148bb9057c..359f52fc85 100644 --- a/vector/src/androidTest/java/im/vector/app/features/widgets/WebviewPermissionUtilsTest.kt +++ b/vector/src/androidTest/java/im/vector/app/features/widgets/WebviewPermissionUtilsTest.kt @@ -18,7 +18,6 @@ package im.vector.app.features.widgets import android.Manifest import android.webkit.PermissionRequest -import im.vector.app.InstrumentedTest import im.vector.app.features.widgets.webview.WebviewPermissionUtils import org.amshove.kluent.shouldBeEqualTo import org.junit.FixMethodOrder diff --git a/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt b/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt index 3650b5b1f1..1be2b0896a 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/webview/WebviewPermissionUtils.kt @@ -16,7 +16,6 @@ package im.vector.app.features.widgets.webview import android.Manifest -import android.annotation.SuppressLint import android.content.Context import android.webkit.PermissionRequest import androidx.activity.result.ActivityResultLauncher From 75da9887be2df8defbc8e300248e55bfa5e4077f Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Mon, 30 May 2022 17:09:28 +0200 Subject: [PATCH 10/13] Move test to the right folder --- .../im/vector/app/features/widgets/WebviewPermissionUtilsTest.kt | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename vector/src/{androidTest => test}/java/im/vector/app/features/widgets/WebviewPermissionUtilsTest.kt (100%) diff --git a/vector/src/androidTest/java/im/vector/app/features/widgets/WebviewPermissionUtilsTest.kt b/vector/src/test/java/im/vector/app/features/widgets/WebviewPermissionUtilsTest.kt similarity index 100% rename from vector/src/androidTest/java/im/vector/app/features/widgets/WebviewPermissionUtilsTest.kt rename to vector/src/test/java/im/vector/app/features/widgets/WebviewPermissionUtilsTest.kt From 5b64946dfb1ab797f35702bec2c9c68d4a906b0b Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Mon, 30 May 2022 20:18:56 +0200 Subject: [PATCH 11/13] Remove empty default implementation --- .../im/vector/app/features/webview/WebChromeEventListener.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/webview/WebChromeEventListener.kt b/vector/src/main/java/im/vector/app/features/webview/WebChromeEventListener.kt index cc9725abc4..d4bd2be186 100644 --- a/vector/src/main/java/im/vector/app/features/webview/WebChromeEventListener.kt +++ b/vector/src/main/java/im/vector/app/features/webview/WebChromeEventListener.kt @@ -25,7 +25,5 @@ interface WebChromeEventListener { * * @param request The permission request. */ - fun onPermissionRequest(request: PermissionRequest) { - // NO-OP - } + fun onPermissionRequest(request: PermissionRequest) } From 70bb2b7fee1e6225fb3b02bbb535b39d62513a30 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Mon, 30 May 2022 20:28:09 +0200 Subject: [PATCH 12/13] Combine event listener interfaces --- .../im/vector/app/features/widgets/WidgetFragment.kt | 8 +++----- .../vector/app/features/widgets/webview/WidgetWebView.kt | 9 ++++----- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt index 897f6d6279..0f399d7c45 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt @@ -44,8 +44,7 @@ import im.vector.app.core.platform.OnBackPressed import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.openUrlInExternalBrowser import im.vector.app.databinding.FragmentRoomWidgetBinding -import im.vector.app.features.webview.WebChromeEventListener -import im.vector.app.features.webview.WebViewEventListener +import im.vector.app.features.webview.WebEventListener import im.vector.app.features.widgets.webview.WebviewPermissionUtils import im.vector.app.features.widgets.webview.clearAfterWidget import im.vector.app.features.widgets.webview.setupForWidget @@ -68,8 +67,7 @@ class WidgetFragment @Inject constructor( private val permissionUtils: WebviewPermissionUtils ) : VectorBaseFragment(), - WebViewEventListener, - WebChromeEventListener, + WebEventListener, OnBackPressed { private val fragmentArgs: WidgetArgs by args() @@ -82,7 +80,7 @@ class WidgetFragment @Inject constructor( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setHasOptionsMenu(true) - views.widgetWebView.setupForWidget(this, this) + views.widgetWebView.setupForWidget(this) if (fragmentArgs.kind.isAdmin()) { viewModel.getPostAPIMediator().setWebView(views.widgetWebView) } diff --git a/vector/src/main/java/im/vector/app/features/widgets/webview/WidgetWebView.kt b/vector/src/main/java/im/vector/app/features/widgets/webview/WidgetWebView.kt index a49eb802da..0207987ca3 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/webview/WidgetWebView.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/webview/WidgetWebView.kt @@ -25,11 +25,10 @@ import android.webkit.WebView import im.vector.app.R import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.webview.VectorWebViewClient -import im.vector.app.features.webview.WebChromeEventListener -import im.vector.app.features.webview.WebViewEventListener +import im.vector.app.features.webview.WebEventListener @SuppressLint("NewApi") -fun WebView.setupForWidget(webViewEventListener: WebViewEventListener, webChromeEventListener: WebChromeEventListener) { +fun WebView.setupForWidget(eventListener: WebEventListener) { // xml value seems ignored setBackgroundColor(ThemeUtils.getColor(context, R.attr.colorSurface)) @@ -60,10 +59,10 @@ fun WebView.setupForWidget(webViewEventListener: WebViewEventListener, webChrome // Permission requests webChromeClient = object : WebChromeClient() { override fun onPermissionRequest(request: PermissionRequest) { - webChromeEventListener.onPermissionRequest(request) + eventListener.onPermissionRequest(request) } } - webViewClient = VectorWebViewClient(webViewEventListener) + webViewClient = VectorWebViewClient(eventListener) val cookieManager = CookieManager.getInstance() cookieManager.setAcceptThirdPartyCookies(this, false) From 373991915edb7f662cc7de74d7615dc97e9f648d Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Tue, 31 May 2022 08:10:13 +0200 Subject: [PATCH 13/13] Add missing file --- .../app/features/webview/WebEventListener.kt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 vector/src/main/java/im/vector/app/features/webview/WebEventListener.kt diff --git a/vector/src/main/java/im/vector/app/features/webview/WebEventListener.kt b/vector/src/main/java/im/vector/app/features/webview/WebEventListener.kt new file mode 100644 index 0000000000..d4bf5b898b --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/webview/WebEventListener.kt @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.webview + +interface WebEventListener : WebViewEventListener, WebChromeEventListener