mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-21 17:05:39 +03:00
Handle M_CONSENT_NOT_GIVEN error (#64)
This commit is contained in:
parent
137dcab734
commit
3e6b65e174
21 changed files with 689 additions and 6 deletions
|
@ -2,7 +2,7 @@ Changes in RiotX 0.5.0 (2019-XX-XX)
|
|||
===================================================
|
||||
|
||||
Features:
|
||||
-
|
||||
- Handle M_CONSENT_NOT_GIVEN error (#64)
|
||||
|
||||
Improvements:
|
||||
- Reduce default release build log level, and lab option to enable more logs.
|
||||
|
|
|
@ -139,6 +139,9 @@ dependencies {
|
|||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||
implementation 'com.facebook.stetho:stetho-okhttp3:1.5.0'
|
||||
|
||||
// Bus
|
||||
implementation 'org.greenrobot:eventbus:3.1.1'
|
||||
|
||||
debugImplementation 'com.airbnb.okreplay:okreplay:1.4.0'
|
||||
releaseImplementation 'com.airbnb.okreplay:noop:1.4.0'
|
||||
androidTestImplementation 'com.airbnb.okreplay:espresso:1.4.0'
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright 2019 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.matrix.android.api.failure
|
||||
|
||||
// This data class will be sent to the bus
|
||||
data class ConsentNotGivenError(
|
||||
val consentUri: String
|
||||
)
|
|
@ -18,10 +18,12 @@ package im.vector.matrix.android.internal.network
|
|||
|
||||
import com.squareup.moshi.JsonDataException
|
||||
import com.squareup.moshi.Moshi
|
||||
import im.vector.matrix.android.api.failure.ConsentNotGivenError
|
||||
import im.vector.matrix.android.api.failure.Failure
|
||||
import im.vector.matrix.android.api.failure.MatrixError
|
||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||
import okhttp3.ResponseBody
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import retrofit2.Call
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
|
@ -65,6 +67,11 @@ internal class Request<DATA> {
|
|||
val matrixError = matrixErrorAdapter.fromJson(errorBodyStr)
|
||||
|
||||
if (matrixError != null) {
|
||||
if (matrixError.code == MatrixError.M_CONSENT_NOT_GIVEN && !matrixError.consentUri.isNullOrBlank()) {
|
||||
// Also send this error to the bus, for a global management
|
||||
EventBus.getDefault().post(ConsentNotGivenError(matrixError.consentUri))
|
||||
}
|
||||
|
||||
return Failure.ServerError(matrixError, httpCode)
|
||||
}
|
||||
} catch (ex: JsonDataException) {
|
||||
|
|
|
@ -280,6 +280,9 @@ dependencies {
|
|||
implementation "ru.noties.markwon:html:$markwon_version"
|
||||
implementation 'me.saket:better-link-movement-method:2.2.0'
|
||||
|
||||
// Bus
|
||||
implementation 'org.greenrobot:eventbus:3.1.1'
|
||||
|
||||
// Passphrase strength helper
|
||||
implementation 'com.nulab-inc:zxcvbn:1.2.5'
|
||||
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
<activity android:name=".features.home.room.detail.RoomDetailActivity" />
|
||||
<activity android:name=".features.debug.DebugMenuActivity" />
|
||||
<activity android:name=".features.home.createdirect.CreateDirectRoomActivity" />
|
||||
<activity android:name=".features.webview.VectorWebViewActivity" />
|
||||
|
||||
<!-- Services -->
|
||||
|
||||
|
|
|
@ -349,6 +349,11 @@ SOFTWARE.
|
|||
<br/>
|
||||
Copyright 2018 The diff-match-patch Authors. https://github.com/google/diff-match-patch
|
||||
</li>
|
||||
<li>
|
||||
<b>EventBus</b>
|
||||
<br/>
|
||||
Copyright (C) 2012-2017 Markus Junginger, greenrobot (http://greenrobot.org)
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
|
|
@ -26,9 +26,9 @@ private const val KEY_DIALOG_IS_DISPLAYED = "DialogLocker.KEY_DIALOG_IS_DISPLAYE
|
|||
/**
|
||||
* Class to avoid displaying twice the same dialog
|
||||
*/
|
||||
class DialogLocker() : Restorable {
|
||||
class DialogLocker(savedInstanceState: Bundle?) : Restorable {
|
||||
|
||||
private var isDialogDisplayed: Boolean = false
|
||||
private var isDialogDisplayed = savedInstanceState?.getBoolean(KEY_DIALOG_IS_DISPLAYED, false) == true
|
||||
|
||||
private fun unlock() {
|
||||
isDialogDisplayed = false
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package im.vector.riotx.core.error
|
||||
|
||||
import im.vector.matrix.android.api.failure.Failure
|
||||
import im.vector.matrix.android.api.failure.MatrixError
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.resources.StringProvider
|
||||
import javax.inject.Inject
|
||||
|
@ -34,8 +35,13 @@ class ErrorFormatter @Inject constructor(val stringProvider: StringProvider) {
|
|||
null -> null
|
||||
is Failure.NetworkConnection -> stringProvider.getString(R.string.error_no_network)
|
||||
is Failure.ServerError -> {
|
||||
throwable.error.message.takeIf { it.isNotEmpty() }
|
||||
?: throwable.error.code.takeIf { it.isNotEmpty() }
|
||||
if (throwable.error.code == MatrixError.M_CONSENT_NOT_GIVEN) {
|
||||
// Special case for terms and conditions
|
||||
stringProvider.getString(R.string.error_terms_not_accepted)
|
||||
} else {
|
||||
throwable.error.message.takeIf { it.isNotEmpty() }
|
||||
?: throwable.error.code.takeIf { it.isNotEmpty() }
|
||||
}
|
||||
}
|
||||
else -> throwable.localizedMessage
|
||||
}
|
||||
|
|
|
@ -36,11 +36,14 @@ import butterknife.Unbinder
|
|||
import com.airbnb.mvrx.BaseMvRxActivity
|
||||
import com.bumptech.glide.util.Util
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import im.vector.matrix.android.api.failure.ConsentNotGivenError
|
||||
import im.vector.riotx.BuildConfig
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.di.*
|
||||
import im.vector.riotx.core.dialogs.DialogLocker
|
||||
import im.vector.riotx.core.utils.toast
|
||||
import im.vector.riotx.features.configuration.VectorConfiguration
|
||||
import im.vector.riotx.features.consent.ConsentNotGivenHelper
|
||||
import im.vector.riotx.features.navigation.Navigator
|
||||
import im.vector.riotx.features.rageshake.BugReportActivity
|
||||
import im.vector.riotx.features.rageshake.BugReporter
|
||||
|
@ -50,6 +53,9 @@ import im.vector.riotx.features.themes.ThemeUtils
|
|||
import im.vector.riotx.receivers.DebugReceiver
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.disposables.Disposable
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import timber.log.Timber
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
|
@ -391,6 +397,31 @@ abstract class VectorBaseActivity : BaseMvRxActivity(), HasScreenInjector {
|
|||
}
|
||||
}
|
||||
|
||||
/* ==========================================================================================
|
||||
* User Consent
|
||||
* ========================================================================================== */
|
||||
|
||||
private val consentNotGivenHelper by lazy {
|
||||
ConsentNotGivenHelper(this, DialogLocker(savedInstanceState))
|
||||
.apply { restorables.add(this) }
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
EventBus.getDefault().register(this)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
EventBus.getDefault().unregister(this)
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onConsentNotGivenError(consentNotGivenError: ConsentNotGivenError) {
|
||||
consentNotGivenHelper.displayDialog(consentNotGivenError.consentUri,
|
||||
screenComponent.session().sessionParams.homeServerConnectionConfig.homeServerUri.host ?: "")
|
||||
}
|
||||
|
||||
/* ==========================================================================================
|
||||
* Temporary method
|
||||
* ========================================================================================== */
|
||||
|
@ -402,5 +433,4 @@ abstract class VectorBaseActivity : BaseMvRxActivity(), HasScreenInjector {
|
|||
toast(getString(R.string.not_implemented))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2018 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.riotx.core.utils
|
||||
|
||||
import java.lang.ref.WeakReference
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
fun <T> weak(value: T) = WeakReferenceDelegate(value)
|
||||
|
||||
class WeakReferenceDelegate<T>(value: T) {
|
||||
|
||||
private var weakReference: WeakReference<T> = WeakReference(value)
|
||||
|
||||
operator fun getValue(thisRef: Any, property: KProperty<*>): T? = weakReference.get()
|
||||
operator fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
|
||||
weakReference = WeakReference(value)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2018 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.riotx.features.consent
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.dialogs.DialogLocker
|
||||
import im.vector.riotx.core.platform.Restorable
|
||||
import im.vector.riotx.features.webview.VectorWebViewActivity
|
||||
import im.vector.riotx.features.webview.WebViewMode
|
||||
|
||||
class ConsentNotGivenHelper(private val activity: Activity,
|
||||
private val dialogLocker: DialogLocker) :
|
||||
Restorable by dialogLocker {
|
||||
|
||||
/* ==========================================================================================
|
||||
* Public methods
|
||||
* ========================================================================================== */
|
||||
|
||||
/**
|
||||
* Display the consent dialog, if not already displayed
|
||||
*/
|
||||
fun displayDialog(consentUri: String, homeServerHost: String) {
|
||||
dialogLocker.displayDialog {
|
||||
AlertDialog.Builder(activity)
|
||||
.setTitle(R.string.settings_app_term_conditions)
|
||||
.setMessage(activity.getString(R.string.dialog_user_consent_content, homeServerHost))
|
||||
.setPositiveButton(R.string.dialog_user_consent_submit) { _, _ ->
|
||||
openWebViewActivity(consentUri)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ==========================================================================================
|
||||
* Private
|
||||
* ========================================================================================== */
|
||||
|
||||
private fun openWebViewActivity(consentUri: String) {
|
||||
val intent = VectorWebViewActivity.getIntent(activity, consentUri, activity.getString(R.string.settings_app_term_conditions), WebViewMode.CONSENT)
|
||||
activity.startActivity(intent)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright 2018 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.riotx.features.webview
|
||||
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||
import im.vector.riotx.core.utils.weak
|
||||
import timber.log.Timber
|
||||
|
||||
private const val SUCCESS_URL_SUFFIX = "/_matrix/consent"
|
||||
private const val RIOT_BOT_ID = "@riot-bot:matrix.org"
|
||||
|
||||
/**
|
||||
* This class is the Consent implementation of WebViewEventListener.
|
||||
* It is used to manage the consent agreement flow.
|
||||
*/
|
||||
class ConsentWebViewEventListener(activity: VectorBaseActivity,
|
||||
private val session: Session,
|
||||
private val delegate: WebViewEventListener)
|
||||
: WebViewEventListener by delegate {
|
||||
|
||||
private val safeActivity: VectorBaseActivity? by weak(activity)
|
||||
|
||||
override fun onPageFinished(url: String) {
|
||||
delegate.onPageFinished(url)
|
||||
if (url.endsWith(SUCCESS_URL_SUFFIX)) {
|
||||
createRiotBotRoomIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This methods try to create the RiotBot room when the user gives his agreement
|
||||
*/
|
||||
private fun createRiotBotRoomIfNeeded() {
|
||||
safeActivity?.let {
|
||||
/* We do not create a Room with RiotBot in RiotX for the moment
|
||||
val joinedRooms = session.dataHandler.store.rooms.filter {
|
||||
it.isJoined
|
||||
}
|
||||
if (joinedRooms.isEmpty()) {
|
||||
it.showWaitingView()
|
||||
// Ensure we can create a Room with riot-bot. Error can be a MatrixError: "Federation denied with matrix.org.", or any other error.
|
||||
session.profileApiClient
|
||||
.displayname(RIOT_BOT_ID, object : MatrixCallback<String>(createRiotBotRoomCallback) {
|
||||
override fun onSuccess(info: String?) {
|
||||
// Ok, the Home Server knows riot-Bot, so create a Room with him
|
||||
session.createDirectMessageRoom(RIOT_BOT_ID, createRiotBotRoomCallback)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
*/
|
||||
it.finish()
|
||||
/*
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* APICallback instance
|
||||
*/
|
||||
private val createRiotBotRoomCallback = object : MatrixCallback<String> {
|
||||
override fun onSuccess(data: String) {
|
||||
Timber.d("## On success : succeed to invite riot-bot")
|
||||
safeActivity?.finish()
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
Timber.e("## On error : failed to invite riot-bot $failure")
|
||||
safeActivity?.finish()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright 2018 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.riotx.features.webview
|
||||
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* This class is the default implementation of WebViewEventListener.
|
||||
* It can be used with delegation pattern
|
||||
*/
|
||||
|
||||
class DefaultWebViewEventListener : WebViewEventListener {
|
||||
|
||||
override fun pageWillStart(url: String) {
|
||||
Timber.v("On page will start: $url")
|
||||
}
|
||||
|
||||
override fun onPageStarted(url: String) {
|
||||
Timber.d("On page started: $url")
|
||||
}
|
||||
|
||||
override fun onPageFinished(url: String) {
|
||||
Timber.d("On page finished: $url")
|
||||
}
|
||||
|
||||
override fun onPageError(url: String, errorCode: Int, description: String) {
|
||||
Timber.e("On received error: $url - errorCode: $errorCode - message: $description")
|
||||
}
|
||||
|
||||
override fun shouldOverrideUrlLoading(url: String): Boolean {
|
||||
Timber.v("Should override url: $url")
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Copyright 2018 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.riotx.features.webview
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.webkit.WebChromeClient
|
||||
import android.webkit.WebView
|
||||
import androidx.annotation.CallSuper
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.di.ScreenComponent
|
||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||
import kotlinx.android.synthetic.main.activity_vector_web_view.*
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* This class is responsible for managing a WebView
|
||||
* It does also have a loading view and a toolbar
|
||||
* It relies on the VectorWebViewClient
|
||||
* This class shouldn't be extended. To add new behaviors, you might create a new WebViewMode and a new WebViewEventListener
|
||||
*/
|
||||
class VectorWebViewActivity : VectorBaseActivity() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.activity_vector_web_view
|
||||
|
||||
@Inject lateinit var session: Session
|
||||
|
||||
@CallSuper
|
||||
override fun injectWith(injector: ScreenComponent) {
|
||||
session = injector.session()
|
||||
}
|
||||
|
||||
override fun initUiAndData() {
|
||||
configureToolbar(webview_toolbar)
|
||||
waitingView = findViewById(R.id.simple_webview_loader)
|
||||
|
||||
simple_webview.settings.apply {
|
||||
// Enable Javascript
|
||||
javaScriptEnabled = true
|
||||
|
||||
// Use WideViewport and Zoom out if there is no viewport defined
|
||||
useWideViewPort = true
|
||||
loadWithOverviewMode = true
|
||||
|
||||
// Enable pinch to zoom without the zoom buttons
|
||||
builtInZoomControls = true
|
||||
|
||||
// Allow use of Local Storage
|
||||
domStorageEnabled = true
|
||||
|
||||
allowFileAccessFromFileURLs = true
|
||||
allowUniversalAccessFromFileURLs = true
|
||||
|
||||
displayZoomControls = false
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
val cookieManager = android.webkit.CookieManager.getInstance()
|
||||
cookieManager.setAcceptThirdPartyCookies(simple_webview, true)
|
||||
}
|
||||
|
||||
val url = intent.extras.getString(EXTRA_URL)
|
||||
val title = intent.extras.getString(EXTRA_TITLE, USE_TITLE_FROM_WEB_PAGE)
|
||||
if (title != USE_TITLE_FROM_WEB_PAGE) {
|
||||
setTitle(title)
|
||||
}
|
||||
|
||||
val webViewMode = intent.extras.getSerializable(EXTRA_MODE) as WebViewMode
|
||||
val eventListener = webViewMode.eventListener(this, session)
|
||||
simple_webview.webViewClient = VectorWebViewClient(eventListener)
|
||||
simple_webview.webChromeClient = object : WebChromeClient() {
|
||||
override fun onReceivedTitle(view: WebView, title: String) {
|
||||
if (title == USE_TITLE_FROM_WEB_PAGE) {
|
||||
setTitle(title)
|
||||
}
|
||||
}
|
||||
}
|
||||
simple_webview.loadUrl(url)
|
||||
}
|
||||
|
||||
/* ==========================================================================================
|
||||
* UI event
|
||||
* ========================================================================================== */
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (simple_webview.canGoBack()) {
|
||||
simple_webview.goBack()
|
||||
} else {
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
/* ==========================================================================================
|
||||
* Companion
|
||||
* ========================================================================================== */
|
||||
|
||||
companion object {
|
||||
private const val EXTRA_URL = "EXTRA_URL"
|
||||
private const val EXTRA_TITLE = "EXTRA_TITLE"
|
||||
private const val EXTRA_MODE = "EXTRA_MODE"
|
||||
|
||||
private const val USE_TITLE_FROM_WEB_PAGE = ""
|
||||
|
||||
fun getIntent(context: Context,
|
||||
url: String,
|
||||
title: String = USE_TITLE_FROM_WEB_PAGE,
|
||||
mode: WebViewMode = WebViewMode.DEFAULT): Intent {
|
||||
return Intent(context, VectorWebViewActivity::class.java)
|
||||
.apply {
|
||||
putExtra(EXTRA_URL, url)
|
||||
putExtra(EXTRA_TITLE, title)
|
||||
putExtra(EXTRA_MODE, mode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright 2018 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.riotx.features.webview
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Build
|
||||
import android.webkit.WebResourceError
|
||||
import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
|
||||
/**
|
||||
* This class inherits from WebViewClient. It has to be used with a WebView.
|
||||
* It's responsible for dispatching events to the WebViewEventListener
|
||||
*/
|
||||
class VectorWebViewClient(private val eventListener: WebViewEventListener) : WebViewClient() {
|
||||
|
||||
private var mInError: Boolean = false
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.N)
|
||||
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
|
||||
return shouldOverrideUrl(request.url.toString())
|
||||
}
|
||||
|
||||
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
|
||||
return shouldOverrideUrl(url)
|
||||
}
|
||||
|
||||
override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
|
||||
super.onPageStarted(view, url, favicon)
|
||||
mInError = false
|
||||
eventListener.onPageStarted(url)
|
||||
}
|
||||
|
||||
override fun onPageFinished(view: WebView, url: String) {
|
||||
super.onPageFinished(view, url)
|
||||
if (!mInError) {
|
||||
eventListener.onPageFinished(url)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onReceivedError(view: WebView, errorCode: Int, description: String, failingUrl: String) {
|
||||
super.onReceivedError(view, errorCode, description, failingUrl)
|
||||
if (!mInError) {
|
||||
mInError = true
|
||||
eventListener.onPageError(failingUrl, errorCode, description)
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.N)
|
||||
override fun onReceivedError(view: WebView, request: WebResourceRequest, error: WebResourceError) {
|
||||
super.onReceivedError(view, request, error)
|
||||
if (!mInError) {
|
||||
mInError = true
|
||||
eventListener.onPageError(request.url.toString(), error.errorCode, error.description.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldOverrideUrl(url: String): Boolean {
|
||||
mInError = false
|
||||
val shouldOverrideUrlLoading = eventListener.shouldOverrideUrlLoading(url)
|
||||
if (!shouldOverrideUrlLoading) {
|
||||
eventListener.pageWillStart(url)
|
||||
}
|
||||
return shouldOverrideUrlLoading
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2018 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.riotx.features.webview
|
||||
|
||||
interface WebViewEventListener {
|
||||
|
||||
/**
|
||||
* Triggered when a webview page is about to be started.
|
||||
*
|
||||
* @param url The url about to be rendered.
|
||||
*/
|
||||
fun pageWillStart(url: String)
|
||||
|
||||
/**
|
||||
* Triggered when a loading webview page has started.
|
||||
*
|
||||
* @param url The rendering url.
|
||||
*/
|
||||
fun onPageStarted(url: String)
|
||||
|
||||
/**
|
||||
* Triggered when a loading webview page has finished loading but has not been rendered yet.
|
||||
*
|
||||
* @param url The finished url.
|
||||
*/
|
||||
fun onPageFinished(url: String)
|
||||
|
||||
/**
|
||||
* Triggered when an error occurred while loading a page.
|
||||
*
|
||||
* @param url The url that failed.
|
||||
* @param errorCode The error code.
|
||||
* @param description The error description.
|
||||
*/
|
||||
fun onPageError(url: String, errorCode: Int, description: String)
|
||||
|
||||
/**
|
||||
* Triggered when a webview load an url
|
||||
*
|
||||
* @param url The url about to be rendered.
|
||||
* @return true if the method needs to manage some custom handling
|
||||
*/
|
||||
fun shouldOverrideUrlLoading(url: String): Boolean
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2018 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.riotx.features.webview
|
||||
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||
|
||||
interface WebViewEventListenerFactory {
|
||||
|
||||
/**
|
||||
* @return an instance of WebViewEventListener
|
||||
*/
|
||||
fun eventListener(activity: VectorBaseActivity, session: Session): WebViewEventListener
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2018 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.riotx.features.webview
|
||||
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||
|
||||
/**
|
||||
* This enum indicates the WebView mode. It's responsible for creating a WebViewEventListener
|
||||
*/
|
||||
enum class WebViewMode : WebViewEventListenerFactory {
|
||||
|
||||
DEFAULT {
|
||||
override fun eventListener(activity: VectorBaseActivity, session: Session): WebViewEventListener {
|
||||
return DefaultWebViewEventListener()
|
||||
}
|
||||
},
|
||||
CONSENT {
|
||||
override fun eventListener(activity: VectorBaseActivity, session: Session): WebViewEventListener {
|
||||
return ConsentWebViewEventListener(activity, session, DefaultWebViewEventListener())
|
||||
}
|
||||
};
|
||||
|
||||
}
|
36
vector/src/main/res/layout/activity_vector_web_view.xml
Normal file
36
vector/src/main/res/layout/activity_vector_web_view.xml
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="im.vector.riotx.features.webview.VectorWebViewActivity">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/webview_toolbar"
|
||||
style="@style/VectorToolbarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:elevation="4dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:title="Title of the web page" />
|
||||
|
||||
<WebView
|
||||
android:id="@+id/simple_webview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/webview_toolbar" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/simple_webview_loader"
|
||||
style="@style/Widget.AppCompat.ProgressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -5,4 +5,7 @@
|
|||
<string name="labs_allow_extended_logging">Enable verbose logs.</string>
|
||||
<string name="labs_allow_extended_logging_summary">Verbose logs will help developers by providing more logs when you send a RageShake. Even when enabled, the application does not log message contents or any other private data.</string>
|
||||
|
||||
|
||||
<string name="error_terms_not_accepted">Please retry once you have accepted the terms and conditions of your homeserver.</string>
|
||||
|
||||
</resources>
|
Loading…
Reference in a new issue