diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/DefaultUserService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/DefaultUserService.kt index 7cd2f1b743..5cad504555 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/DefaultUserService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/DefaultUserService.kt @@ -17,101 +17,41 @@ package im.vector.matrix.android.internal.session.user import androidx.lifecycle.LiveData -import androidx.lifecycle.Transformations -import androidx.paging.DataSource -import androidx.paging.LivePagedListBuilder import androidx.paging.PagedList -import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.user.UserService import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.api.util.Optional -import im.vector.matrix.android.api.util.toOptional -import im.vector.matrix.android.internal.database.mapper.asDomain -import im.vector.matrix.android.internal.database.model.IgnoredUserEntity -import im.vector.matrix.android.internal.database.model.IgnoredUserEntityFields -import im.vector.matrix.android.internal.database.model.UserEntity -import im.vector.matrix.android.internal.database.model.UserEntityFields -import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.session.user.accountdata.UpdateIgnoredUserIdsTask import im.vector.matrix.android.internal.session.user.model.SearchUserTask import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith -import im.vector.matrix.android.internal.util.fetchCopied import javax.inject.Inject -internal class DefaultUserService @Inject constructor(private val monarchy: Monarchy, +internal class DefaultUserService @Inject constructor(private val userDataSource: UserDataSource, private val searchUserTask: SearchUserTask, private val updateIgnoredUserIdsTask: UpdateIgnoredUserIdsTask, private val taskExecutor: TaskExecutor) : UserService { - private val realmDataSourceFactory: Monarchy.RealmDataSourceFactory by lazy { - monarchy.createDataSourceFactory { realm -> - realm.where(UserEntity::class.java) - .isNotEmpty(UserEntityFields.USER_ID) - .sort(UserEntityFields.DISPLAY_NAME) - } - } - - private val domainDataSourceFactory: DataSource.Factory by lazy { - realmDataSourceFactory.map { - it.asDomain() - } - } - - private val livePagedListBuilder: LivePagedListBuilder by lazy { - LivePagedListBuilder(domainDataSourceFactory, PagedList.Config.Builder().setPageSize(100).setEnablePlaceholders(false).build()) - } override fun getUser(userId: String): User? { - val userEntity = monarchy.fetchCopied { UserEntity.where(it, userId).findFirst() } - ?: return null - - return userEntity.asDomain() + return userDataSource.getUser(userId) } override fun getUserLive(userId: String): LiveData> { - val liveData = monarchy.findAllMappedWithChanges( - { UserEntity.where(it, userId) }, - { it.asDomain() } - ) - return Transformations.map(liveData) { results -> - results.firstOrNull().toOptional() - } + return userDataSource.getUserLive(userId) } override fun getUsersLive(): LiveData> { - return monarchy.findAllMappedWithChanges( - { realm -> - realm.where(UserEntity::class.java) - .isNotEmpty(UserEntityFields.USER_ID) - .sort(UserEntityFields.DISPLAY_NAME) - }, - { it.asDomain() } - ) + return userDataSource.getUsersLive() } override fun getPagedUsersLive(filter: String?, excludedUserIds: Set?): LiveData> { - realmDataSourceFactory.updateQuery { realm -> - val query = realm.where(UserEntity::class.java) - if (filter.isNullOrEmpty()) { - query.isNotEmpty(UserEntityFields.USER_ID) - } else { - query - .beginGroup() - .contains(UserEntityFields.DISPLAY_NAME, filter) - .or() - .contains(UserEntityFields.USER_ID, filter) - .endGroup() - } - excludedUserIds - ?.takeIf { it.isNotEmpty() } - ?.let { - query.not().`in`(UserEntityFields.USER_ID, it.toTypedArray()) - } - query.sort(UserEntityFields.DISPLAY_NAME) - } - return monarchy.findAllPagedWithChanges(realmDataSourceFactory, livePagedListBuilder) + return userDataSource.getPagedUsersLive(filter, excludedUserIds) + } + + override fun getIgnoredUsersLive(): LiveData> { + return userDataSource.getIgnoredUsersLive() } override fun searchUsersDirectory(search: String, @@ -126,17 +66,6 @@ internal class DefaultUserService @Inject constructor(private val monarchy: Mona .executeBy(taskExecutor) } - override fun getIgnoredUsersLive(): LiveData> { - return monarchy.findAllMappedWithChanges( - { realm -> - realm.where(IgnoredUserEntity::class.java) - .isNotEmpty(IgnoredUserEntityFields.USER_ID) - .sort(IgnoredUserEntityFields.USER_ID) - }, - { getUser(it.userId) ?: User(userId = it.userId) } - ) - } - override fun ignoreUserIds(userIds: List, callback: MatrixCallback): Cancelable { val params = UpdateIgnoredUserIdsTask.Params(userIdsToIgnore = userIds.toList()) return updateIgnoredUserIdsTask diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UserDataSource.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UserDataSource.kt new file mode 100644 index 0000000000..3fcd0bbb74 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UserDataSource.kt @@ -0,0 +1,118 @@ +/* + * 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.matrix.android.internal.session.user + +import androidx.lifecycle.LiveData +import androidx.lifecycle.Transformations +import androidx.paging.DataSource +import androidx.paging.LivePagedListBuilder +import androidx.paging.PagedList +import com.zhuinden.monarchy.Monarchy +import im.vector.matrix.android.api.session.user.model.User +import im.vector.matrix.android.api.util.Optional +import im.vector.matrix.android.api.util.toOptional +import im.vector.matrix.android.internal.database.mapper.asDomain +import im.vector.matrix.android.internal.database.model.IgnoredUserEntity +import im.vector.matrix.android.internal.database.model.IgnoredUserEntityFields +import im.vector.matrix.android.internal.database.model.UserEntity +import im.vector.matrix.android.internal.database.model.UserEntityFields +import im.vector.matrix.android.internal.database.query.where +import im.vector.matrix.android.internal.util.fetchCopied +import javax.inject.Inject + +internal class UserDataSource @Inject constructor(private val monarchy: Monarchy) { + + private val realmDataSourceFactory: Monarchy.RealmDataSourceFactory by lazy { + monarchy.createDataSourceFactory { realm -> + realm.where(UserEntity::class.java) + .isNotEmpty(UserEntityFields.USER_ID) + .sort(UserEntityFields.DISPLAY_NAME) + } + } + + private val domainDataSourceFactory: DataSource.Factory by lazy { + realmDataSourceFactory.map { + it.asDomain() + } + } + + private val livePagedListBuilder: LivePagedListBuilder by lazy { + LivePagedListBuilder(domainDataSourceFactory, PagedList.Config.Builder().setPageSize(100).setEnablePlaceholders(false).build()) + } + + fun getUser(userId: String): User? { + val userEntity = monarchy.fetchCopied { UserEntity.where(it, userId).findFirst() } + ?: return null + + return userEntity.asDomain() + } + + fun getUserLive(userId: String): LiveData> { + val liveData = monarchy.findAllMappedWithChanges( + { UserEntity.where(it, userId) }, + { it.asDomain() } + ) + return Transformations.map(liveData) { results -> + results.firstOrNull().toOptional() + } + } + + fun getUsersLive(): LiveData> { + return monarchy.findAllMappedWithChanges( + { realm -> + realm.where(UserEntity::class.java) + .isNotEmpty(UserEntityFields.USER_ID) + .sort(UserEntityFields.DISPLAY_NAME) + }, + { it.asDomain() } + ) + } + + fun getPagedUsersLive(filter: String?, excludedUserIds: Set?): LiveData> { + realmDataSourceFactory.updateQuery { realm -> + val query = realm.where(UserEntity::class.java) + if (filter.isNullOrEmpty()) { + query.isNotEmpty(UserEntityFields.USER_ID) + } else { + query + .beginGroup() + .contains(UserEntityFields.DISPLAY_NAME, filter) + .or() + .contains(UserEntityFields.USER_ID, filter) + .endGroup() + } + excludedUserIds + ?.takeIf { it.isNotEmpty() } + ?.let { + query.not().`in`(UserEntityFields.USER_ID, it.toTypedArray()) + } + query.sort(UserEntityFields.DISPLAY_NAME) + } + return monarchy.findAllPagedWithChanges(realmDataSourceFactory, livePagedListBuilder) + } + + fun getIgnoredUsersLive(): LiveData> { + return monarchy.findAllMappedWithChanges( + { realm -> + realm.where(IgnoredUserEntity::class.java) + .isNotEmpty(IgnoredUserEntityFields.USER_ID) + .sort(IgnoredUserEntityFields.USER_ID) + }, + { getUser(it.userId) ?: User(userId = it.userId) } + ) + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/Widget.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/Widget.kt index ca26183d8a..418ab38f41 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/Widget.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/Widget.kt @@ -25,12 +25,12 @@ data class Widget( val event: Event, val widgetId: String, val senderInfo: SenderInfo?, - val isAddedByMe: Boolean + val isAddedByMe: Boolean, + val computedUrl: String? ) { - val isActive = widgetContent.type != null && widgetContent.url != null + val isActive = widgetContent.type != null && computedUrl != null val name = widgetContent.getHumanName() - } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/helper/WidgetFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/helper/WidgetFactory.kt index d3bf130f04..4346882e8e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/helper/WidgetFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/helper/WidgetFactory.kt @@ -23,12 +23,15 @@ import im.vector.matrix.android.api.session.widgets.model.WidgetContent import im.vector.matrix.android.internal.di.SessionDatabase import im.vector.matrix.android.internal.di.UserId import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper +import im.vector.matrix.android.internal.session.user.UserDataSource import im.vector.matrix.android.internal.session.widgets.Widget import io.realm.Realm import io.realm.RealmConfiguration +import java.net.URLEncoder import javax.inject.Inject internal class WidgetFactory @Inject constructor(@SessionDatabase private val realmConfiguration: RealmConfiguration, + private val userDataSource: UserDataSource, @UserId private val userId: String) { fun create(widgetEvent: Event): Widget? { @@ -50,6 +53,22 @@ internal class WidgetFactory @Inject constructor(@SessionDatabase private val re } } val isAddedByMe = widgetEvent.senderId == userId - return Widget(widgetContent, widgetEvent, widgetId, senderInfo, isAddedByMe) + val computedUrl = widgetContent.computeURL() + return Widget(widgetContent, widgetEvent, widgetId, senderInfo, isAddedByMe, computedUrl) + } + + private fun WidgetContent.computeURL(): String? { + var computedUrl = url ?: return null + val myUser = userDataSource.getUser(userId) + computedUrl = computedUrl + .replace("\$matrix_user_id", userId) + .replace("\$matrix_display_name", myUser?.displayName ?: userId) + .replace("\$matrix_avatar_url", myUser?.avatarUrl ?: "") + for ((key, value) in data) { + if (value is String) { + computedUrl = computedUrl.replace("$$key", URLEncoder.encode(value, "utf-8")) + } + } + return computedUrl } } diff --git a/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt b/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt index f7bca33d4f..532b42cad1 100644 --- a/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt +++ b/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt @@ -36,6 +36,7 @@ import im.vector.riotx.features.home.room.detail.readreceipts.DisplayReadReceipt import im.vector.riotx.features.home.room.detail.timeline.action.MessageActionsBottomSheet import im.vector.riotx.features.home.room.detail.timeline.edithistory.ViewEditHistoryBottomSheet import im.vector.riotx.features.home.room.detail.timeline.reactions.ViewReactionsBottomSheet +import im.vector.riotx.features.home.room.detail.widget.RoomWidgetsBottomSheet import im.vector.riotx.features.home.room.filtered.FilteredRoomsActivity import im.vector.riotx.features.home.room.list.RoomListModule import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsBottomSheet @@ -138,6 +139,7 @@ interface ScreenComponent { fun inject(bottomSheet: DeviceListBottomSheet) fun inject(bottomSheet: BootstrapBottomSheet) fun inject(bottomSheet: RoomWidgetPermissionBottomSheet) + fun inject(bottomSheet: RoomWidgetsBottomSheet) /* ========================================================================================== * Others @@ -147,7 +149,6 @@ interface ScreenComponent { fun inject(preference: UserAvatarPreference) fun inject(button: ReactionButton) - /* ========================================================================================== * Factory * ========================================================================================== */ diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index 031d66420e..fb38a4f08e 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -148,6 +148,7 @@ import im.vector.riotx.features.home.room.detail.timeline.item.MessageTextItem import im.vector.riotx.features.home.room.detail.timeline.item.ReadReceiptData import im.vector.riotx.features.home.room.detail.timeline.reactions.ViewReactionsBottomSheet import im.vector.riotx.features.home.room.detail.widget.RoomWidgetsBannerView +import im.vector.riotx.features.home.room.detail.widget.RoomWidgetsBottomSheet import im.vector.riotx.features.html.EventHtmlRenderer import im.vector.riotx.features.html.PillImageSpan import im.vector.riotx.features.invite.VectorInviteView @@ -1465,6 +1466,7 @@ class RoomDetailFragment @Inject constructor( } override fun onViewWidgetsClicked() { - Toast.makeText(requireContext(), "Show widgets", Toast.LENGTH_SHORT).show() + RoomWidgetsBottomSheet.newInstance() + .show(childFragmentManager, "ROOM_WIDGETS_BOTTOM_SHEET") } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/sticker/StickerPickerActionHandler.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/sticker/StickerPickerActionHandler.kt index 487b2d8c7b..6227fe9658 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/sticker/StickerPickerActionHandler.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/sticker/StickerPickerActionHandler.kt @@ -27,7 +27,7 @@ class StickerPickerActionHandler @Inject constructor(private val session: Sessio suspend fun handle(): RoomDetailViewEvents = withContext(Dispatchers.Default) { // Search for the sticker picker widget in the user account val stickerWidget = session.widgetService().getUserWidgets(setOf(StickerPickerConstants.WIDGET_NAME)).firstOrNull() - if (stickerWidget == null || stickerWidget.widgetContent.url.isNullOrBlank()) { + if (stickerWidget == null || stickerWidget.computedUrl.isNullOrBlank()) { RoomDetailViewEvents.DisplayPromptForIntegrationManager } else { RoomDetailViewEvents.OpenStickerPicker( diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/widget/RoomWidgetController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/widget/RoomWidgetController.kt new file mode 100644 index 0000000000..f09509777c --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/widget/RoomWidgetController.kt @@ -0,0 +1,43 @@ +/* + * 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.home.room.detail.widget + +import com.airbnb.epoxy.TypedEpoxyController +import im.vector.matrix.android.internal.session.widgets.Widget +import javax.inject.Inject + +/** + * Epoxy controller for room widgets list + */ +class RoomWidgetController @Inject constructor() : TypedEpoxyController>() { + + var listener: Listener? = null + + override fun buildModels(widget: List) { + widget.forEach { + RoomWidgetItem_() + .id(it.widgetId) + .widget(it) + .widgetClicked { listener?.didSelectWidget(it) } + .addTo(this) + } + } + + interface Listener { + fun didSelectWidget(widget: Widget) + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/widget/RoomWidgetItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/widget/RoomWidgetItem.kt new file mode 100644 index 0000000000..d62ee807f5 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/widget/RoomWidgetItem.kt @@ -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.home.room.detail.widget + +import android.widget.TextView +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import com.airbnb.epoxy.EpoxyModelWithHolder +import im.vector.matrix.android.internal.session.widgets.Widget +import im.vector.riotx.R +import im.vector.riotx.core.epoxy.VectorEpoxyHolder + +@EpoxyModelClass(layout = R.layout.item_room_widget) +abstract class RoomWidgetItem : EpoxyModelWithHolder() { + + @EpoxyAttribute lateinit var widget: Widget + @EpoxyAttribute var widgetClicked: (() -> Unit)? = null + + override fun bind(holder: Holder) { + holder.widgetName.text = widget.name + holder.view.setOnClickListener { widgetClicked?.invoke() } + } + + class Holder : VectorEpoxyHolder() { + val widgetName by bind(R.id.roomWidgetName) + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt new file mode 100644 index 0000000000..28d16125b3 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt @@ -0,0 +1,85 @@ +/* + * 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.home.room.detail.widget + +import android.os.Bundle +import androidx.recyclerview.widget.RecyclerView +import butterknife.BindView +import com.airbnb.mvrx.parentFragmentViewModel +import com.airbnb.mvrx.withState +import im.vector.matrix.android.internal.session.widgets.Widget +import im.vector.riotx.R +import im.vector.riotx.core.di.ScreenComponent +import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith +import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment +import im.vector.riotx.core.resources.ColorProvider +import im.vector.riotx.features.home.room.detail.RoomDetailViewModel +import im.vector.riotx.features.home.room.detail.RoomDetailViewState +import im.vector.riotx.features.navigation.Navigator +import kotlinx.android.synthetic.main.bottom_sheet_generic_list_with_title.* +import javax.inject.Inject + +/** + * Bottom sheet displaying active widgets in a room + */ +class RoomWidgetsBottomSheet : VectorBaseBottomSheetDialogFragment(), RoomWidgetController.Listener { + + @Inject lateinit var epoxyController: RoomWidgetController + @Inject lateinit var colorProvider: ColorProvider + @Inject lateinit var navigator: Navigator + + @BindView(R.id.bottomSheetRecyclerView) + lateinit var recyclerView: RecyclerView + + private val roomDetailViewModel: RoomDetailViewModel by parentFragmentViewModel() + + override fun injectWith(injector: ScreenComponent) { + injector.inject(this) + } + + override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + recyclerView.configureWith(epoxyController, hasFixedSize = false) + bottomSheetTitle.text = getString(R.string.active_widgets_title) + bottomSheetTitle.textSize = 20f + bottomSheetTitle.setTextColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary)) + epoxyController.listener = this + roomDetailViewModel.asyncSubscribe(this, RoomDetailViewState::activeRoomWidgets) { + epoxyController.setData(it) + } + } + + override fun onDestroyView() { + recyclerView.cleanup() + epoxyController.listener = null + super.onDestroyView() + } + + override fun didSelectWidget(widget: Widget) = withState(roomDetailViewModel) { + navigator.openRoomWidget(requireContext(), it.roomId, widget) + dismiss() + } + + companion object { + fun newInstance(): RoomWidgetsBottomSheet { + return RoomWidgetsBottomSheet() + } + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt index fc49564a8c..39b3116696 100644 --- a/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt @@ -238,6 +238,12 @@ class DefaultNavigator @Inject constructor( context.startActivity(WidgetActivity.newIntent(context, widgetArgs)) } + override fun openRoomWidget(context: Context, roomId: String, widget: Widget) { + val widgetArgs = widgetArgsBuilder.buildRoomWidgetArgs(roomId, widget) + context.startActivity(WidgetActivity.newIntent(context, widgetArgs)) + } + + override fun openImageViewer(activity: Activity, mediaData: ImageContentRenderer.Data, view: View, options: ((MutableList>) -> Unit)?) { val intent = ImageMediaViewerActivity.newIntent(activity, mediaData, ViewCompat.getTransitionName(view)) val pairs = ArrayList>() diff --git a/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt index c2798062e1..e231493a59 100644 --- a/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt @@ -89,6 +89,8 @@ interface Navigator { fun openIntegrationManager(context: Context, roomId: String, integId: String?, screen: String?) + fun openRoomWidget(context: Context, roomId: String, widget: Widget) + fun openImageViewer(activity: Activity, mediaData: ImageContentRenderer.Data, view: View, options: ((MutableList>) -> Unit)?) fun openVideoViewer(activity: Activity, mediaData: VideoContentRenderer.Data) diff --git a/vector/src/main/java/im/vector/riotx/features/widgets/WidgetArgsBuilder.kt b/vector/src/main/java/im/vector/riotx/features/widgets/WidgetArgsBuilder.kt index d56b02ed8d..d7c6829707 100644 --- a/vector/src/main/java/im/vector/riotx/features/widgets/WidgetArgsBuilder.kt +++ b/vector/src/main/java/im/vector/riotx/features/widgets/WidgetArgsBuilder.kt @@ -46,7 +46,7 @@ class WidgetArgsBuilder @Inject constructor(private val sessionHolder: ActiveSes @Suppress("UNCHECKED_CAST") fun buildStickerPickerArgs(roomId: String, widget: Widget): WidgetArgs { val widgetId = widget.widgetId - val baseUrl = widget.widgetContent.url ?: throw IllegalStateException() + val baseUrl = widget.computedUrl ?: throw IllegalStateException() return WidgetArgs( baseUrl = baseUrl, kind = WidgetKind.STICKER_PICKER, @@ -59,6 +59,17 @@ class WidgetArgsBuilder @Inject constructor(private val sessionHolder: ActiveSes ) } + fun buildRoomWidgetArgs(roomId: String, widget: Widget): WidgetArgs { + val widgetId = widget.widgetId + val baseUrl = widget.computedUrl?: throw IllegalStateException() + return WidgetArgs( + baseUrl = baseUrl, + kind = WidgetKind.ROOM, + roomId = roomId, + widgetId = widgetId + ) + } + @Suppress("UNCHECKED_CAST") private fun Map.filterNotNull(): Map { return filterValues { it != null } as Map diff --git a/vector/src/main/java/im/vector/riotx/features/widgets/WidgetViewModel.kt b/vector/src/main/java/im/vector/riotx/features/widgets/WidgetViewModel.kt index 5d7e576058..e96dbcf192 100644 --- a/vector/src/main/java/im/vector/riotx/features/widgets/WidgetViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/widgets/WidgetViewModel.kt @@ -168,6 +168,7 @@ class WidgetViewModel @AssistedInject constructor(@Assisted val initialState: Wi val widgetId = initialState.widgetId ?: return@launch awaitCallback { widgetService.destroyRoomWidget(initialState.roomId, widgetId, it) + _viewEvents.post(WidgetViewEvents.Close()) } } } diff --git a/vector/src/main/java/im/vector/riotx/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt index 01c149b2f0..133567a766 100644 --- a/vector/src/main/java/im/vector/riotx/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt @@ -15,8 +15,8 @@ */ package im.vector.riotx.features.widgets.permissions +import android.content.DialogInterface import android.os.Build -import android.os.Parcelable import android.text.Spannable import android.text.SpannableStringBuilder import android.text.style.BulletSpan @@ -34,10 +34,9 @@ import im.vector.riotx.core.extensions.withArgs import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.widgets.WidgetArgs -import kotlinx.android.parcel.Parcelize import javax.inject.Inject -class RoomWidgetPermissionBottomSheet : VectorBaseBottomSheetDialogFragment() { +class RoomWidgetPermissionBottomSheet : VectorBaseBottomSheetDialogFragment(), RoomWidgetPermissionViewModel.Factory { override fun getLayoutResId(): Int = R.layout.bottom_sheet_room_widget_permission @@ -56,13 +55,20 @@ class RoomWidgetPermissionBottomSheet : VectorBaseBottomSheetDialogFragment() { lateinit var authorAvatarView: ImageView @Inject lateinit var avatarRenderer: AvatarRenderer + @Inject lateinit var viewModelFactory: RoomWidgetPermissionViewModel.Factory var onFinish: ((Boolean) -> Unit)? = null + override val showExpanded = true + override fun injectWith(injector: ScreenComponent) { injector.inject(this) } + override fun create(initialState: RoomWidgetPermissionViewState): RoomWidgetPermissionViewModel { + return viewModelFactory.create(initialState) + } + override fun invalidate() = withState(viewModel) { state -> super.invalidate() val permissionData = state.permissionData() ?: return@withState @@ -117,6 +123,11 @@ class RoomWidgetPermissionBottomSheet : VectorBaseBottomSheetDialogFragment() { onFinish?.invoke(true) } + override fun onCancel(dialog: DialogInterface) { + super.onCancel(dialog) + onFinish?.invoke(false) + } + companion object { fun newInstance(widgetArgs: WidgetArgs) = RoomWidgetPermissionBottomSheet().withArgs { diff --git a/vector/src/main/java/im/vector/riotx/features/widgets/permissions/RoomWidgetPermissionViewModel.kt b/vector/src/main/java/im/vector/riotx/features/widgets/permissions/RoomWidgetPermissionViewModel.kt index 4e9b65f804..64dc581401 100644 --- a/vector/src/main/java/im/vector/riotx/features/widgets/permissions/RoomWidgetPermissionViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/widgets/permissions/RoomWidgetPermissionViewModel.kt @@ -51,7 +51,7 @@ class RoomWidgetPermissionViewModel @AssistedInject constructor(@Assisted val in .map { val widget = it.first() val domain = try { - URL(widget.widgetContent.url).host + URL(widget.computedUrl).host } catch (e: Throwable) { null } diff --git a/vector/src/main/java/im/vector/riotx/features/widgets/permissions/WidgetPermissionsHelper.kt b/vector/src/main/java/im/vector/riotx/features/widgets/permissions/WidgetPermissionsHelper.kt index 3d192098ad..bcf5f2c541 100644 --- a/vector/src/main/java/im/vector/riotx/features/widgets/permissions/WidgetPermissionsHelper.kt +++ b/vector/src/main/java/im/vector/riotx/features/widgets/permissions/WidgetPermissionsHelper.kt @@ -20,19 +20,15 @@ import im.vector.matrix.android.api.query.QueryStringValue import im.vector.matrix.android.api.session.integrationmanager.IntegrationManagerService import im.vector.matrix.android.api.session.widgets.WidgetService import im.vector.matrix.android.internal.util.awaitCallback -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext class WidgetPermissionsHelper(private val integrationManagerService: IntegrationManagerService, private val widgetService: WidgetService) { suspend fun changePermission(roomId: String, widgetId: String, allow: Boolean) { - val widget = withContext(Dispatchers.Default) { - widgetService.getRoomWidgets( - roomId = roomId, - widgetId = QueryStringValue.Equals(widgetId, QueryStringValue.Case.SENSITIVE) - ).firstOrNull() - } + val widget = widgetService.getRoomWidgets( + roomId = roomId, + widgetId = QueryStringValue.Equals(widgetId, QueryStringValue.Case.SENSITIVE) + ).firstOrNull() val eventId = widget?.event?.eventId ?: return awaitCallback { integrationManagerService.setWidgetAllowed(eventId, allow, it) diff --git a/vector/src/main/res/layout/bottom_sheet_room_widget_permission.xml b/vector/src/main/res/layout/bottom_sheet_room_widget_permission.xml index 2cb6c2fd4c..1bbf539656 100644 --- a/vector/src/main/res/layout/bottom_sheet_room_widget_permission.xml +++ b/vector/src/main/res/layout/bottom_sheet_room_widget_permission.xml @@ -38,7 +38,7 @@ android:layout_marginEnd="@dimen/layout_horizontal_margin" android:orientation="horizontal"> - + + + + + diff --git a/vector/src/main/res/layout/item_room_widget.xml b/vector/src/main/res/layout/item_room_widget.xml new file mode 100644 index 0000000000..8de7f244ee --- /dev/null +++ b/vector/src/main/res/layout/item_room_widget.xml @@ -0,0 +1,26 @@ + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 695b43c46e..3c9f9533b7 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -1117,6 +1117,7 @@ %d active widgets "VIEW" + "Active widgets" Widget