mirror of
https://github.com/element-hq/element-android
synced 2024-11-24 02:15:35 +03:00
Widget: makes the integration manager screen mostly working
This commit is contained in:
parent
00fd067c6b
commit
df973a6275
34 changed files with 495 additions and 160 deletions
|
@ -16,7 +16,9 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.api.session.identity
|
package im.vector.matrix.android.api.session.identity
|
||||||
|
|
||||||
sealed class IdentityServiceError : Throwable() {
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
|
|
||||||
|
sealed class IdentityServiceError : Failure.FeatureFailure() {
|
||||||
object OutdatedIdentityServer : IdentityServiceError()
|
object OutdatedIdentityServer : IdentityServiceError()
|
||||||
object OutdatedHomeServer : IdentityServiceError()
|
object OutdatedHomeServer : IdentityServiceError()
|
||||||
object NoIdentityServerConfigured : IdentityServiceError()
|
object NoIdentityServerConfigured : IdentityServiceError()
|
||||||
|
|
|
@ -18,20 +18,27 @@ package im.vector.matrix.android.api.session.widgets
|
||||||
|
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
import im.vector.matrix.android.api.util.JsonDict
|
import im.vector.matrix.android.api.util.JsonDict
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
|
||||||
interface WidgetPostAPIMediator {
|
interface WidgetPostAPIMediator {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This initialize the mediator and configure the webview.
|
* This initialize the webview to handle.
|
||||||
* It will add a JavaScript Interface.
|
* It will add a JavaScript Interface.
|
||||||
* Please call [clear] method when finished to clean the provided webview
|
* Please call [clearWebView] method when finished to clean the provided webview
|
||||||
*/
|
*/
|
||||||
fun initialize(webView: WebView, handler: Handler)
|
fun setWebView(webView: WebView)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set handler to communicate with the widgetPostAPIMediator.
|
||||||
|
* Please remove the reference by passing null when finished.
|
||||||
|
*/
|
||||||
|
fun setHandler(handler: Handler?)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This clear the mediator by removing the JavaScript Interface and cleaning references.
|
* This clear the mediator by removing the JavaScript Interface and cleaning references.
|
||||||
*/
|
*/
|
||||||
fun clear()
|
fun clearWebView()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inject the necessary javascript into the configured WebView.
|
* Inject the necessary javascript into the configured WebView.
|
||||||
|
@ -62,7 +69,7 @@ interface WidgetPostAPIMediator {
|
||||||
* @param response the response
|
* @param response the response
|
||||||
* @param eventData the modular data
|
* @param eventData the modular data
|
||||||
*/
|
*/
|
||||||
fun <T> sendObjectResponse(klass: Class<T>, response: T?, eventData: JsonDict)
|
fun <T> sendObjectResponse(type: Type, response: T?, eventData: JsonDict)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send success
|
* Send success
|
||||||
|
|
|
@ -25,7 +25,7 @@ import im.vector.matrix.android.internal.session.widgets.Widget
|
||||||
|
|
||||||
interface WidgetService {
|
interface WidgetService {
|
||||||
|
|
||||||
fun getWidgetURLBuilder(): WidgetURLBuilder
|
fun getWidgetURLFormatter(): WidgetURLFormatter
|
||||||
|
|
||||||
fun getWidgetPostAPIMediator(): WidgetPostAPIMediator
|
fun getWidgetPostAPIMediator(): WidgetPostAPIMediator
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,16 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.api.session.widgets
|
package im.vector.matrix.android.api.session.widgets
|
||||||
|
|
||||||
interface WidgetURLBuilder {
|
interface WidgetURLFormatter {
|
||||||
/**
|
/**
|
||||||
* Takes care of fetching a scalar token if required and build the final url.
|
* Takes care of fetching a scalar token if required and build the final url.
|
||||||
|
* This methods can throw, you should take care of handling failure.
|
||||||
*/
|
*/
|
||||||
suspend fun build(baseUrl: String, params: Map<String, String> = emptyMap(), forceFetchScalarToken: Boolean = false): String
|
suspend fun format(
|
||||||
|
baseUrl: String,
|
||||||
|
params: Map<String, String> = emptyMap(),
|
||||||
|
forceFetchScalarToken: Boolean = false,
|
||||||
|
bypassWhitelist: Boolean
|
||||||
|
): String
|
||||||
|
|
||||||
}
|
}
|
|
@ -31,6 +31,5 @@ internal object NetworkConstants {
|
||||||
const val URI_IDENTITY_PREFIX_PATH = "_matrix/identity/v2"
|
const val URI_IDENTITY_PREFIX_PATH = "_matrix/identity/v2"
|
||||||
const val URI_IDENTITY_PATH_V2 = "$URI_IDENTITY_PREFIX_PATH/"
|
const val URI_IDENTITY_PATH_V2 = "$URI_IDENTITY_PREFIX_PATH/"
|
||||||
|
|
||||||
// TODO Ganfra, use correct value
|
const val URI_INTEGRATION_MANAGER_PATH = "_matrix/integrations/v1/"
|
||||||
const val URI_INTEGRATION_MANAGER_PATH = "TODO/"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,14 @@ import javax.inject.Inject
|
||||||
class RetrofitFactory @Inject constructor(private val moshi: Moshi) {
|
class RetrofitFactory @Inject constructor(private val moshi: Moshi) {
|
||||||
|
|
||||||
fun create(okHttpClient: Lazy<OkHttpClient>, baseUrl: String): Retrofit {
|
fun create(okHttpClient: Lazy<OkHttpClient>, baseUrl: String): Retrofit {
|
||||||
|
// ensure trailing /
|
||||||
|
val safeBaseUrl = if (!baseUrl.endsWith("/")) {
|
||||||
|
"$baseUrl/"
|
||||||
|
} else {
|
||||||
|
baseUrl
|
||||||
|
}
|
||||||
return Retrofit.Builder()
|
return Retrofit.Builder()
|
||||||
.baseUrl(baseUrl)
|
.baseUrl(safeBaseUrl)
|
||||||
.callFactory(object : Call.Factory {
|
.callFactory(object : Call.Factory {
|
||||||
override fun newCall(request: Request): Call {
|
override fun newCall(request: Request): Call {
|
||||||
return okHttpClient.get().newCall(request)
|
return okHttpClient.get().newCall(request)
|
||||||
|
|
|
@ -113,9 +113,10 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
|
||||||
callback.onFailure(InvalidParameterException("Only MXCRYPTO_ALGORITHM_MEGOLM algorithm is supported"))
|
callback.onFailure(InvalidParameterException("Only MXCRYPTO_ALGORITHM_MEGOLM algorithm is supported"))
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val params = SendStateTask.Params(roomId,
|
val params = SendStateTask.Params(
|
||||||
EventType.STATE_ROOM_ENCRYPTION,
|
roomId = roomId,
|
||||||
mapOf(
|
eventType = EventType.STATE_ROOM_ENCRYPTION,
|
||||||
|
body = mapOf(
|
||||||
"algorithm" to algorithm
|
"algorithm" to algorithm
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,8 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private
|
||||||
return sendStateEvent(
|
return sendStateEvent(
|
||||||
eventType = EventType.STATE_ROOM_TOPIC,
|
eventType = EventType.STATE_ROOM_TOPIC,
|
||||||
body = mapOf("topic" to topic),
|
body = mapOf("topic" to topic),
|
||||||
callback = callback
|
callback = callback,
|
||||||
|
stateKey = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,9 @@ import com.squareup.moshi.Moshi
|
||||||
import im.vector.matrix.android.api.session.widgets.WidgetPostAPIMediator
|
import im.vector.matrix.android.api.session.widgets.WidgetPostAPIMediator
|
||||||
import im.vector.matrix.android.api.util.JSON_DICT_PARAMETERIZED_TYPE
|
import im.vector.matrix.android.api.util.JSON_DICT_PARAMETERIZED_TYPE
|
||||||
import im.vector.matrix.android.api.util.JsonDict
|
import im.vector.matrix.android.api.util.JsonDict
|
||||||
|
import im.vector.matrix.android.internal.util.createUIHandler
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import java.lang.reflect.Type
|
||||||
import java.util.HashMap
|
import java.util.HashMap
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -35,22 +37,28 @@ internal class DefaultWidgetPostAPIMediator @Inject constructor(private val mosh
|
||||||
private var handler: WidgetPostAPIMediator.Handler? = null
|
private var handler: WidgetPostAPIMediator.Handler? = null
|
||||||
private var webView: WebView? = null
|
private var webView: WebView? = null
|
||||||
|
|
||||||
override fun initialize(webView: WebView, handler: WidgetPostAPIMediator.Handler) {
|
private val uiHandler = createUIHandler()
|
||||||
|
|
||||||
|
override fun setWebView(webView: WebView) {
|
||||||
this.webView = webView
|
this.webView = webView
|
||||||
this.handler = handler
|
webView.addJavascriptInterface(this, "Android")
|
||||||
webView.addJavascriptInterface(this, "WidgetPostAPIMediator")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun clear() {
|
override fun clearWebView() {
|
||||||
handler = null
|
webView?.removeJavascriptInterface("Android")
|
||||||
webView?.removeJavascriptInterface("WidgetPostAPIMediator")
|
|
||||||
webView = null
|
webView = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun setHandler(handler: WidgetPostAPIMediator.Handler?) {
|
||||||
|
this.handler = handler
|
||||||
|
}
|
||||||
|
|
||||||
override fun injectAPI() {
|
override fun injectAPI() {
|
||||||
val js = widgetPostMessageAPIProvider.get()
|
val js = widgetPostMessageAPIProvider.get()
|
||||||
if (null != js) {
|
if (js != null) {
|
||||||
webView?.loadUrl("javascript:$js")
|
uiHandler.post {
|
||||||
|
webView?.loadUrl("javascript:$js")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,10 +119,10 @@ internal class DefaultWidgetPostAPIMediator @Inject constructor(private val mosh
|
||||||
* @param response the response
|
* @param response the response
|
||||||
* @param eventData the modular data
|
* @param eventData the modular data
|
||||||
*/
|
*/
|
||||||
override fun <T> sendObjectResponse(klass: Class<T>, response: T?, eventData: JsonDict) {
|
override fun <T> sendObjectResponse(type: Type, response: T?, eventData: JsonDict) {
|
||||||
var jsString: String? = null
|
var jsString: String? = null
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
val objectAdapter = moshi.adapter(klass)
|
val objectAdapter = moshi.adapter<T>(type)
|
||||||
try {
|
try {
|
||||||
jsString = "JSON.parse('${objectAdapter.toJson(response)}')"
|
jsString = "JSON.parse('${objectAdapter.toJson(response)}')"
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -157,7 +165,7 @@ internal class DefaultWidgetPostAPIMediator @Inject constructor(private val mosh
|
||||||
* @param jsString the response data
|
* @param jsString the response data
|
||||||
* @param eventData the modular data
|
* @param eventData the modular data
|
||||||
*/
|
*/
|
||||||
private fun sendResponse(jsString: String, eventData: JsonDict) {
|
private fun sendResponse(jsString: String, eventData: JsonDict) = uiHandler.post {
|
||||||
try {
|
try {
|
||||||
val functionLine = "sendResponseFromRiotAndroid('" + eventData["_id"] + "' , " + jsString + ");"
|
val functionLine = "sendResponseFromRiotAndroid('" + eventData["_id"] + "' , " + jsString + ");"
|
||||||
Timber.v("BRIDGE sendResponse: $functionLine")
|
Timber.v("BRIDGE sendResponse: $functionLine")
|
||||||
|
|
|
@ -22,21 +22,21 @@ import im.vector.matrix.android.api.query.QueryStringValue
|
||||||
import im.vector.matrix.android.api.session.events.model.Content
|
import im.vector.matrix.android.api.session.events.model.Content
|
||||||
import im.vector.matrix.android.api.session.widgets.WidgetPostAPIMediator
|
import im.vector.matrix.android.api.session.widgets.WidgetPostAPIMediator
|
||||||
import im.vector.matrix.android.api.session.widgets.WidgetService
|
import im.vector.matrix.android.api.session.widgets.WidgetService
|
||||||
import im.vector.matrix.android.api.session.widgets.WidgetURLBuilder
|
import im.vector.matrix.android.api.session.widgets.WidgetURLFormatter
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Provider
|
import javax.inject.Provider
|
||||||
|
|
||||||
internal class DefaultWidgetService @Inject constructor(private val widgetManager: WidgetManager,
|
internal class DefaultWidgetService @Inject constructor(private val widgetManager: WidgetManager,
|
||||||
private val widgetURLBuilder: Provider<WidgetURLBuilder>,
|
private val widgetURLFormatter: WidgetURLFormatter,
|
||||||
private val widgetPostAPIMediator: Provider<WidgetPostAPIMediator>) : WidgetService {
|
private val widgetPostAPIMediator: WidgetPostAPIMediator) : WidgetService {
|
||||||
|
|
||||||
override fun getWidgetURLBuilder(): WidgetURLBuilder {
|
override fun getWidgetURLFormatter(): WidgetURLFormatter {
|
||||||
return widgetURLBuilder.get()
|
return widgetURLFormatter
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getWidgetPostAPIMediator(): WidgetPostAPIMediator {
|
override fun getWidgetPostAPIMediator(): WidgetPostAPIMediator {
|
||||||
return widgetPostAPIMediator.get()
|
return widgetPostAPIMediator
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getRoomWidgets(roomId: String, widgetId: QueryStringValue, widgetTypes: Set<String>?, excludedTypes: Set<String>?): List<Widget> {
|
override fun getRoomWidgets(roomId: String, widgetId: QueryStringValue, widgetTypes: Set<String>?, excludedTypes: Set<String>?): List<Widget> {
|
||||||
|
|
|
@ -17,20 +17,20 @@
|
||||||
package im.vector.matrix.android.internal.session.widgets
|
package im.vector.matrix.android.internal.session.widgets
|
||||||
|
|
||||||
import im.vector.matrix.android.R
|
import im.vector.matrix.android.R
|
||||||
import im.vector.matrix.android.api.session.widgets.WidgetURLBuilder
|
import im.vector.matrix.android.api.session.integrationmanager.IntegrationManagerConfig
|
||||||
|
import im.vector.matrix.android.api.session.widgets.WidgetURLFormatter
|
||||||
import im.vector.matrix.android.internal.session.SessionScope
|
import im.vector.matrix.android.internal.session.SessionScope
|
||||||
import im.vector.matrix.android.internal.session.integrationmanager.IntegrationManager
|
import im.vector.matrix.android.internal.session.integrationmanager.IntegrationManager
|
||||||
import im.vector.matrix.android.api.session.integrationmanager.IntegrationManagerConfig
|
|
||||||
import im.vector.matrix.android.internal.session.widgets.token.GetScalarTokenTask
|
import im.vector.matrix.android.internal.session.widgets.token.GetScalarTokenTask
|
||||||
import im.vector.matrix.android.internal.util.StringProvider
|
import im.vector.matrix.android.internal.util.StringProvider
|
||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@SessionScope
|
@SessionScope
|
||||||
internal class DefaultWidgetURLBuilder @Inject constructor(private val integrationManager: IntegrationManager,
|
internal class DefaultWidgetURLFormatter @Inject constructor(private val integrationManager: IntegrationManager,
|
||||||
private val getScalarTokenTask: GetScalarTokenTask,
|
private val getScalarTokenTask: GetScalarTokenTask,
|
||||||
private val stringProvider: StringProvider
|
private val stringProvider: StringProvider
|
||||||
) : IntegrationManager.Listener, WidgetURLBuilder {
|
) : IntegrationManager.Listener, WidgetURLFormatter {
|
||||||
|
|
||||||
private var currentConfig = integrationManager.getPreferredConfig()
|
private var currentConfig = integrationManager.getPreferredConfig()
|
||||||
private var whiteListedUrls: List<String> = emptyList()
|
private var whiteListedUrls: List<String> = emptyList()
|
||||||
|
@ -64,13 +64,13 @@ internal class DefaultWidgetURLBuilder @Inject constructor(private val integrati
|
||||||
/**
|
/**
|
||||||
* Takes care of fetching a scalar token if required and build the final url.
|
* Takes care of fetching a scalar token if required and build the final url.
|
||||||
*/
|
*/
|
||||||
override suspend fun build(baseUrl: String, params: Map<String, String>, forceFetchScalarToken: Boolean): String {
|
override suspend fun format(baseUrl: String, params: Map<String, String>, forceFetchScalarToken: Boolean, bypassWhitelist: Boolean): String {
|
||||||
return if (isScalarUrl(baseUrl) || forceFetchScalarToken) {
|
return if (bypassWhitelist || isWhiteListed(baseUrl)) {
|
||||||
val taskParams = GetScalarTokenTask.Params(baseUrl)
|
val taskParams = GetScalarTokenTask.Params(currentConfig.apiUrl, forceFetchScalarToken)
|
||||||
val scalarToken = getScalarTokenTask.execute(taskParams)
|
val scalarToken = getScalarTokenTask.execute(taskParams)
|
||||||
buildString {
|
buildString {
|
||||||
append(baseUrl)
|
append(baseUrl)
|
||||||
append("scalar_token", scalarToken)
|
appendParamToUrl("scalar_token", scalarToken)
|
||||||
appendParamsToUrl(params)
|
appendParamsToUrl(params)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -81,7 +81,7 @@ internal class DefaultWidgetURLBuilder @Inject constructor(private val integrati
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isScalarUrl(url: String): Boolean {
|
private fun isWhiteListed(url: String): Boolean {
|
||||||
val allowed: List<String> = whiteListedUrls
|
val allowed: List<String> = whiteListedUrls
|
||||||
for (allowedUrl in allowed) {
|
for (allowedUrl in allowed) {
|
||||||
if (url.startsWith(allowedUrl)) {
|
if (url.startsWith(allowedUrl)) {
|
|
@ -21,7 +21,7 @@ import javax.inject.Inject
|
||||||
|
|
||||||
internal class WidgetDependenciesHolder @Inject constructor(private val integrationManager: IntegrationManager,
|
internal class WidgetDependenciesHolder @Inject constructor(private val integrationManager: IntegrationManager,
|
||||||
private val widgetManager: WidgetManager,
|
private val widgetManager: WidgetManager,
|
||||||
private val widgetURLBuilder: DefaultWidgetURLBuilder) {
|
private val widgetURLBuilder: DefaultWidgetURLFormatter) {
|
||||||
|
|
||||||
fun start() {
|
fun start() {
|
||||||
integrationManager.start()
|
integrationManager.start()
|
||||||
|
@ -30,8 +30,8 @@ internal class WidgetDependenciesHolder @Inject constructor(private val integrat
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stop() {
|
fun stop() {
|
||||||
integrationManager.stop()
|
|
||||||
widgetManager.stop()
|
|
||||||
widgetURLBuilder.stop()
|
widgetURLBuilder.stop()
|
||||||
|
widgetManager.stop()
|
||||||
|
integrationManager.stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,4 +21,5 @@ import im.vector.matrix.android.api.failure.Failure
|
||||||
sealed class WidgetManagementFailure : Failure.FeatureFailure() {
|
sealed class WidgetManagementFailure : Failure.FeatureFailure() {
|
||||||
object NotEnoughPower : WidgetManagementFailure()
|
object NotEnoughPower : WidgetManagementFailure()
|
||||||
object CreationFailed : WidgetManagementFailure()
|
object CreationFailed : WidgetManagementFailure()
|
||||||
|
data class TermsNotSignedException(val baseUrl: String, val token: String) : WidgetManagementFailure()
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import im.vector.matrix.android.api.session.widgets.WidgetPostAPIMediator
|
import im.vector.matrix.android.api.session.widgets.WidgetPostAPIMediator
|
||||||
import im.vector.matrix.android.api.session.widgets.WidgetService
|
import im.vector.matrix.android.api.session.widgets.WidgetService
|
||||||
import im.vector.matrix.android.api.session.widgets.WidgetURLBuilder
|
import im.vector.matrix.android.api.session.widgets.WidgetURLFormatter
|
||||||
import im.vector.matrix.android.internal.session.widgets.token.DefaultGetScalarTokenTask
|
import im.vector.matrix.android.internal.session.widgets.token.DefaultGetScalarTokenTask
|
||||||
import im.vector.matrix.android.internal.session.widgets.token.GetScalarTokenTask
|
import im.vector.matrix.android.internal.session.widgets.token.GetScalarTokenTask
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
|
@ -42,7 +42,7 @@ internal abstract class WidgetModule {
|
||||||
abstract fun bindWidgetService(widgetService: DefaultWidgetService): WidgetService
|
abstract fun bindWidgetService(widgetService: DefaultWidgetService): WidgetService
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindWidgetURLBuilder(widgetURLBuilder: DefaultWidgetURLBuilder): WidgetURLBuilder
|
abstract fun bindWidgetURLBuilder(widgetURLBuilder: DefaultWidgetURLFormatter): WidgetURLFormatter
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindWidgetPostAPIMediator(widgetPostMessageAPIProvider: DefaultWidgetPostAPIMediator): WidgetPostAPIMediator
|
abstract fun bindWidgetPostAPIMediator(widgetPostMessageAPIProvider: DefaultWidgetPostAPIMediator): WidgetPostAPIMediator
|
||||||
|
|
|
@ -17,19 +17,22 @@
|
||||||
package im.vector.matrix.android.internal.session.widgets.token
|
package im.vector.matrix.android.internal.session.widgets.token
|
||||||
|
|
||||||
import im.vector.matrix.android.api.failure.Failure
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
|
import im.vector.matrix.android.api.failure.MatrixError
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
import im.vector.matrix.android.internal.network.executeRequest
|
||||||
import im.vector.matrix.android.internal.session.openid.GetOpenIdTokenTask
|
import im.vector.matrix.android.internal.session.openid.GetOpenIdTokenTask
|
||||||
import im.vector.matrix.android.internal.session.widgets.RegisterWidgetResponse
|
import im.vector.matrix.android.internal.session.widgets.RegisterWidgetResponse
|
||||||
|
import im.vector.matrix.android.internal.session.widgets.WidgetManagementFailure
|
||||||
import im.vector.matrix.android.internal.session.widgets.WidgetsAPI
|
import im.vector.matrix.android.internal.session.widgets.WidgetsAPI
|
||||||
import im.vector.matrix.android.internal.session.widgets.WidgetsAPIProvider
|
import im.vector.matrix.android.internal.session.widgets.WidgetsAPIProvider
|
||||||
import im.vector.matrix.android.internal.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
import java.net.HttpURLConnection
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import javax.net.ssl.HttpsURLConnection
|
||||||
|
|
||||||
internal interface GetScalarTokenTask : Task<GetScalarTokenTask.Params, String> {
|
internal interface GetScalarTokenTask : Task<GetScalarTokenTask.Params, String> {
|
||||||
|
|
||||||
data class Params(
|
data class Params(
|
||||||
val serverUrl: String
|
val serverUrl: String,
|
||||||
|
val forceRefresh: Boolean = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,11 +44,16 @@ internal class DefaultGetScalarTokenTask @Inject constructor(private val widgets
|
||||||
|
|
||||||
override suspend fun execute(params: GetScalarTokenTask.Params): String {
|
override suspend fun execute(params: GetScalarTokenTask.Params): String {
|
||||||
val widgetsAPI = widgetsAPIProvider.get(params.serverUrl)
|
val widgetsAPI = widgetsAPIProvider.get(params.serverUrl)
|
||||||
val scalarToken = scalarTokenStore.getToken(params.serverUrl)
|
return if (params.forceRefresh) {
|
||||||
return if (scalarToken == null) {
|
scalarTokenStore.clearToken(params.serverUrl)
|
||||||
getNewScalarToken(widgetsAPI, params.serverUrl)
|
getNewScalarToken(widgetsAPI, params.serverUrl)
|
||||||
} else {
|
} else {
|
||||||
validateToken(widgetsAPI, params.serverUrl, scalarToken)
|
val scalarToken = scalarTokenStore.getToken(params.serverUrl)
|
||||||
|
if (scalarToken == null) {
|
||||||
|
getNewScalarToken(widgetsAPI, params.serverUrl)
|
||||||
|
} else {
|
||||||
|
validateToken(widgetsAPI, params.serverUrl, scalarToken)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,19 +73,21 @@ internal class DefaultGetScalarTokenTask @Inject constructor(private val widgets
|
||||||
|
|
||||||
private suspend fun validateToken(widgetsAPI: WidgetsAPI, serverUrl: String, scalarToken: String): String {
|
private suspend fun validateToken(widgetsAPI: WidgetsAPI, serverUrl: String, scalarToken: String): String {
|
||||||
return try {
|
return try {
|
||||||
widgetsAPI.validateToken(scalarToken, WIDGET_API_VERSION)
|
executeRequest<Unit>(null) {
|
||||||
|
apiCall = widgetsAPI.validateToken(scalarToken, WIDGET_API_VERSION)
|
||||||
|
}
|
||||||
scalarToken
|
scalarToken
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
if (failure.isScalarTokenError()) {
|
if (failure is Failure.ServerError && failure.httpCode == HttpsURLConnection.HTTP_FORBIDDEN) {
|
||||||
scalarTokenStore.clearToken(serverUrl)
|
if (failure.error.code == MatrixError.M_TERMS_NOT_SIGNED) {
|
||||||
getNewScalarToken(widgetsAPI, serverUrl)
|
throw WidgetManagementFailure.TermsNotSignedException(serverUrl, scalarToken)
|
||||||
|
} else {
|
||||||
|
scalarTokenStore.clearToken(serverUrl)
|
||||||
|
getNewScalarToken(widgetsAPI, serverUrl)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw failure
|
throw failure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Throwable.isScalarTokenError(): Boolean {
|
|
||||||
return this is Failure.ServerError && this.httpCode == HttpURLConnection.HTTP_FORBIDDEN
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ internal class ScalarTokenStore @Inject constructor(private val monarchy: Monarc
|
||||||
return monarchy.fetchCopyMap({ realm ->
|
return monarchy.fetchCopyMap({ realm ->
|
||||||
ScalarTokenEntity.where(realm, apiUrl).findFirst()
|
ScalarTokenEntity.where(realm, apiUrl).findFirst()
|
||||||
}, { scalarToken, _ ->
|
}, { scalarToken, _ ->
|
||||||
scalarToken.serverUrl
|
scalarToken.token
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -162,6 +162,7 @@
|
||||||
android:theme="@style/AppTheme.AttachmentsPreview" />
|
android:theme="@style/AppTheme.AttachmentsPreview" />
|
||||||
|
|
||||||
<activity android:name=".features.terms.ReviewTermsActivity" />
|
<activity android:name=".features.terms.ReviewTermsActivity" />
|
||||||
|
<activity android:name=".features.widgets.WidgetActivity" />
|
||||||
|
|
||||||
<!-- Services -->
|
<!-- Services -->
|
||||||
|
|
||||||
|
|
|
@ -434,18 +434,24 @@ class RoomDetailFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
if (item.itemId == R.id.clear_message_queue) {
|
return when (item.itemId) {
|
||||||
// This a temporary option during dev as it is not super stable
|
R.id.clear_message_queue -> {
|
||||||
// Cancel all pending actions in room queue and post a dummy
|
// This a temporary option during dev as it is not super stable
|
||||||
// Then mark all sending events as undelivered
|
// Cancel all pending actions in room queue and post a dummy
|
||||||
roomDetailViewModel.handle(RoomDetailAction.ClearSendQueue)
|
// Then mark all sending events as undelivered
|
||||||
return true
|
roomDetailViewModel.handle(RoomDetailAction.ClearSendQueue)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
R.id.resend_all -> {
|
||||||
|
roomDetailViewModel.handle(RoomDetailAction.ResendAll)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
R.id.open_matrix_apps -> {
|
||||||
|
navigator.openIntegrationManager(requireContext(), roomDetailArgs.roomId, null, null)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
if (item.itemId == R.id.resend_all) {
|
|
||||||
roomDetailViewModel.handle(RoomDetailAction.ResendAll)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return super.onOptionsItemSelected(item)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderRegularMode(text: String) {
|
private fun renderRegularMode(text: String) {
|
||||||
|
|
|
@ -320,6 +320,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
timeline.pendingEventCount() > 0 && vectorPreferences.developerMode()
|
timeline.pendingEventCount() > 0 && vectorPreferences.developerMode()
|
||||||
R.id.resend_all -> timeline.failedToDeliverEventCount() > 0
|
R.id.resend_all -> timeline.failedToDeliverEventCount() > 0
|
||||||
R.id.clear_all -> timeline.failedToDeliverEventCount() > 0
|
R.id.clear_all -> timeline.failedToDeliverEventCount() > 0
|
||||||
|
R.id.open_matrix_apps -> session.integrationManagerService().isIntegrationEnabled()
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package im.vector.riotx.features.home.room.detail
|
package im.vector.riotx.features.home.room.detail
|
||||||
|
|
||||||
|
import android.util.SparseArray
|
||||||
import com.airbnb.mvrx.Async
|
import com.airbnb.mvrx.Async
|
||||||
import com.airbnb.mvrx.MvRxState
|
import com.airbnb.mvrx.MvRxState
|
||||||
import com.airbnb.mvrx.Uninitialized
|
import com.airbnb.mvrx.Uninitialized
|
||||||
|
@ -63,6 +64,7 @@ data class RoomDetailViewState(
|
||||||
val syncState: SyncState = SyncState.Idle,
|
val syncState: SyncState = SyncState.Idle,
|
||||||
val highlightedEventId: String? = null,
|
val highlightedEventId: String? = null,
|
||||||
val unreadState: UnreadState = UnreadState.Unknown,
|
val unreadState: UnreadState = UnreadState.Unknown,
|
||||||
|
val menuItemsVisibility: SparseArray<Boolean> = SparseArray(4),
|
||||||
val canShowJumpToReadMarker: Boolean = true
|
val canShowJumpToReadMarker: Boolean = true
|
||||||
) : MvRxState {
|
) : MvRxState {
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,8 @@ import im.vector.riotx.features.settings.VectorPreferences
|
||||||
import im.vector.riotx.features.settings.VectorSettingsActivity
|
import im.vector.riotx.features.settings.VectorSettingsActivity
|
||||||
import im.vector.riotx.features.share.SharedData
|
import im.vector.riotx.features.share.SharedData
|
||||||
import im.vector.riotx.features.terms.ReviewTermsActivity
|
import im.vector.riotx.features.terms.ReviewTermsActivity
|
||||||
|
import im.vector.riotx.features.widgets.WidgetActivity
|
||||||
|
import im.vector.riotx.features.widgets.WidgetArgsBuilder
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@ -62,6 +64,7 @@ import javax.inject.Singleton
|
||||||
class DefaultNavigator @Inject constructor(
|
class DefaultNavigator @Inject constructor(
|
||||||
private val sessionHolder: ActiveSessionHolder,
|
private val sessionHolder: ActiveSessionHolder,
|
||||||
private val vectorPreferences: VectorPreferences,
|
private val vectorPreferences: VectorPreferences,
|
||||||
|
private val widgetArgsBuilder: WidgetArgsBuilder,
|
||||||
private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider
|
private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider
|
||||||
) : Navigator {
|
) : Navigator {
|
||||||
|
|
||||||
|
@ -215,8 +218,9 @@ class DefaultNavigator @Inject constructor(
|
||||||
fragment.startActivityForResult(intent, requestCode)
|
fragment.startActivityForResult(intent, requestCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openIntegrationManager(context: Context) {
|
override fun openIntegrationManager(context: Context, roomId: String, integId: String?, screenId: String?) {
|
||||||
//TODO
|
val widgetArgs = widgetArgsBuilder.buildIntegrationManagerArgs(roomId, integId, screenId)
|
||||||
|
context.startActivity(WidgetActivity.newIntent(context, widgetArgs))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startActivity(context: Context, intent: Intent, buildTask: Boolean) {
|
private fun startActivity(context: Context, intent: Intent, buildTask: Boolean) {
|
||||||
|
|
|
@ -77,6 +77,6 @@ interface Navigator {
|
||||||
token: String?,
|
token: String?,
|
||||||
requestCode: Int = ReviewTermsActivity.TERMS_REQUEST_CODE)
|
requestCode: Int = ReviewTermsActivity.TERMS_REQUEST_CODE)
|
||||||
|
|
||||||
fun openIntegrationManager(context: Context, integId: String?, integType: String?)
|
fun openIntegrationManager(context: Context, roomId: String, integId: String?, screenId: String?)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,10 @@ import android.graphics.Bitmap
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.webkit.WebResourceError
|
import android.webkit.WebResourceError
|
||||||
import android.webkit.WebResourceRequest
|
import android.webkit.WebResourceRequest
|
||||||
|
import android.webkit.WebResourceResponse
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
import android.webkit.WebViewClient
|
import android.webkit.WebViewClient
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class inherits from WebViewClient. It has to be used with a WebView.
|
* This class inherits from WebViewClient. It has to be used with a WebView.
|
||||||
|
@ -56,6 +58,14 @@ class VectorWebViewClient(private val eventListener: WebViewEventListener) : Web
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
override fun onReceivedHttpError(view: WebView, request: WebResourceRequest, errorResponse: WebResourceResponse) {
|
||||||
|
super.onReceivedHttpError(view, request, errorResponse)
|
||||||
|
eventListener.onHttpError(request.url.toString(),
|
||||||
|
errorResponse.statusCode,
|
||||||
|
errorResponse.reasonPhrase)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onReceivedError(view: WebView, errorCode: Int, description: String, failingUrl: String) {
|
override fun onReceivedError(view: WebView, errorCode: Int, description: String, failingUrl: String) {
|
||||||
super.onReceivedError(view, errorCode, description, failingUrl)
|
super.onReceivedError(view, errorCode, description, failingUrl)
|
||||||
if (!mInError) {
|
if (!mInError) {
|
||||||
|
|
|
@ -56,6 +56,17 @@ interface WebViewEventListener {
|
||||||
//NO-OP
|
//NO-OP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 onHttpError(url: String, errorCode: Int, description: String){
|
||||||
|
//NO-OP
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggered when a webview load an url
|
* Triggered when a webview load an url
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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.widgets
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.appcompat.widget.Toolbar
|
||||||
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.extensions.addFragment
|
||||||
|
import im.vector.riotx.core.platform.ToolbarConfigurable
|
||||||
|
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||||
|
import im.vector.riotx.features.widgets.room.RoomWidgetFragment
|
||||||
|
import im.vector.riotx.features.widgets.room.WidgetArgs
|
||||||
|
|
||||||
|
class WidgetActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private const val EXTRA_FRAGMENT_ARGS = "EXTRA_FRAGMENT_ARGS"
|
||||||
|
|
||||||
|
fun newIntent(context: Context, args: WidgetArgs): Intent {
|
||||||
|
return Intent(context, WidgetActivity::class.java).apply {
|
||||||
|
putExtra(EXTRA_FRAGMENT_ARGS, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getLayoutRes() = R.layout.activity_simple
|
||||||
|
|
||||||
|
override fun initUiAndData() {
|
||||||
|
if (isFirstCreation()) {
|
||||||
|
val fragmentArgs: WidgetArgs = intent?.extras?.getParcelable(EXTRA_FRAGMENT_ARGS)
|
||||||
|
?: return
|
||||||
|
addFragment(R.id.simpleFragmentContainer, RoomWidgetFragment::class.java, fragmentArgs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun configure(toolbar: Toolbar) {
|
||||||
|
configureToolbar(toolbar)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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.widgets
|
||||||
|
|
||||||
|
import im.vector.riotx.core.di.ActiveSessionHolder
|
||||||
|
import im.vector.riotx.features.widgets.room.WidgetArgs
|
||||||
|
import im.vector.riotx.features.widgets.room.WidgetKind
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class WidgetArgsBuilder @Inject constructor(private val sessionHolder: ActiveSessionHolder) {
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun buildIntegrationManagerArgs(roomId: String, integId: String?, screenId: String?): WidgetArgs {
|
||||||
|
val session = sessionHolder.getActiveSession()
|
||||||
|
val integrationManagerConfig = session.integrationManagerService().getPreferredConfig()
|
||||||
|
return WidgetArgs(
|
||||||
|
baseUrl = integrationManagerConfig.uiUrl,
|
||||||
|
kind = WidgetKind.INTEGRATION_MANAGER,
|
||||||
|
roomId = roomId,
|
||||||
|
urlParams = mapOf(
|
||||||
|
"screen" to screenId,
|
||||||
|
"integ_id" to integId,
|
||||||
|
"room_id" to roomId
|
||||||
|
).filterValues { it != null } as Map<String, String>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,10 +18,13 @@ package im.vector.riotx.features.widgets
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
|
import com.squareup.inject.assisted.Assisted
|
||||||
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
import im.vector.matrix.android.api.query.QueryStringValue
|
import im.vector.matrix.android.api.query.QueryStringValue
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
|
import im.vector.matrix.android.api.session.events.model.toContent
|
||||||
import im.vector.matrix.android.api.session.events.model.toModel
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
import im.vector.matrix.android.api.session.room.model.Membership
|
import im.vector.matrix.android.api.session.room.model.Membership
|
||||||
import im.vector.matrix.android.api.session.room.model.PowerLevelsContent
|
import im.vector.matrix.android.api.session.room.model.PowerLevelsContent
|
||||||
|
@ -32,15 +35,21 @@ import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.resources.StringProvider
|
import im.vector.riotx.core.resources.StringProvider
|
||||||
import im.vector.riotx.features.navigation.Navigator
|
import im.vector.riotx.features.navigation.Navigator
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import java.util.ArrayList
|
||||||
import java.util.HashMap
|
import java.util.HashMap
|
||||||
|
|
||||||
class WidgetPostAPIHandler(private val context: Context,
|
class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roomId: String,
|
||||||
private val roomId: String,
|
private val context: Context,
|
||||||
private val navigator: Navigator,
|
private val navigator: Navigator,
|
||||||
private val stringProvider: StringProvider,
|
private val stringProvider: StringProvider,
|
||||||
private val widgetPostAPIMediator: WidgetPostAPIMediator,
|
private val session: Session) : WidgetPostAPIMediator.Handler {
|
||||||
private val session: Session) : WidgetPostAPIMediator.Handler {
|
|
||||||
|
|
||||||
|
@AssistedInject.Factory
|
||||||
|
interface Factory {
|
||||||
|
fun create(roomId: String): WidgetPostAPIHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
private val widgetPostAPIMediator = session.widgetService().getWidgetPostAPIMediator()
|
||||||
private val room = session.getRoom(roomId)!!
|
private val room = session.getRoom(roomId)!!
|
||||||
|
|
||||||
override fun handleWidgetRequest(eventData: JsonDict): Boolean {
|
override fun handleWidgetRequest(eventData: JsonDict): Boolean {
|
||||||
|
@ -50,7 +59,7 @@ class WidgetPostAPIHandler(private val context: Context,
|
||||||
"can_send_event" -> canSendEvent(eventData).run { true }
|
"can_send_event" -> canSendEvent(eventData).run { true }
|
||||||
//"close_scalar" -> finish().run { true }
|
//"close_scalar" -> finish().run { true }
|
||||||
"get_membership_count" -> getMembershipCount(eventData).run { true }
|
"get_membership_count" -> getMembershipCount(eventData).run { true }
|
||||||
//"get_widgets" -> getWidgets(eventData).run { true }
|
"get_widgets" -> getWidgets(eventData).run { true }
|
||||||
//"invite" -> inviteUser(eventData).run { true }
|
//"invite" -> inviteUser(eventData).run { true }
|
||||||
"join_rules_state" -> getJoinRules(eventData).run { true }
|
"join_rules_state" -> getJoinRules(eventData).run { true }
|
||||||
"membership_state" -> getMembershipState(eventData).run { true }
|
"membership_state" -> getMembershipState(eventData).run { true }
|
||||||
|
@ -82,7 +91,7 @@ class WidgetPostAPIHandler(private val context: Context,
|
||||||
// Add "type_" as a prefix
|
// Add "type_" as a prefix
|
||||||
integType?.let { integType = "type_$integType" }
|
integType?.let { integType = "type_$integType" }
|
||||||
}
|
}
|
||||||
navigator.openIntegrationManager(context, integId, integType)
|
navigator.openIntegrationManager(context, roomId, integId, integType)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -158,6 +167,11 @@ class WidgetPostAPIHandler(private val context: Context,
|
||||||
val userId = eventData["user_id"] as String
|
val userId = eventData["user_id"] as String
|
||||||
Timber.d("membership_state of $userId in room $roomId requested")
|
Timber.d("membership_state of $userId in room $roomId requested")
|
||||||
val roomMemberStateEvent = room.getStateEvent(EventType.STATE_ROOM_MEMBER, stateKey = QueryStringValue.Equals(userId, QueryStringValue.Case.SENSITIVE))
|
val roomMemberStateEvent = room.getStateEvent(EventType.STATE_ROOM_MEMBER, stateKey = QueryStringValue.Equals(userId, QueryStringValue.Case.SENSITIVE))
|
||||||
|
if (roomMemberStateEvent != null) {
|
||||||
|
widgetPostAPIMediator.sendObjectResponse(Map::class.java, roomMemberStateEvent.content, eventData)
|
||||||
|
} else {
|
||||||
|
widgetPostAPIMediator.sendError(stringProvider.getString(R.string.widget_integration_failed_to_send_request), eventData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -178,6 +192,27 @@ class WidgetPostAPIHandler(private val context: Context,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide the widgets list
|
||||||
|
*
|
||||||
|
* @param eventData the modular data
|
||||||
|
*/
|
||||||
|
private fun getWidgets(eventData: JsonDict) {
|
||||||
|
if (checkRoomId(eventData)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Timber.d("Received request to get widget in room $roomId")
|
||||||
|
val roomWidgets = session.widgetService().getRoomWidgets(roomId)
|
||||||
|
val responseData = ArrayList<JsonDict>()
|
||||||
|
for (widget in roomWidgets) {
|
||||||
|
val map = widget.event.toContent()
|
||||||
|
responseData.add(map)
|
||||||
|
}
|
||||||
|
// TODO ADD USER WIDGETS
|
||||||
|
Timber.d("## getWidgets() returns $responseData")
|
||||||
|
widgetPostAPIMediator.sendObjectResponse(List::class.java, responseData, eventData)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the 'plumbing state"
|
* Update the 'plumbing state"
|
||||||
*
|
*
|
||||||
|
@ -207,6 +242,7 @@ class WidgetPostAPIHandler(private val context: Context,
|
||||||
*
|
*
|
||||||
* @param eventData the modular data
|
* @param eventData the modular data
|
||||||
*/
|
*/
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
private fun setBotOptions(eventData: JsonDict) {
|
private fun setBotOptions(eventData: JsonDict) {
|
||||||
if (checkRoomId(eventData) || checkUserId(eventData)) {
|
if (checkRoomId(eventData) || checkUserId(eventData)) {
|
||||||
return
|
return
|
||||||
|
|
|
@ -20,6 +20,7 @@ import im.vector.riotx.core.platform.VectorViewModelAction
|
||||||
|
|
||||||
sealed class RoomWidgetAction : VectorViewModelAction {
|
sealed class RoomWidgetAction : VectorViewModelAction {
|
||||||
data class OnWebViewStartedToLoad(val url: String) : RoomWidgetAction()
|
data class OnWebViewStartedToLoad(val url: String) : RoomWidgetAction()
|
||||||
data class OnWebViewLoadingError(val url: String) : RoomWidgetAction()
|
data class OnWebViewLoadingError(val url: String, val isHttpError: Boolean, val errorCode: Int, val errorDescription: String) : RoomWidgetAction()
|
||||||
data class OnWebViewLoadingSuccess(val url: String) : RoomWidgetAction()
|
data class OnWebViewLoadingSuccess(val url: String) : RoomWidgetAction()
|
||||||
|
object OnTermsReviewed: RoomWidgetAction()
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,16 +16,24 @@
|
||||||
|
|
||||||
package im.vector.riotx.features.widgets.room
|
package im.vector.riotx.features.widgets.room
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.core.view.isInvisible
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import com.airbnb.mvrx.Fail
|
||||||
|
import com.airbnb.mvrx.Loading
|
||||||
|
import com.airbnb.mvrx.Success
|
||||||
|
import com.airbnb.mvrx.Uninitialized
|
||||||
import com.airbnb.mvrx.args
|
import com.airbnb.mvrx.args
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import im.vector.matrix.android.api.session.widgets.WidgetPostAPIMediator
|
import im.vector.matrix.android.api.session.terms.TermsService
|
||||||
import im.vector.matrix.android.api.util.JsonDict
|
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||||
|
import im.vector.riotx.features.terms.ReviewTermsActivity
|
||||||
import im.vector.riotx.features.webview.WebViewEventListener
|
import im.vector.riotx.features.webview.WebViewEventListener
|
||||||
import im.vector.riotx.features.widgets.webview.clearAfterWidget
|
import im.vector.riotx.features.widgets.webview.clearAfterWidget
|
||||||
import im.vector.riotx.features.widgets.webview.setupForWidget
|
import im.vector.riotx.features.widgets.webview.setupForWidget
|
||||||
|
@ -39,12 +47,13 @@ data class WidgetArgs(
|
||||||
val baseUrl: String,
|
val baseUrl: String,
|
||||||
val kind: WidgetKind,
|
val kind: WidgetKind,
|
||||||
val roomId: String,
|
val roomId: String,
|
||||||
val widgetId: String? = null
|
val widgetId: String? = null,
|
||||||
|
val urlParams: Map<String, String> = emptyMap()
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
class RoomWidgetFragment @Inject constructor(
|
class RoomWidgetFragment @Inject constructor(
|
||||||
private val viewModelFactory: RoomWidgetViewModel.Factory
|
private val viewModelFactory: RoomWidgetViewModel.Factory
|
||||||
) : VectorBaseFragment(), RoomWidgetViewModel.Factory by viewModelFactory, WebViewEventListener, WidgetPostAPIMediator.Handler {
|
) : VectorBaseFragment(), RoomWidgetViewModel.Factory by viewModelFactory, WebViewEventListener {
|
||||||
|
|
||||||
private val fragmentArgs: WidgetArgs by args()
|
private val fragmentArgs: WidgetArgs by args()
|
||||||
private val viewModel: RoomWidgetViewModel by fragmentViewModel()
|
private val viewModel: RoomWidgetViewModel by fragmentViewModel()
|
||||||
|
@ -54,12 +63,31 @@ class RoomWidgetFragment @Inject constructor(
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
widgetWebView.setupForWidget(this)
|
widgetWebView.setupForWidget(this)
|
||||||
viewModel.getPostAPIMediator().initialize(widgetWebView, this)
|
if (fragmentArgs.kind.isAdmin()) {
|
||||||
|
viewModel.getPostAPIMediator().setWebView(widgetWebView)
|
||||||
|
}
|
||||||
|
viewModel.observeViewEvents {
|
||||||
|
when (it) {
|
||||||
|
is RoomWidgetViewEvents.DisplayTerms -> displayTerms(it)
|
||||||
|
is RoomWidgetViewEvents.LoadFormattedURL -> loadFormattedUrl(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
if (requestCode == ReviewTermsActivity.TERMS_REQUEST_CODE) {
|
||||||
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
|
viewModel.handle(RoomWidgetAction.OnTermsReviewed)
|
||||||
|
} else {
|
||||||
|
vectorBaseActivity.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
viewModel.getPostAPIMediator().clear()
|
if (fragmentArgs.kind.isAdmin()) {
|
||||||
|
viewModel.getPostAPIMediator().clearWebView()
|
||||||
|
}
|
||||||
widgetWebView.clearAfterWidget()
|
widgetWebView.clearAfterWidget()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,21 +108,99 @@ class RoomWidgetFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) { state ->
|
override fun invalidate() = withState(viewModel) { state ->
|
||||||
Timber.v("Invalidate with state: $state")
|
Timber.v("Invalidate state: $state")
|
||||||
|
when (state.status) {
|
||||||
|
WidgetStatus.UNKNOWN -> {
|
||||||
|
//Hide all?
|
||||||
|
widgetWebView.isVisible = false
|
||||||
|
}
|
||||||
|
WidgetStatus.WIDGET_NOT_ALLOWED -> {
|
||||||
|
widgetWebView.isVisible = false
|
||||||
|
}
|
||||||
|
WidgetStatus.WIDGET_ALLOWED -> {
|
||||||
|
widgetWebView.isVisible = true
|
||||||
|
when (state.formattedURL) {
|
||||||
|
Uninitialized -> {
|
||||||
|
}
|
||||||
|
is Loading -> {
|
||||||
|
setStateError(null)
|
||||||
|
widgetProgressBar.isIndeterminate = true
|
||||||
|
widgetProgressBar.isVisible = true
|
||||||
|
}
|
||||||
|
is Success -> {
|
||||||
|
setStateError(null)
|
||||||
|
when (state.webviewLoadedUrl) {
|
||||||
|
Uninitialized -> {
|
||||||
|
widgetWebView.isInvisible = true
|
||||||
|
}
|
||||||
|
is Loading -> {
|
||||||
|
setStateError(null)
|
||||||
|
widgetWebView.isInvisible = false
|
||||||
|
widgetProgressBar.isIndeterminate = true
|
||||||
|
widgetProgressBar.isVisible = true
|
||||||
|
}
|
||||||
|
is Success -> {
|
||||||
|
widgetWebView.isInvisible = false
|
||||||
|
widgetProgressBar.isVisible = false
|
||||||
|
setStateError(null)
|
||||||
|
}
|
||||||
|
is Fail -> {
|
||||||
|
widgetProgressBar.isInvisible = true
|
||||||
|
setStateError(state.webviewLoadedUrl.error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Fail -> {
|
||||||
|
//we need to show Error
|
||||||
|
widgetWebView.isInvisible = true
|
||||||
|
widgetProgressBar.isVisible = false
|
||||||
|
setStateError(state.formattedURL.error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun displayTerms(displayTerms: RoomWidgetViewEvents.DisplayTerms) {
|
||||||
|
navigator.openTerms(
|
||||||
|
fragment = this,
|
||||||
|
serviceType = TermsService.ServiceType.IntegrationManager,
|
||||||
|
baseUrl = displayTerms.url,
|
||||||
|
token = displayTerms.token
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun loadFormattedUrl(loadFormattedUrl: RoomWidgetViewEvents.LoadFormattedURL) {
|
||||||
|
widgetWebView.clearHistory()
|
||||||
|
widgetWebView.loadUrl(loadFormattedUrl.formattedURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setStateError(message: String?) {
|
||||||
|
if (message == null) {
|
||||||
|
widgetErrorLayout.isVisible = false
|
||||||
|
widgetErrorText.text = null
|
||||||
|
} else {
|
||||||
|
widgetProgressBar.isVisible = false
|
||||||
|
widgetErrorLayout.isVisible = true
|
||||||
|
widgetWebView.isInvisible = true
|
||||||
|
widgetErrorText.text = getString(R.string.room_widget_failed_to_load, message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPageStarted(url: String) {
|
override fun onPageStarted(url: String) {
|
||||||
|
viewModel.handle(RoomWidgetAction.OnWebViewStartedToLoad(url))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPageFinished(url: String) {
|
override fun onPageFinished(url: String) {
|
||||||
|
viewModel.handle(RoomWidgetAction.OnWebViewLoadingSuccess(url))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPageError(url: String, errorCode: Int, description: String) {
|
override fun onPageError(url: String, errorCode: Int, description: String) {
|
||||||
|
viewModel.handle(RoomWidgetAction.OnWebViewLoadingError(url, false, errorCode, description))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleWidgetRequest(eventData: JsonDict): Boolean {
|
override fun onHttpError(url: String, errorCode: Int, description: String) {
|
||||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
viewModel.handle(RoomWidgetAction.OnWebViewLoadingError(url, true, errorCode, description))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,4 +18,7 @@ package im.vector.riotx.features.widgets.room
|
||||||
|
|
||||||
import im.vector.riotx.core.platform.VectorViewEvents
|
import im.vector.riotx.core.platform.VectorViewEvents
|
||||||
|
|
||||||
sealed class RoomWidgetViewEvents : VectorViewEvents
|
sealed class RoomWidgetViewEvents : VectorViewEvents {
|
||||||
|
data class LoadFormattedURL(val formattedURL: String): RoomWidgetViewEvents()
|
||||||
|
data class DisplayTerms(val url: String, val token: String): RoomWidgetViewEvents()
|
||||||
|
}
|
||||||
|
|
|
@ -28,10 +28,15 @@ import com.squareup.inject.assisted.Assisted
|
||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
import im.vector.matrix.android.api.query.QueryStringValue
|
import im.vector.matrix.android.api.query.QueryStringValue
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
|
import im.vector.matrix.android.internal.session.widgets.WidgetManagementFailure
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
|
import im.vector.riotx.features.widgets.WidgetPostAPIHandler
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.net.ssl.HttpsURLConnection
|
||||||
|
|
||||||
class RoomWidgetViewModel @AssistedInject constructor(@Assisted val initialState: WidgetViewState,
|
class RoomWidgetViewModel @AssistedInject constructor(@Assisted val initialState: WidgetViewState,
|
||||||
|
private val widgetPostAPIHandlerFactory: WidgetPostAPIHandler.Factory,
|
||||||
private val session: Session)
|
private val session: Session)
|
||||||
: VectorViewModel<WidgetViewState, RoomWidgetAction, RoomWidgetViewEvents>(initialState) {
|
: VectorViewModel<WidgetViewState, RoomWidgetAction, RoomWidgetViewEvents>(initialState) {
|
||||||
|
|
||||||
|
@ -54,76 +59,119 @@ class RoomWidgetViewModel @AssistedInject constructor(@Assisted val initialState
|
||||||
|
|
||||||
private val widgetService = session.widgetService()
|
private val widgetService = session.widgetService()
|
||||||
private val integrationManagerService = session.integrationManagerService()
|
private val integrationManagerService = session.integrationManagerService()
|
||||||
private val widgetBuilder = widgetService.getWidgetURLBuilder()
|
private val widgetBuilder = widgetService.getWidgetURLFormatter()
|
||||||
private val postAPIMediator = widgetService.getWidgetPostAPIMediator()
|
private val postAPIMediator = widgetService.getWidgetPostAPIMediator()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
if(initialState.widgetKind.isAdmin()) {
|
||||||
|
val widgetPostAPIHandler = widgetPostAPIHandlerFactory.create(initialState.roomId)
|
||||||
|
postAPIMediator.setHandler(widgetPostAPIHandler)
|
||||||
|
}
|
||||||
refreshPermissionStatus()
|
refreshPermissionStatus()
|
||||||
|
observePermissionStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observePermissionStatus() {
|
||||||
|
selectSubscribe(WidgetViewState::status) {
|
||||||
|
Timber.v("Widget status: $it")
|
||||||
|
if (it == WidgetStatus.WIDGET_ALLOWED) {
|
||||||
|
loadFormattedUrl()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPostAPIMediator() = postAPIMediator
|
fun getPostAPIMediator() = postAPIMediator
|
||||||
|
|
||||||
override fun handle(action: RoomWidgetAction) {
|
override fun handle(action: RoomWidgetAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is RoomWidgetAction.OnWebViewLoadingError -> handleWebViewLoadingError(action.url)
|
is RoomWidgetAction.OnWebViewLoadingError -> handleWebViewLoadingError(action.isHttpError, action.errorCode, action.errorDescription)
|
||||||
is RoomWidgetAction.OnWebViewLoadingSuccess -> handleWebViewLoadingSuccess(action.url)
|
is RoomWidgetAction.OnWebViewLoadingSuccess -> handleWebViewLoadingSuccess(action.url)
|
||||||
is RoomWidgetAction.OnWebViewStartedToLoad -> handleWebViewStartLoading(action.url)
|
is RoomWidgetAction.OnWebViewStartedToLoad -> handleWebViewStartLoading()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshPermissionStatus() {
|
private fun refreshPermissionStatus() {
|
||||||
if (initialState.widgetKind == WidgetKind.USER || initialState.widgetKind == WidgetKind.INTEGRATION_MANAGER) {
|
if (initialState.widgetKind.isAdmin()) {
|
||||||
onWidgetAllowed()
|
setWidgetStatus(WidgetStatus.WIDGET_ALLOWED)
|
||||||
} else {
|
} else {
|
||||||
val widgetId = initialState.widgetId
|
val widgetId = initialState.widgetId
|
||||||
if (widgetId == null) {
|
if (widgetId == null) {
|
||||||
setState { copy(status = WidgetStatus.WIDGET_NOT_ALLOWED) }
|
setWidgetStatus(WidgetStatus.WIDGET_NOT_ALLOWED)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val roomWidget = widgetService.getRoomWidgets(initialState.roomId, widgetId = QueryStringValue.Equals(widgetId, QueryStringValue.Case.SENSITIVE)).firstOrNull()
|
val roomWidget = widgetService.getRoomWidgets(initialState.roomId, widgetId = QueryStringValue.Equals(widgetId, QueryStringValue.Case.SENSITIVE)).firstOrNull()
|
||||||
if (roomWidget == null) {
|
if (roomWidget == null) {
|
||||||
setState { copy(status = WidgetStatus.WIDGET_NOT_ALLOWED) }
|
setWidgetStatus(WidgetStatus.WIDGET_NOT_ALLOWED)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (roomWidget.event?.senderId == session.myUserId) {
|
if (roomWidget.event?.senderId == session.myUserId) {
|
||||||
onWidgetAllowed()
|
setWidgetStatus(WidgetStatus.WIDGET_ALLOWED)
|
||||||
} else {
|
} else {
|
||||||
val stateEventId = roomWidget.event?.eventId
|
val stateEventId = roomWidget.event?.eventId
|
||||||
// This should not happen
|
// This should not happen
|
||||||
if (stateEventId == null) {
|
if (stateEventId == null) {
|
||||||
setState { copy(status = WidgetStatus.WIDGET_NOT_ALLOWED) }
|
setWidgetStatus(WidgetStatus.WIDGET_NOT_ALLOWED)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val isAllowed = integrationManagerService.isWidgetAllowed(stateEventId)
|
val isAllowed = integrationManagerService.isWidgetAllowed(stateEventId)
|
||||||
if (!isAllowed) {
|
if (!isAllowed) {
|
||||||
setState { copy(status = WidgetStatus.WIDGET_NOT_ALLOWED) }
|
setWidgetStatus(WidgetStatus.WIDGET_NOT_ALLOWED)
|
||||||
} else {
|
} else {
|
||||||
onWidgetAllowed()
|
setWidgetStatus(WidgetStatus.WIDGET_ALLOWED)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onWidgetAllowed() {
|
private fun setWidgetStatus(widgetStatus: WidgetStatus) {
|
||||||
setState {
|
setState { copy(status = widgetStatus) }
|
||||||
copy(status = WidgetStatus.WIDGET_ALLOWED, formattedURL = Loading())
|
}
|
||||||
}
|
|
||||||
|
private fun loadFormattedUrl(forceFetchToken: Boolean = false) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
val formattedUrl = widgetBuilder.build(initialState.baseUrl)
|
setState { copy(formattedURL = Loading()) }
|
||||||
|
val formattedUrl = widgetBuilder.format(
|
||||||
|
baseUrl = initialState.baseUrl,
|
||||||
|
params = initialState.urlParams,
|
||||||
|
forceFetchScalarToken = forceFetchToken,
|
||||||
|
bypassWhitelist = initialState.widgetKind == WidgetKind.INTEGRATION_MANAGER
|
||||||
|
)
|
||||||
setState { copy(formattedURL = Success(formattedUrl)) }
|
setState { copy(formattedURL = Success(formattedUrl)) }
|
||||||
|
_viewEvents.post(RoomWidgetViewEvents.LoadFormattedURL(formattedUrl))
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
|
if (failure is WidgetManagementFailure.TermsNotSignedException) {
|
||||||
|
_viewEvents.post(RoomWidgetViewEvents.DisplayTerms(failure.baseUrl, failure.token))
|
||||||
|
}
|
||||||
setState { copy(formattedURL = Fail(failure)) }
|
setState { copy(formattedURL = Fail(failure)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleWebViewStartLoading(url: String) {
|
private fun handleWebViewStartLoading() {
|
||||||
|
setState { copy(webviewLoadedUrl = Loading()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleWebViewLoadingSuccess(url: String) {
|
private fun handleWebViewLoadingSuccess(url: String) {
|
||||||
|
if (initialState.widgetKind.isAdmin()) {
|
||||||
|
postAPIMediator.injectAPI()
|
||||||
|
}
|
||||||
|
setState { copy(webviewLoadedUrl = Success(url)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleWebViewLoadingError(url: String) {
|
private fun handleWebViewLoadingError(isHttpError: Boolean, reason: Int, errorDescription: String) {
|
||||||
|
if (isHttpError) {
|
||||||
|
// In case of 403, try to refresh the scalar token
|
||||||
|
if (reason == HttpsURLConnection.HTTP_FORBIDDEN) {
|
||||||
|
loadFormattedUrl(true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setState { copy(webviewLoadedUrl = Fail(Throwable(errorDescription))) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCleared() {
|
||||||
|
super.onCleared()
|
||||||
|
postAPIMediator.setHandler(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,12 +29,17 @@ enum class WidgetStatus {
|
||||||
enum class WidgetKind {
|
enum class WidgetKind {
|
||||||
ROOM,
|
ROOM,
|
||||||
USER,
|
USER,
|
||||||
INTEGRATION_MANAGER
|
INTEGRATION_MANAGER;
|
||||||
|
|
||||||
|
fun isAdmin(): Boolean {
|
||||||
|
return this == USER || this == INTEGRATION_MANAGER
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class WidgetViewState(
|
data class WidgetViewState(
|
||||||
val roomId: String,
|
val roomId: String,
|
||||||
val baseUrl: String,
|
val baseUrl: String,
|
||||||
|
val urlParams: Map<String, String> = emptyMap(),
|
||||||
val widgetId: String? = null,
|
val widgetId: String? = null,
|
||||||
val widgetKind: WidgetKind,
|
val widgetKind: WidgetKind,
|
||||||
val status: WidgetStatus = WidgetStatus.UNKNOWN,
|
val status: WidgetStatus = WidgetStatus.UNKNOWN,
|
||||||
|
@ -49,6 +54,7 @@ data class WidgetViewState(
|
||||||
widgetKind = widgetArgs.kind,
|
widgetKind = widgetArgs.kind,
|
||||||
baseUrl = widgetArgs.baseUrl,
|
baseUrl = widgetArgs.baseUrl,
|
||||||
roomId = widgetArgs.roomId,
|
roomId = widgetArgs.roomId,
|
||||||
widgetId = widgetArgs.widgetId
|
widgetId = widgetArgs.widgetId,
|
||||||
|
urlParams = widgetArgs.urlParams
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 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.widgets.room
|
|
||||||
|
|
||||||
interface WidgetParams {
|
|
||||||
val params: Map<String, String>
|
|
||||||
}
|
|
||||||
|
|
||||||
class IntegrationManagerParams(
|
|
||||||
private val widgetId: String? = null,
|
|
||||||
private val screenId: String? = null) : WidgetParams {
|
|
||||||
|
|
||||||
override val params: Map<String, String> by lazy {
|
|
||||||
buildParams()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildParams(): Map<String, String> {
|
|
||||||
val map = HashMap<String, String>()
|
|
||||||
if (widgetId != null) {
|
|
||||||
map["integ_id"] = widgetId
|
|
||||||
}
|
|
||||||
if (screenId != null) {
|
|
||||||
map["screen"] = screenId
|
|
||||||
}
|
|
||||||
return map
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,6 +3,11 @@
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/open_matrix_apps"
|
||||||
|
android:title="@string/room_add_matrix_apps"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/resend_all"
|
android:id="@+id/resend_all"
|
||||||
android:icon="@drawable/ic_refresh_cw"
|
android:icon="@drawable/ic_refresh_cw"
|
||||||
|
|
Loading…
Reference in a new issue