mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-22 09:25:49 +03:00
Merge branch 'develop' into feature/fix_1543
This commit is contained in:
commit
c0c300925d
38 changed files with 229 additions and 179 deletions
|
@ -12,6 +12,7 @@ Bugfix 🐛:
|
|||
- Fix dark theme issue on login screen (#1097)
|
||||
- Incomplete predicate in RealmCryptoStore#getOutgoingRoomKeyRequest (#1519)
|
||||
- User could not redact message that they have sent (#1543)
|
||||
- Use vendor prefix for non merged MSC (#1537)
|
||||
|
||||
Translations 🗣:
|
||||
-
|
||||
|
@ -27,7 +28,7 @@ Other changes:
|
|||
- Use `retrofit2.Call.awaitResponse` extension provided by Retrofit 2. (#1526)
|
||||
- Fix minor typo in contribution guide (#1512)
|
||||
- Fix self-assignment of callback in `DefaultRoomPushRuleService#setRoomNotificationState` (#1520)
|
||||
- Random housekeeping clean-ups indicated by Lint (#1520)
|
||||
- Random housekeeping clean-ups indicated by Lint (#1520, #1541)
|
||||
|
||||
Changes in RiotX 0.22.0 (2020-06-15)
|
||||
===================================================
|
||||
|
|
|
@ -241,14 +241,14 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
|||
val eventWireContent = event.content.toContent()
|
||||
assertNotNull(eventWireContent)
|
||||
|
||||
assertNull(eventWireContent.get("body"))
|
||||
assertEquals(MXCRYPTO_ALGORITHM_MEGOLM, eventWireContent.get("algorithm"))
|
||||
assertNull(eventWireContent["body"])
|
||||
assertEquals(MXCRYPTO_ALGORITHM_MEGOLM, eventWireContent["algorithm"])
|
||||
|
||||
assertNotNull(eventWireContent.get("ciphertext"))
|
||||
assertNotNull(eventWireContent.get("session_id"))
|
||||
assertNotNull(eventWireContent.get("sender_key"))
|
||||
assertNotNull(eventWireContent["ciphertext"])
|
||||
assertNotNull(eventWireContent["session_id"])
|
||||
assertNotNull(eventWireContent["sender_key"])
|
||||
|
||||
assertEquals(senderSession.sessionParams.deviceId, eventWireContent.get("device_id"))
|
||||
assertEquals(senderSession.sessionParams.deviceId, eventWireContent["device_id"])
|
||||
|
||||
assertNotNull(event.eventId)
|
||||
assertEquals(roomId, event.roomId)
|
||||
|
@ -257,7 +257,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
|||
|
||||
val eventContent = event.toContent()
|
||||
assertNotNull(eventContent)
|
||||
assertEquals(clearMessage, eventContent.get("body"))
|
||||
assertEquals(clearMessage, eventContent["body"])
|
||||
assertEquals(senderSession.myUserId, event.senderId)
|
||||
}
|
||||
|
||||
|
|
|
@ -144,7 +144,7 @@ class QuadSTests : InstrumentedTest {
|
|||
|
||||
val secretAccountData = assertAccountData(aliceSession, "secret.of.life")
|
||||
|
||||
val encryptedContent = secretAccountData.content.get("encrypted") as? Map<*, *>
|
||||
val encryptedContent = secretAccountData.content["encrypted"] as? Map<*, *>
|
||||
assertNotNull("Element should be encrypted", encryptedContent)
|
||||
assertNotNull("Secret should be encrypted with default key", encryptedContent?.get(keyId))
|
||||
|
||||
|
|
|
@ -87,14 +87,13 @@ class EventMatchCondition(
|
|||
// Very simple glob to regexp converter
|
||||
private fun simpleGlobToRegExp(glob: String): String {
|
||||
var out = "" // "^"
|
||||
for (i in 0 until glob.length) {
|
||||
val c = glob[i]
|
||||
when (c) {
|
||||
for (element in glob) {
|
||||
when (element) {
|
||||
'*' -> out += ".*"
|
||||
'?' -> out += '.'.toString()
|
||||
'.' -> out += "\\."
|
||||
'\\' -> out += "\\\\"
|
||||
else -> out += c
|
||||
else -> out += element
|
||||
}
|
||||
}
|
||||
out += "" // '$'.toString()
|
||||
|
|
|
@ -26,5 +26,5 @@ object RelationType {
|
|||
/** Lets you define an event which references an existing event.*/
|
||||
const val REFERENCE = "m.reference"
|
||||
/** Lets you define an event which adds a response to an existing event.*/
|
||||
const val RESPONSE = "m.response"
|
||||
const val RESPONSE = "org.matrix.response"
|
||||
}
|
||||
|
|
|
@ -90,6 +90,6 @@ interface WidgetPostAPIMediator {
|
|||
/**
|
||||
* Triggered when a widget is posting
|
||||
*/
|
||||
fun handleWidgetRequest(eventData: JsonDict): Boolean
|
||||
fun handleWidgetRequest(mediator: WidgetPostAPIMediator, eventData: JsonDict): Boolean
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,8 @@ interface WidgetService {
|
|||
fun getWidgetURLFormatter(): WidgetURLFormatter
|
||||
|
||||
/**
|
||||
* Returns an instance of [WidgetPostAPIMediator].
|
||||
* Returns a new instance of [WidgetPostAPIMediator].
|
||||
* Be careful to call clearWebView method and setHandler to null to avoid memory leaks.
|
||||
* This is to be used for "admin" widgets so you can interact through JS.
|
||||
*/
|
||||
fun getWidgetPostAPIMediator(): WidgetPostAPIMediator
|
||||
|
|
|
@ -16,6 +16,22 @@
|
|||
|
||||
package im.vector.matrix.android.api.session.widgets.model
|
||||
|
||||
private val DEFINED_TYPES by lazy {
|
||||
listOf(
|
||||
WidgetType.Jitsi,
|
||||
WidgetType.TradingView,
|
||||
WidgetType.Spotify,
|
||||
WidgetType.Video,
|
||||
WidgetType.GoogleDoc,
|
||||
WidgetType.GoogleCalendar,
|
||||
WidgetType.Etherpad,
|
||||
WidgetType.StickerPicker,
|
||||
WidgetType.Grafana,
|
||||
WidgetType.Custom,
|
||||
WidgetType.IntegrationManager
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Ref: https://github.com/matrix-org/matrix-doc/issues/1236
|
||||
*/
|
||||
|
@ -33,7 +49,7 @@ sealed class WidgetType(open val preferred: String, open val legacy: String = pr
|
|||
object IntegrationManager : WidgetType("m.integration_manager")
|
||||
data class Fallback(override val preferred: String) : WidgetType(preferred)
|
||||
|
||||
fun matches(type: String?): Boolean {
|
||||
fun matches(type: String): Boolean {
|
||||
return type == preferred || type == legacy
|
||||
}
|
||||
|
||||
|
@ -43,20 +59,6 @@ sealed class WidgetType(open val preferred: String, open val legacy: String = pr
|
|||
|
||||
companion object {
|
||||
|
||||
private val DEFINED_TYPES = listOf(
|
||||
Jitsi,
|
||||
TradingView,
|
||||
Spotify,
|
||||
Video,
|
||||
GoogleDoc,
|
||||
GoogleCalendar,
|
||||
Etherpad,
|
||||
StickerPicker,
|
||||
Grafana,
|
||||
Custom,
|
||||
IntegrationManager
|
||||
)
|
||||
|
||||
fun fromString(type: String): WidgetType {
|
||||
val matchingType = DEFINED_TYPES.firstOrNull {
|
||||
it.matches(type)
|
||||
|
|
|
@ -273,7 +273,7 @@ internal abstract class SASDefaultVerificationTransaction(
|
|||
if (keyIDNoPrefix == otherCrossSigningMasterKeyPublic) {
|
||||
// Check the signature
|
||||
val mac = macUsingAgreedMethod(otherCrossSigningMasterKeyPublic, baseInfo + it)
|
||||
if (mac != theirMacSafe.mac.get(it)) {
|
||||
if (mac != theirMacSafe.mac[it]) {
|
||||
// WRONG!
|
||||
Timber.e("## SAS Verification: mac mismatch for MasterKey with id $keyIDNoPrefix")
|
||||
cancel(CancelCode.MismatchedKeys)
|
||||
|
|
|
@ -25,7 +25,7 @@ internal object TimelineEventFilter {
|
|||
*/
|
||||
internal object Content {
|
||||
internal const val EDIT = """{*"m.relates_to"*"rel_type":*"m.replace"*}"""
|
||||
internal const val RESPONSE = """{*"m.relates_to"*"rel_type":*"m.response"*}"""
|
||||
internal const val RESPONSE = """{*"m.relates_to"*"rel_type":*"org.matrix.response"*}"""
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -78,7 +78,7 @@ internal class DefaultWidgetPostAPIMediator @Inject constructor(private val mosh
|
|||
|
||||
private fun onWidgetMessage(eventData: JsonDict) {
|
||||
try {
|
||||
if (handler?.handleWidgetRequest(eventData) == false) {
|
||||
if (handler?.handleWidgetRequest(this, eventData) == false) {
|
||||
sendError("", eventData)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
|
|
@ -26,10 +26,11 @@ import im.vector.matrix.android.api.session.widgets.WidgetURLFormatter
|
|||
import im.vector.matrix.android.api.session.widgets.model.Widget
|
||||
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,
|
||||
private val widgetPostAPIMediator: WidgetPostAPIMediator)
|
||||
private val widgetPostAPIMediator: Provider<WidgetPostAPIMediator>)
|
||||
: WidgetService {
|
||||
|
||||
override fun getWidgetURLFormatter(): WidgetURLFormatter {
|
||||
|
@ -37,7 +38,7 @@ internal class DefaultWidgetService @Inject constructor(private val widgetManage
|
|||
}
|
||||
|
||||
override fun getWidgetPostAPIMediator(): WidgetPostAPIMediator {
|
||||
return widgetPostAPIMediator
|
||||
return widgetPostAPIMediator.get()
|
||||
}
|
||||
|
||||
override fun getRoomWidgets(
|
||||
|
|
|
@ -67,8 +67,7 @@ internal class DefaultGetScalarTokenTask @Inject constructor(private val widgets
|
|||
throw IllegalStateException("Scalar token is null")
|
||||
}
|
||||
scalarTokenStore.setToken(serverUrl, registerWidgetResponse.scalarToken)
|
||||
widgetsAPI.validateToken(registerWidgetResponse.scalarToken, WIDGET_API_VERSION)
|
||||
return registerWidgetResponse.scalarToken
|
||||
return validateToken(widgetsAPI, serverUrl, registerWidgetResponse.scalarToken)
|
||||
}
|
||||
|
||||
private suspend fun validateToken(widgetsAPI: WidgetsAPI, serverUrl: String, scalarToken: String): String {
|
||||
|
|
|
@ -62,9 +62,9 @@ class ContactPicker(override val requestCode: Int) : Picker<MultiPickerContactTy
|
|||
|
||||
val contactId = cursor.getInt(idColumn)
|
||||
var name = cursor.getString(nameColumn)
|
||||
var photoUri = cursor.getString(photoUriColumn)
|
||||
var phoneNumberList = mutableListOf<String>()
|
||||
var emailList = mutableListOf<String>()
|
||||
val photoUri = cursor.getString(photoUriColumn)
|
||||
val phoneNumberList = mutableListOf<String>()
|
||||
val emailList = mutableListOf<String>()
|
||||
|
||||
getRawContactId(context.contentResolver, contactId)?.let { rawContactId ->
|
||||
val selection = ContactsContract.Data.RAW_CONTACT_ID + " = ?"
|
||||
|
|
|
@ -40,14 +40,14 @@ class DefaultErrorFormatter @Inject constructor(
|
|||
null -> null
|
||||
is IdentityServiceError -> identityServerError(throwable)
|
||||
is Failure.NetworkConnection -> {
|
||||
when {
|
||||
throwable.ioException is SocketTimeoutException ->
|
||||
when (throwable.ioException) {
|
||||
is SocketTimeoutException ->
|
||||
stringProvider.getString(R.string.error_network_timeout)
|
||||
throwable.ioException is UnknownHostException ->
|
||||
is UnknownHostException ->
|
||||
// Invalid homeserver?
|
||||
// TODO Check network state, airplane mode, etc.
|
||||
stringProvider.getString(R.string.login_error_unknown_host)
|
||||
else ->
|
||||
else ->
|
||||
stringProvider.getString(R.string.error_no_network)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ class VectorEditTextPreference : EditTextPreference {
|
|||
override fun onBindViewHolder(holder: PreferenceViewHolder) {
|
||||
// display the title in multi-line to avoid ellipsis.
|
||||
try {
|
||||
holder.itemView.findViewById<TextView>(android.R.id.title)?.setSingleLine(false)
|
||||
holder.itemView.findViewById<TextView>(android.R.id.title)?.isSingleLine = false
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "onBindView")
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ open class VectorPreference : Preference {
|
|||
val title = itemView.findViewById<TextView>(android.R.id.title)
|
||||
val summary = itemView.findViewById<TextView>(android.R.id.summary)
|
||||
if (title != null) {
|
||||
title.setSingleLine(false)
|
||||
title.isSingleLine = false
|
||||
title.setTypeface(null, mTypeface)
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ class VectorSwitchPreference : SwitchPreference {
|
|||
|
||||
override fun onBindViewHolder(holder: PreferenceViewHolder) {
|
||||
// display the title in multi-line to avoid ellipsis.
|
||||
holder.itemView.findViewById<TextView>(android.R.id.title)?.setSingleLine(false)
|
||||
holder.itemView.findViewById<TextView>(android.R.id.title)?.isSingleLine = false
|
||||
|
||||
super.onBindViewHolder(holder)
|
||||
}
|
||||
|
|
|
@ -78,4 +78,5 @@ sealed class RoomDetailAction : VectorViewModelAction {
|
|||
data class ReRequestKeys(val eventId: String) : RoomDetailAction()
|
||||
|
||||
object SelectStickerAttachment : RoomDetailAction()
|
||||
object OpenIntegrationManager: RoomDetailAction()
|
||||
}
|
||||
|
|
|
@ -144,7 +144,6 @@ import im.vector.riotx.features.crypto.verification.VerificationBottomSheet
|
|||
import im.vector.riotx.features.home.AvatarRenderer
|
||||
import im.vector.riotx.features.home.room.detail.composer.TextComposerView
|
||||
import im.vector.riotx.features.home.room.detail.readreceipts.DisplayReadReceiptsBottomSheet
|
||||
import im.vector.riotx.features.home.room.detail.sticker.StickerPickerConstants
|
||||
import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController
|
||||
import im.vector.riotx.features.home.room.detail.timeline.action.EventSharedAction
|
||||
import im.vector.riotx.features.home.room.detail.timeline.action.MessageActionsBottomSheet
|
||||
|
@ -159,6 +158,7 @@ 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.home.room.detail.widget.WidgetRequestCodes
|
||||
import im.vector.riotx.features.html.EventHtmlRenderer
|
||||
import im.vector.riotx.features.html.PillImageSpan
|
||||
import im.vector.riotx.features.invite.VectorInviteView
|
||||
|
@ -330,22 +330,33 @@ class RoomDetailFragment @Inject constructor(
|
|||
|
||||
roomDetailViewModel.observeViewEvents {
|
||||
when (it) {
|
||||
is RoomDetailViewEvents.Failure -> showErrorInSnackbar(it.throwable)
|
||||
is RoomDetailViewEvents.OnNewTimelineEvents -> scrollOnNewMessageCallback.addNewTimelineEventIds(it.eventIds)
|
||||
is RoomDetailViewEvents.ActionSuccess -> displayRoomDetailActionSuccess(it)
|
||||
is RoomDetailViewEvents.ActionFailure -> displayRoomDetailActionFailure(it)
|
||||
is RoomDetailViewEvents.ShowMessage -> showSnackWithMessage(it.message, Snackbar.LENGTH_LONG)
|
||||
is RoomDetailViewEvents.NavigateToEvent -> navigateToEvent(it)
|
||||
is RoomDetailViewEvents.FileTooBigError -> displayFileTooBigError(it)
|
||||
is RoomDetailViewEvents.DownloadFileState -> handleDownloadFileState(it)
|
||||
is RoomDetailViewEvents.JoinRoomCommandSuccess -> handleJoinedToAnotherRoom(it)
|
||||
is RoomDetailViewEvents.SendMessageResult -> renderSendMessageResult(it)
|
||||
RoomDetailViewEvents.DisplayPromptForIntegrationManager -> displayPromptForIntegrationManager()
|
||||
is RoomDetailViewEvents.OpenStickerPicker -> openStickerPicker(it)
|
||||
is RoomDetailViewEvents.Failure -> showErrorInSnackbar(it.throwable)
|
||||
is RoomDetailViewEvents.OnNewTimelineEvents -> scrollOnNewMessageCallback.addNewTimelineEventIds(it.eventIds)
|
||||
is RoomDetailViewEvents.ActionSuccess -> displayRoomDetailActionSuccess(it)
|
||||
is RoomDetailViewEvents.ActionFailure -> displayRoomDetailActionFailure(it)
|
||||
is RoomDetailViewEvents.ShowMessage -> showSnackWithMessage(it.message, Snackbar.LENGTH_LONG)
|
||||
is RoomDetailViewEvents.NavigateToEvent -> navigateToEvent(it)
|
||||
is RoomDetailViewEvents.FileTooBigError -> displayFileTooBigError(it)
|
||||
is RoomDetailViewEvents.DownloadFileState -> handleDownloadFileState(it)
|
||||
is RoomDetailViewEvents.JoinRoomCommandSuccess -> handleJoinedToAnotherRoom(it)
|
||||
is RoomDetailViewEvents.SendMessageResult -> renderSendMessageResult(it)
|
||||
RoomDetailViewEvents.DisplayPromptForIntegrationManager -> displayPromptForIntegrationManager()
|
||||
is RoomDetailViewEvents.OpenStickerPicker -> openStickerPicker(it)
|
||||
is RoomDetailViewEvents.DisplayEnableIntegrationsWarning -> displayDisabledIntegrationDialog()
|
||||
is RoomDetailViewEvents.OpenIntegrationManager -> openIntegrationManager()
|
||||
}.exhaustive
|
||||
}
|
||||
}
|
||||
|
||||
private fun openIntegrationManager(screen: String? = null) {
|
||||
navigator.openIntegrationManager(
|
||||
fragment = this,
|
||||
roomId = roomDetailArgs.roomId,
|
||||
integId = null,
|
||||
screen = screen
|
||||
)
|
||||
}
|
||||
|
||||
private fun setupWidgetsBannerView() {
|
||||
roomWidgetsBannerView.callback = this
|
||||
}
|
||||
|
@ -362,10 +373,7 @@ class RoomDetailFragment @Inject constructor(
|
|||
.setView(v)
|
||||
.setPositiveButton(R.string.yes) { _, _ ->
|
||||
// Open integration manager, to the sticker installation page
|
||||
navigator.openIntegrationManager(
|
||||
context = requireContext(),
|
||||
roomId = roomDetailArgs.roomId,
|
||||
integId = null,
|
||||
openIntegrationManager(
|
||||
screen = WidgetType.StickerPicker.preferred
|
||||
)
|
||||
}
|
||||
|
@ -508,11 +516,7 @@ class RoomDetailFragment @Inject constructor(
|
|||
true
|
||||
}
|
||||
R.id.open_matrix_apps -> {
|
||||
if (session.integrationManagerService().isIntegrationEnabled()) {
|
||||
navigator.openIntegrationManager(requireContext(), roomDetailArgs.roomId, null, null)
|
||||
} else {
|
||||
displayDisabledIntegrationDialog()
|
||||
}
|
||||
roomDetailViewModel.handle(RoomDetailAction.OpenIntegrationManager)
|
||||
true
|
||||
}
|
||||
R.id.voice_call,
|
||||
|
@ -645,16 +649,16 @@ class RoomDetailFragment @Inject constructor(
|
|||
val hasBeenHandled = attachmentsHelper.onActivityResult(requestCode, resultCode, data)
|
||||
if (!hasBeenHandled && resultCode == RESULT_OK && data != null) {
|
||||
when (requestCode) {
|
||||
AttachmentsPreviewActivity.REQUEST_CODE -> {
|
||||
AttachmentsPreviewActivity.REQUEST_CODE -> {
|
||||
val sendData = AttachmentsPreviewActivity.getOutput(data)
|
||||
val keepOriginalSize = AttachmentsPreviewActivity.getKeepOriginalSize(data)
|
||||
roomDetailViewModel.handle(RoomDetailAction.SendMedia(sendData, !keepOriginalSize))
|
||||
}
|
||||
REACTION_SELECT_REQUEST_CODE -> {
|
||||
REACTION_SELECT_REQUEST_CODE -> {
|
||||
val (eventId, reaction) = EmojiReactionPickerActivity.getOutput(data) ?: return
|
||||
roomDetailViewModel.handle(RoomDetailAction.SendReaction(eventId, reaction))
|
||||
}
|
||||
StickerPickerConstants.STICKER_PICKER_REQUEST_CODE -> {
|
||||
WidgetRequestCodes.STICKER_PICKER_REQUEST_CODE -> {
|
||||
val content = WidgetActivity.getOutput(data).toModel<MessageStickerContent>() ?: return
|
||||
roomDetailViewModel.handle(RoomDetailAction.SendSticker(content))
|
||||
}
|
||||
|
|
|
@ -52,8 +52,12 @@ sealed class RoomDetailViewEvents : VectorViewEvents {
|
|||
|
||||
object DisplayPromptForIntegrationManager: RoomDetailViewEvents()
|
||||
|
||||
object DisplayEnableIntegrationsWarning: RoomDetailViewEvents()
|
||||
|
||||
data class OpenStickerPicker(val widget: Widget): RoomDetailViewEvents()
|
||||
|
||||
object OpenIntegrationManager: RoomDetailViewEvents()
|
||||
|
||||
object MessageSent : SendMessageResult()
|
||||
data class JoinRoomCommandSuccess(val roomId: String) : SendMessageResult()
|
||||
class SlashCommandError(val command: Command) : SendMessageResult()
|
||||
|
|
|
@ -81,7 +81,9 @@ import io.reactivex.Observable
|
|||
import io.reactivex.functions.BiFunction
|
||||
import io.reactivex.rxkotlin.subscribeBy
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.commonmark.parser.Parser
|
||||
import org.commonmark.renderer.html.HtmlRenderer
|
||||
import timber.log.Timber
|
||||
|
@ -257,6 +259,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||
is RoomDetailAction.ResumeVerification -> handleResumeRequestVerification(action)
|
||||
is RoomDetailAction.ReRequestKeys -> handleReRequestKeys(action)
|
||||
is RoomDetailAction.SelectStickerAttachment -> handleSelectStickerAttachment()
|
||||
is RoomDetailAction.OpenIntegrationManager -> handleOpenIntegrationManager()
|
||||
is RoomDetailAction.StartCall -> handleStartCall(action)
|
||||
is RoomDetailAction.EndCall -> handleEndCall()
|
||||
}.exhaustive
|
||||
|
@ -283,6 +286,19 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleOpenIntegrationManager() {
|
||||
viewModelScope.launch {
|
||||
val viewEvent = withContext(Dispatchers.Default) {
|
||||
if (isIntegrationEnabled()) {
|
||||
RoomDetailViewEvents.OpenIntegrationManager
|
||||
} else {
|
||||
RoomDetailViewEvents.DisplayEnableIntegrationsWarning
|
||||
}
|
||||
}
|
||||
_viewEvents.post(viewEvent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startTrackingUnreadMessages() {
|
||||
trackUnreadMessages.set(true)
|
||||
setState { copy(canShowJumpToReadMarker = false) }
|
||||
|
@ -382,6 +398,8 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun isIntegrationEnabled() = session.integrationManagerService().isIntegrationEnabled()
|
||||
|
||||
fun isMenuItemVisible(@IdRes itemId: Int) = when (itemId) {
|
||||
R.id.clear_message_queue ->
|
||||
// For now always disable when not in developer mode, worker cancellation is not working properly
|
||||
|
|
|
@ -33,6 +33,7 @@ import com.airbnb.epoxy.EpoxyTouchHelperCallback
|
|||
import com.airbnb.epoxy.EpoxyViewHolder
|
||||
import timber.log.Timber
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.min
|
||||
|
||||
class RoomMessageTouchHelperCallback(private val context: Context,
|
||||
@DrawableRes actionIcon: Int,
|
||||
|
@ -92,7 +93,7 @@ class RoomMessageTouchHelperCallback(private val context: Context,
|
|||
setTouchListener(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
|
||||
}
|
||||
val size = triggerDistance
|
||||
if (Math.abs(viewHolder.itemView.translationX) < size || dX > this.dX /*going back*/) {
|
||||
if (abs(viewHolder.itemView.translationX) < size || dX > this.dX /*going back*/) {
|
||||
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
|
||||
this.dX = dX
|
||||
startTracking = true
|
||||
|
@ -127,9 +128,9 @@ class RoomMessageTouchHelperCallback(private val context: Context,
|
|||
|
||||
private fun drawReplyButton(canvas: Canvas, itemView: View) {
|
||||
// Timber.v("drawReplyButton")
|
||||
val translationX = Math.abs(itemView.translationX)
|
||||
val translationX = abs(itemView.translationX)
|
||||
val newTime = System.currentTimeMillis()
|
||||
val dt = Math.min(17, newTime - lastReplyButtonAnimationTime)
|
||||
val dt = min(17, newTime - lastReplyButtonAnimationTime)
|
||||
lastReplyButtonAnimationTime = newTime
|
||||
val showing = translationX >= minShowDistance
|
||||
if (showing) {
|
||||
|
@ -163,10 +164,10 @@ class RoomMessageTouchHelperCallback(private val context: Context,
|
|||
} else {
|
||||
1.2f - 0.2f * ((replyButtonProgress - 0.8f) / 0.2f)
|
||||
}
|
||||
alpha = Math.min(255f, 255 * (replyButtonProgress / 0.8f)).toInt()
|
||||
alpha = min(255f, 255 * (replyButtonProgress / 0.8f)).toInt()
|
||||
} else {
|
||||
scale = replyButtonProgress
|
||||
alpha = Math.min(255f, 255 * replyButtonProgress).toInt()
|
||||
alpha = min(255f, 255 * replyButtonProgress).toInt()
|
||||
}
|
||||
|
||||
imageDrawable.alpha = alpha
|
||||
|
|
|
@ -27,6 +27,10 @@ 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 integrationsEnabled = session.integrationManagerService().isIntegrationEnabled()
|
||||
if (!integrationsEnabled) {
|
||||
return@withContext RoomDetailViewEvents.DisplayEnableIntegrationsWarning
|
||||
}
|
||||
val stickerWidget = session.widgetService().getUserWidgets(WidgetType.StickerPicker.values()).firstOrNull { it.isActive }
|
||||
if (stickerWidget == null || stickerWidget.computedUrl.isNullOrBlank()) {
|
||||
RoomDetailViewEvents.DisplayPromptForIntegrationManager
|
||||
|
|
|
@ -64,8 +64,8 @@ class PollResultLineView @JvmOverloads constructor(
|
|||
set(value) {
|
||||
field = value
|
||||
// Text in main color
|
||||
labelTextView.setTypeface(labelTextView.getTypeface(), if (value) Typeface.BOLD else Typeface.NORMAL)
|
||||
percentTextView.setTypeface(percentTextView.getTypeface(), if (value) Typeface.BOLD else Typeface.NORMAL)
|
||||
labelTextView.setTypeface(labelTextView.typeface, if (value) Typeface.BOLD else Typeface.NORMAL)
|
||||
percentTextView.setTypeface(percentTextView.typeface, if (value) Typeface.BOLD else Typeface.NORMAL)
|
||||
}
|
||||
|
||||
init {
|
||||
|
|
|
@ -14,8 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.home.room.detail.sticker
|
||||
package im.vector.riotx.features.home.room.detail.widget
|
||||
|
||||
object StickerPickerConstants {
|
||||
object WidgetRequestCodes {
|
||||
const val STICKER_PICKER_REQUEST_CODE = 16000
|
||||
const val INTEGRATION_MANAGER_REQUEST_CODE = 16001
|
||||
}
|
|
@ -30,8 +30,8 @@ import androidx.fragment.app.Fragment
|
|||
import im.vector.matrix.android.api.session.crypto.verification.IncomingSasVerificationTransaction
|
||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
|
||||
import im.vector.matrix.android.api.session.terms.TermsService
|
||||
import im.vector.matrix.android.api.util.MatrixItem
|
||||
import im.vector.matrix.android.api.session.widgets.model.Widget
|
||||
import im.vector.matrix.android.api.util.MatrixItem
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.di.ActiveSessionHolder
|
||||
import im.vector.riotx.core.error.fatalError
|
||||
|
@ -46,7 +46,7 @@ import im.vector.riotx.features.crypto.verification.VerificationBottomSheet
|
|||
import im.vector.riotx.features.debug.DebugMenuActivity
|
||||
import im.vector.riotx.features.home.room.detail.RoomDetailActivity
|
||||
import im.vector.riotx.features.home.room.detail.RoomDetailArgs
|
||||
import im.vector.riotx.features.home.room.detail.sticker.StickerPickerConstants
|
||||
import im.vector.riotx.features.home.room.detail.widget.WidgetRequestCodes
|
||||
import im.vector.riotx.features.home.room.filtered.FilteredRoomsActivity
|
||||
import im.vector.riotx.features.invite.InviteUsersToRoomActivity
|
||||
import im.vector.riotx.features.media.BigImageViewerActivity
|
||||
|
@ -230,12 +230,13 @@ class DefaultNavigator @Inject constructor(
|
|||
override fun openStickerPicker(fragment: Fragment, roomId: String, widget: Widget, requestCode: Int) {
|
||||
val widgetArgs = widgetArgsBuilder.buildStickerPickerArgs(roomId, widget)
|
||||
val intent = WidgetActivity.newIntent(fragment.requireContext(), widgetArgs)
|
||||
fragment.startActivityForResult(intent, StickerPickerConstants.STICKER_PICKER_REQUEST_CODE)
|
||||
fragment.startActivityForResult(intent, WidgetRequestCodes.STICKER_PICKER_REQUEST_CODE)
|
||||
}
|
||||
|
||||
override fun openIntegrationManager(context: Context, roomId: String, integId: String?, screen: String?) {
|
||||
override fun openIntegrationManager(fragment: Fragment, roomId: String, integId: String?, screen: String?) {
|
||||
val widgetArgs = widgetArgsBuilder.buildIntegrationManagerArgs(roomId, integId, screen)
|
||||
context.startActivity(WidgetActivity.newIntent(context, widgetArgs))
|
||||
val intent = WidgetActivity.newIntent(fragment.requireContext(), widgetArgs)
|
||||
fragment.startActivityForResult(intent, WidgetRequestCodes.INTEGRATION_MANAGER_REQUEST_CODE)
|
||||
}
|
||||
|
||||
override fun openRoomWidget(context: Context, roomId: String, widget: Widget) {
|
||||
|
|
|
@ -25,7 +25,7 @@ import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
|
|||
import im.vector.matrix.android.api.session.terms.TermsService
|
||||
import im.vector.matrix.android.api.util.MatrixItem
|
||||
import im.vector.matrix.android.api.session.widgets.model.Widget
|
||||
import im.vector.riotx.features.home.room.detail.sticker.StickerPickerConstants
|
||||
import im.vector.riotx.features.home.room.detail.widget.WidgetRequestCodes
|
||||
import im.vector.riotx.features.media.ImageContentRenderer
|
||||
import im.vector.riotx.features.media.VideoContentRenderer
|
||||
import im.vector.riotx.features.settings.VectorSettingsActivity
|
||||
|
@ -85,9 +85,9 @@ interface Navigator {
|
|||
fun openStickerPicker(fragment: Fragment,
|
||||
roomId: String,
|
||||
widget: Widget,
|
||||
requestCode: Int = StickerPickerConstants.STICKER_PICKER_REQUEST_CODE)
|
||||
requestCode: Int = WidgetRequestCodes.STICKER_PICKER_REQUEST_CODE)
|
||||
|
||||
fun openIntegrationManager(context: Context, roomId: String, integId: String?, screen: String?)
|
||||
fun openIntegrationManager(fragment: Fragment, roomId: String, integId: String?, screen: String?)
|
||||
|
||||
fun openRoomWidget(context: Context, roomId: String, widget: Widget)
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@ import android.graphics.Paint
|
|||
import android.util.AttributeSet
|
||||
import android.util.Property
|
||||
import android.view.View
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
/**
|
||||
* This view will draw dots floating around the center of it's view
|
||||
|
@ -84,16 +86,16 @@ class DotsView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|||
|
||||
private fun drawOuterDotsFrame(canvas: Canvas) {
|
||||
for (i in 0 until DOTS_COUNT) {
|
||||
val cX = (centerX + currentRadius1 * Math.cos(i.toDouble() * OUTER_DOTS_POSITION_ANGLE.toDouble() * Math.PI / 180)).toFloat()
|
||||
val cY = (centerY + currentRadius1 * Math.sin(i.toDouble() * OUTER_DOTS_POSITION_ANGLE.toDouble() * Math.PI / 180)).toFloat()
|
||||
val cX = (centerX + currentRadius1 * cos(i.toDouble() * OUTER_DOTS_POSITION_ANGLE.toDouble() * Math.PI / 180)).toFloat()
|
||||
val cY = (centerY + currentRadius1 * sin(i.toDouble() * OUTER_DOTS_POSITION_ANGLE.toDouble() * Math.PI / 180)).toFloat()
|
||||
canvas.drawCircle(cX, cY, currentDotSize1, circlePaints[i % circlePaints.size])
|
||||
}
|
||||
}
|
||||
|
||||
private fun drawInnerDotsFrame(canvas: Canvas) {
|
||||
for (i in 0 until DOTS_COUNT) {
|
||||
val cX = (centerX + currentRadius2 * Math.cos((i * OUTER_DOTS_POSITION_ANGLE - 10) * Math.PI / 180)).toFloat()
|
||||
val cY = (centerY + currentRadius2 * Math.sin((i * OUTER_DOTS_POSITION_ANGLE - 10) * Math.PI / 180)).toFloat()
|
||||
val cX = (centerX + currentRadius2 * cos((i * OUTER_DOTS_POSITION_ANGLE - 10) * Math.PI / 180)).toFloat()
|
||||
val cY = (centerY + currentRadius2 * sin((i * OUTER_DOTS_POSITION_ANGLE - 10) * Math.PI / 180)).toFloat()
|
||||
canvas.drawCircle(cX, cY, currentDotSize2, circlePaints[(i + 1) % circlePaints.size])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -337,7 +337,7 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor(
|
|||
* @param aMyDeviceInfo the device info
|
||||
*/
|
||||
private fun refreshCryptographyPreference(devices: List<DeviceInfo>) {
|
||||
showDeviceListPref.isEnabled = devices.size > 0
|
||||
showDeviceListPref.isEnabled = devices.isNotEmpty()
|
||||
showDeviceListPref.summary = resources.getQuantityString(R.plurals.settings_active_sessions_count, devices.size, devices.size)
|
||||
// val userId = session.myUserId
|
||||
// val deviceId = session.sessionParams.deviceId
|
||||
|
|
|
@ -139,7 +139,7 @@ class ReviewTermsViewModel @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
} catch (failure: Throwable) {
|
||||
Timber.e(failure, "Failed to agree to terms")
|
||||
Timber.e(failure, "Failed to load terms")
|
||||
setState {
|
||||
copy(
|
||||
termsList = Uninitialized
|
||||
|
|
|
@ -40,6 +40,7 @@ import im.vector.riotx.R
|
|||
import im.vector.riotx.core.platform.OnBackPressed
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import im.vector.riotx.core.utils.openUrlInExternalBrowser
|
||||
import im.vector.riotx.features.home.room.detail.widget.WidgetRequestCodes
|
||||
import im.vector.riotx.features.terms.ReviewTermsActivity
|
||||
import im.vector.riotx.features.webview.WebViewEventListener
|
||||
import im.vector.riotx.features.widgets.webview.clearAfterWidget
|
||||
|
@ -77,7 +78,7 @@ class WidgetFragment @Inject constructor() : VectorBaseFragment(), WebViewEventL
|
|||
Timber.v("Observed view events: $it")
|
||||
when (it) {
|
||||
is WidgetViewEvents.DisplayTerms -> displayTerms(it)
|
||||
is WidgetViewEvents.LoadFormattedURL -> loadFormattedUrl(it)
|
||||
is WidgetViewEvents.OnURLFormatted -> loadFormattedUrl(it)
|
||||
is WidgetViewEvents.DisplayIntegrationManager -> displayIntegrationManager(it)
|
||||
is WidgetViewEvents.Failure -> displayErrorDialog(it.throwable)
|
||||
}
|
||||
|
@ -86,11 +87,17 @@ class WidgetFragment @Inject constructor() : VectorBaseFragment(), WebViewEventL
|
|||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (requestCode == ReviewTermsActivity.TERMS_REQUEST_CODE) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
viewModel.handle(WidgetAction.OnTermsReviewed)
|
||||
} else {
|
||||
vectorBaseActivity.finish()
|
||||
when (requestCode) {
|
||||
ReviewTermsActivity.TERMS_REQUEST_CODE -> {
|
||||
Timber.v("On terms results")
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
viewModel.handle(WidgetAction.OnTermsReviewed)
|
||||
} else {
|
||||
vectorBaseActivity.finish()
|
||||
}
|
||||
}
|
||||
WidgetRequestCodes.INTEGRATION_MANAGER_REQUEST_CODE -> {
|
||||
viewModel.handle(WidgetAction.LoadFormattedUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -139,7 +146,7 @@ class WidgetFragment @Inject constructor() : VectorBaseFragment(), WebViewEventL
|
|||
override fun onOptionsItemSelected(item: MenuItem): Boolean = withState(viewModel) { state ->
|
||||
when (item.itemId) {
|
||||
R.id.action_edit -> {
|
||||
navigator.openIntegrationManager(requireContext(), state.roomId, state.widgetId, state.widgetKind.screenId)
|
||||
navigator.openIntegrationManager(this, state.roomId, state.widgetId, state.widgetKind.screenId)
|
||||
return@withState true
|
||||
}
|
||||
R.id.action_delete -> {
|
||||
|
@ -261,9 +268,9 @@ class WidgetFragment @Inject constructor() : VectorBaseFragment(), WebViewEventL
|
|||
)
|
||||
}
|
||||
|
||||
private fun loadFormattedUrl(loadFormattedUrl: WidgetViewEvents.LoadFormattedURL) {
|
||||
private fun loadFormattedUrl(event: WidgetViewEvents.OnURLFormatted) {
|
||||
widgetWebView.clearHistory()
|
||||
widgetWebView.loadUrl(loadFormattedUrl.formattedURL)
|
||||
widgetWebView.loadUrl(event.formattedURL)
|
||||
}
|
||||
|
||||
private fun setStateError(message: String?) {
|
||||
|
@ -280,7 +287,7 @@ class WidgetFragment @Inject constructor() : VectorBaseFragment(), WebViewEventL
|
|||
|
||||
private fun displayIntegrationManager(event: WidgetViewEvents.DisplayIntegrationManager) {
|
||||
navigator.openIntegrationManager(
|
||||
context = vectorBaseActivity,
|
||||
fragment = this,
|
||||
roomId = fragmentArgs.roomId,
|
||||
integId = event.integId,
|
||||
screen = event.integType
|
||||
|
|
|
@ -39,13 +39,12 @@ import java.util.ArrayList
|
|||
import java.util.HashMap
|
||||
|
||||
class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roomId: String,
|
||||
@Assisted private val navigationCallback: NavigationCallback,
|
||||
private val stringProvider: StringProvider,
|
||||
private val session: Session) : WidgetPostAPIMediator.Handler {
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
fun create(roomId: String, navigationCallback: NavigationCallback): WidgetPostAPIHandler
|
||||
fun create(roomId: String): WidgetPostAPIHandler
|
||||
}
|
||||
|
||||
interface NavigationCallback {
|
||||
|
@ -54,31 +53,31 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
|||
fun openIntegrationManager(integId: String?, integType: String?)
|
||||
}
|
||||
|
||||
private val widgetPostAPIMediator = session.widgetService().getWidgetPostAPIMediator()
|
||||
private val room = session.getRoom(roomId)!!
|
||||
var navigationCallback: NavigationCallback? = null
|
||||
|
||||
override fun handleWidgetRequest(eventData: JsonDict): Boolean {
|
||||
override fun handleWidgetRequest(mediator: WidgetPostAPIMediator, eventData: JsonDict): Boolean {
|
||||
return when (eventData["action"] as String?) {
|
||||
"integration_manager_open" -> handleIntegrationManagerOpenAction(eventData).run { true }
|
||||
"bot_options" -> getBotOptions(eventData).run { true }
|
||||
"can_send_event" -> canSendEvent(eventData).run { true }
|
||||
"bot_options" -> getBotOptions(mediator, eventData).run { true }
|
||||
"can_send_event" -> canSendEvent(mediator, eventData).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 }
|
||||
"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 }
|
||||
"m.sticker" -> pickStickerData(eventData).run { true }
|
||||
"get_membership_count" -> getMembershipCount(mediator, eventData).run { true }
|
||||
"get_widgets" -> getWidgets(mediator, eventData).run { true }
|
||||
"invite" -> inviteUser(mediator, eventData).run { true }
|
||||
"join_rules_state" -> getJoinRules(mediator, eventData).run { true }
|
||||
"membership_state" -> getMembershipState(mediator, eventData).run { true }
|
||||
"set_bot_options" -> setBotOptions(mediator, eventData).run { true }
|
||||
"set_bot_power" -> setBotPower(mediator, eventData).run { true }
|
||||
"set_plumbing_state" -> setPlumbingState(mediator, eventData).run { true }
|
||||
"set_widget" -> setWidget(mediator, eventData).run { true }
|
||||
"m.sticker" -> pickStickerData(mediator, eventData).run { true }
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCloseScalar() {
|
||||
navigationCallback.close()
|
||||
navigationCallback?.close()
|
||||
}
|
||||
|
||||
private fun handleIntegrationManagerOpenAction(eventData: JsonDict) {
|
||||
|
@ -101,7 +100,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
|||
// Add "type_" as a prefix
|
||||
integType?.let { integType = "type_$integType" }
|
||||
}
|
||||
navigationCallback.openIntegrationManager(integId, integType)
|
||||
navigationCallback?.openIntegrationManager(integId, integType)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -109,8 +108,8 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
|||
*
|
||||
* @param eventData the modular data
|
||||
*/
|
||||
private fun getBotOptions(eventData: JsonDict) {
|
||||
if (checkRoomId(eventData) || checkUserId(eventData)) {
|
||||
private fun getBotOptions(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict) {
|
||||
if (checkRoomId(widgetPostAPIMediator, eventData) || checkUserId(widgetPostAPIMediator, eventData)) {
|
||||
return
|
||||
}
|
||||
val userId = eventData["user_id"] as String
|
||||
|
@ -134,8 +133,8 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
|||
}
|
||||
}
|
||||
|
||||
private fun canSendEvent(eventData: JsonDict) {
|
||||
if (checkRoomId(eventData)) {
|
||||
private fun canSendEvent(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict) {
|
||||
if (checkRoomId(widgetPostAPIMediator, eventData)) {
|
||||
return
|
||||
}
|
||||
Timber.d("Received request canSendEvent in room $roomId")
|
||||
|
@ -170,8 +169,8 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
|||
*
|
||||
* @param eventData the modular data
|
||||
*/
|
||||
private fun getMembershipState(eventData: JsonDict) {
|
||||
if (checkRoomId(eventData) || checkUserId(eventData)) {
|
||||
private fun getMembershipState(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict) {
|
||||
if (checkRoomId(widgetPostAPIMediator, eventData) || checkUserId(widgetPostAPIMediator, eventData)) {
|
||||
return
|
||||
}
|
||||
val userId = eventData["user_id"] as String
|
||||
|
@ -189,8 +188,8 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
|||
*
|
||||
* @param eventData the modular data
|
||||
*/
|
||||
private fun getJoinRules(eventData: JsonDict) {
|
||||
if (checkRoomId(eventData)) {
|
||||
private fun getJoinRules(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict) {
|
||||
if (checkRoomId(widgetPostAPIMediator, eventData)) {
|
||||
return
|
||||
}
|
||||
Timber.d("Received request join rules in room $roomId")
|
||||
|
@ -207,8 +206,8 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
|||
*
|
||||
* @param eventData the modular data
|
||||
*/
|
||||
private fun getWidgets(eventData: JsonDict) {
|
||||
if (checkRoomId(eventData)) {
|
||||
private fun getWidgets(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict) {
|
||||
if (checkRoomId(widgetPostAPIMediator, eventData)) {
|
||||
return
|
||||
}
|
||||
Timber.d("Received request to get widget in room $roomId")
|
||||
|
@ -227,12 +226,12 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
|||
*
|
||||
* @param eventData the modular data
|
||||
*/
|
||||
private fun setWidget(eventData: JsonDict) {
|
||||
private fun setWidget(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict) {
|
||||
val userWidget = eventData["userWidget"] as Boolean?
|
||||
if (userWidget == true) {
|
||||
Timber.d("Received request to set widget for user")
|
||||
} else {
|
||||
if (checkRoomId(eventData)) {
|
||||
if (checkRoomId(widgetPostAPIMediator, eventData)) {
|
||||
return
|
||||
}
|
||||
Timber.d("Received request to set widget in room $roomId")
|
||||
|
@ -283,14 +282,14 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
|||
session.updateAccountData(
|
||||
type = UserAccountData.TYPE_WIDGETS,
|
||||
content = addUserWidgetBody,
|
||||
callback = createWidgetAPICallback(eventData)
|
||||
callback = createWidgetAPICallback(widgetPostAPIMediator, eventData)
|
||||
)
|
||||
} else {
|
||||
session.widgetService().createRoomWidget(
|
||||
roomId = roomId,
|
||||
widgetId = widgetId,
|
||||
content = widgetEventContent,
|
||||
callback = createWidgetAPICallback(eventData)
|
||||
callback = createWidgetAPICallback(widgetPostAPIMediator, eventData)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -300,8 +299,8 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
|||
*
|
||||
* @param eventData the modular data
|
||||
*/
|
||||
private fun setPlumbingState(eventData: JsonDict) {
|
||||
if (checkRoomId(eventData)) {
|
||||
private fun setPlumbingState(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict) {
|
||||
if (checkRoomId(widgetPostAPIMediator, eventData)) {
|
||||
return
|
||||
}
|
||||
val description = "Received request to set plumbing state to status " + eventData["status"] + " in room " + roomId + " requested"
|
||||
|
@ -315,7 +314,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
|||
eventType = EventType.PLUMBING,
|
||||
stateKey = null,
|
||||
body = params,
|
||||
callback = createWidgetAPICallback(eventData)
|
||||
callback = createWidgetAPICallback(widgetPostAPIMediator, eventData)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -325,8 +324,8 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
|||
* @param eventData the modular data
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun setBotOptions(eventData: JsonDict) {
|
||||
if (checkRoomId(eventData) || checkUserId(eventData)) {
|
||||
private fun setBotOptions(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict) {
|
||||
if (checkRoomId(widgetPostAPIMediator, eventData) || checkUserId(widgetPostAPIMediator, eventData)) {
|
||||
return
|
||||
}
|
||||
val userId = eventData["user_id"] as String
|
||||
|
@ -338,7 +337,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
|||
eventType = EventType.BOT_OPTIONS,
|
||||
stateKey = stateKey,
|
||||
body = content,
|
||||
callback = createWidgetAPICallback(eventData)
|
||||
callback = createWidgetAPICallback(widgetPostAPIMediator, eventData)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -347,8 +346,8 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
|||
*
|
||||
* @param eventData the modular data
|
||||
*/
|
||||
private fun setBotPower(eventData: JsonDict) {
|
||||
if (checkRoomId(eventData) || checkUserId(eventData)) {
|
||||
private fun setBotPower(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict) {
|
||||
if (checkRoomId(widgetPostAPIMediator, eventData) || checkUserId(widgetPostAPIMediator, eventData)) {
|
||||
return
|
||||
}
|
||||
val userId = eventData["user_id"] as String
|
||||
|
@ -369,8 +368,8 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
|||
*
|
||||
* @param eventData the modular data
|
||||
*/
|
||||
private fun inviteUser(eventData: JsonDict) {
|
||||
if (checkRoomId(eventData) || checkUserId(eventData)) {
|
||||
private fun inviteUser(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict) {
|
||||
if (checkRoomId(widgetPostAPIMediator, eventData) || checkUserId(widgetPostAPIMediator, eventData)) {
|
||||
return
|
||||
}
|
||||
val userId = eventData["user_id"] as String
|
||||
|
@ -380,7 +379,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
|||
if (member != null && member.membership == Membership.JOIN) {
|
||||
widgetPostAPIMediator.sendSuccess(eventData)
|
||||
} else {
|
||||
room.invite(userId = userId, callback = createWidgetAPICallback(eventData))
|
||||
room.invite(userId = userId, callback = createWidgetAPICallback(widgetPostAPIMediator, eventData))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -389,8 +388,8 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
|||
*
|
||||
* @param eventData the modular data
|
||||
*/
|
||||
private fun getMembershipCount(eventData: JsonDict) {
|
||||
if (checkRoomId(eventData)) {
|
||||
private fun getMembershipCount(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict) {
|
||||
if (checkRoomId(widgetPostAPIMediator, eventData)) {
|
||||
return
|
||||
}
|
||||
val numberOfJoinedMembers = room.getNumberOfJoinedMembers()
|
||||
|
@ -398,7 +397,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
|||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun pickStickerData(eventData: JsonDict) {
|
||||
private fun pickStickerData(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict) {
|
||||
Timber.d("Received request send sticker")
|
||||
val data = eventData["data"]
|
||||
if (data == null) {
|
||||
|
@ -411,7 +410,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
|||
return
|
||||
}
|
||||
widgetPostAPIMediator.sendSuccess(eventData)
|
||||
navigationCallback.closeWithResult(content)
|
||||
navigationCallback?.closeWithResult(content)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -420,7 +419,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
|||
*
|
||||
* @return true in case of error
|
||||
*/
|
||||
private fun checkRoomId(eventData: JsonDict): Boolean {
|
||||
private fun checkRoomId(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict): Boolean {
|
||||
val roomIdInEvent = eventData["room_id"] as String?
|
||||
// Check if param is present
|
||||
if (null == roomIdInEvent) {
|
||||
|
@ -443,7 +442,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
|||
*
|
||||
* @return true in case of error
|
||||
*/
|
||||
private fun checkUserId(eventData: JsonDict): Boolean {
|
||||
private fun checkUserId(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict): Boolean {
|
||||
val userIdInEvent = eventData["user_id"] as String?
|
||||
// Check if param is present
|
||||
if (null == userIdInEvent) {
|
||||
|
@ -454,7 +453,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
|
|||
return false
|
||||
}
|
||||
|
||||
private fun createWidgetAPICallback(eventData: JsonDict): WidgetAPICallback {
|
||||
private fun createWidgetAPICallback(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict): WidgetAPICallback {
|
||||
return WidgetAPICallback(widgetPostAPIMediator, eventData, stringProvider)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,6 @@ sealed class WidgetViewEvents : VectorViewEvents {
|
|||
data class Failure(val throwable: Throwable): WidgetViewEvents()
|
||||
data class Close(val content: Content? = null) : WidgetViewEvents()
|
||||
data class DisplayIntegrationManager(val integId: String?, val integType: String?) : WidgetViewEvents()
|
||||
data class LoadFormattedURL(val formattedURL: String) : WidgetViewEvents()
|
||||
data class OnURLFormatted(val formattedURL: String) : WidgetViewEvents()
|
||||
data class DisplayTerms(val url: String, val token: String) : WidgetViewEvents()
|
||||
}
|
||||
|
|
|
@ -76,13 +76,22 @@ class WidgetViewModel @AssistedInject constructor(@Assisted val initialState: Wi
|
|||
private val integrationManagerService = session.integrationManagerService()
|
||||
private val widgetURLFormatter = widgetService.getWidgetURLFormatter()
|
||||
private val postAPIMediator = widgetService.getWidgetPostAPIMediator()
|
||||
private var widgetPostAPIHandler: WidgetPostAPIHandler? = null
|
||||
|
||||
// Flag to avoid infinite loop
|
||||
private var canRefreshToken = true
|
||||
|
||||
init {
|
||||
integrationManagerService.addListener(this)
|
||||
if (initialState.widgetKind.isAdmin()) {
|
||||
val widgetPostAPIHandler = widgetPostAPIHandlerFactory.create(initialState.roomId, this)
|
||||
widgetPostAPIHandler = widgetPostAPIHandlerFactory.create(initialState.roomId).apply {
|
||||
navigationCallback = this@WidgetViewModel
|
||||
}
|
||||
postAPIMediator.setHandler(widgetPostAPIHandler)
|
||||
}
|
||||
if (!integrationManagerService.isIntegrationEnabled()) {
|
||||
_viewEvents.post(WidgetViewEvents.Close(null))
|
||||
}
|
||||
setupName()
|
||||
refreshPermissionStatus()
|
||||
observePowerLevel()
|
||||
|
@ -139,10 +148,10 @@ class WidgetViewModel @AssistedInject constructor(@Assisted val initialState: Wi
|
|||
is WidgetAction.OnWebViewLoadingError -> handleWebViewLoadingError(action)
|
||||
is WidgetAction.OnWebViewLoadingSuccess -> handleWebViewLoadingSuccess(action)
|
||||
is WidgetAction.OnWebViewStartedToLoad -> handleWebViewStartLoading()
|
||||
WidgetAction.LoadFormattedUrl -> loadFormattedUrl()
|
||||
WidgetAction.LoadFormattedUrl -> loadFormattedUrl(forceFetchToken = false)
|
||||
WidgetAction.DeleteWidget -> handleDeleteWidget()
|
||||
WidgetAction.RevokeWidget -> handleRevokeWidget()
|
||||
WidgetAction.OnTermsReviewed -> refreshPermissionStatus()
|
||||
WidgetAction.OnTermsReviewed -> loadFormattedUrl(forceFetchToken = false)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,10 +233,10 @@ class WidgetViewModel @AssistedInject constructor(@Assisted val initialState: Wi
|
|||
)
|
||||
setState { copy(formattedURL = Success(formattedUrl)) }
|
||||
Timber.v("Post load formatted url event: $formattedUrl")
|
||||
_viewEvents.post(WidgetViewEvents.LoadFormattedURL(formattedUrl))
|
||||
_viewEvents.post(WidgetViewEvents.OnURLFormatted(formattedUrl))
|
||||
} catch (failure: Throwable) {
|
||||
if (failure is WidgetManagementFailure.TermsNotSignedException) {
|
||||
_viewEvents.post(WidgetViewEvents.DisplayTerms(failure.baseUrl, failure.token))
|
||||
_viewEvents.post(WidgetViewEvents.DisplayTerms(initialState.baseUrl, failure.token))
|
||||
}
|
||||
setState { copy(formattedURL = Fail(failure)) }
|
||||
}
|
||||
|
@ -251,7 +260,8 @@ class WidgetViewModel @AssistedInject constructor(@Assisted val initialState: Wi
|
|||
}
|
||||
if (action.isHttpError) {
|
||||
// In case of 403, try to refresh the scalar token
|
||||
if (it.formattedURL is Success && action.errorCode == HttpsURLConnection.HTTP_FORBIDDEN) {
|
||||
if (it.formattedURL is Success && action.errorCode == HttpsURLConnection.HTTP_FORBIDDEN && canRefreshToken) {
|
||||
canRefreshToken = false
|
||||
loadFormattedUrl(true)
|
||||
}
|
||||
} else {
|
||||
|
@ -261,17 +271,24 @@ class WidgetViewModel @AssistedInject constructor(@Assisted val initialState: Wi
|
|||
|
||||
override fun onCleared() {
|
||||
integrationManagerService.removeListener(this)
|
||||
widgetPostAPIHandler?.navigationCallback = null
|
||||
postAPIMediator.setHandler(null)
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
// IntegrationManagerService.Listener
|
||||
// IntegrationManagerService.Listener
|
||||
|
||||
override fun onWidgetPermissionsChanged(widgets: Map<String, Boolean>) {
|
||||
refreshPermissionStatus()
|
||||
}
|
||||
|
||||
// WidgetPostAPIHandler.NavigationCallback
|
||||
override fun onIsEnabledChanged(enabled: Boolean) {
|
||||
if (!enabled) {
|
||||
_viewEvents.post(WidgetViewEvents.Close(null))
|
||||
}
|
||||
}
|
||||
|
||||
// WidgetPostAPIHandler.NavigationCallback
|
||||
|
||||
override fun close() {
|
||||
_viewEvents.post(WidgetViewEvents.Close(null))
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package im.vector.fragments.roomwidgets
|
||||
package im.vector.riotx.features.widgets.webview
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
|
|
|
@ -24,7 +24,6 @@ import android.webkit.PermissionRequest
|
|||
import android.webkit.WebChromeClient
|
||||
import android.webkit.WebSettings
|
||||
import android.webkit.WebView
|
||||
import im.vector.fragments.roomwidgets.WebviewPermissionUtils
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.features.themes.ThemeUtils
|
||||
import im.vector.riotx.features.webview.VectorWebViewClient
|
||||
|
@ -81,22 +80,10 @@ fun WebView.clearAfterWidget() {
|
|||
webChromeClient = null
|
||||
webViewClient = null
|
||||
clearHistory()
|
||||
|
||||
// NOTE: clears RAM cache, if you pass true, it will also clear the disk cache.
|
||||
clearCache(true)
|
||||
|
||||
// Loading a blank page is optional, but will ensure that the WebView isn't doing anything when you destroy it.
|
||||
loadUrl("about:blank")
|
||||
|
||||
onPause()
|
||||
removeAllViews()
|
||||
|
||||
// NOTE: This pauses JavaScript execution for ALL WebViews,
|
||||
// do not use if you have other WebViews still alive.
|
||||
// If you create another WebView after calling this,
|
||||
// make sure to call mWebView.resumeTimers().
|
||||
pauseTimers()
|
||||
|
||||
// NOTE: This can occasionally cause a segfault below API 17 (4.2)
|
||||
destroy()
|
||||
}
|
||||
|
|
|
@ -1181,6 +1181,7 @@
|
|||
<string name="room_widget_webview_read_protected_media">Read DRM protected Media</string>
|
||||
|
||||
<!-- Widget Integration Manager -->
|
||||
|
||||
<string name="widget_integration_unable_to_create">Unable to create widget.</string>
|
||||
<string name="widget_integration_failed_to_send_request">Failed to send request.</string>
|
||||
<string name="widget_integration_positive_power_level">Power level must be positive integer.</string>
|
||||
|
|
Loading…
Reference in a new issue