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