Merge branch 'develop' into feature/fix_1543

This commit is contained in:
Benoit Marty 2020-06-24 21:24:25 +02:00 committed by GitHub
commit c0c300925d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 229 additions and 179 deletions

View File

@ -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)
===================================================

View File

@ -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)
}

View File

@ -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))

View File

@ -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()

View File

@ -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"
}

View File

@ -90,6 +90,6 @@ interface WidgetPostAPIMediator {
/**
* Triggered when a widget is posting
*/
fun handleWidgetRequest(eventData: JsonDict): Boolean
fun handleWidgetRequest(mediator: WidgetPostAPIMediator, eventData: JsonDict): Boolean
}
}

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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"*}"""
}
/**

View File

@ -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) {

View File

@ -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(

View File

@ -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 {

View File

@ -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 + " = ?"

View File

@ -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)
}
}

View File

@ -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")
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -78,4 +78,5 @@ sealed class RoomDetailAction : VectorViewModelAction {
data class ReRequestKeys(val eventId: String) : RoomDetailAction()
object SelectStickerAttachment : RoomDetailAction()
object OpenIntegrationManager: RoomDetailAction()
}

View File

@ -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))
}

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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
}

View File

@ -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) {

View File

@ -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)

View File

@ -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])
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)
}
}

View File

@ -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()
}

View File

@ -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))

View File

@ -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

View File

@ -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()
}

View File

@ -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>