Clean after Benoit's review

This commit is contained in:
ganfra 2020-06-02 19:02:21 +02:00
parent 82b4415f7d
commit 06cc2f527e
35 changed files with 225 additions and 98 deletions

View File

@ -15,6 +15,9 @@
*/
package im.vector.matrix.android.api.session.integrationmanager
/**
* This class holds configuration of integration manager.
*/
data class IntegrationManagerConfig(
val uiUrl: String,
val apiUrl: String,
@ -22,9 +25,21 @@ data class IntegrationManagerConfig(
) {
// Order matters, first is preferred
/**
* The kind of config, it will reflect where the data is coming from.
*/
enum class Kind {
/**
* Defined in UserAccountData
*/
ACCOUNT,
/**
* Defined in Wellknown
*/
HOMESERVER,
/**
* Fallback value, hardcoded by the SDK
*/
DEFAULT
}
}

View File

@ -19,39 +19,98 @@ package im.vector.matrix.android.api.session.integrationmanager
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.util.Cancelable
/**
* This is the entry point to manage integration. You can grab an instance of this service through an active session.
*/
interface IntegrationManagerService {
/**
* This listener allow you to observe change related to integrations.
*/
interface Listener {
/**
* Is called whenever integration is enabled or disabled, comes from user account data.
*/
fun onIsEnabledChanged(enabled: Boolean) {
// No-op
}
/**
* Is called whenever configs from user account data or wellknown are updated.
*/
fun onConfigurationChanged(configs: List<IntegrationManagerConfig>) {
// No-op
}
/**
* Is called whenever widget permissions from user account data are updated.
*/
fun onWidgetPermissionsChanged(widgets: Map<String, Boolean>) {
// No-op
}
}
/**
* Adds a listener to observe changes.
*/
fun addListener(listener: Listener)
/**
* Removes a previously added listener.
*/
fun removeListener(listener: Listener)
/**
* Return the list of current configurations, sorted by kind. First one is preferred.
* See [IntegrationManagerConfig.Kind]
*/
fun getOrderedConfigs(): List<IntegrationManagerConfig>
/**
* Return the preferred current configuration.
* See [IntegrationManagerConfig.Kind]
*/
fun getPreferredConfig(): IntegrationManagerConfig
/**
* Returns true if integration is enabled, false otherwise.
*/
fun isIntegrationEnabled(): Boolean
/**
* Offers to enable or disable the integration.
* @param enable the param to change
* @param callback the matrix callback to listen for result.
* @return Cancelable
*/
fun setIntegrationEnabled(enable: Boolean, callback: MatrixCallback<Unit>): Cancelable
/**
* Offers to allow or disallow a widget.
* @param stateEventId the eventId of the state event defining the widget.
* @param allowed the param to change
* @param callback the matrix callback to listen for result.
* @return Cancelable
*/
fun setWidgetAllowed(stateEventId: String, allowed: Boolean, callback: MatrixCallback<Unit>): Cancelable
/**
* Returns true if the widget is allowed, false otherwise.
* @param stateEventId the eventId of the state event defining the widget.
*/
fun isWidgetAllowed(stateEventId: String): Boolean
/**
* Offers to allow or disallow a native widget domain.
* @param widgetType the widget type to check for
* @param domain the domain to check for
*/
fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean, callback: MatrixCallback<Unit>): Cancelable
fun isNativeWidgetAllowed(widgetType: String, domain: String?): Boolean
/**
* Returns true if the widget domain is allowed, false otherwise.
* @param widgetType the widget type to check for
* @param domain the domain to check for
*/
fun isNativeWidgetDomainAllowed(widgetType: String, domain: String): Boolean
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package im.vector.matrix.android.internal.session.widgets
package im.vector.matrix.android.api.session.widgets
import im.vector.matrix.android.api.failure.Failure

View File

@ -23,12 +23,32 @@ import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.api.session.widgets.model.Widget
/**
* This is the entry point to manage widgets. You can grab an instance of this service through an active session.
*/
interface WidgetService {
/**
* Returns an instance of [WidgetURLFormatter].
*/
fun getWidgetURLFormatter(): WidgetURLFormatter
/**
* Returns an instance of [WidgetPostAPIMediator].
* This is to be used for "admin" widgets so you can interact through JS.
*/
fun getWidgetPostAPIMediator(): WidgetPostAPIMediator
/**
* Returns the current room widgets defined through state events.
* Some widgets can be deactivated, so be sure to check for isActive if needed.
*
* @param roomId the room where you want to fetch widgets
* @param widgetId if you want to fetch for some particular widget
* @param widgetTypes if you want to filter some widget type.
* @param excludedTypes if you want to exclude some widget type.
*/
fun getRoomWidgets(
roomId: String,
widgetId: QueryStringValue = QueryStringValue.NoCondition,
@ -36,6 +56,15 @@ interface WidgetService {
excludedTypes: Set<String>? = null
): List<Widget>
/**
* Returns the live room widgets so you can listen to them.
* Some widgets can be deactivated, so be sure to check for isActive.
*
* @param roomId the room where you want to fetch widgets
* @param widgetId if you want to fetch for some particular widget
* @param widgetTypes if you want to filter some widget type.
* @param excludedTypes if you want to exclude some widget type.
*/
fun getRoomWidgetsLive(
roomId: String,
widgetId: QueryStringValue = QueryStringValue.NoCondition,
@ -43,19 +72,53 @@ interface WidgetService {
excludedTypes: Set<String>? = null
): LiveData<List<Widget>>
/**
* Returns the current user widgets.
* Some widgets can be deactivated, so be sure to check for isActive.
*
* @param widgetTypes if you want to filter some widget type.
* @param excludedTypes if you want to exclude some widget type.
*/
fun getUserWidgets(
widgetTypes: Set<String>? = null,
excludedTypes: Set<String>? = null
): List<Widget>
/**
* Returns the live user widgets so you can listen to them.
* Some widgets can be deactivated, so be sure to check for isActive.
*
* @param widgetTypes if you want to filter some widget type.
* @param excludedTypes if you want to exclude some widget type.
*/
fun getUserWidgetsLive(
widgetTypes: Set<String>? = null,
excludedTypes: Set<String>? = null
): LiveData<List<Widget>>
/**
* Creates a new widget in a room. It makes sure you have the rights to handle this.
*
* @param roomId: the room where you want to deactivate the widget.
* @param widgetId: the widget to deactivate.
* @param callback the matrix callback to listen for result.
* @return Cancelable
*/
fun createRoomWidget(roomId: String, widgetId: String, content: Content, callback: MatrixCallback<Widget>): Cancelable
/**
* Deactivate a widget in a room. It makes sure you have the rights to handle this.
*
* @param roomId: the room where you want to deactivate the widget.
* @param widgetId: the widget to deactivate.
* @param callback the matrix callback to listen for result.
* @return Cancelable
*/
fun destroyRoomWidget(roomId: String, widgetId: String, callback: MatrixCallback<Unit>): Cancelable
/**
* Returns true if you can add/remove widgets. It goes through
* @param roomId the room where you want to administrate widgets.
*/
fun hasPermissionsToHandleWidgets(roomId: String): Boolean
}

View File

@ -20,6 +20,12 @@ interface WidgetURLFormatter {
/**
* 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.
*
* @param baseUrl the baseUrl which will be checked for scalar token
* @param params additional params you want to append to the base url.
* @param forceFetchScalarToken if true, you will force to fetch a new scalar token
* from the server (only if the base url is whitelisted)
* @param bypassWhitelist if true, the base url will be considered as whitelisted
*/
suspend fun format(
baseUrl: String,

View File

@ -16,19 +16,19 @@
package im.vector.matrix.android.api.session.widgets.model
sealed class WidgetType(open val preferred: String, open val legacy: String) {
sealed class WidgetType(open val preferred: String, open val legacy: String = preferred) {
object Jitsi : WidgetType("m.jitsi", "jitsi")
object TradingView : WidgetType("m.tradingview", "m.tradingview")
object Spotify : WidgetType("m.spotify", "m.spotify")
object Video : WidgetType("m.video", "m.video")
object GoogleDoc : WidgetType("m.googledoc", "m.googledoc")
object GoogleCalendar : WidgetType("m.googlecalendar", "m.googlecalendar")
object Etherpad : WidgetType("m.etherpad", "m.etherpad")
object StickerPicker : WidgetType("m.stickerpicker", "m.stickerpicker")
object Grafana : WidgetType("m.grafana", "m.grafana")
object Custom : WidgetType("m.custom", "m.custom")
object IntegrationManager : WidgetType("m.integration_manager", "m.integration_manager")
data class Fallback(override val preferred: String, override val legacy: String) : WidgetType(preferred, legacy)
object TradingView : WidgetType("m.tradingview")
object Spotify : WidgetType("m.spotify")
object Video : WidgetType("m.video")
object GoogleDoc : WidgetType("m.googledoc")
object GoogleCalendar : WidgetType("m.googlecalendar")
object Etherpad : WidgetType("m.etherpad")
object StickerPicker : WidgetType("m.stickerpicker")
object Grafana : WidgetType("m.grafana")
object Custom : WidgetType("m.custom")
object IntegrationManager : WidgetType("m.integration_manager")
data class Fallback(override val preferred: String) : WidgetType(preferred)
fun matches(type: String?): Boolean {
return type == preferred || type == legacy
@ -58,7 +58,7 @@ sealed class WidgetType(open val preferred: String, open val legacy: String) {
val matchingType = DEFINED_TYPES.firstOrNull {
it.matches(type)
}
return matchingType ?: Fallback(type, type)
return matchingType ?: Fallback(type)
}
}
}

View File

@ -29,7 +29,7 @@ internal class AccountDataMapper @Inject constructor(moshi: Moshi) {
fun map(entity: UserAccountDataEntity): UserAccountDataEvent {
return UserAccountDataEvent(
type = entity.type ?: "",
content = entity.contentStr?.let { adapter.fromJson(it) } ?: emptyMap()
content = entity.contentStr?.let { adapter.fromJson(it) }.orEmpty()
)
}
}

View File

@ -21,14 +21,16 @@ import im.vector.matrix.android.api.auth.data.Versions
import im.vector.matrix.android.api.auth.data.isLoginAndRegistrationSupportedBySdk
import im.vector.matrix.android.api.auth.wellknown.WellknownResult
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilities
import im.vector.matrix.android.internal.wellknown.GetWellknownTask
import im.vector.matrix.android.internal.database.model.HomeServerCapabilitiesEntity
import im.vector.matrix.android.internal.database.query.getOrCreate
import im.vector.matrix.android.internal.di.UserId
import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.session.integrationmanager.IntegrationManagerConfigExtractor
import im.vector.matrix.android.internal.task.Task
import im.vector.matrix.android.internal.util.awaitTransaction
import im.vector.matrix.android.internal.wellknown.GetWellknownTask
import org.greenrobot.eventbus.EventBus
import timber.log.Timber
import java.util.Date
import javax.inject.Inject
@ -39,6 +41,7 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
private val monarchy: Monarchy,
private val eventBus: EventBus,
private val getWellknownTask: GetWellknownTask,
private val configExtractor: IntegrationManagerConfigExtractor,
@UserId
private val userId: String
) : GetHomeServerCapabilitiesTask {
@ -102,8 +105,14 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
if (getWellknownResult != null && getWellknownResult is WellknownResult.Prompt) {
homeServerCapabilitiesEntity.defaultIdentityServerUrl = getWellknownResult.identityServerUrl
}
// We are also checking for integration manager configurations
val config = configExtractor.extract(getWellknownResult.wellKnown)
if (config != null) {
Timber.v("Extracted integration config : $config")
realm.insertOrUpdate(config)
}
}
homeServerCapabilitiesEntity.lastUpdatedTimestamp = Date().time
}
}

View File

@ -20,7 +20,7 @@ import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class AllowedWidgetsContent(
internal data class AllowedWidgetsContent(
/**
* Map of stateEventId to Allowed
*/

View File

@ -60,7 +60,7 @@ internal class DefaultIntegrationManagerService @Inject constructor(private val
return integrationManager.setNativeWidgetDomainAllowed(widgetType, domain, allowed, callback)
}
override fun isNativeWidgetAllowed(widgetType: String, domain: String?): Boolean {
return integrationManager.isNativeWidgetAllowed(widgetType, domain)
override fun isNativeWidgetDomainAllowed(widgetType: String, domain: String): Boolean {
return integrationManager.isNativeWidgetDomainAllowed(widgetType, domain)
}
}

View File

@ -90,7 +90,6 @@ internal class IntegrationManager @Inject constructor(@UserId private val userId
}
fun start() {
refreshWellknown()
lifecycleRegistry.currentState = Lifecycle.State.STARTED
observeWellknownConfig()
accountDataDataSource
@ -209,7 +208,7 @@ internal class IntegrationManager @Inject constructor(@UserId private val userId
.executeBy(taskExecutor)
}
fun isNativeWidgetAllowed(widgetType: String, domain: String?): Boolean {
fun isNativeWidgetDomainAllowed(widgetType: String, domain: String?): Boolean {
val currentAllowedWidgets = accountDataDataSource.getAccountDataEvent(UserAccountData.TYPE_ALLOWED_WIDGETS)
val currentContent = currentAllowedWidgets?.content?.toModel<AllowedWidgetsContent>()
return currentContent?.native?.get(widgetType)?.get(domain) ?: false
@ -273,25 +272,6 @@ internal class IntegrationManager @Inject constructor(@UserId private val userId
.firstOrNull()?.widgetContent
}
private fun refreshWellknown() {
taskExecutor.executorScope.launch {
val params = GetWellknownTask.Params(matrixId = userId)
val wellknownResult = try {
getWellknownTask.execute(params)
} catch (failure: Throwable) {
Timber.v("Get wellknown failed: $failure")
null
}
if (wellknownResult != null && wellknownResult is WellknownResult.Prompt) {
val config = configExtractor.extract(wellknownResult.wellKnown) ?: return@launch
Timber.v("Extracted config: $config")
monarchy.awaitTransaction {
it.insertOrUpdate(config)
}
}
}
}
private fun observeWellknownConfig() {
val liveData = monarchy.findAllMappedWithChanges(
{ it.where(WellknownIntegrationManagerConfigEntity::class.java) },

View File

@ -24,5 +24,5 @@ import im.vector.matrix.android.api.session.integrationmanager.IntegrationManage
internal abstract class IntegrationManagerModule {
@Binds
abstract fun bindIntegrationManagerService(integrationManagerService: DefaultIntegrationManagerService): IntegrationManagerService
abstract fun bindIntegrationManagerService(service: DefaultIntegrationManagerService): IntegrationManagerService
}

View File

@ -20,6 +20,6 @@ import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class IntegrationManagerWidgetData(
internal data class IntegrationManagerWidgetData(
@Json(name = "api_url") val apiUrl: String? = null
)

View File

@ -20,6 +20,6 @@ import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class IntegrationProvisioningContent(
internal data class IntegrationProvisioningContent(
@Json(name = "enabled") val enabled: Boolean
)

View File

@ -118,6 +118,7 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
else -> {
val params = SendStateTask.Params(
roomId = roomId,
stateKey = null,
eventType = EventType.STATE_ROOM_ENCRYPTION,
body = mapOf(
"algorithm" to algorithm

View File

@ -346,7 +346,7 @@ internal class LocalEchoEventFactory @Inject constructor(
return createMessageEvent(roomId, content)
}
private fun createMessageEvent(roomId: String, content: Any? = null): Event {
private fun createMessageEvent(roomId: String, content: MessageContent? = null): Event {
return createEvent(roomId, EventType.MESSAGE, content.toContent())
}

View File

@ -65,6 +65,7 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private
): Cancelable {
val params = SendStateTask.Params(
roomId = roomId,
stateKey = stateKey,
eventType = eventType,
body = body
)

View File

@ -26,7 +26,7 @@ import javax.inject.Inject
internal interface SendStateTask : Task<SendStateTask.Params, Unit> {
data class Params(
val roomId: String,
val stateKey: String? = null,
val stateKey: String?,
val eventType: String,
val body: JsonDict
)

View File

@ -20,6 +20,6 @@ import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class RegisterWidgetResponse(
internal data class RegisterWidgetResponse(
@Json(name = "scalar_token") val scalarToken: String?
)

View File

@ -30,6 +30,7 @@ import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.integrationmanager.IntegrationManagerService
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.WidgetManagementFailure
import im.vector.matrix.android.api.session.widgets.model.Widget
import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.internal.di.UserId

View File

@ -39,13 +39,13 @@ internal abstract class WidgetModule {
}
@Binds
abstract fun bindWidgetService(widgetService: DefaultWidgetService): WidgetService
abstract fun bindWidgetService(service: DefaultWidgetService): WidgetService
@Binds
abstract fun bindWidgetURLBuilder(widgetURLBuilder: DefaultWidgetURLFormatter): WidgetURLFormatter
abstract fun bindWidgetURLBuilder(formatter: DefaultWidgetURLFormatter): WidgetURLFormatter
@Binds
abstract fun bindWidgetPostAPIMediator(widgetPostMessageAPIProvider: DefaultWidgetPostAPIMediator): WidgetPostAPIMediator
abstract fun bindWidgetPostAPIMediator(mediator: DefaultWidgetPostAPIMediator): WidgetPostAPIMediator
@Binds
abstract fun bindCreateWidgetTask(task: DefaultCreateWidgetTask): CreateWidgetTask

View File

@ -20,7 +20,7 @@ import android.content.Context
import timber.log.Timber
import javax.inject.Inject
class WidgetPostMessageAPIProvider @Inject constructor(private val context: Context) {
internal class WidgetPostMessageAPIProvider @Inject constructor(private val context: Context) {
private var postMessageAPIString: String? = null

View File

@ -30,7 +30,7 @@ internal interface WidgetsAPI {
* @param requestOpenIdTokenResponse the body content (Ref: https://github.com/matrix-org/matrix-doc/pull/1961)
*/
@POST("register")
fun register(@Body requestOpenIdTokenResponse: RequestOpenIdTokenResponse, @Query("v") version: String?): Call<RegisterWidgetResponse>
fun register(@Body body: RequestOpenIdTokenResponse, @Query("v") version: String?): Call<RegisterWidgetResponse>
@GET("account")
fun validateToken(@Query("scalar_token") scalarToken: String?, @Query("v") version: String?): Call<Unit>

View File

@ -21,7 +21,7 @@ import im.vector.matrix.android.api.failure.MatrixError
import im.vector.matrix.android.internal.network.executeRequest
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.WidgetManagementFailure
import im.vector.matrix.android.api.session.widgets.WidgetManagementFailure
import im.vector.matrix.android.internal.session.widgets.WidgetsAPI
import im.vector.matrix.android.internal.session.widgets.WidgetsAPIProvider
import im.vector.matrix.android.internal.task.Task

View File

@ -123,7 +123,7 @@ interface ScreenComponent {
fun inject(activity: BigImageViewerActivity)
fun inject(activity: InviteUsersToRoomActivity)
fun inject(activity: ReviewTermsActivity)
fun inject(widgetActivity: WidgetActivity)
fun inject(activity: WidgetActivity)
/* ==========================================================================================
* BottomSheets

View File

@ -202,7 +202,8 @@ class RoomDetailFragment @Inject constructor(
VectorInviteView.Callback,
JumpToReadMarkerView.Callback,
AttachmentTypeSelectorView.Callback,
AttachmentsHelper.Callback, RoomWidgetsBannerView.Callback {
AttachmentsHelper.Callback,
RoomWidgetsBannerView.Callback {
companion object {
@ -327,8 +328,6 @@ class RoomDetailFragment @Inject constructor(
private fun displayPromptForIntegrationManager() {
// The Sticker picker widget is not installed yet. Propose the user to install it
val builder = AlertDialog.Builder(requireContext())
// Use the builder context
// Use the builder context
val v: View = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_no_sticker_pack, null)
builder
.setView(v)

View File

@ -66,7 +66,6 @@ data class RoomDetailViewState(
val syncState: SyncState = SyncState.Idle,
val highlightedEventId: String? = null,
val unreadState: UnreadState = UnreadState.Unknown,
val menuItemsVisibility: SparseArray<Boolean> = SparseArray(4),
val canShowJumpToReadMarker: Boolean = true
) : MvRxState {

View File

@ -22,17 +22,19 @@ import com.airbnb.epoxy.EpoxyModelClass
import com.airbnb.epoxy.EpoxyModelWithHolder
import im.vector.matrix.android.api.session.widgets.model.Widget
import im.vector.riotx.R
import im.vector.riotx.core.epoxy.ClickListener
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.epoxy.onClick
@EpoxyModelClass(layout = R.layout.item_room_widget)
abstract class RoomWidgetItem : EpoxyModelWithHolder<RoomWidgetItem.Holder>() {
@EpoxyAttribute lateinit var widget: Widget
@EpoxyAttribute var widgetClicked: (() -> Unit)? = null
@EpoxyAttribute var widgetClicked: ClickListener? = null
override fun bind(holder: Holder) {
holder.widgetName.text = widget.name
holder.view.setOnClickListener { widgetClicked?.invoke() }
holder.view.onClick(widgetClicked)
}
class Holder : VectorEpoxyHolder() {

View File

@ -27,12 +27,10 @@ class WidgetAPICallback(private val postAPIMediator: WidgetPostAPIMediator,
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: Any) {
super.onSuccess(data)
postAPIMediator.sendSuccess(eventData)
}
}

View File

@ -80,9 +80,11 @@ class WidgetActivity : VectorBaseActivity(), ToolbarConfigurable, WidgetViewMode
}
override fun initUiAndData() {
val widgetArgs: WidgetArgs = intent?.extras?.getParcelable(MvRx.KEY_ARG)
?: return
val widgetArgs: WidgetArgs? = intent?.extras?.getParcelable(MvRx.KEY_ARG)
if (widgetArgs == null) {
finish()
return
}
configure(toolbar)
toolbar.isVisible = widgetArgs.kind.nameRes != 0
viewModel.observeViewEvents {

View File

@ -34,7 +34,7 @@ import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.integrationmanager.IntegrationManagerService
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.internal.session.widgets.WidgetManagementFailure
import im.vector.matrix.android.api.session.widgets.WidgetManagementFailure
import im.vector.matrix.android.internal.util.awaitCallback
import im.vector.matrix.rx.mapOptional
import im.vector.matrix.rx.rx

View File

@ -34,6 +34,7 @@ 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.synthetic.main.bottom_sheet_room_widget_permission.*
import javax.inject.Inject
class RoomWidgetPermissionBottomSheet : VectorBaseBottomSheetDialogFragment() {
@ -42,18 +43,6 @@ class RoomWidgetPermissionBottomSheet : VectorBaseBottomSheetDialogFragment() {
private val viewModel: RoomWidgetPermissionViewModel by activityViewModel()
@BindView(R.id.bottom_sheet_widget_permission_shared_info)
lateinit var sharedInfoTextView: TextView
@BindView(R.id.bottom_sheet_widget_permission_owner_id)
lateinit var authorIdText: TextView
@BindView(R.id.bottom_sheet_widget_permission_owner_display_name)
lateinit var authorNameText: TextView
@BindView(R.id.bottom_sheet_widget_permission_owner_avatar)
lateinit var authorAvatarView: ImageView
@Inject lateinit var avatarRenderer: AvatarRenderer
override val showExpanded = true
@ -65,10 +54,10 @@ class RoomWidgetPermissionBottomSheet : VectorBaseBottomSheetDialogFragment() {
override fun invalidate() = withState(viewModel) { state ->
super.invalidate()
val permissionData = state.permissionData() ?: return@withState
authorIdText.text = permissionData.widget.senderInfo?.userId ?: ""
authorNameText.text = permissionData.widget.senderInfo?.disambiguatedDisplayName
widgetPermissionOwnerId.text = permissionData.widget.senderInfo?.userId ?: ""
widgetPermissionOwnerDisplayName.text = permissionData.widget.senderInfo?.disambiguatedDisplayName
permissionData.widget.senderInfo?.toMatrixItem()?.also {
avatarRenderer.render(it, authorAvatarView)
avatarRenderer.render(it, widgetPermissionOwnerAvatar)
}
val domain = permissionData.widgetDomain ?: ""
@ -96,18 +85,17 @@ class RoomWidgetPermissionBottomSheet : VectorBaseBottomSheetDialogFragment() {
}
}
infoBuilder.append("\n")
sharedInfoTextView.text = infoBuilder
widgetPermissionSharedInfo.text = infoBuilder
}
@OnClick(R.id.bottom_sheet_widget_permission_decline_button)
@OnClick(R.id.widgetPermissionDecline)
fun doDecline() {
viewModel.handle(RoomWidgetPermissionActions.BlockWidget)
// optimistic dismiss
dismiss()
}
@OnClick(R.id.bottom_sheet_widget_permission_continue_button)
@OnClick(R.id.widgetPermissionContinue)
fun doAccept() {
viewModel.handle(RoomWidgetPermissionActions.AllowWidget)
// optimistic dismiss

View File

@ -17,17 +17,18 @@
android:layout_marginBottom="8dp"
android:text="@string/room_widget_permission_title"
android:textSize="20sp"
android:textColor="?riotx_text_primary"
android:textStyle="bold" />
<TextView
android:id="@+id/bottom_sheet_widget_permission_h2_text"
android:id="@+id/widgetPermissionHeader2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/layout_horizontal_margin"
android:layout_marginEnd="@dimen/layout_horizontal_margin"
android:layout_marginBottom="8dp"
android:text="@string/room_widget_permission_added_by"
android:textColor="?android:attr/textColorSecondary"
android:textColor="?riotx_text_secondary"
android:textSize="16sp" />
<LinearLayout
@ -39,7 +40,7 @@
android:orientation="horizontal">
<ImageView
android:id="@+id/bottom_sheet_widget_permission_owner_avatar"
android:id="@+id/widgetPermissionOwnerAvatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center"
@ -54,33 +55,35 @@
android:orientation="vertical">
<TextView
android:id="@+id/bottom_sheet_widget_permission_owner_display_name"
android:id="@+id/widgetPermissionOwnerDisplayName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAlignment="center"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="?riotx_text_primary"
tools:text="User name" />
<TextView
android:id="@+id/bottom_sheet_widget_permission_owner_id"
android:id="@+id/widgetPermissionOwnerId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAlignment="center"
android:textSize="14sp"
android:textColor="?riotx_text_secondary"
tools:text="\@foo:matrix.org" />
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/bottom_sheet_widget_permission_shared_info"
android:id="@+id/widgetPermissionSharedInfo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/layout_horizontal_margin"
android:layout_marginTop="@dimen/layout_vertical_margin_big"
android:layout_marginEnd="@dimen/layout_horizontal_margin"
android:layout_marginBottom="8dp"
android:textColor="?android:attr/textColorSecondary"
android:textColor="?riotx_text_secondary"
android:textSize="16sp"
tools:text="@string/room_widget_permission_shared_info_title" />
@ -91,16 +94,16 @@
android:gravity="end"
android:orientation="horizontal">
<Button
android:id="@+id/bottom_sheet_widget_permission_decline_button"
<com.google.android.material.button.MaterialButton
android:id="@+id/widgetPermissionDecline"
style="@style/VectorButtonStyleDestructive"
android:layout_marginEnd="@dimen/layout_vertical_margin"
android:layout_marginRight="@dimen/layout_vertical_margin"
android:text="@string/decline"
android:textAllCaps="true"/>
<Button
android:id="@+id/bottom_sheet_widget_permission_continue_button"
<com.google.android.material.button.MaterialButton
android:id="@+id/widgetPermissionContinue"
style="@style/VectorButtonStylePositive"
android:layout_marginEnd="@dimen/layout_vertical_margin"
android:layout_marginRight="@dimen/layout_vertical_margin"

View File

@ -44,7 +44,7 @@
android:layout_gravity="center"
android:layout_marginStart="@dimen/layout_horizontal_margin"
android:layout_marginEnd="@dimen/layout_horizontal_margin"
android:textColor="?android:textColorPrimary"
android:textColor="?riotx_text_primary"
android:textStyle="bold"
tools:text="Fail to load widget " />
</LinearLayout>

View File

@ -105,6 +105,7 @@
<im.vector.riotx.core.preference.VectorSwitchPreference
android:key="SETTINGS_ALLOW_INTEGRATIONS_KEY"
android:persistent="false"
android:title="@string/settings_integration_allow" />
<im.vector.riotx.core.preference.VectorPreference