Widget post api: handle last methods

This commit is contained in:
ganfra 2020-05-26 11:29:42 +02:00
parent 7409fde650
commit 4f4afd6840
9 changed files with 154 additions and 31 deletions

View file

@ -25,6 +25,10 @@ import im.vector.matrix.android.internal.session.widgets.Widget
interface WidgetService {
companion object {
const val WIDGET_EVENT_TYPE = "im.vector.modular.widgets"
}
fun getWidgetURLFormatter(): WidgetURLFormatter
fun getWidgetPostAPIMediator(): WidgetPostAPIMediator

View file

@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.session.widgets
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.session.widgets.WidgetService
import im.vector.matrix.android.internal.database.awaitNotEmptyResult
import im.vector.matrix.android.internal.database.model.CurrentStateEventEntity
import im.vector.matrix.android.internal.database.model.CurrentStateEventEntityFields
@ -47,14 +48,14 @@ internal class DefaultCreateWidgetTask @Inject constructor(private val monarchy:
executeRequest<Unit>(eventBus) {
apiCall = roomAPI.sendStateEvent(
roomId = params.roomId,
stateEventType = WidgetManager.WIDGET_EVENT_TYPE,
stateEventType = WidgetService.WIDGET_EVENT_TYPE,
stateKey = params.widgetId,
params = params.content
)
}
awaitNotEmptyResult(monarchy.realmConfiguration, 30_000L) {
CurrentStateEventEntity
.whereStateKey(it, params.roomId, type = WidgetManager.WIDGET_EVENT_TYPE, stateKey = params.widgetId)
.whereStateKey(it, params.roomId, type = WidgetService.WIDGET_EVENT_TYPE, stateKey = params.widgetId)
.and()
.equalTo(CurrentStateEventEntityFields.ROOT.SENDER, userId)
}

View file

@ -25,7 +25,6 @@ import im.vector.matrix.android.api.session.widgets.WidgetService
import im.vector.matrix.android.api.session.widgets.WidgetURLFormatter
import im.vector.matrix.android.api.util.Cancelable
import javax.inject.Inject
import javax.inject.Provider
internal class DefaultWidgetService @Inject constructor(private val widgetManager: WidgetManager,
private val widgetURLFormatter: WidgetURLFormatter,
@ -56,11 +55,11 @@ internal class DefaultWidgetService @Inject constructor(private val widgetManage
}
override fun createRoomWidget(roomId: String, widgetId: String, content: Content, callback: MatrixCallback<Widget>): Cancelable {
return widgetManager.createWidget(roomId, widgetId, content, callback)
return widgetManager.createRoomWidget(roomId, widgetId, content, callback)
}
override fun destroyRoomWidget(roomId: String, widgetId: String, callback: MatrixCallback<Unit>): Cancelable {
return widgetManager.destroyWidget(roomId, widgetId, callback)
return widgetManager.destroyRoomWidget(roomId, widgetId, callback)
}
override fun hasPermissionsToHandleWidgets(roomId: String): Boolean {

View file

@ -29,6 +29,7 @@ import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.PowerLevelsContent
import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsHelper
import im.vector.matrix.android.api.session.widgets.WidgetService
import im.vector.matrix.android.api.session.widgets.model.WidgetContent
import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.internal.di.UserId
@ -52,10 +53,6 @@ internal class WidgetManager @Inject constructor(private val integrationManager:
private val createWidgetTask: CreateWidgetTask,
@UserId private val userId: String) : IntegrationManager.Listener {
companion object {
const val WIDGET_EVENT_TYPE = "im.vector.modular.widgets"
}
private val lifecycleOwner: LifecycleOwner = LifecycleOwner { lifecycleRegistry }
private val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(lifecycleOwner)
@ -76,7 +73,7 @@ internal class WidgetManager @Inject constructor(private val integrationManager:
excludedTypes: Set<String>? = null
): LiveData<List<Widget>> {
// Get all im.vector.modular.widgets state events in the room
val liveWidgetEvents = stateEventDataSource.getStateEventsLive(roomId, setOf(WIDGET_EVENT_TYPE), widgetId)
val liveWidgetEvents = stateEventDataSource.getStateEventsLive(roomId, setOf(WidgetService.WIDGET_EVENT_TYPE), widgetId)
return Transformations.map(liveWidgetEvents) { widgetEvents ->
widgetEvents.mapEventsToWidgets(widgetTypes, excludedTypes)
}
@ -89,7 +86,7 @@ internal class WidgetManager @Inject constructor(private val integrationManager:
excludedTypes: Set<String>? = null
): List<Widget> {
// Get all im.vector.modular.widgets state events in the room
val widgetEvents: List<Event> = stateEventDataSource.getStateEvents(roomId, setOf(WIDGET_EVENT_TYPE), widgetId)
val widgetEvents: List<Event> = stateEventDataSource.getStateEvents(roomId, setOf(WidgetService.WIDGET_EVENT_TYPE), widgetId)
return widgetEvents.mapEventsToWidgets(widgetTypes, excludedTypes)
}
@ -153,7 +150,7 @@ internal class WidgetManager @Inject constructor(private val integrationManager:
.toList()
}
fun createWidget(roomId: String, widgetId: String, content: Content, callback: MatrixCallback<Widget>): Cancelable {
fun createRoomWidget(roomId: String, widgetId: String, content: Content, callback: MatrixCallback<Widget>): Cancelable {
return taskExecutor.executorScope.launchToCallback(callback = callback) {
if (!hasPermissionsToHandleWidgets(roomId)) {
throw WidgetManagementFailure.NotEnoughPower
@ -172,7 +169,7 @@ internal class WidgetManager @Inject constructor(private val integrationManager:
}
}
fun destroyWidget(roomId: String, widgetId: String, callback: MatrixCallback<Unit>): Cancelable {
fun destroyRoomWidget(roomId: String, widgetId: String, callback: MatrixCallback<Unit>): Cancelable {
return taskExecutor.executorScope.launchToCallback(callback = callback) {
if (!hasPermissionsToHandleWidgets(roomId)) {
throw WidgetManagementFailure.NotEnoughPower

View file

@ -24,14 +24,14 @@ import im.vector.riotx.core.resources.StringProvider
class WidgetAPICallback(private val postAPIMediator: WidgetPostAPIMediator,
private val eventData: JsonDict,
private val stringProvider: StringProvider) : MatrixCallback<Unit> {
private val stringProvider: StringProvider) : MatrixCallback<Any> {
override fun onFailure(failure: Throwable) {
super.onFailure(failure)
postAPIMediator.sendError(stringProvider.getString(R.string.widget_integration_failed_to_send_request), eventData)
}
override fun onSuccess(data: Unit) {
override fun onSuccess(data: Any) {
super.onSuccess(data)
postAPIMediator.sendSuccess(eventData)
}

View file

@ -16,7 +16,6 @@
package im.vector.riotx.features.widgets
import android.content.Context
import android.text.TextUtils
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
@ -31,22 +30,26 @@ import im.vector.matrix.android.api.session.room.model.PowerLevelsContent
import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsHelper
import im.vector.matrix.android.api.session.widgets.WidgetPostAPIMediator
import im.vector.matrix.android.api.util.JsonDict
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
import im.vector.riotx.R
import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.features.navigation.Navigator
import timber.log.Timber
import java.util.ArrayList
import java.util.HashMap
class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roomId: String,
private val context: Context,
private val navigator: Navigator,
@Assisted private val navigationCallback: NavigationCallback,
private val stringProvider: StringProvider,
private val session: Session) : WidgetPostAPIMediator.Handler {
@AssistedInject.Factory
interface Factory {
fun create(roomId: String): WidgetPostAPIHandler
fun create(roomId: String, navigationCallback: NavigationCallback): WidgetPostAPIHandler
}
interface NavigationCallback {
fun close()
fun openIntegrationManager(integId: String?, integType: String?)
}
private val widgetPostAPIMediator = session.widgetService().getWidgetPostAPIMediator()
@ -57,20 +60,24 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
"integration_manager_open" -> handleIntegrationManagerOpenAction(eventData).run { true }
"bot_options" -> getBotOptions(eventData).run { true }
"can_send_event" -> canSendEvent(eventData).run { true }
//"close_scalar" -> finish().run { true }
"close_scalar" -> handleCloseScalar().run { true }
"get_membership_count" -> getMembershipCount(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 }
"membership_state" -> getMembershipState(eventData).run { true }
"set_bot_options" -> setBotOptions(eventData).run { true }
"set_bot_power" -> setBotPower(eventData).run { true }
"set_plumbing_state" -> setPlumbingState(eventData).run { true }
//"set_widget" -> setWidget(eventData).run { true }
"set_widget" -> setWidget(eventData).run { true }
else -> false
}
}
private fun handleCloseScalar() {
navigationCallback.close()
}
private fun handleIntegrationManagerOpenAction(eventData: JsonDict) {
var integType: String? = null
var integId: String? = null
@ -91,7 +98,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
// Add "type_" as a prefix
integType?.let { integType = "type_$integType" }
}
navigator.openIntegrationManager(context, roomId, integId, integType)
navigationCallback.openIntegrationManager(integId, integType)
}
/**
@ -212,6 +219,79 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
widgetPostAPIMediator.sendObjectResponse(List::class.java, responseData, eventData)
}
/**
* Set a new widget
*
* @param eventData the modular data
*/
private fun setWidget(eventData: JsonDict) {
val userWidget = eventData["userWidget"] as Boolean?
if (userWidget == true) {
Timber.d("Received request to set widget for user")
} else {
if (checkRoomId(eventData)) {
return
}
Timber.d("Received request to set widget in room $roomId")
}
val widgetId = eventData["widget_id"] as String?
val widgetType = eventData["type"] as String?
val widgetUrl = eventData["url"] as String?
// optional
val widgetName = eventData["name"] as String?
// optional
val widgetData = eventData["data"]
if (widgetId == null) {
widgetPostAPIMediator.sendError(stringProvider.getString(R.string.widget_integration_unable_to_create), eventData)
return
}
val widgetEventContent = HashMap<String, Any>()
if (null != widgetUrl) {
if (null == widgetType) {
widgetPostAPIMediator.sendError(stringProvider.getString(R.string.widget_integration_unable_to_create), eventData)
return
}
widgetEventContent["type"] = widgetType
widgetEventContent["url"] = widgetUrl
if (null != widgetName) {
widgetEventContent["name"] = widgetName
}
if (null != widgetData) {
widgetEventContent["data"] = widgetData
}
}
if (userWidget == true) {
val addUserWidgetBody = mapOf(
widgetId to mapOf(
"content" to widgetEventContent,
"state_key" to widgetId,
"id" to widgetId,
"sender" to session.myUserId,
"type" to "m.widget"
)
)
session.updateAccountData(
type = UserAccountData.TYPE_WIDGETS,
content = addUserWidgetBody,
callback = createWidgetAPICallback(eventData)
)
} else {
session.widgetService().createRoomWidget(
roomId = roomId,
widgetId = widgetId,
content = widgetEventContent,
callback = createWidgetAPICallback(eventData)
)
}
}
/**
* Update the 'plumbing state"
*
@ -232,7 +312,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
eventType = EventType.PLUMBING,
stateKey = null,
body = params,
callback = WidgetAPICallback(widgetPostAPIMediator, eventData, stringProvider)
callback = createWidgetAPICallback(eventData)
)
}
@ -255,7 +335,8 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
eventType = EventType.BOT_OPTIONS,
stateKey = stateKey,
body = content,
callback = WidgetAPICallback(widgetPostAPIMediator, eventData, stringProvider))
callback = createWidgetAPICallback(eventData)
)
}
/**
@ -280,6 +361,26 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
}
}
/**
* Invite an user to this room
*
* @param eventData the modular data
*/
private fun inviteUser(eventData: JsonDict) {
if (checkRoomId(eventData) || checkUserId(eventData)) {
return
}
val userId = eventData["user_id"] as String
val description = "Received request to invite $userId into room $roomId"
Timber.d(description)
val member = room.getRoomMember(userId)
if (member != null && member.membership == Membership.JOIN) {
widgetPostAPIMediator.sendSuccess(eventData)
} else {
room.invite(userId = userId, callback = createWidgetAPICallback(eventData))
}
}
/**
* Provides the number of members in the rooms
*
@ -332,6 +433,10 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
// OK
return false
}
private fun createWidgetAPICallback(eventData: JsonDict): WidgetAPICallback {
return WidgetAPICallback(widgetPostAPIMediator, eventData, stringProvider)
}
}

View file

@ -68,11 +68,19 @@ class RoomWidgetFragment @Inject constructor(
}
viewModel.observeViewEvents {
when (it) {
is RoomWidgetViewEvents.DisplayTerms -> displayTerms(it)
is RoomWidgetViewEvents.LoadFormattedURL -> loadFormattedUrl(it)
is RoomWidgetViewEvents.DisplayTerms -> displayTerms(it)
is RoomWidgetViewEvents.LoadFormattedURL -> loadFormattedUrl(it)
is RoomWidgetViewEvents.Close -> vectorBaseActivity.finish()
is RoomWidgetViewEvents.DisplayIntegrationManager -> navigator.openIntegrationManager(
context = vectorBaseActivity,
roomId = fragmentArgs.roomId,
integId = it.integId,
screenId = it.integType
)
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == ReviewTermsActivity.TERMS_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
@ -170,7 +178,6 @@ class RoomWidgetFragment @Inject constructor(
)
}
private fun loadFormattedUrl(loadFormattedUrl: RoomWidgetViewEvents.LoadFormattedURL) {
widgetWebView.clearHistory()
widgetWebView.loadUrl(loadFormattedUrl.formattedURL)

View file

@ -19,6 +19,8 @@ package im.vector.riotx.features.widgets.room
import im.vector.riotx.core.platform.VectorViewEvents
sealed class RoomWidgetViewEvents : VectorViewEvents {
object Close: RoomWidgetViewEvents()
data class DisplayIntegrationManager(val integId: String?, val integType: String?): RoomWidgetViewEvents()
data class LoadFormattedURL(val formattedURL: String): RoomWidgetViewEvents()
data class DisplayTerms(val url: String, val token: String): RoomWidgetViewEvents()
}

View file

@ -38,7 +38,7 @@ import javax.net.ssl.HttpsURLConnection
class RoomWidgetViewModel @AssistedInject constructor(@Assisted val initialState: WidgetViewState,
private val widgetPostAPIHandlerFactory: WidgetPostAPIHandler.Factory,
private val session: Session)
: VectorViewModel<WidgetViewState, RoomWidgetAction, RoomWidgetViewEvents>(initialState) {
: VectorViewModel<WidgetViewState, RoomWidgetAction, RoomWidgetViewEvents>(initialState), WidgetPostAPIHandler.NavigationCallback {
@AssistedInject.Factory
interface Factory {
@ -64,7 +64,7 @@ class RoomWidgetViewModel @AssistedInject constructor(@Assisted val initialState
init {
if(initialState.widgetKind.isAdmin()) {
val widgetPostAPIHandler = widgetPostAPIHandlerFactory.create(initialState.roomId)
val widgetPostAPIHandler = widgetPostAPIHandlerFactory.create(initialState.roomId, this)
postAPIMediator.setHandler(widgetPostAPIHandler)
}
refreshPermissionStatus()
@ -174,4 +174,12 @@ class RoomWidgetViewModel @AssistedInject constructor(@Assisted val initialState
super.onCleared()
postAPIMediator.setHandler(null)
}
override fun close() {
_viewEvents.post(RoomWidgetViewEvents.Close)
}
override fun openIntegrationManager(integId: String?, integType: String?) {
_viewEvents.post(RoomWidgetViewEvents.DisplayIntegrationManager(integId, integType))
}
}