Merge branch 'release/1.0.14'

This commit is contained in:
Benoit Marty 2021-01-15 11:13:49 +01:00
commit 7c013de7b9
546 changed files with 12123 additions and 5498 deletions

View file

@ -1,3 +1,37 @@
Changes in Element 1.0.14 (2020-XX-XX)
===================================================
Features ✨:
- Enable url previews for notices (#2562)
- Edit room permissions (#2471)
Improvements 🙌:
- Add System theme option and set as default (#904, #2387)
- Store megolm outbound session to improve send time of first message after app launch.
- Warn user when they are leaving a not public room (#1460)
- Option to disable emoji keyboard (#2563)
Bugfix 🐛:
- Unspecced msgType field in m.sticker (#2580)
- Wait for all room members to be known before sending a message to a e2e room (#2518)
- Url previews sometimes attached to wrong message (#2561)
- Room Topic not displayed correctly after visiting a link (#2551)
- Hiding membership events works the exact opposite (#2603)
- Tapping drawer having more than 1 room in notifications gives "malformed link" error (#2605)
- Sent image not displayed when opened immediately after sending (#409)
- Initial sync is not retried correctly when there is some network error. (#2632)
- Fix switch theme issue, and white field issue (#2599, #2528)
- Fix request too large Uri error when joining a room
Translations 🗣:
- New language supported: Hebrew
Build 🧱:
- Remove dependency to org.greenrobot.eventbus library
Other changes:
- Migrate to ViewBindings (#1072)
Changes in Element 1.0.13 (2020-12-18) Changes in Element 1.0.13 (2020-12-18)
=================================================== ===================================================

View file

@ -16,7 +16,6 @@
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
buildscript { buildscript {
repositories { repositories {
@ -55,6 +54,10 @@ android {
kotlinOptions { kotlinOptions {
jvmTarget = '1.8' jvmTarget = '1.8'
} }
buildFeatures {
viewBinding true
}
} }
dependencies { dependencies {

View file

@ -17,19 +17,17 @@
package im.vector.lib.attachmentviewer package im.vector.lib.attachmentviewer
import android.view.View import android.view.View
import android.widget.ImageView import im.vector.lib.attachmentviewer.databinding.ItemAnimatedImageAttachmentBinding
import android.widget.ProgressBar
class AnimatedImageViewHolder constructor(itemView: View) : class AnimatedImageViewHolder constructor(itemView: View) :
BaseViewHolder(itemView) { BaseViewHolder(itemView) {
val touchImageView: ImageView = itemView.findViewById(R.id.imageView) val views = ItemAnimatedImageAttachmentBinding.bind(itemView)
val imageLoaderProgress: ProgressBar = itemView.findViewById(R.id.imageLoaderProgress)
internal val target = DefaultImageLoaderTarget(this, this.touchImageView) internal val target = DefaultImageLoaderTarget(this, views.imageView)
override fun onRecycled() { override fun onRecycled() {
super.onRecycled() super.onRecycled()
touchImageView.setImageDrawable(null) views.imageView.setImageDrawable(null)
} }
} }

View file

@ -33,43 +33,51 @@ import androidx.core.view.isVisible
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.transition.TransitionManager import androidx.transition.TransitionManager
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import kotlinx.android.synthetic.main.activity_attachment_viewer.* import im.vector.lib.attachmentviewer.databinding.ActivityAttachmentViewerBinding
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import kotlin.math.abs import kotlin.math.abs
abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventListener { abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventListener {
lateinit var pager2: ViewPager2 protected val pager2: ViewPager2
lateinit var imageTransitionView: ImageView get() = views.attachmentPager
lateinit var transitionImageContainer: ViewGroup protected val imageTransitionView: ImageView
get() = views.transitionImageView
protected val transitionImageContainer: ViewGroup
get() = views.transitionImageContainer
var topInset = 0 private var topInset = 0
var bottomInset = 0 private var bottomInset = 0
var systemUiVisibility = true private var systemUiVisibility = true
private var overlayView: View? = null private var overlayView: View? = null
set(value) { set(value) {
if (value == overlayView) return if (value == overlayView) return
overlayView?.let { rootContainer.removeView(it) } overlayView?.let { views.rootContainer.removeView(it) }
rootContainer.addView(value) views.rootContainer.addView(value)
value?.updatePadding(top = topInset, bottom = bottomInset) value?.updatePadding(top = topInset, bottom = bottomInset)
field = value field = value
} }
private lateinit var views: ActivityAttachmentViewerBinding
private lateinit var swipeDismissHandler: SwipeToDismissHandler private lateinit var swipeDismissHandler: SwipeToDismissHandler
private lateinit var directionDetector: SwipeDirectionDetector private lateinit var directionDetector: SwipeDirectionDetector
private lateinit var scaleDetector: ScaleGestureDetector private lateinit var scaleDetector: ScaleGestureDetector
private lateinit var gestureDetector: GestureDetectorCompat private lateinit var gestureDetector: GestureDetectorCompat
var currentPosition = 0 var currentPosition = 0
private set
private var swipeDirection: SwipeDirection? = null private var swipeDirection: SwipeDirection? = null
private fun isScaled() = attachmentsAdapter.isScaled(currentPosition) private fun isScaled() = attachmentsAdapter.isScaled(currentPosition)
private val attachmentsAdapter = AttachmentsAdapter()
private var wasScaled: Boolean = false private var wasScaled: Boolean = false
private var isSwipeToDismissAllowed: Boolean = true private var isSwipeToDismissAllowed: Boolean = true
private lateinit var attachmentsAdapter: AttachmentsAdapter
private var isOverlayWasClicked = false private var isOverlayWasClicked = false
// private val shouldDismissToBottom: Boolean // private val shouldDismissToBottom: Boolean
@ -95,17 +103,14 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
setContentView(R.layout.activity_attachment_viewer) views = ActivityAttachmentViewerBinding.inflate(layoutInflater)
attachmentPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL setContentView(views.root)
attachmentsAdapter = AttachmentsAdapter() views.attachmentPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL
attachmentPager.adapter = attachmentsAdapter views.attachmentPager.adapter = attachmentsAdapter
imageTransitionView = transitionImageView
transitionImageContainer = findViewById(R.id.transitionImageContainer)
pager2 = attachmentPager
directionDetector = createSwipeDirectionDetector() directionDetector = createSwipeDirectionDetector()
gestureDetector = createGestureDetector() gestureDetector = createGestureDetector()
attachmentPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { views.attachmentPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrollStateChanged(state: Int) { override fun onPageScrollStateChanged(state: Int) {
isImagePagerIdle = state == ViewPager2.SCROLL_STATE_IDLE isImagePagerIdle = state == ViewPager2.SCROLL_STATE_IDLE
} }
@ -116,12 +121,12 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
}) })
swipeDismissHandler = createSwipeToDismissHandler() swipeDismissHandler = createSwipeToDismissHandler()
rootContainer.setOnTouchListener(swipeDismissHandler) views.rootContainer.setOnTouchListener(swipeDismissHandler)
rootContainer.viewTreeObserver.addOnGlobalLayoutListener { swipeDismissHandler.translationLimit = dismissContainer.height / 4 } views.rootContainer.viewTreeObserver.addOnGlobalLayoutListener { swipeDismissHandler.translationLimit = views.dismissContainer.height / 4 }
scaleDetector = createScaleGestureDetector() scaleDetector = createScaleGestureDetector()
ViewCompat.setOnApplyWindowInsetsListener(rootContainer) { _, insets -> ViewCompat.setOnApplyWindowInsetsListener(views.rootContainer) { _, insets ->
overlayView?.updatePadding(top = insets.systemWindowInsetTop, bottom = insets.systemWindowInsetBottom) overlayView?.updatePadding(top = insets.systemWindowInsetTop, bottom = insets.systemWindowInsetBottom)
topInset = insets.systemWindowInsetTop topInset = insets.systemWindowInsetTop
bottomInset = insets.systemWindowInsetBottom bottomInset = insets.systemWindowInsetBottom
@ -170,7 +175,7 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
if (swipeDirection == null && (scaleDetector.isInProgress || ev.pointerCount > 1 || wasScaled)) { if (swipeDirection == null && (scaleDetector.isInProgress || ev.pointerCount > 1 || wasScaled)) {
wasScaled = true wasScaled = true
// Log.v("ATTACHEMENTS", "dispatch to pager") // Log.v("ATTACHEMENTS", "dispatch to pager")
return attachmentPager.dispatchTouchEvent(ev) return views.attachmentPager.dispatchTouchEvent(ev)
} }
// Log.v("ATTACHEMENTS", "is current item scaled ${isScaled()}") // Log.v("ATTACHEMENTS", "is current item scaled ${isScaled()}")
@ -196,16 +201,16 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
private fun handleEventActionDown(event: MotionEvent) { private fun handleEventActionDown(event: MotionEvent) {
swipeDirection = null swipeDirection = null
wasScaled = false wasScaled = false
attachmentPager.dispatchTouchEvent(event) views.attachmentPager.dispatchTouchEvent(event)
swipeDismissHandler.onTouch(rootContainer, event) swipeDismissHandler.onTouch(views.rootContainer, event)
isOverlayWasClicked = dispatchOverlayTouch(event) isOverlayWasClicked = dispatchOverlayTouch(event)
} }
private fun handleEventActionUp(event: MotionEvent) { private fun handleEventActionUp(event: MotionEvent) {
// wasDoubleTapped = false // wasDoubleTapped = false
swipeDismissHandler.onTouch(rootContainer, event) swipeDismissHandler.onTouch(views.rootContainer, event)
attachmentPager.dispatchTouchEvent(event) views.attachmentPager.dispatchTouchEvent(event)
isOverlayWasClicked = dispatchOverlayTouch(event) isOverlayWasClicked = dispatchOverlayTouch(event)
} }
@ -220,12 +225,12 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
private fun toggleOverlayViewVisibility() { private fun toggleOverlayViewVisibility() {
if (systemUiVisibility) { if (systemUiVisibility) {
// we hide // we hide
TransitionManager.beginDelayedTransition(rootContainer) TransitionManager.beginDelayedTransition(views.rootContainer)
hideSystemUI() hideSystemUI()
overlayView?.isVisible = false overlayView?.isVisible = false
} else { } else {
// we show // we show
TransitionManager.beginDelayedTransition(rootContainer) TransitionManager.beginDelayedTransition(views.rootContainer)
showSystemUI() showSystemUI()
overlayView?.isVisible = true overlayView?.isVisible = true
} }
@ -238,11 +243,11 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
return when (swipeDirection) { return when (swipeDirection) {
SwipeDirection.Up, SwipeDirection.Down -> { SwipeDirection.Up, SwipeDirection.Down -> {
if (isSwipeToDismissAllowed && !wasScaled && isImagePagerIdle) { if (isSwipeToDismissAllowed && !wasScaled && isImagePagerIdle) {
swipeDismissHandler.onTouch(rootContainer, event) swipeDismissHandler.onTouch(views.rootContainer, event)
} else true } else true
} }
SwipeDirection.Left, SwipeDirection.Right -> { SwipeDirection.Left, SwipeDirection.Right -> {
attachmentPager.dispatchTouchEvent(event) views.attachmentPager.dispatchTouchEvent(event)
} }
else -> true else -> true
} }
@ -250,8 +255,8 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
private fun handleSwipeViewMove(translationY: Float, translationLimit: Int) { private fun handleSwipeViewMove(translationY: Float, translationLimit: Int) {
val alpha = calculateTranslationAlpha(translationY, translationLimit) val alpha = calculateTranslationAlpha(translationY, translationLimit)
backgroundView.alpha = alpha views.backgroundView.alpha = alpha
dismissContainer.alpha = alpha views.dismissContainer.alpha = alpha
overlayView?.alpha = alpha overlayView?.alpha = alpha
} }
@ -265,7 +270,7 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
private fun createSwipeToDismissHandler() private fun createSwipeToDismissHandler()
: SwipeToDismissHandler = SwipeToDismissHandler( : SwipeToDismissHandler = SwipeToDismissHandler(
swipeView = dismissContainer, swipeView = views.dismissContainer,
shouldAnimateDismiss = { shouldAnimateDismiss() }, shouldAnimateDismiss = { shouldAnimateDismiss() },
onDismiss = { animateClose() }, onDismiss = { animateClose() },
onSwipeViewMove = ::handleSwipeViewMove) onSwipeViewMove = ::handleSwipeViewMove)

View file

@ -98,7 +98,7 @@ class AttachmentsAdapter : RecyclerView.Adapter<BaseViewHolder>() {
fun isScaled(position: Int): Boolean { fun isScaled(position: Int): Boolean {
val holder = recyclerView?.findViewHolderForAdapterPosition(position) val holder = recyclerView?.findViewHolderForAdapterPosition(position)
if (holder is ZoomableImageViewHolder) { if (holder is ZoomableImageViewHolder) {
return holder.touchImageView.attacher.scale > 1f return holder.views.touchImageView.attacher.scale > 1f
} }
return false return false
} }

View file

@ -44,29 +44,29 @@ internal class DefaultImageLoaderTarget(val holder: AnimatedImageViewHolder, pri
override fun onResourceLoading(uid: String, placeholder: Drawable?) { override fun onResourceLoading(uid: String, placeholder: Drawable?) {
if (holder.boundResourceUid != uid) return if (holder.boundResourceUid != uid) return
holder.imageLoaderProgress.isVisible = true holder.views.imageLoaderProgress.isVisible = true
} }
override fun onLoadFailed(uid: String, errorDrawable: Drawable?) { override fun onLoadFailed(uid: String, errorDrawable: Drawable?) {
if (holder.boundResourceUid != uid) return if (holder.boundResourceUid != uid) return
holder.imageLoaderProgress.isVisible = false holder.views.imageLoaderProgress.isVisible = false
holder.touchImageView.setImageDrawable(errorDrawable) holder.views.imageView.setImageDrawable(errorDrawable)
} }
override fun onResourceCleared(uid: String, placeholder: Drawable?) { override fun onResourceCleared(uid: String, placeholder: Drawable?) {
if (holder.boundResourceUid != uid) return if (holder.boundResourceUid != uid) return
holder.touchImageView.setImageDrawable(placeholder) holder.views.imageView.setImageDrawable(placeholder)
} }
override fun onResourceReady(uid: String, resource: Drawable) { override fun onResourceReady(uid: String, resource: Drawable) {
if (holder.boundResourceUid != uid) return if (holder.boundResourceUid != uid) return
holder.imageLoaderProgress.isVisible = false holder.views.imageLoaderProgress.isVisible = false
// Glide mess up the view size :/ // Glide mess up the view size :/
holder.touchImageView.updateLayoutParams { holder.views.imageView.updateLayoutParams {
width = LinearLayout.LayoutParams.MATCH_PARENT width = LinearLayout.LayoutParams.MATCH_PARENT
height = LinearLayout.LayoutParams.MATCH_PARENT height = LinearLayout.LayoutParams.MATCH_PARENT
} }
holder.touchImageView.setImageDrawable(resource) holder.views.imageView.setImageDrawable(resource)
if (resource is Animatable) { if (resource is Animatable) {
resource.start() resource.start()
} }
@ -77,30 +77,30 @@ internal class DefaultImageLoaderTarget(val holder: AnimatedImageViewHolder, pri
override fun onResourceLoading(uid: String, placeholder: Drawable?) { override fun onResourceLoading(uid: String, placeholder: Drawable?) {
if (holder.boundResourceUid != uid) return if (holder.boundResourceUid != uid) return
holder.imageLoaderProgress.isVisible = true holder.views.imageLoaderProgress.isVisible = true
holder.touchImageView.setImageDrawable(placeholder) holder.views.touchImageView.setImageDrawable(placeholder)
} }
override fun onLoadFailed(uid: String, errorDrawable: Drawable?) { override fun onLoadFailed(uid: String, errorDrawable: Drawable?) {
if (holder.boundResourceUid != uid) return if (holder.boundResourceUid != uid) return
holder.imageLoaderProgress.isVisible = false holder.views.imageLoaderProgress.isVisible = false
holder.touchImageView.setImageDrawable(errorDrawable) holder.views.touchImageView.setImageDrawable(errorDrawable)
} }
override fun onResourceCleared(uid: String, placeholder: Drawable?) { override fun onResourceCleared(uid: String, placeholder: Drawable?) {
if (holder.boundResourceUid != uid) return if (holder.boundResourceUid != uid) return
holder.touchImageView.setImageDrawable(placeholder) holder.views.touchImageView.setImageDrawable(placeholder)
} }
override fun onResourceReady(uid: String, resource: Drawable) { override fun onResourceReady(uid: String, resource: Drawable) {
if (holder.boundResourceUid != uid) return if (holder.boundResourceUid != uid) return
holder.imageLoaderProgress.isVisible = false holder.views.imageLoaderProgress.isVisible = false
// Glide mess up the view size :/ // Glide mess up the view size :/
holder.touchImageView.updateLayoutParams { holder.views.touchImageView.updateLayoutParams {
width = LinearLayout.LayoutParams.MATCH_PARENT width = LinearLayout.LayoutParams.MATCH_PARENT
height = LinearLayout.LayoutParams.MATCH_PARENT height = LinearLayout.LayoutParams.MATCH_PARENT
} }
holder.touchImageView.setImageDrawable(resource) holder.views.touchImageView.setImageDrawable(resource)
} }
} }
} }

View file

@ -49,19 +49,19 @@ internal class DefaultVideoLoaderTarget(val holder: VideoViewHolder, private val
override fun onThumbnailResourceCleared(uid: String, placeholder: Drawable?) { override fun onThumbnailResourceCleared(uid: String, placeholder: Drawable?) {
if (holder.boundResourceUid != uid) return if (holder.boundResourceUid != uid) return
holder.thumbnailImage.setImageDrawable(placeholder) holder.views.videoThumbnailImage.setImageDrawable(placeholder)
} }
override fun onThumbnailResourceReady(uid: String, resource: Drawable) { override fun onThumbnailResourceReady(uid: String, resource: Drawable) {
if (holder.boundResourceUid != uid) return if (holder.boundResourceUid != uid) return
holder.thumbnailImage.setImageDrawable(resource) holder.views.videoThumbnailImage.setImageDrawable(resource)
} }
override fun onVideoFileLoading(uid: String) { override fun onVideoFileLoading(uid: String) {
if (holder.boundResourceUid != uid) return if (holder.boundResourceUid != uid) return
holder.thumbnailImage.isVisible = true holder.views.videoThumbnailImage.isVisible = true
holder.loaderProgressBar.isVisible = true holder.views.videoLoaderProgress.isVisible = true
holder.videoView.isVisible = false holder.views.videoView.isVisible = false
} }
override fun onVideoFileLoadFailed(uid: String) { override fun onVideoFileLoadFailed(uid: String) {
@ -82,8 +82,8 @@ internal class DefaultVideoLoaderTarget(val holder: VideoViewHolder, private val
} }
private fun arrangeForVideoReady() { private fun arrangeForVideoReady() {
holder.thumbnailImage.isVisible = false holder.views.videoThumbnailImage.isVisible = false
holder.loaderProgressBar.isVisible = false holder.views.videoLoaderProgress.isVisible = false
holder.videoView.isVisible = true holder.views.videoView.isVisible = true
} }
} }

View file

@ -18,11 +18,8 @@ package im.vector.lib.attachmentviewer
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import android.widget.VideoView
import androidx.core.view.isVisible import androidx.core.view.isVisible
import im.vector.lib.attachmentviewer.databinding.ItemVideoAttachmentBinding
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
@ -44,13 +41,9 @@ class VideoViewHolder constructor(itemView: View) :
var eventListener: WeakReference<AttachmentEventListener>? = null var eventListener: WeakReference<AttachmentEventListener>? = null
val thumbnailImage: ImageView = itemView.findViewById(R.id.videoThumbnailImage) val views = ItemVideoAttachmentBinding.bind(itemView)
val videoView: VideoView = itemView.findViewById(R.id.videoView)
val loaderProgressBar: ProgressBar = itemView.findViewById(R.id.videoLoaderProgress)
val videoControlIcon: ImageView = itemView.findViewById(R.id.videoControlIcon)
val errorTextView: TextView = itemView.findViewById(R.id.videoMediaViewerErrorView)
internal val target = DefaultVideoLoaderTarget(this, thumbnailImage) internal val target = DefaultVideoLoaderTarget(this, views.videoThumbnailImage)
override fun onRecycled() { override fun onRecycled() {
super.onRecycled() super.onRecycled()
@ -77,12 +70,12 @@ class VideoViewHolder constructor(itemView: View) :
} }
override fun entersBackground() { override fun entersBackground() {
if (videoView.isPlaying) { if (views.videoView.isPlaying) {
progress = videoView.currentPosition progress = views.videoView.currentPosition
progressDisposable?.dispose() progressDisposable?.dispose()
progressDisposable = null progressDisposable = null
videoView.stopPlayback() views.videoView.stopPlayback()
videoView.pause() views.videoView.pause()
} }
} }
@ -92,9 +85,9 @@ class VideoViewHolder constructor(itemView: View) :
override fun onSelected(selected: Boolean) { override fun onSelected(selected: Boolean) {
if (!selected) { if (!selected) {
if (videoView.isPlaying) { if (views.videoView.isPlaying) {
progress = videoView.currentPosition progress = views.videoView.currentPosition
videoView.stopPlayback() views.videoView.stopPlayback()
} else { } else {
progress = 0 progress = 0
} }
@ -109,34 +102,34 @@ class VideoViewHolder constructor(itemView: View) :
} }
private fun startPlaying() { private fun startPlaying() {
thumbnailImage.isVisible = false views.videoThumbnailImage.isVisible = false
loaderProgressBar.isVisible = false views.videoLoaderProgress.isVisible = false
videoView.isVisible = true views.videoView.isVisible = true
videoView.setOnPreparedListener { views.videoView.setOnPreparedListener {
progressDisposable?.dispose() progressDisposable?.dispose()
progressDisposable = Observable.interval(100, TimeUnit.MILLISECONDS) progressDisposable = Observable.interval(100, TimeUnit.MILLISECONDS)
.timeInterval() .timeInterval()
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe { .subscribe {
val duration = videoView.duration val duration = views.videoView.duration
val progress = videoView.currentPosition val progress = views.videoView.currentPosition
val isPlaying = videoView.isPlaying val isPlaying = views.videoView.isPlaying
// Log.v("FOO", "isPlaying $isPlaying $progress/$duration") // Log.v("FOO", "isPlaying $isPlaying $progress/$duration")
eventListener?.get()?.onEvent(AttachmentEvents.VideoEvent(isPlaying, progress, duration)) eventListener?.get()?.onEvent(AttachmentEvents.VideoEvent(isPlaying, progress, duration))
} }
} }
try { try {
videoView.setVideoPath(mVideoPath) views.videoView.setVideoPath(mVideoPath)
} catch (failure: Throwable) { } catch (failure: Throwable) {
// Couldn't open // Couldn't open
Log.v(VideoViewHolder::class.java.name, "Failed to start video") Log.v(VideoViewHolder::class.java.name, "Failed to start video")
} }
if (!wasPaused) { if (!wasPaused) {
videoView.start() views.videoView.start()
if (progress > 0) { if (progress > 0) {
videoView.seekTo(progress) views.videoView.seekTo(progress)
} }
} }
} }
@ -146,17 +139,17 @@ class VideoViewHolder constructor(itemView: View) :
when (commands) { when (commands) {
AttachmentCommands.StartVideo -> { AttachmentCommands.StartVideo -> {
wasPaused = false wasPaused = false
videoView.start() views.videoView.start()
} }
AttachmentCommands.PauseVideo -> { AttachmentCommands.PauseVideo -> {
wasPaused = true wasPaused = true
videoView.pause() views.videoView.pause()
} }
is AttachmentCommands.SeekTo -> { is AttachmentCommands.SeekTo -> {
val duration = videoView.duration val duration = views.videoView.duration
if (duration > 0) { if (duration > 0) {
val seekDuration = duration * (commands.percentProgress / 100f) val seekDuration = duration * (commands.percentProgress / 100f)
videoView.seekTo(seekDuration.toInt()) views.videoView.seekTo(seekDuration.toInt())
} }
} }
} }

View file

@ -17,31 +17,29 @@
package im.vector.lib.attachmentviewer package im.vector.lib.attachmentviewer
import android.view.View import android.view.View
import android.widget.ProgressBar import im.vector.lib.attachmentviewer.databinding.ItemImageAttachmentBinding
import com.github.chrisbanes.photoview.PhotoView
class ZoomableImageViewHolder constructor(itemView: View) : class ZoomableImageViewHolder constructor(itemView: View) :
BaseViewHolder(itemView) { BaseViewHolder(itemView) {
val touchImageView: PhotoView = itemView.findViewById(R.id.touchImageView) val views = ItemImageAttachmentBinding.bind(itemView)
val imageLoaderProgress: ProgressBar = itemView.findViewById(R.id.imageLoaderProgress)
init { init {
touchImageView.setAllowParentInterceptOnEdge(false) views.touchImageView.setAllowParentInterceptOnEdge(false)
touchImageView.setOnScaleChangeListener { scaleFactor, _, _ -> views.touchImageView.setOnScaleChangeListener { scaleFactor, _, _ ->
// Log.v("ATTACHEMENTS", "scaleFactor $scaleFactor") // Log.v("ATTACHEMENTS", "scaleFactor $scaleFactor")
// It's a bit annoying but when you pitch down the scaling // It's a bit annoying but when you pitch down the scaling
// is not exactly one :/ // is not exactly one :/
touchImageView.setAllowParentInterceptOnEdge(scaleFactor <= 1.0008f) views.touchImageView.setAllowParentInterceptOnEdge(scaleFactor <= 1.0008f)
} }
touchImageView.setScale(1.0f, true) views.touchImageView.setScale(1.0f, true)
touchImageView.setAllowParentInterceptOnEdge(true) views.touchImageView.setAllowParentInterceptOnEdge(true)
} }
internal val target = DefaultImageLoaderTarget.ZoomableImageTarget(this, touchImageView) internal val target = DefaultImageLoaderTarget.ZoomableImageTarget(this, views.touchImageView)
override fun onRecycled() { override fun onRecycled() {
super.onRecycled() super.onRecycled()
touchImageView.setImageDrawable(null) views.touchImageView.setImageDrawable(null)
} }
} }

View file

@ -27,7 +27,6 @@ $ source env/bin/activate
Every time you want to launch these test homeservers, type: Every time you want to launch these test homeservers, type:
```shell script ```shell script
$ virtualenv -p python3 env
$ source env/bin/activate $ source env/bin/activate
(env) $ demo/start.sh --no-rate-limit (env) $ demo/start.sh --no-rate-limit
``` ```

View file

@ -1 +1,2 @@
// TODO Diese neue Version enthält hauptsächlich Fehlerkorrekturen und Verbesserungen. Nachrichten verschicken geht jetzt viel schneller.
Vollständige Versionshinweise: https://github.com/vector-im/element-android/releases/tag/v1.0.10

View file

@ -0,0 +1,2 @@
Diese neue Version enthält hauptsächlich Verbesserungen der Benutzer*innenoberfläche und der Handhabung. Du kannst jetzt ganz schnell Freund*innen einladen und DMs erstellen, indem du schlicht einen QR-Code scannst.
Vollständige Versionshinweise: https://github.com/vector-im/element-android/releases/tag/v1.0.11

View file

@ -1,2 +1,2 @@
Main changes in this version: URL Preview, new Emoji keyboard, new room settings capabilities, and snow for Christmas! Main changes in this version: URL Preview, new Emoji keyboard, new room settings capabilities, and snow for Christmas!
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.0.12 Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.0.13

View file

@ -0,0 +1,2 @@
Main changes in this version: Edit room permissions, automatic light/dark theme, and a bunch of bug fixes.
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.0.14

View file

@ -0,0 +1,2 @@
Selles uues versioonis leidub põhiliselt veaparandusi ja pisikohendusi. Sõnumite saatmine on nüüd märkatavalt kiirem.
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.0.10

View file

@ -0,0 +1,2 @@
Uues versioonis leidub põhiliselt kasutajaliidese ning kasutajakogemuse parandusi. Nüüd saad sõpradele kutseid saata ning otsevestlusi alustada QR-koodi lugemise abil.
Kõik muudatused: https://github.com/vector-im/element-android/releases/tag/v1.0.11

View file

@ -1 +1,2 @@
// برای انجام این نگارش جدید به طور عمده شامل رفع اشکال‌ها و بهبودها است. ارسال پیام اکنون بسیار سریعتر است.
گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.0.10

View file

@ -0,0 +1,2 @@
این نگارش جدید به طور عمده شامل رابط کاربری و بهبود تجربه کاربر است. اکنون می‌توانید با پویش کدهای QR دوستانتان را دعوت کرده و بسیار سریع پیام مستقیم ایجاد کنید.
گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.0.11

View file

@ -1 +1 @@
گپ و تماس نامتمرکز امن. داده‌هایتان را از شرکت‌ها امن نگه دارید. گپ و تماس نامتمرکز امن. داده‌هایتان را از اشخاص سوم امن نگه دارید.

View file

@ -1 +1 @@
المنت (ریوت سابق) Element (پیشتر Riot.im)

View file

@ -0,0 +1,2 @@
Tämä versio sisältää pääosin käyttöliittymä- ja käyttökokemusparannuksia. Voit nyt kutsua kavereita ja luoda yksityisviestejä nopeasti QR-koodeja lukemalla.
Täysi muutosloki: https://github.com/vector-im/element-android/releases/tag/v1.0.11

View file

@ -1,30 +1,30 @@
Element on uudenlainen viestinsovellus, joka: Element on uudenlainen viestinsovellus, joka:
1. Antaa sinun päättää yksityisyydestäsi. 1. Antaa sinun päättää yksityisyydestäsi
2. Antaa sinun kommunikoida kenen tahansa kanssa Matrix-verkossa ja jopa sen ulkopuolella siltaamalla sovelluksiin, kuten Slack 2. Antaa sinun kommunikoida kenen tahansa kanssa Matrix-verkossa ja jopa sen ulkopuolella siltaamalla sovelluksiin, kuten Slack
3. Suojaa sinua mainonnalta, tietojen keräämiseltä ja suljetuilta alustoilta 3. Suojaa sinua mainonnalta, tietojen keräämiseltä ja suljetuilta alustoilta
4. Suojaa sinut päästä päähän -salauksella sekä ristiin varmentamisella muiden todentamiseksi 4. Suojaa sinut päästä päähän -salauksella sekä ristiin varmentamisella muiden todentamiseksi
Element eroaa täysin muista viestintäsovelluksista, koska se on hajautettu ja avointa lähdekoodia. Element eroaa täysin muista viestintäsovelluksista, koska se on hajautettu ja avointa lähdekoodia.
Element antaa sinun isännöidä itse - valita isännän - jotta sinulla on yksityisyys ja voit hallita tietojasi sekä keskustelujasi. Se antaa sinulle pääsyn avoimeen verkkoon; joten et ole jumissa Elementin käyttäjissä. Element antaa sinun isännöidä itse - tai valita palveluntarjoajan - jotta sinulla on yksityisyys ja voit hallita tietojasi sekä keskustelujasi. Se antaa sinulle pääsyn avoimeen verkkoon, joten et jää juttelemaan vain toisten Elementin käyttäjien kanssa. Se on myös hyvin turvallinen.
Element pystyy tekemään kaiken tämän, koska se toimii Matrixilla - avoimella, hajautetun viestinnän standardilla. Element pystyy tekemään kaiken tämän, koska se toimii Matrixilla - avoimella, hajautetun viestinnän standardilla.
Element antaa sinulle hallinnan antamalla sinun valita, kuka isännöi keskustelujasi. Element-sovelluksessa voit valita isännän eri tavoin: Element antaa sinulle päätösvallan antamalla sinun valita, kuka isännöi keskustelujasi. Element-sovelluksessa voit valita isännän eri tavoin:
1. Hanki ilmainen tili Matrix-kehittäjien ylläpitämällä matrix.org-palvelimella tai valitse tuhansista vapaaehtoisten ylläpitämistä julkisista palvelimista. 1. Hanki ilmainen tili Matrix-kehittäjien ylläpitämällä matrix.org-palvelimella tai valitse tuhansista vapaaehtoisten ylläpitämistä julkisista palvelimista.
2. Isännöi tiliäsi itse suorittamalla palvelinta omalla laitteellasi 2. Isännöi tiliäsi itse ylläpitämällä palvelinta omalla laitteellasi
3. Luo tili mukautetulla palvelimella yksinkertaisesti tilaamalla Element Matrix Services -palvelu 3. Luo tili sinua varten tehdyllä palvelimella tilaamalla Element Matrix Services -palvelu
<b>Miksi valita Element?</b> <b>Miksi valita Element?</b>
<b>OMAT TIEDOT</b>: Sinä päätät, missä tietosi ja viestisi säilytetään. Hallitset sitä itse, eikä jokin MEGAYHTIÖ, joka tutkii tietojasi tai antaa niitä kolmansille osapuolille. <b>OMAT TIEDOT</b>: Sinä päätät, missä tietosi ja viestisi säilytetään. Sinä määräät, ei jokin jättiyhtiö, joka tutkii tietojasi tai antaa niitä kolmansille osapuolille.
<b>AVOIN KOMMUNIKOINYI JA YHTEISTYÖ</b>: Voit keskustella kaikkien muiden Matrix-verkon käyttäjien kanssa, riippumatta siitä käyttävätkö he Elementiä tai muuta Matrix-sovellusta, ja vaikka he käyttäisivät eri viestijärjestelmiä, kuten Slack, IRC tai XMPP. <b>AVOINTA VIESTINTÄÄ JA YHTEISTYÖTÄ</b>: Voit keskustella kaikkien muiden Matrix-verkon käyttäjien kanssa, riippumatta siitä käyttävätkö he Elementiä tai muuta Matrix-sovellusta, ja vaikka he käyttäisivät eri viestijärjestelmiä, kuten Slack, IRC tai XMPP.
<b>ERITTÄIN TURVALLINEN</b>: Vahva päästä päähän -salaus (vain keskustelussa olevat voivat purkaa viestien salauksen), ja ristiin varmentaminen keskustelun osallistujien laitteiden tarkistamiseksi. <b>ERITTÄIN TURVALLINEN</b>: Vahva päästä päähän -salaus (vain keskustelussa olevat voivat purkaa viestien salauksen), ja ristiin varmentaminen keskustelun osallistujien laitteiden tarkistamiseksi.
<b>TÄYDELLISTÄ VIESTINTÄÄ</b>: Viestit, ääni- ja videopuhelut, tiedostojen jakaminen, näytön jakaminen ja koko joukko integraatioita, botteja ja widgettejä. Rakenna huoneita, yhteisöjä, pidä yhteyttä ja tee asioita. <b>KATTAVAA VIESTINTÄÄ</b>: Viestit, ääni- ja videopuhelut, tiedostojen jakaminen, näytön jakaminen ja koko joukko integraatioita, botteja ja sovelmia. Rakenna huoneita ja yhteisöjä, pidä yhteyttä ja hoida asiasi.
<b>MISSÄ TAHANSA OLETKIN</b>: Pidä yhteyttä missä tahansa, täysin synkronoidun viestihistorian kautta kaikilla laitteillasi ja verkossa osoitteessa https://app.element.io. <b>MISSÄ TAHANSA OLETKIN</b>: Pidä yhteyttä missä tahansa, täysin synkronoidun viestihistorian kautta kaikilla laitteillasi ja verkossa osoitteessa https://app.element.io.

View file

@ -1 +1 @@
Turvallista, hajautettua, keskusteluja ja VoIP-puheluita. Pidä tietosi turvassa. Turvallista, hajautettua keskustelua ja VoIP-puheluita. Pidä tietosi turvassa.

View file

@ -0,0 +1,2 @@
Cette nouvelle version contient principalement des corrections de bogues et des améliorations. Envoyer un message est maintenant plus rapide.
Liste complète des changements : https://github.com/vector-im/element-android/releases/tag/v1.0.10

View file

@ -0,0 +1,2 @@
Ez az új verzió főképp hibajavításokat, és teljesítménybeli fejlesztéseket tartalmaz. Most már sokkal gyorsabb az üzenetek elküldése.
A változtatások teljes listája itt található: https://github.com/vector-im/element-android/releases/tag/v1.0.10

View file

@ -0,0 +1,2 @@
Ez az új verzió főleg a felhasználói felülettel és a felhasználói élménnyel kapcsolatos javításokat tartalmaz. Mostantól már sokkal gyorsabban hívhatsz meg új ismerősöket a QR kód beolvasás által.
A változtatások teljes listája itt található: https://github.com/vector-im/element-android/releases/tag/v1.0.11

View file

@ -17,7 +17,7 @@ Az Element a te kezedbe adja az irányítást azáltal, hogy eldöntheted, ki t
2. A saját számítógépeden is futtathatsz szervert 2. A saját számítógépeden is futtathatsz szervert
3. Előfizethetsz egy saját szerverre az Element Matrix Szolgáltatások platformon 3. Előfizethetsz egy saját szerverre az Element Matrix Szolgáltatások platformon
<b>Miért válaszd az Element-et?</b> <b>Miért jó az Element-et választani?</b>
<b>ADATAID MEGVÉDÉSE</b>: Eldöntheted, hol tárold az adataid és üzeneteid. A te tulajdonodban van, nem valami megacégnél, ami bányássza az adataid, vagy továbbadja másoknak. <b>ADATAID MEGVÉDÉSE</b>: Eldöntheted, hol tárold az adataid és üzeneteid. A te tulajdonodban van, nem valami megacégnél, ami bányássza az adataid, vagy továbbadja másoknak.

View file

@ -0,0 +1,2 @@
Questa nuova versione contiene principalmente miglioramenti di interfaccia ed esperienza utente. Ora puoi invitare amici e iniziare messaggi diretti rapidamente tramite codici QR.
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.0.11

View file

@ -0,0 +1,2 @@
Denne nye versjonen inneholder hovedsakelig feilrettinger og forbedringer. Å sende en melding er nå mye raskere.
Full endringslogg: https://github.com/vector-im/element-android/releases/tag/v1.0.10

View file

@ -0,0 +1,2 @@
Denne nye versjonen inneholder hovedsakelig forbedringer av brukergrensesnittet og brukeropplevelsen. Nå kan du invitere venner og opprette DM veldig raskt ved å skanne QR-koder.
Full endringslogg: https://github.com/vector-im/element-android/releases/tag/v1.0.11

View file

@ -0,0 +1 @@
Sikker desentralisert chat & VoIP. Beskytt dataene dine fra tredjeparter.

View file

@ -0,0 +1 @@
Element (tidligere Riot.im)

View file

@ -0,0 +1,2 @@
Esta nova versão contém principalmente melhorias na interface do usuário e na experiência do usuário. Agora você pode convidar amigos e criar conversas rapidamente, digitalizando códigos QR.
Registro completo de alterações: https://github.com/vector-im/element-android/releases/tag/v1.0.11

View file

@ -0,0 +1,2 @@
Эта новая версия в основном содержит исправления ошибок и улучшения. Отправка сообщения стала намного быстрее.
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.0.10

View file

@ -0,0 +1,2 @@
Эта новая версия в основном содержит улучшения пользовательского интерфейса и взаимодействия с пользователем. Теперь вы можете приглашать друзей и очень быстро создавать чаты, сканируя QR-коды.
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.0.11

View file

@ -0,0 +1,2 @@
Táto verzia obsahuje predovšetkým opravy chýb. Odosielanie správ je odteraz omnoho rýchlejšie.
Kompletný zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.0.10

View file

@ -0,0 +1,2 @@
Táto verzia obsahuje najmä vylepšenia používateľského rozhrania. Pozývať priateľov alebo vytvárať priame konverzácie môžete veľmi rýchlo naskenovaním QR kódov.
Kompletný zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.0.11

View file

@ -0,0 +1,2 @@
Den här nya versionen innehåller mest förbättringar för användargränssnittet och användarupplevelsen. Du kan nu bjuda in vänner och skapa direktmeddelanden väldigt snabbt genom att skanna QR-koder.
Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.0.11

View file

@ -0,0 +1,2 @@
Ця нова версія містить переважно поліпшення інтерфейсу та зручності користування. Тепер ви можете запросити друзів і створити прямі повідомлення дуже швидко, скануючи QR-коди.
Повний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.0.11

View file

@ -0,0 +1,2 @@
Phiên bản mới này chủ yếu bao gồm sửa lỗi và một số cải thiện. Gửi tin nhắn trở nên nhanh chóng hơn trước.
Danh sách đầy đủ các thay đổi: https://github.com/vector-im/element-android/releases/tag/v1.0.10

View file

@ -0,0 +1,2 @@
Phiên bản mới này chủ yếu bao gồm các cải thiện về giao diện và trải nghiệm người dùng. Bây giờ bạn có thể mời bạn bè và bắt đầu nói chuyện nhanh chóng bằng cách quét mã QR.
Danh sách đầy đủ các thay đổi: https://github.com/vector-im/element-android/releases/tag/v1.0.11

View file

@ -0,0 +1 @@
Ứng dụng chat và gọi phân tán bảo mật. Bảo vệ dữ liệu của bạn khỏi bên thứ ba.

View file

@ -0,0 +1 @@
Element (trước là Riot.im)

View file

@ -0,0 +1,2 @@
這個新版本主要包含使用者介面與使用者體驗改善。現在您可以邀請朋友,並透過掃描 QR code 來快速建立直接訊息了。
完整變更紀錄https://github.com/vector-im/element-android/releases/tag/v1.0.11

View file

@ -0,0 +1,2 @@
此版本中的主要變更URL 預覽、新的表情符號鍵盤、新的聊天室設定功能以及聖誕節降雪!
完整變更紀錄https://github.com/vector-im/element-android/releases/tag/v1.0.12

View file

@ -0,0 +1,2 @@
此版本中的主要變更URL 預覽、新的表情符號鍵盤、新的聊天室設定功能以及聖誕節降雪!
完整變更紀錄https://github.com/vector-im/element-android/releases/tag/v1.0.12

View file

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionSha256Sum=22449f5231796abd892c98b2a07c9ceebe4688d192cd2d6763f8e3bf8acbedeb distributionSha256Sum=a7ca23b3ccf265680f2bfd35f1f00b1424f4466292c7337c85d46c9641b3f053
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-all.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View file

@ -1,7 +1,7 @@
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-parcelize'
apply plugin: 'realm-android' apply plugin: 'realm-android'
buildscript { buildscript {
@ -13,10 +13,6 @@ buildscript {
} }
} }
androidExtensions {
experimental = true
}
android { android {
compileSdkVersion 29 compileSdkVersion 29
testOptions.unitTests.includeAndroidResources = true testOptions.unitTests.includeAndroidResources = true
@ -171,9 +167,6 @@ dependencies {
implementation 'com.jakewharton.timber:timber:4.7.1' implementation 'com.jakewharton.timber:timber:4.7.1'
implementation 'com.facebook.stetho:stetho-okhttp3:1.5.1' implementation 'com.facebook.stetho:stetho-okhttp3:1.5.1'
// Bus
implementation 'org.greenrobot:eventbus:3.1.1'
// Phone number https://github.com/google/libphonenumber // Phone number https://github.com/google/libphonenumber
implementation 'com.googlecode.libphonenumber:libphonenumber:8.10.23' implementation 'com.googlecode.libphonenumber:libphonenumber:8.10.23'

View file

@ -20,14 +20,8 @@
# hide the original source file name. # hide the original source file name.
#-renamesourcefileattribute SourceFile #-renamesourcefileattribute SourceFile
# BMA: Not sure I can delete this one without side effect
### EVENT BUS ###
-keepattributes *Annotation* -keepattributes *Annotation*
-keepclassmembers class * {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
### MOSHI ### ### MOSHI ###

View file

@ -86,7 +86,7 @@ class CommonTestHelper(context: Context) {
* *
* @param session the session to sync * @param session the session to sync
*/ */
fun syncSession(session: Session) { fun syncSession(session: Session, timeout: Long = TestConstants.timeOutMillis) {
val lock = CountDownLatch(1) val lock = CountDownLatch(1)
val job = GlobalScope.launch(Dispatchers.Main) { val job = GlobalScope.launch(Dispatchers.Main) {
@ -109,7 +109,7 @@ class CommonTestHelper(context: Context) {
} }
GlobalScope.launch(Dispatchers.Main) { syncLiveData.observeForever(syncObserver) } GlobalScope.launch(Dispatchers.Main) { syncLiveData.observeForever(syncObserver) }
await(lock) await(lock, timeout)
} }
/** /**
@ -119,7 +119,7 @@ class CommonTestHelper(context: Context) {
* @param message the message to send * @param message the message to send
* @param nbOfMessages the number of time the message will be sent * @param nbOfMessages the number of time the message will be sent
*/ */
fun sendTextMessage(room: Room, message: String, nbOfMessages: Int): List<TimelineEvent> { fun sendTextMessage(room: Room, message: String, nbOfMessages: Int, timeout: Long = TestConstants.timeOutMillis): List<TimelineEvent> {
val timeline = room.createTimeline(null, TimelineSettings(10)) val timeline = room.createTimeline(null, TimelineSettings(10))
val sentEvents = ArrayList<TimelineEvent>(nbOfMessages) val sentEvents = ArrayList<TimelineEvent>(nbOfMessages)
val latch = CountDownLatch(1) val latch = CountDownLatch(1)
@ -151,7 +151,7 @@ class CommonTestHelper(context: Context) {
room.sendTextMessage(message + " #" + (i + 1)) room.sendTextMessage(message + " #" + (i + 1))
} }
// Wait 3 second more per message // Wait 3 second more per message
await(latch, timeout = TestConstants.timeOutMillis + 3_000L * nbOfMessages) await(latch, timeout = timeout + 3_000L * nbOfMessages)
timeline.dispose() timeline.dispose()
// Check that all events has been created // Check that all events has been created
@ -215,14 +215,14 @@ class CommonTestHelper(context: Context) {
.getLoginFlow(hs, it) .getLoginFlow(hs, it)
} }
doSync<RegistrationResult> { doSync<RegistrationResult>(timeout = 60_000) {
matrix.authenticationService matrix.authenticationService
.getRegistrationWizard() .getRegistrationWizard()
.createAccount(userName, password, null, it) .createAccount(userName, password, null, it)
} }
// Perform dummy step // Perform dummy step
val registrationResult = doSync<RegistrationResult> { val registrationResult = doSync<RegistrationResult>(timeout = 60_000) {
matrix.authenticationService matrix.authenticationService
.getRegistrationWizard() .getRegistrationWizard()
.dummy(it) .dummy(it)
@ -231,7 +231,7 @@ class CommonTestHelper(context: Context) {
assertTrue(registrationResult is RegistrationResult.Success) assertTrue(registrationResult is RegistrationResult.Success)
val session = (registrationResult as RegistrationResult.Success).session val session = (registrationResult as RegistrationResult.Success).session
if (sessionTestParams.withInitialSync) { if (sessionTestParams.withInitialSync) {
syncSession(session) syncSession(session, 60_000)
} }
return session return session

View file

@ -18,14 +18,21 @@ package org.matrix.android.sdk.common
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
data class CryptoTestData(val firstSession: Session, data class CryptoTestData(val roomId: String,
val roomId: String, val sessions: List<Session>) {
val secondSession: Session? = null,
val thirdSession: Session? = null) { val firstSession: Session
get() = sessions.first()
val secondSession: Session?
get() = sessions.getOrNull(1)
val thirdSession: Session?
get() = sessions.getOrNull(2)
fun cleanUp(testHelper: CommonTestHelper) { fun cleanUp(testHelper: CommonTestHelper) {
testHelper.signOutAndClose(firstSession) sessions.forEach {
secondSession?.let { testHelper.signOutAndClose(it) } testHelper.signOutAndClose(it)
thirdSession?.let { testHelper.signOutAndClose(it) } }
} }
} }

View file

@ -73,7 +73,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
} }
} }
return CryptoTestData(aliceSession, roomId) return CryptoTestData(roomId, listOf(aliceSession))
} }
/** /**
@ -139,7 +139,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
// assertNotNull(roomFromBobPOV.powerLevels) // assertNotNull(roomFromBobPOV.powerLevels)
// assertTrue(roomFromBobPOV.powerLevels.maySendMessage(bobSession.myUserId)) // assertTrue(roomFromBobPOV.powerLevels.maySendMessage(bobSession.myUserId))
return CryptoTestData(aliceSession, aliceRoomId, bobSession) return CryptoTestData(aliceRoomId, listOf(aliceSession, bobSession))
} }
/** /**
@ -157,7 +157,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
// wait the initial sync // wait the initial sync
SystemClock.sleep(1000) SystemClock.sleep(1000)
return CryptoTestData(aliceSession, aliceRoomId, cryptoTestData.secondSession, samSession) return CryptoTestData(aliceRoomId, listOf(aliceSession, cryptoTestData.secondSession!!, samSession))
} }
/** /**
@ -381,4 +381,30 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
} }
} }
} }
fun doE2ETestWithManyMembers(numberOfMembers: Int): CryptoTestData {
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
aliceSession.cryptoService().setWarnOnUnknownDevices(false)
val roomId = mTestHelper.doSync<String> {
aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" }, it)
}
val room = aliceSession.getRoom(roomId)!!
mTestHelper.runBlockingTest {
room.enableEncryption()
}
val sessions = mutableListOf(aliceSession)
for (index in 1 until numberOfMembers) {
val session = mTestHelper.createAccount("User_$index", defaultSessionParams)
mTestHelper.doSync<Unit>(timeout = 600_000) { room.invite(session.myUserId, null, it) }
println("TEST -> " + session.myUserId + " invited")
mTestHelper.doSync<Unit> { session.joinRoom(room.roomId, null, emptyList(), it) }
println("TEST -> " + session.myUserId + " joined")
sessions.add(session)
}
return CryptoTestData(roomId, sessions)
}
} }

View file

@ -50,6 +50,8 @@ import org.junit.FixMethodOrder
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.junit.runners.MethodSorters import org.junit.runners.MethodSorters
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@ -296,4 +298,77 @@ class KeyShareTests : InstrumentedTest {
mTestHelper.signOutAndClose(aliceSession1) mTestHelper.signOutAndClose(aliceSession1)
mTestHelper.signOutAndClose(aliceSession2) mTestHelper.signOutAndClose(aliceSession2)
} }
@Test
fun test_ImproperKeyShareBug() {
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
mTestHelper.doSync<Unit> {
aliceSession.cryptoService().crossSigningService()
.initializeCrossSigning(UserPasswordAuth(
user = aliceSession.myUserId,
password = TestConstants.PASSWORD
), it)
}
// Create an encrypted room and send a couple of messages
val roomId = mTestHelper.doSync<String> {
aliceSession.createRoom(
CreateRoomParams().apply {
visibility = RoomDirectoryVisibility.PRIVATE
enableEncryption()
},
it
)
}
val roomAlicePov = aliceSession.getRoom(roomId)
assertNotNull(roomAlicePov)
Thread.sleep(1_000)
assertTrue(roomAlicePov?.isEncrypted() == true)
val secondEventId = mTestHelper.sendTextMessage(roomAlicePov!!, "Message", 3)[1].eventId
// Create bob session
val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true))
mTestHelper.doSync<Unit> {
bobSession.cryptoService().crossSigningService()
.initializeCrossSigning(UserPasswordAuth(
user = bobSession.myUserId,
password = TestConstants.PASSWORD
), it)
}
// Let alice invite bob
mTestHelper.doSync<Unit> {
roomAlicePov.invite(bobSession.myUserId, null, it)
}
mTestHelper.doSync<Unit> {
bobSession.joinRoom(roomAlicePov.roomId, null, emptyList(), it)
}
// we want to discard alice outbound session
aliceSession.cryptoService().discardOutboundSession(roomAlicePov.roomId)
// and now resend a new message to reset index to 0
mTestHelper.sendTextMessage(roomAlicePov, "After", 1)
val roomRoomBobPov = aliceSession.getRoom(roomId)
val beforeJoin = roomRoomBobPov!!.getTimeLineEvent(secondEventId)
var dRes = tryOrNull { bobSession.cryptoService().decryptEvent(beforeJoin!!.root, "") }
assert(dRes == null)
// Try to re-ask the keys
bobSession.cryptoService().reRequestRoomKeyForEvent(beforeJoin!!.root)
Thread.sleep(3_000)
// With the bug the first session would have improperly reshare that key :/
dRes = tryOrNull { bobSession.cryptoService().decryptEvent(beforeJoin.root, "") }
Log.d("#TEST", "KS: sgould not decrypt that ${beforeJoin.root.getClearContent().toModel<MessageContent>()?.body}")
assert(dRes?.clearEvent == null)
}
} }

View file

@ -0,0 +1,92 @@
/*
* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.session.room.timeline
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.junit.runners.MethodSorters
import org.matrix.android.sdk.InstrumentedTest
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
import org.matrix.android.sdk.common.CommonTestHelper
import org.matrix.android.sdk.common.CryptoTestHelper
import java.util.concurrent.CountDownLatch
import kotlin.test.fail
@RunWith(JUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
class TimelineWithManyMembersTest : InstrumentedTest {
companion object {
private const val NUMBER_OF_MEMBERS = 6
}
private val commonTestHelper = CommonTestHelper(context())
private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
/**
* Ensures when someone sends a message to a crowded room, everyone can decrypt the message.
*/
@Test
fun everyone_should_decrypt_message_in_a_crowded_room() {
val cryptoTestData = cryptoTestHelper.doE2ETestWithManyMembers(NUMBER_OF_MEMBERS)
val sessionForFirstMember = cryptoTestData.firstSession
val roomForFirstMember = sessionForFirstMember.getRoom(cryptoTestData.roomId)!!
val firstMessage = "First messages from Alice"
commonTestHelper.sendTextMessage(
roomForFirstMember,
firstMessage,
1,
600_000
)
for (index in 1 until cryptoTestData.sessions.size) {
val session = cryptoTestData.sessions[index]
val roomForCurrentMember = session.getRoom(cryptoTestData.roomId)!!
val timelineForCurrentMember = roomForCurrentMember.createTimeline(null, TimelineSettings(30))
timelineForCurrentMember.start()
session.startSync(true)
run {
val lock = CountDownLatch(1)
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
snapshot
.find { it.isEncrypted() }
?.let {
val body = it.root.getClearContent()?.toModel<MessageContent>()?.body
if (body?.startsWith(firstMessage).orFalse()) {
println("User " + session.myUserId + " decrypted as " + body)
return@createEventListener true
} else {
fail("User " + session.myUserId + " decrypted as " + body + " CryptoError: " + it.root.mCryptoError)
}
} ?: return@createEventListener false
}
timelineForCurrentMember.addListener(eventsListener)
commonTestHelper.await(lock, 600_000)
}
session.stopSync()
}
}
}

View file

@ -41,6 +41,16 @@ interface AuthenticationService {
*/ */
fun getLoginFlowOfSession(sessionId: String, callback: MatrixCallback<LoginFlowResult>): Cancelable fun getLoginFlowOfSession(sessionId: String, callback: MatrixCallback<LoginFlowResult>): Cancelable
/**
* Get a SSO url
*/
fun getSsoUrl(redirectUrl: String, deviceId: String?, providerId: String?): String?
/**
* Get the sign in or sign up fallback URL
*/
fun getFallbackUrl(forSignIn: Boolean, deviceId: String?): String?
/** /**
* Return a LoginWizard, to login to the homeserver. The login flow has to be retrieved first. * Return a LoginWizard, to login to the homeserver. The login flow has to be retrieved first.
*/ */

View file

@ -19,7 +19,7 @@ package org.matrix.android.sdk.api.auth.data
import android.os.Parcelable import android.os.Parcelable
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
@Parcelize @Parcelize

View file

@ -37,6 +37,6 @@ class SenderNotificationPermissionCondition(
fun isSatisfied(event: Event, powerLevels: PowerLevelsContent): Boolean { fun isSatisfied(event: Event, powerLevels: PowerLevelsContent): Boolean {
val powerLevelsHelper = PowerLevelsHelper(powerLevels) val powerLevelsHelper = PowerLevelsHelper(powerLevels)
return event.senderId != null && powerLevelsHelper.getUserPowerLevelValue(event.senderId) >= powerLevelsHelper.notificationLevel(key) return event.senderId != null && powerLevelsHelper.getUserPowerLevelValue(event.senderId) >= powerLevels.notificationLevel(key)
} }
} }

View file

@ -20,7 +20,7 @@ import android.net.Uri
import android.os.Parcelable import android.os.Parcelable
import androidx.exifinterface.media.ExifInterface import androidx.exifinterface.media.ExifInterface
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
import org.matrix.android.sdk.api.util.MimeTypes.normalizeMimeType import org.matrix.android.sdk.api.util.MimeTypes.normalizeMimeType
@Parcelize @Parcelize

View file

@ -25,7 +25,6 @@ interface PermalinkService {
companion object { companion object {
const val MATRIX_TO_URL_BASE = "https://matrix.to/#/" const val MATRIX_TO_URL_BASE = "https://matrix.to/#/"
const val MATRIX_TO_CUSTOM_SCHEME_URL_BASE = "element://"
} }
/** /**

View file

@ -25,28 +25,85 @@ import org.matrix.android.sdk.api.session.room.powerlevels.Role
*/ */
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class PowerLevelsContent( data class PowerLevelsContent(
/**
* The level required to ban a user. Defaults to 50 if unspecified.
*/
@Json(name = "ban") val ban: Int = Role.Moderator.value, @Json(name = "ban") val ban: Int = Role.Moderator.value,
/**
* The level required to kick a user. Defaults to 50 if unspecified.
*/
@Json(name = "kick") val kick: Int = Role.Moderator.value, @Json(name = "kick") val kick: Int = Role.Moderator.value,
/**
* The level required to invite a user. Defaults to 50 if unspecified.
*/
@Json(name = "invite") val invite: Int = Role.Moderator.value, @Json(name = "invite") val invite: Int = Role.Moderator.value,
/**
* The level required to redact an event. Defaults to 50 if unspecified.
*/
@Json(name = "redact") val redact: Int = Role.Moderator.value, @Json(name = "redact") val redact: Int = Role.Moderator.value,
/**
* The default level required to send message events. Can be overridden by the events key. Defaults to 0 if unspecified.
*/
@Json(name = "events_default") val eventsDefault: Int = Role.Default.value, @Json(name = "events_default") val eventsDefault: Int = Role.Default.value,
@Json(name = "events") val events: MutableMap<String, Int> = HashMap(), /**
* The level required to send specific event types. This is a mapping from event type to power level required.
*/
@Json(name = "events") val events: Map<String, Int> = emptyMap(),
/**
* The default power level for every user in the room, unless their user_id is mentioned in the users key. Defaults to 0 if unspecified.
*/
@Json(name = "users_default") val usersDefault: Int = Role.Default.value, @Json(name = "users_default") val usersDefault: Int = Role.Default.value,
@Json(name = "users") val users: MutableMap<String, Int> = HashMap(), /**
* The power levels for specific users. This is a mapping from user_id to power level for that user.
*/
@Json(name = "users") val users: Map<String, Int> = emptyMap(),
/**
* The default level required to send state events. Can be overridden by the events key. Defaults to 50 if unspecified.
*/
@Json(name = "state_default") val stateDefault: Int = Role.Moderator.value, @Json(name = "state_default") val stateDefault: Int = Role.Moderator.value,
@Json(name = "notifications") val notifications: Map<String, Any> = HashMap() /**
* The power level requirements for specific notification types. This is a mapping from key to power level for that notifications key.
*/
@Json(name = "notifications") val notifications: Map<String, Any> = emptyMap()
) { ) {
/** /**
* Alter this content with a new power level for the specified user * Return a copy of this content with a new power level for the specified user
* *
* @param userId the userId to alter the power level of * @param userId the userId to alter the power level of
* @param powerLevel the new power level, or null to set the default value. * @param powerLevel the new power level, or null to set the default value.
*/ */
fun setUserPowerLevel(userId: String, powerLevel: Int?) { fun setUserPowerLevel(userId: String, powerLevel: Int?): PowerLevelsContent {
return copy(
users = users.toMutableMap().apply {
if (powerLevel == null || powerLevel == usersDefault) { if (powerLevel == null || powerLevel == usersDefault) {
users.remove(userId) remove(userId)
} else { } else {
users[userId] = powerLevel put(userId, powerLevel)
} }
} }
)
}
/**
* Get the notification level for a dedicated key.
*
* @param key the notification key
* @return the level, default to Moderator if the key is not found
*/
fun notificationLevel(key: String): Int {
return when (val value = notifications[key]) {
// the first implementation was a string value
is String -> value.toInt()
is Double -> value.toInt()
is Int -> value
else -> Role.Moderator.value
}
}
companion object {
/**
* Key to use for content.notifications and get the level required to trigger an @room notification. Defaults to 50 if unspecified.
*/
const val NOTIFICATIONS_ROOM_KEY = "room"
}
} }

View file

@ -27,6 +27,7 @@ data class MessageStickerContent(
/** /**
* Set in local, not from server * Set in local, not from server
*/ */
@Transient
override val msgType: String = MessageType.MSGTYPE_STICKER_LOCAL, override val msgType: String = MessageType.MSGTYPE_STICKER_LOCAL,
/** /**

View file

@ -108,19 +108,4 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) {
val powerLevel = getUserPowerLevelValue(userId) val powerLevel = getUserPowerLevelValue(userId)
return powerLevel >= powerLevelsContent.redact return powerLevel >= powerLevelsContent.redact
} }
/**
* Get the notification level for a dedicated key.
*
* @param key the notification key
* @return the level
*/
fun notificationLevel(key: String): Int {
return when (val value = powerLevelsContent.notifications[key]) {
// the first implementation was a string value
is String -> value.toInt()
is Int -> value
else -> Role.Moderator.value
}
}
} }

View file

@ -0,0 +1,33 @@
/*
* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.session.room.state
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
/**
* Return true if a room can be joined by anyone (RoomJoinRules.PUBLIC)
*/
fun StateService.isPublic(): Boolean {
return getStateEvent(EventType.STATE_ROOM_JOIN_RULES, QueryStringValue.NoCondition)
?.content
?.toModel<RoomJoinRulesContent>()
?.joinRules == RoomJoinRules.PUBLIC
}

View file

@ -0,0 +1,37 @@
/*
* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.util
import java.net.URLEncoder
/**
* Append param and value to a Url, using "?" or "&". Value parameter will be encoded
* Return this for chaining purpose
*/
fun StringBuilder.appendParamToUrl(param: String, value: String): StringBuilder {
if (contains("?")) {
append("&")
} else {
append("?")
}
append(param)
append("=")
append(URLEncoder.encode(value, "utf-8"))
return this
}

View file

@ -14,25 +14,25 @@
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.auth package org.matrix.android.sdk.internal.auth
/** /**
* Path to use when the client does not supported any or all login flows * Path to use when the client does not supported any or all login flows
* Ref: https://matrix.org/docs/spec/client_server/latest#login-fallback * Ref: https://matrix.org/docs/spec/client_server/latest#login-fallback
*/ */
const val LOGIN_FALLBACK_PATH = "/_matrix/static/client/login/" internal const val LOGIN_FALLBACK_PATH = "/_matrix/static/client/login/"
/** /**
* Path to use when the client does not supported any or all registration flows * Path to use when the client does not supported any or all registration flows
* Not documented * Not documented
*/ */
const val REGISTER_FALLBACK_PATH = "/_matrix/static/client/register/" internal const val REGISTER_FALLBACK_PATH = "/_matrix/static/client/register/"
/** /**
* Path to use when the client want to connect using SSO * Path to use when the client want to connect using SSO
* Ref: https://matrix.org/docs/spec/client_server/latest#sso-client-login * Ref: https://matrix.org/docs/spec/client_server/latest#sso-client-login
*/ */
const val SSO_REDIRECT_PATH = "/_matrix/client/r0/login/sso/redirect" internal const val SSO_REDIRECT_PATH = "/_matrix/client/r0/login/sso/redirect"
const val MSC2858_SSO_REDIRECT_PATH = "/_matrix/client/unstable/org.matrix.msc2858/login/sso/redirect" internal const val MSC2858_SSO_REDIRECT_PATH = "/_matrix/client/unstable/org.matrix.msc2858/login/sso/redirect"
const val SSO_REDIRECT_URL_PARAM = "redirectUrl" internal const val SSO_REDIRECT_URL_PARAM = "redirectUrl"

View file

@ -34,6 +34,7 @@ import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.api.util.NoOpCancellable import org.matrix.android.sdk.api.util.NoOpCancellable
import org.matrix.android.sdk.api.util.appendParamToUrl
import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.auth.data.LoginFlowResponse import org.matrix.android.sdk.internal.auth.data.LoginFlowResponse
import org.matrix.android.sdk.internal.auth.data.RiotConfig import org.matrix.android.sdk.internal.auth.data.RiotConfig
@ -99,6 +100,52 @@ internal class DefaultAuthenticationService @Inject constructor(
} }
} }
override fun getSsoUrl(redirectUrl: String, deviceId: String?, providerId: String?): String? {
val homeServerUrlBase = getHomeServerUrlBase() ?: return null
return buildString {
append(homeServerUrlBase)
if (providerId != null) {
append(MSC2858_SSO_REDIRECT_PATH)
append("/$providerId")
} else {
append(SSO_REDIRECT_PATH)
}
// Set the redirect url
appendParamToUrl(SSO_REDIRECT_URL_PARAM, redirectUrl)
deviceId?.takeIf { it.isNotBlank() }?.let {
// But https://github.com/matrix-org/synapse/issues/5755
appendParamToUrl("device_id", it)
}
}
}
override fun getFallbackUrl(forSignIn: Boolean, deviceId: String?): String? {
val homeServerUrlBase = getHomeServerUrlBase() ?: return null
return buildString {
append(homeServerUrlBase)
if (forSignIn) {
append(LOGIN_FALLBACK_PATH)
deviceId?.takeIf { it.isNotBlank() }?.let {
// But https://github.com/matrix-org/synapse/issues/5755
appendParamToUrl("device_id", it)
}
} else {
// For sign up
append(REGISTER_FALLBACK_PATH)
}
}
}
private fun getHomeServerUrlBase(): String? {
return pendingSessionData
?.homeServerConnectionConfig
?.homeServerUri
?.toString()
?.trim { it == '/' }
}
override fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<LoginFlowResult>): Cancelable { override fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<LoginFlowResult>): Cancelable {
pendingSessionData = null pendingSessionData = null

View file

@ -17,7 +17,7 @@
package org.matrix.android.sdk.internal.auth.registration package org.matrix.android.sdk.internal.auth.registration
import android.os.Parcelable import android.os.Parcelable
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
/** /**
* This class represent a localized privacy policy for registration Flow. * This class represent a localized privacy policy for registration Flow.

View file

@ -19,7 +19,6 @@ package org.matrix.android.sdk.internal.crypto
import android.content.Context import android.content.Context
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import org.greenrobot.eventbus.EventBus
import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.auth.data.Credentials
import org.matrix.android.sdk.api.failure.shouldBeRetried import org.matrix.android.sdk.api.failure.shouldBeRetried
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
@ -60,7 +59,6 @@ internal class CancelGossipRequestWorker(context: Context,
@Inject lateinit var sendToDeviceTask: SendToDeviceTask @Inject lateinit var sendToDeviceTask: SendToDeviceTask
@Inject lateinit var cryptoStore: IMXCryptoStore @Inject lateinit var cryptoStore: IMXCryptoStore
@Inject lateinit var eventBus: EventBus
@Inject lateinit var credentials: Credentials @Inject lateinit var credentials: Credentials
override fun injectWith(injector: SessionComponent) { override fun injectWith(injector: SessionComponent) {

View file

@ -32,6 +32,7 @@ import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding
import org.matrix.android.sdk.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey import org.matrix.android.sdk.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey
import org.matrix.android.sdk.internal.crypto.model.rest.GossipingDefaultContent import org.matrix.android.sdk.internal.crypto.model.rest.GossipingDefaultContent
import org.matrix.android.sdk.internal.crypto.model.rest.GossipingToDeviceObject import org.matrix.android.sdk.internal.crypto.model.rest.GossipingToDeviceObject
import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
import org.matrix.android.sdk.internal.di.SessionId import org.matrix.android.sdk.internal.di.SessionId
import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.SessionScope
@ -206,34 +207,7 @@ internal class IncomingGossipingRequestManager @Inject constructor(
Timber.v("## CRYPTO | GOSSIP processIncomingRoomKeyRequest from $userId:$deviceId for $roomId / ${body.sessionId} id ${request.requestId}") Timber.v("## CRYPTO | GOSSIP processIncomingRoomKeyRequest from $userId:$deviceId for $roomId / ${body.sessionId} id ${request.requestId}")
if (credentials.userId != userId) { if (credentials.userId != userId) {
Timber.w("## CRYPTO | GOSSIP processReceivedGossipingRequests() : room key request from other user") handleKeyRequestFromOtherUser(body, request, alg, roomId, userId, deviceId)
val senderKey = body.senderKey ?: return Unit
.also { Timber.w("missing senderKey") }
.also { cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED) }
val sessionId = body.sessionId ?: return Unit
.also { Timber.w("missing sessionId") }
.also { cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED) }
if (alg != MXCRYPTO_ALGORITHM_MEGOLM) {
return Unit
.also { Timber.w("Only megolm is accepted here") }
.also { cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED) }
}
val roomEncryptor = roomEncryptorsStore.get(roomId) ?: return Unit
.also { Timber.w("no room Encryptor") }
.also { cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED) }
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
val isSuccess = roomEncryptor.reshareKey(sessionId, userId, deviceId, senderKey)
if (isSuccess) {
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.ACCEPTED)
} else {
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.UNABLE_TO_PROCESS)
}
}
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.RE_REQUESTED)
return return
} }
// TODO: should we queue up requests we don't yet have keys for, in case they turn up later? // TODO: should we queue up requests we don't yet have keys for, in case they turn up later?
@ -291,6 +265,42 @@ internal class IncomingGossipingRequestManager @Inject constructor(
onRoomKeyRequest(request) onRoomKeyRequest(request)
} }
private fun handleKeyRequestFromOtherUser(body: RoomKeyRequestBody,
request: IncomingRoomKeyRequest,
alg: String,
roomId: String,
userId: String,
deviceId: String) {
Timber.w("## CRYPTO | GOSSIP processReceivedGossipingRequests() : room key request from other user")
val senderKey = body.senderKey ?: return Unit
.also { Timber.w("missing senderKey") }
.also { cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED) }
val sessionId = body.sessionId ?: return Unit
.also { Timber.w("missing sessionId") }
.also { cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED) }
if (alg != MXCRYPTO_ALGORITHM_MEGOLM) {
return Unit
.also { Timber.w("Only megolm is accepted here") }
.also { cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED) }
}
val roomEncryptor = roomEncryptorsStore.get(roomId) ?: return Unit
.also { Timber.w("no room Encryptor") }
.also { cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED) }
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
val isSuccess = roomEncryptor.reshareKey(sessionId, userId, deviceId, senderKey)
if (isSuccess) {
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.ACCEPTED)
} else {
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.UNABLE_TO_PROCESS)
}
}
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.RE_REQUESTED)
}
private fun processIncomingSecretShareRequest(request: IncomingSecretShareRequest) { private fun processIncomingSecretShareRequest(request: IncomingSecretShareRequest) {
val secretName = request.secretName ?: return Unit.also { val secretName = request.secretName ?: return Unit.also {
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED) cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)

View file

@ -19,6 +19,8 @@ package org.matrix.android.sdk.internal.crypto
import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.crypto.MXCryptoError
import org.matrix.android.sdk.api.util.JSON_DICT_PARAMETERIZED_TYPE import org.matrix.android.sdk.api.util.JSON_DICT_PARAMETERIZED_TYPE
import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.internal.crypto.algorithms.megolm.MXOutboundSessionInfo
import org.matrix.android.sdk.internal.crypto.algorithms.megolm.SharedWithHelper
import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult
import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2 import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
@ -63,11 +65,15 @@ internal class MXOlmDevice @Inject constructor(
// The OLM lib utility instance. // The OLM lib utility instance.
private var olmUtility: OlmUtility? = null private var olmUtility: OlmUtility? = null
private data class GroupSessionCacheItem(
val groupId: String,
val groupSession: OlmOutboundGroupSession
)
// The outbound group session. // The outbound group session.
// They are not stored in 'store' to avoid to remember to which devices we sent the session key. // Caches active outbound session to avoid to sync with DB before read
// Plus, in cryptography, it is good to refresh sessions from time to time. // The key is the session id, the value the <roomID,outbound group session>.
// The key is the session id, the value the outbound group session. private val outboundGroupSessionCache: MutableMap<String, GroupSessionCacheItem> = HashMap()
private val outboundGroupSessionStore: MutableMap<String, OlmOutboundGroupSession> = HashMap()
// Store a set of decrypted message indexes for each group session. // Store a set of decrypted message indexes for each group session.
// This partially mitigates a replay attack where a MITM resends a group // This partially mitigates a replay attack where a MITM resends a group
@ -135,6 +141,10 @@ internal class MXOlmDevice @Inject constructor(
*/ */
fun release() { fun release() {
olmUtility?.releaseUtility() olmUtility?.releaseUtility()
outboundGroupSessionCache.values.forEach {
it.groupSession.releaseSession()
}
outboundGroupSessionCache.clear()
} }
/** /**
@ -406,11 +416,12 @@ internal class MXOlmDevice @Inject constructor(
* *
* @return the session id for the outbound session. * @return the session id for the outbound session.
*/ */
fun createOutboundGroupSession(): String? { fun createOutboundGroupSessionForRoom(roomId: String): String? {
var session: OlmOutboundGroupSession? = null var session: OlmOutboundGroupSession? = null
try { try {
session = OlmOutboundGroupSession() session = OlmOutboundGroupSession()
outboundGroupSessionStore[session.sessionIdentifier()] = session outboundGroupSessionCache[session.sessionIdentifier()] = GroupSessionCacheItem(roomId, session)
store.storeCurrentOutboundGroupSessionForRoom(roomId, session)
return session.sessionIdentifier() return session.sessionIdentifier()
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e, "createOutboundGroupSession") Timber.e(e, "createOutboundGroupSession")
@ -421,6 +432,39 @@ internal class MXOlmDevice @Inject constructor(
return null return null
} }
fun storeOutboundGroupSessionForRoom(roomId: String, sessionId: String) {
outboundGroupSessionCache[sessionId]?.let {
store.storeCurrentOutboundGroupSessionForRoom(roomId, it.groupSession)
}
}
fun restoreOutboundGroupSessionForRoom(roomId: String): MXOutboundSessionInfo? {
val restoredOutboundGroupSession = store.getCurrentOutboundGroupSessionForRoom(roomId)
if (restoredOutboundGroupSession != null) {
val sessionId = restoredOutboundGroupSession.outboundGroupSession.sessionIdentifier()
// cache it
outboundGroupSessionCache[sessionId] = GroupSessionCacheItem(roomId, restoredOutboundGroupSession.outboundGroupSession)
return MXOutboundSessionInfo(
sessionId = sessionId,
sharedWithHelper = SharedWithHelper(roomId, sessionId, store),
restoredOutboundGroupSession.creationTime
)
}
return null
}
fun discardOutboundGroupSessionForRoom(roomId: String) {
val toDiscard = outboundGroupSessionCache.filter {
it.value.groupId == roomId
}
toDiscard.forEach { (sessionId, cacheItem) ->
cacheItem.groupSession.releaseSession()
outboundGroupSessionCache.remove(sessionId)
}
store.storeCurrentOutboundGroupSessionForRoom(roomId, null)
}
/** /**
* Get the current session key of an outbound group session. * Get the current session key of an outbound group session.
* *
@ -430,7 +474,7 @@ internal class MXOlmDevice @Inject constructor(
fun getSessionKey(sessionId: String): String? { fun getSessionKey(sessionId: String): String? {
if (sessionId.isNotEmpty()) { if (sessionId.isNotEmpty()) {
try { try {
return outboundGroupSessionStore[sessionId]!!.sessionKey() return outboundGroupSessionCache[sessionId]!!.groupSession.sessionKey()
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e, "## getSessionKey() : failed") Timber.e(e, "## getSessionKey() : failed")
} }
@ -446,7 +490,7 @@ internal class MXOlmDevice @Inject constructor(
*/ */
fun getMessageIndex(sessionId: String): Int { fun getMessageIndex(sessionId: String): Int {
return if (sessionId.isNotEmpty()) { return if (sessionId.isNotEmpty()) {
outboundGroupSessionStore[sessionId]!!.messageIndex() outboundGroupSessionCache[sessionId]!!.groupSession.messageIndex()
} else 0 } else 0
} }
@ -460,7 +504,7 @@ internal class MXOlmDevice @Inject constructor(
fun encryptGroupMessage(sessionId: String, payloadString: String): String? { fun encryptGroupMessage(sessionId: String, payloadString: String): String? {
if (sessionId.isNotEmpty() && payloadString.isNotEmpty()) { if (sessionId.isNotEmpty() && payloadString.isNotEmpty()) {
try { try {
return outboundGroupSessionStore[sessionId]!!.encryptMessage(payloadString) return outboundGroupSessionCache[sessionId]!!.groupSession.encryptMessage(payloadString)
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e, "## encryptGroupMessage() : failed") Timber.e(e, "## encryptGroupMessage() : failed")
} }

View file

@ -19,7 +19,6 @@ package org.matrix.android.sdk.internal.crypto
import android.content.Context import android.content.Context
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import org.greenrobot.eventbus.EventBus
import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.auth.data.Credentials
import org.matrix.android.sdk.api.failure.shouldBeRetried import org.matrix.android.sdk.api.failure.shouldBeRetried
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
@ -52,7 +51,6 @@ internal class SendGossipRequestWorker(context: Context,
@Inject lateinit var sendToDeviceTask: SendToDeviceTask @Inject lateinit var sendToDeviceTask: SendToDeviceTask
@Inject lateinit var cryptoStore: IMXCryptoStore @Inject lateinit var cryptoStore: IMXCryptoStore
@Inject lateinit var eventBus: EventBus
@Inject lateinit var credentials: Credentials @Inject lateinit var credentials: Credentials
override fun injectWith(injector: SessionComponent) { override fun injectWith(injector: SessionComponent) {

View file

@ -19,7 +19,6 @@ package org.matrix.android.sdk.internal.crypto
import android.content.Context import android.content.Context
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import org.greenrobot.eventbus.EventBus
import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.auth.data.Credentials
import org.matrix.android.sdk.api.failure.shouldBeRetried import org.matrix.android.sdk.api.failure.shouldBeRetried
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
@ -54,7 +53,6 @@ internal class SendGossipWorker(context: Context,
@Inject lateinit var sendToDeviceTask: SendToDeviceTask @Inject lateinit var sendToDeviceTask: SendToDeviceTask
@Inject lateinit var cryptoStore: IMXCryptoStore @Inject lateinit var cryptoStore: IMXCryptoStore
@Inject lateinit var eventBus: EventBus
@Inject lateinit var credentials: Credentials @Inject lateinit var credentials: Credentials
@Inject lateinit var messageEncrypter: MessageEncrypter @Inject lateinit var messageEncrypter: MessageEncrypter
@Inject lateinit var ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction @Inject lateinit var ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction

View file

@ -68,6 +68,10 @@ internal class MXMegolmEncryption(
// case outboundSession.shareOperation will be non-null.) // case outboundSession.shareOperation will be non-null.)
private var outboundSession: MXOutboundSessionInfo? = null private var outboundSession: MXOutboundSessionInfo? = null
init {
// restore existing outbound session if any
outboundSession = olmDevice.restoreOutboundGroupSessionForRoom(roomId)
}
// Default rotation periods // Default rotation periods
// TODO: Make it configurable via parameters // TODO: Make it configurable via parameters
// Session rotation periods // Session rotation periods
@ -86,6 +90,9 @@ internal class MXMegolmEncryption(
return encryptContent(outboundSession, eventType, eventContent) return encryptContent(outboundSession, eventType, eventContent)
.also { .also {
notifyWithheldForSession(devices.withHeldDevices, outboundSession) notifyWithheldForSession(devices.withHeldDevices, outboundSession)
// annoyingly we have to serialize again the saved outbound session to store message index :/
// if not we would see duplicate message index errors
olmDevice.storeOutboundGroupSessionForRoom(roomId, outboundSession.sessionId)
} }
} }
@ -107,6 +114,7 @@ internal class MXMegolmEncryption(
override fun discardSessionKey() { override fun discardSessionKey() {
outboundSession = null outboundSession = null
olmDevice.discardOutboundGroupSessionForRoom(roomId)
} }
/** /**
@ -116,7 +124,7 @@ internal class MXMegolmEncryption(
*/ */
private fun prepareNewSessionInRoom(): MXOutboundSessionInfo { private fun prepareNewSessionInRoom(): MXOutboundSessionInfo {
Timber.v("## CRYPTO | prepareNewSessionInRoom() ") Timber.v("## CRYPTO | prepareNewSessionInRoom() ")
val sessionId = olmDevice.createOutboundGroupSession() val sessionId = olmDevice.createOutboundGroupSessionForRoom(roomId)
val keysClaimedMap = HashMap<String, String>() val keysClaimedMap = HashMap<String, String>()
keysClaimedMap["ed25519"] = olmDevice.deviceEd25519Key!! keysClaimedMap["ed25519"] = olmDevice.deviceEd25519Key!!
@ -152,7 +160,7 @@ internal class MXMegolmEncryption(
val deviceIds = devicesInRoom.getUserDeviceIds(userId) val deviceIds = devicesInRoom.getUserDeviceIds(userId)
for (deviceId in deviceIds!!) { for (deviceId in deviceIds!!) {
val deviceInfo = devicesInRoom.getObject(userId, deviceId) val deviceInfo = devicesInRoom.getObject(userId, deviceId)
if (deviceInfo != null && !cryptoStore.wasSessionSharedWithUser(roomId, safeSession.sessionId, userId, deviceId).found) { if (deviceInfo != null && !cryptoStore.getSharedSessionInfo(roomId, safeSession.sessionId, userId, deviceId).found) {
val devices = shareMap.getOrPut(userId) { ArrayList() } val devices = shareMap.getOrPut(userId) { ArrayList() }
devices.add(deviceInfo) devices.add(deviceInfo)
} }
@ -401,11 +409,18 @@ internal class MXMegolmEncryption(
.also { Timber.w("## Crypto reshareKey: Device not found") } .also { Timber.w("## Crypto reshareKey: Device not found") }
// Get the chain index of the key we previously sent this device // Get the chain index of the key we previously sent this device
val chainIndex = outboundSession?.sharedWithHelper?.wasSharedWith(userId, deviceId) ?: return false val wasSessionSharedWithUser = cryptoStore.getSharedSessionInfo(roomId, sessionId, userId, deviceId)
.also { if (!wasSessionSharedWithUser.found) {
// This session was never shared with this user
// Send a room key with held // Send a room key with held
notifyKeyWithHeld(listOf(UserDevice(userId, deviceId)), sessionId, senderKey, WithHeldCode.UNAUTHORISED) notifyKeyWithHeld(listOf(UserDevice(userId, deviceId)), sessionId, senderKey, WithHeldCode.UNAUTHORISED)
Timber.w("## Crypto reshareKey: ERROR : Never share megolm with this device") Timber.w("## Crypto reshareKey: ERROR : Never shared megolm with this device")
return false
}
// if found chain index should not be null
val chainIndex = wasSessionSharedWithUser.chainIndex ?: return false
.also {
Timber.w("## Crypto reshareKey: Null chain index")
} }
val devicesByUser = mapOf(userId to listOf(deviceInfo)) val devicesByUser = mapOf(userId to listOf(deviceInfo))

View file

@ -23,9 +23,9 @@ import timber.log.Timber
internal class MXOutboundSessionInfo( internal class MXOutboundSessionInfo(
// The id of the session // The id of the session
val sessionId: String, val sessionId: String,
val sharedWithHelper: SharedWithHelper) { val sharedWithHelper: SharedWithHelper,
// When the session was created // When the session was created
private val creationTime = System.currentTimeMillis() private val creationTime: Long = System.currentTimeMillis()) {
// Number of times this session has been used // Number of times this session has been used
var useCount: Int = 0 var useCount: Int = 0

View file

@ -28,10 +28,6 @@ internal class SharedWithHelper(
return cryptoStore.getSharedWithInfo(roomId, sessionId) return cryptoStore.getSharedWithInfo(roomId, sessionId)
} }
fun wasSharedWith(userId: String, deviceId: String): Int? {
return cryptoStore.wasSessionSharedWithUser(roomId, sessionId, userId, deviceId).chainIndex
}
fun markedSessionAsShared(userId: String, deviceId: String, chainIndex: Int) { fun markedSessionAsShared(userId: String, deviceId: String, chainIndex: Int) {
cryptoStore.markedSessionAsShared(roomId, sessionId, userId, deviceId, chainIndex) cryptoStore.markedSessionAsShared(roomId, sessionId, userId, deviceId, chainIndex)
} }

View file

@ -18,7 +18,7 @@ package org.matrix.android.sdk.internal.crypto.attachments
import android.os.Parcelable import android.os.Parcelable
import org.matrix.android.sdk.internal.crypto.model.rest.EncryptedFileInfo import org.matrix.android.sdk.internal.crypto.model.rest.EncryptedFileInfo
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
fun EncryptedFileInfo.toElementToDecrypt(): ElementToDecrypt? { fun EncryptedFileInfo.toElementToDecrypt(): ElementToDecrypt? {
// Check the validity of some fields // Check the validity of some fields

View file

@ -19,20 +19,20 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.CreateKeysBackupVersionBody import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.CreateKeysBackupVersionBody
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
import org.greenrobot.eventbus.EventBus
import javax.inject.Inject import javax.inject.Inject
internal interface CreateKeysBackupVersionTask : Task<CreateKeysBackupVersionBody, KeysVersion> internal interface CreateKeysBackupVersionTask : Task<CreateKeysBackupVersionBody, KeysVersion>
internal class DefaultCreateKeysBackupVersionTask @Inject constructor( internal class DefaultCreateKeysBackupVersionTask @Inject constructor(
private val roomKeysApi: RoomKeysApi, private val roomKeysApi: RoomKeysApi,
private val eventBus: EventBus private val globalErrorReceiver: GlobalErrorReceiver
) : CreateKeysBackupVersionTask { ) : CreateKeysBackupVersionTask {
override suspend fun execute(params: CreateKeysBackupVersionBody): KeysVersion { override suspend fun execute(params: CreateKeysBackupVersionBody): KeysVersion {
return executeRequest(eventBus) { return executeRequest(globalErrorReceiver) {
apiCall = roomKeysApi.createKeysBackupVersion(params) apiCall = roomKeysApi.createKeysBackupVersion(params)
} }
} }

View file

@ -17,9 +17,9 @@
package org.matrix.android.sdk.internal.crypto.keysbackup.tasks package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
import org.greenrobot.eventbus.EventBus
import javax.inject.Inject import javax.inject.Inject
internal interface DeleteBackupTask : Task<DeleteBackupTask.Params, Unit> { internal interface DeleteBackupTask : Task<DeleteBackupTask.Params, Unit> {
@ -30,11 +30,11 @@ internal interface DeleteBackupTask : Task<DeleteBackupTask.Params, Unit> {
internal class DefaultDeleteBackupTask @Inject constructor( internal class DefaultDeleteBackupTask @Inject constructor(
private val roomKeysApi: RoomKeysApi, private val roomKeysApi: RoomKeysApi,
private val eventBus: EventBus private val globalErrorReceiver: GlobalErrorReceiver
) : DeleteBackupTask { ) : DeleteBackupTask {
override suspend fun execute(params: DeleteBackupTask.Params) { override suspend fun execute(params: DeleteBackupTask.Params) {
return executeRequest(eventBus) { return executeRequest(globalErrorReceiver) {
apiCall = roomKeysApi.deleteBackup(params.version) apiCall = roomKeysApi.deleteBackup(params.version)
} }
} }

View file

@ -17,9 +17,9 @@
package org.matrix.android.sdk.internal.crypto.keysbackup.tasks package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
import org.greenrobot.eventbus.EventBus
import javax.inject.Inject import javax.inject.Inject
internal interface DeleteRoomSessionDataTask : Task<DeleteRoomSessionDataTask.Params, Unit> { internal interface DeleteRoomSessionDataTask : Task<DeleteRoomSessionDataTask.Params, Unit> {
@ -32,11 +32,11 @@ internal interface DeleteRoomSessionDataTask : Task<DeleteRoomSessionDataTask.Pa
internal class DefaultDeleteRoomSessionDataTask @Inject constructor( internal class DefaultDeleteRoomSessionDataTask @Inject constructor(
private val roomKeysApi: RoomKeysApi, private val roomKeysApi: RoomKeysApi,
private val eventBus: EventBus private val globalErrorReceiver: GlobalErrorReceiver
) : DeleteRoomSessionDataTask { ) : DeleteRoomSessionDataTask {
override suspend fun execute(params: DeleteRoomSessionDataTask.Params) { override suspend fun execute(params: DeleteRoomSessionDataTask.Params) {
return executeRequest(eventBus) { return executeRequest(globalErrorReceiver) {
apiCall = roomKeysApi.deleteRoomSessionData( apiCall = roomKeysApi.deleteRoomSessionData(
params.roomId, params.roomId,
params.sessionId, params.sessionId,

View file

@ -17,9 +17,9 @@
package org.matrix.android.sdk.internal.crypto.keysbackup.tasks package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
import org.greenrobot.eventbus.EventBus
import javax.inject.Inject import javax.inject.Inject
internal interface DeleteRoomSessionsDataTask : Task<DeleteRoomSessionsDataTask.Params, Unit> { internal interface DeleteRoomSessionsDataTask : Task<DeleteRoomSessionsDataTask.Params, Unit> {
@ -31,11 +31,11 @@ internal interface DeleteRoomSessionsDataTask : Task<DeleteRoomSessionsDataTask.
internal class DefaultDeleteRoomSessionsDataTask @Inject constructor( internal class DefaultDeleteRoomSessionsDataTask @Inject constructor(
private val roomKeysApi: RoomKeysApi, private val roomKeysApi: RoomKeysApi,
private val eventBus: EventBus private val globalErrorReceiver: GlobalErrorReceiver
) : DeleteRoomSessionsDataTask { ) : DeleteRoomSessionsDataTask {
override suspend fun execute(params: DeleteRoomSessionsDataTask.Params) { override suspend fun execute(params: DeleteRoomSessionsDataTask.Params) {
return executeRequest(eventBus) { return executeRequest(globalErrorReceiver) {
apiCall = roomKeysApi.deleteRoomSessionsData( apiCall = roomKeysApi.deleteRoomSessionsData(
params.roomId, params.roomId,
params.version) params.version)

View file

@ -17,9 +17,9 @@
package org.matrix.android.sdk.internal.crypto.keysbackup.tasks package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
import org.greenrobot.eventbus.EventBus
import javax.inject.Inject import javax.inject.Inject
internal interface DeleteSessionsDataTask : Task<DeleteSessionsDataTask.Params, Unit> { internal interface DeleteSessionsDataTask : Task<DeleteSessionsDataTask.Params, Unit> {
@ -30,11 +30,11 @@ internal interface DeleteSessionsDataTask : Task<DeleteSessionsDataTask.Params,
internal class DefaultDeleteSessionsDataTask @Inject constructor( internal class DefaultDeleteSessionsDataTask @Inject constructor(
private val roomKeysApi: RoomKeysApi, private val roomKeysApi: RoomKeysApi,
private val eventBus: EventBus private val globalErrorReceiver: GlobalErrorReceiver
) : DeleteSessionsDataTask { ) : DeleteSessionsDataTask {
override suspend fun execute(params: DeleteSessionsDataTask.Params) { override suspend fun execute(params: DeleteSessionsDataTask.Params) {
return executeRequest(eventBus) { return executeRequest(globalErrorReceiver) {
apiCall = roomKeysApi.deleteSessionsData(params.version) apiCall = roomKeysApi.deleteSessionsData(params.version)
} }
} }

View file

@ -18,20 +18,20 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
import org.greenrobot.eventbus.EventBus
import javax.inject.Inject import javax.inject.Inject
internal interface GetKeysBackupLastVersionTask : Task<Unit, KeysVersionResult> internal interface GetKeysBackupLastVersionTask : Task<Unit, KeysVersionResult>
internal class DefaultGetKeysBackupLastVersionTask @Inject constructor( internal class DefaultGetKeysBackupLastVersionTask @Inject constructor(
private val roomKeysApi: RoomKeysApi, private val roomKeysApi: RoomKeysApi,
private val eventBus: EventBus private val globalErrorReceiver: GlobalErrorReceiver
) : GetKeysBackupLastVersionTask { ) : GetKeysBackupLastVersionTask {
override suspend fun execute(params: Unit): KeysVersionResult { override suspend fun execute(params: Unit): KeysVersionResult {
return executeRequest(eventBus) { return executeRequest(globalErrorReceiver) {
apiCall = roomKeysApi.getKeysBackupLastVersion() apiCall = roomKeysApi.getKeysBackupLastVersion()
} }
} }

View file

@ -18,20 +18,20 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
import org.greenrobot.eventbus.EventBus
import javax.inject.Inject import javax.inject.Inject
internal interface GetKeysBackupVersionTask : Task<String, KeysVersionResult> internal interface GetKeysBackupVersionTask : Task<String, KeysVersionResult>
internal class DefaultGetKeysBackupVersionTask @Inject constructor( internal class DefaultGetKeysBackupVersionTask @Inject constructor(
private val roomKeysApi: RoomKeysApi, private val roomKeysApi: RoomKeysApi,
private val eventBus: EventBus private val globalErrorReceiver: GlobalErrorReceiver
) : GetKeysBackupVersionTask { ) : GetKeysBackupVersionTask {
override suspend fun execute(params: String): KeysVersionResult { override suspend fun execute(params: String): KeysVersionResult {
return executeRequest(eventBus) { return executeRequest(globalErrorReceiver) {
apiCall = roomKeysApi.getKeysBackupVersion(params) apiCall = roomKeysApi.getKeysBackupVersion(params)
} }
} }

View file

@ -18,9 +18,9 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeyBackupData import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeyBackupData
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
import org.greenrobot.eventbus.EventBus
import javax.inject.Inject import javax.inject.Inject
internal interface GetRoomSessionDataTask : Task<GetRoomSessionDataTask.Params, KeyBackupData> { internal interface GetRoomSessionDataTask : Task<GetRoomSessionDataTask.Params, KeyBackupData> {
@ -33,11 +33,11 @@ internal interface GetRoomSessionDataTask : Task<GetRoomSessionDataTask.Params,
internal class DefaultGetRoomSessionDataTask @Inject constructor( internal class DefaultGetRoomSessionDataTask @Inject constructor(
private val roomKeysApi: RoomKeysApi, private val roomKeysApi: RoomKeysApi,
private val eventBus: EventBus private val globalErrorReceiver: GlobalErrorReceiver
) : GetRoomSessionDataTask { ) : GetRoomSessionDataTask {
override suspend fun execute(params: GetRoomSessionDataTask.Params): KeyBackupData { override suspend fun execute(params: GetRoomSessionDataTask.Params): KeyBackupData {
return executeRequest(eventBus) { return executeRequest(globalErrorReceiver) {
apiCall = roomKeysApi.getRoomSessionData( apiCall = roomKeysApi.getRoomSessionData(
params.roomId, params.roomId,
params.sessionId, params.sessionId,

View file

@ -18,9 +18,9 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.RoomKeysBackupData import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.RoomKeysBackupData
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
import org.greenrobot.eventbus.EventBus
import javax.inject.Inject import javax.inject.Inject
internal interface GetRoomSessionsDataTask : Task<GetRoomSessionsDataTask.Params, RoomKeysBackupData> { internal interface GetRoomSessionsDataTask : Task<GetRoomSessionsDataTask.Params, RoomKeysBackupData> {
@ -32,11 +32,11 @@ internal interface GetRoomSessionsDataTask : Task<GetRoomSessionsDataTask.Params
internal class DefaultGetRoomSessionsDataTask @Inject constructor( internal class DefaultGetRoomSessionsDataTask @Inject constructor(
private val roomKeysApi: RoomKeysApi, private val roomKeysApi: RoomKeysApi,
private val eventBus: EventBus private val globalErrorReceiver: GlobalErrorReceiver
) : GetRoomSessionsDataTask { ) : GetRoomSessionsDataTask {
override suspend fun execute(params: GetRoomSessionsDataTask.Params): RoomKeysBackupData { override suspend fun execute(params: GetRoomSessionsDataTask.Params): RoomKeysBackupData {
return executeRequest(eventBus) { return executeRequest(globalErrorReceiver) {
apiCall = roomKeysApi.getRoomSessionsData( apiCall = roomKeysApi.getRoomSessionsData(
params.roomId, params.roomId,
params.version) params.version)

View file

@ -18,9 +18,9 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysBackupData import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysBackupData
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
import org.greenrobot.eventbus.EventBus
import javax.inject.Inject import javax.inject.Inject
internal interface GetSessionsDataTask : Task<GetSessionsDataTask.Params, KeysBackupData> { internal interface GetSessionsDataTask : Task<GetSessionsDataTask.Params, KeysBackupData> {
@ -31,11 +31,11 @@ internal interface GetSessionsDataTask : Task<GetSessionsDataTask.Params, KeysBa
internal class DefaultGetSessionsDataTask @Inject constructor( internal class DefaultGetSessionsDataTask @Inject constructor(
private val roomKeysApi: RoomKeysApi, private val roomKeysApi: RoomKeysApi,
private val eventBus: EventBus private val globalErrorReceiver: GlobalErrorReceiver
) : GetSessionsDataTask { ) : GetSessionsDataTask {
override suspend fun execute(params: GetSessionsDataTask.Params): KeysBackupData { override suspend fun execute(params: GetSessionsDataTask.Params): KeysBackupData {
return executeRequest(eventBus) { return executeRequest(globalErrorReceiver) {
apiCall = roomKeysApi.getSessionsData(params.version) apiCall = roomKeysApi.getSessionsData(params.version)
} }
} }

View file

@ -19,9 +19,9 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.BackupKeysResult import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.BackupKeysResult
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeyBackupData import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeyBackupData
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
import org.greenrobot.eventbus.EventBus
import javax.inject.Inject import javax.inject.Inject
internal interface StoreRoomSessionDataTask : Task<StoreRoomSessionDataTask.Params, BackupKeysResult> { internal interface StoreRoomSessionDataTask : Task<StoreRoomSessionDataTask.Params, BackupKeysResult> {
@ -35,11 +35,11 @@ internal interface StoreRoomSessionDataTask : Task<StoreRoomSessionDataTask.Para
internal class DefaultStoreRoomSessionDataTask @Inject constructor( internal class DefaultStoreRoomSessionDataTask @Inject constructor(
private val roomKeysApi: RoomKeysApi, private val roomKeysApi: RoomKeysApi,
private val eventBus: EventBus private val globalErrorReceiver: GlobalErrorReceiver
) : StoreRoomSessionDataTask { ) : StoreRoomSessionDataTask {
override suspend fun execute(params: StoreRoomSessionDataTask.Params): BackupKeysResult { override suspend fun execute(params: StoreRoomSessionDataTask.Params): BackupKeysResult {
return executeRequest(eventBus) { return executeRequest(globalErrorReceiver) {
apiCall = roomKeysApi.storeRoomSessionData( apiCall = roomKeysApi.storeRoomSessionData(
params.roomId, params.roomId,
params.sessionId, params.sessionId,

View file

@ -19,9 +19,9 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.BackupKeysResult import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.BackupKeysResult
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.RoomKeysBackupData import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.RoomKeysBackupData
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
import org.greenrobot.eventbus.EventBus
import javax.inject.Inject import javax.inject.Inject
internal interface StoreRoomSessionsDataTask : Task<StoreRoomSessionsDataTask.Params, BackupKeysResult> { internal interface StoreRoomSessionsDataTask : Task<StoreRoomSessionsDataTask.Params, BackupKeysResult> {
@ -34,11 +34,11 @@ internal interface StoreRoomSessionsDataTask : Task<StoreRoomSessionsDataTask.Pa
internal class DefaultStoreRoomSessionsDataTask @Inject constructor( internal class DefaultStoreRoomSessionsDataTask @Inject constructor(
private val roomKeysApi: RoomKeysApi, private val roomKeysApi: RoomKeysApi,
private val eventBus: EventBus private val globalErrorReceiver: GlobalErrorReceiver
) : StoreRoomSessionsDataTask { ) : StoreRoomSessionsDataTask {
override suspend fun execute(params: StoreRoomSessionsDataTask.Params): BackupKeysResult { override suspend fun execute(params: StoreRoomSessionsDataTask.Params): BackupKeysResult {
return executeRequest(eventBus) { return executeRequest(globalErrorReceiver) {
apiCall = roomKeysApi.storeRoomSessionsData( apiCall = roomKeysApi.storeRoomSessionsData(
params.roomId, params.roomId,
params.version, params.version,

View file

@ -19,9 +19,9 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.BackupKeysResult import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.BackupKeysResult
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysBackupData import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysBackupData
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
import org.greenrobot.eventbus.EventBus
import javax.inject.Inject import javax.inject.Inject
internal interface StoreSessionsDataTask : Task<StoreSessionsDataTask.Params, BackupKeysResult> { internal interface StoreSessionsDataTask : Task<StoreSessionsDataTask.Params, BackupKeysResult> {
@ -33,11 +33,11 @@ internal interface StoreSessionsDataTask : Task<StoreSessionsDataTask.Params, Ba
internal class DefaultStoreSessionsDataTask @Inject constructor( internal class DefaultStoreSessionsDataTask @Inject constructor(
private val roomKeysApi: RoomKeysApi, private val roomKeysApi: RoomKeysApi,
private val eventBus: EventBus private val globalErrorReceiver: GlobalErrorReceiver
) : StoreSessionsDataTask { ) : StoreSessionsDataTask {
override suspend fun execute(params: StoreSessionsDataTask.Params): BackupKeysResult { override suspend fun execute(params: StoreSessionsDataTask.Params): BackupKeysResult {
return executeRequest(eventBus) { return executeRequest(globalErrorReceiver) {
apiCall = roomKeysApi.storeSessionsData( apiCall = roomKeysApi.storeSessionsData(
params.version, params.version,
params.keysBackupData) params.keysBackupData)

View file

@ -18,9 +18,9 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.tasks
import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.UpdateKeysBackupVersionBody import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.UpdateKeysBackupVersionBody
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
import org.greenrobot.eventbus.EventBus
import javax.inject.Inject import javax.inject.Inject
internal interface UpdateKeysBackupVersionTask : Task<UpdateKeysBackupVersionTask.Params, Unit> { internal interface UpdateKeysBackupVersionTask : Task<UpdateKeysBackupVersionTask.Params, Unit> {
@ -32,11 +32,11 @@ internal interface UpdateKeysBackupVersionTask : Task<UpdateKeysBackupVersionTas
internal class DefaultUpdateKeysBackupVersionTask @Inject constructor( internal class DefaultUpdateKeysBackupVersionTask @Inject constructor(
private val roomKeysApi: RoomKeysApi, private val roomKeysApi: RoomKeysApi,
private val eventBus: EventBus private val globalErrorReceiver: GlobalErrorReceiver
) : UpdateKeysBackupVersionTask { ) : UpdateKeysBackupVersionTask {
override suspend fun execute(params: UpdateKeysBackupVersionTask.Params) { override suspend fun execute(params: UpdateKeysBackupVersionTask.Params) {
return executeRequest(eventBus) { return executeRequest(globalErrorReceiver) {
apiCall = roomKeysApi.updateKeysBackupVersion(params.version, params.keysBackupVersionBody) apiCall = roomKeysApi.updateKeysBackupVersion(params.version, params.keysBackupVersionBody)
} }
} }

View file

@ -0,0 +1,24 @@
/*
* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.crypto.model
import org.matrix.olm.OlmOutboundGroupSession
data class OutboundGroupSessionWrapper(
val outboundGroupSession: OlmOutboundGroupSession,
val creationTime: Long
)

View file

@ -33,11 +33,13 @@ import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2 import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
import org.matrix.android.sdk.internal.crypto.model.OutboundGroupSessionWrapper
import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyWithHeldContent import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyWithHeldContent
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody
import org.matrix.android.sdk.internal.crypto.store.db.model.KeysBackupDataEntity import org.matrix.android.sdk.internal.crypto.store.db.model.KeysBackupDataEntity
import org.matrix.olm.OlmAccount import org.matrix.olm.OlmAccount
import org.matrix.olm.OlmOutboundGroupSession
/** /**
* the crypto data store * the crypto data store
@ -293,6 +295,16 @@ internal interface IMXCryptoStore {
*/ */
fun getInboundGroupSession(sessionId: String, senderKey: String): OlmInboundGroupSessionWrapper2? fun getInboundGroupSession(sessionId: String, senderKey: String): OlmInboundGroupSessionWrapper2?
/**
* Get the current outbound group session for this encrypted room
*/
fun getCurrentOutboundGroupSessionForRoom(roomId: String): OutboundGroupSessionWrapper?
/**
* Get the current outbound group session for this encrypted room
*/
fun storeCurrentOutboundGroupSessionForRoom(roomId: String, outboundGroupSession: OlmOutboundGroupSession?)
/** /**
* Remove an inbound group session * Remove an inbound group session
* *
@ -439,7 +451,15 @@ internal interface IMXCryptoStore {
fun getWithHeldMegolmSession(roomId: String, sessionId: String): RoomKeyWithHeldContent? fun getWithHeldMegolmSession(roomId: String, sessionId: String): RoomKeyWithHeldContent?
fun markedSessionAsShared(roomId: String?, sessionId: String, userId: String, deviceId: String, chainIndex: Int) fun markedSessionAsShared(roomId: String?, sessionId: String, userId: String, deviceId: String, chainIndex: Int)
fun wasSessionSharedWithUser(roomId: String?, sessionId: String, userId: String, deviceId: String): SharedSessionResult
/**
* Query for information on this session sharing history.
* @return SharedSessionResult
* if found is true then this session was initialy shared with that user|device,
* in this case chainIndex is not nullindicates the ratchet position.
* In found is false, chainIndex is null
*/
fun getSharedSessionInfo(roomId: String?, sessionId: String, userId: String, deviceId: String): SharedSessionResult
data class SharedSessionResult(val found: Boolean, val chainIndex: Int?) data class SharedSessionResult(val found: Boolean, val chainIndex: Int?)
fun getSharedWithInfo(roomId: String?, sessionId: String): MXUsersDevicesMap<Int> fun getSharedWithInfo(roomId: String?, sessionId: String): MXUsersDevicesMap<Int>

View file

@ -47,6 +47,7 @@ import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2 import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
import org.matrix.android.sdk.internal.crypto.model.OutboundGroupSessionWrapper
import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyWithHeldContent import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyWithHeldContent
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody
@ -73,6 +74,7 @@ import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSess
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSessionEntityFields import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSessionEntityFields
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmSessionEntity import org.matrix.android.sdk.internal.crypto.store.db.model.OlmSessionEntity
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmSessionEntityFields import org.matrix.android.sdk.internal.crypto.store.db.model.OlmSessionEntityFields
import org.matrix.android.sdk.internal.crypto.store.db.model.OutboundGroupSessionInfoEntity
import org.matrix.android.sdk.internal.crypto.store.db.model.OutgoingGossipingRequestEntity import org.matrix.android.sdk.internal.crypto.store.db.model.OutgoingGossipingRequestEntity
import org.matrix.android.sdk.internal.crypto.store.db.model.OutgoingGossipingRequestEntityFields import org.matrix.android.sdk.internal.crypto.store.db.model.OutgoingGossipingRequestEntityFields
import org.matrix.android.sdk.internal.crypto.store.db.model.SharedSessionEntity import org.matrix.android.sdk.internal.crypto.store.db.model.SharedSessionEntity
@ -95,6 +97,7 @@ import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.olm.OlmAccount import org.matrix.olm.OlmAccount
import org.matrix.olm.OlmException import org.matrix.olm.OlmException
import org.matrix.olm.OlmOutboundGroupSession
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import kotlin.collections.set import kotlin.collections.set
@ -756,6 +759,42 @@ internal class RealmCryptoStore @Inject constructor(
return inboundGroupSessionToRelease[key] return inboundGroupSessionToRelease[key]
} }
override fun getCurrentOutboundGroupSessionForRoom(roomId: String): OutboundGroupSessionWrapper? {
return doWithRealm(realmConfiguration) { realm ->
realm.where<CryptoRoomEntity>()
.equalTo(CryptoRoomEntityFields.ROOM_ID, roomId)
.findFirst()?.outboundSessionInfo?.let { entity ->
entity.getOutboundGroupSession()?.let {
OutboundGroupSessionWrapper(
it,
entity.creationTime ?: 0
)
}
}
}
}
override fun storeCurrentOutboundGroupSessionForRoom(roomId: String, outboundGroupSession: OlmOutboundGroupSession?) {
// we can do this async, as it's just for restoring on next launch
// the olmdevice is caching the active instance
// this is called for each sent message (so not high frequency), thus we can use basic realm async without
// risk of reaching max async operation limit?
doRealmTransactionAsync(realmConfiguration) { realm ->
CryptoRoomEntity.getById(realm, roomId)?.let { entity ->
// we should delete existing outbound session info if any
entity.outboundSessionInfo?.deleteFromRealm()
if (outboundGroupSession != null) {
val info = realm.createObject(OutboundGroupSessionInfoEntity::class.java).apply {
creationTime = System.currentTimeMillis()
putOutboundGroupSession(outboundGroupSession)
}
entity.outboundSessionInfo = info
}
}
}
}
/** /**
* Note: the result will be only use to export all the keys and not to use the OlmInboundGroupSessionWrapper2, * Note: the result will be only use to export all the keys and not to use the OlmInboundGroupSessionWrapper2,
* so there is no need to use or update `inboundGroupSessionToRelease` for native memory management * so there is no need to use or update `inboundGroupSessionToRelease` for native memory management
@ -1645,7 +1684,7 @@ internal class RealmCryptoStore @Inject constructor(
} }
} }
override fun wasSessionSharedWithUser(roomId: String?, sessionId: String, userId: String, deviceId: String): IMXCryptoStore.SharedSessionResult { override fun getSharedSessionInfo(roomId: String?, sessionId: String, userId: String, deviceId: String): IMXCryptoStore.SharedSessionResult {
return doWithRealm(realmConfiguration) { realm -> return doWithRealm(realmConfiguration) { realm ->
SharedSessionEntity.get(realm, roomId, sessionId, userId, deviceId)?.let { SharedSessionEntity.get(realm, roomId, sessionId, userId, deviceId)?.let {
IMXCryptoStore.SharedSessionResult(true, it.chainIndex) IMXCryptoStore.SharedSessionResult(true, it.chainIndex)

View file

@ -44,6 +44,7 @@ import org.matrix.android.sdk.internal.di.SerializeNulls
import io.realm.DynamicRealm import io.realm.DynamicRealm
import io.realm.RealmMigration import io.realm.RealmMigration
import io.realm.RealmObjectSchema import io.realm.RealmObjectSchema
import org.matrix.android.sdk.internal.crypto.store.db.model.OutboundGroupSessionInfoEntityFields
import org.matrix.androidsdk.crypto.data.MXOlmInboundGroupSession2 import org.matrix.androidsdk.crypto.data.MXOlmInboundGroupSession2
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -55,7 +56,7 @@ internal class RealmCryptoStoreMigration @Inject constructor(private val crossSi
// 0, 1, 2: legacy Riot-Android // 0, 1, 2: legacy Riot-Android
// 3: migrate to RiotX schema // 3: migrate to RiotX schema
// 4, 5, 6, 7, 8, 9: migrations from RiotX (which was previously 1, 2, 3, 4, 5, 6) // 4, 5, 6, 7, 8, 9: migrations from RiotX (which was previously 1, 2, 3, 4, 5, 6)
const val CRYPTO_STORE_SCHEMA_VERSION = 11L const val CRYPTO_STORE_SCHEMA_VERSION = 12L
} }
private fun RealmObjectSchema.addFieldIfNotExists(fieldName: String, fieldType: Class<*>): RealmObjectSchema { private fun RealmObjectSchema.addFieldIfNotExists(fieldName: String, fieldType: Class<*>): RealmObjectSchema {
@ -93,6 +94,7 @@ internal class RealmCryptoStoreMigration @Inject constructor(private val crossSi
if (oldVersion <= 8) migrateTo9(realm) if (oldVersion <= 8) migrateTo9(realm)
if (oldVersion <= 9) migrateTo10(realm) if (oldVersion <= 9) migrateTo10(realm)
if (oldVersion <= 10) migrateTo11(realm) if (oldVersion <= 10) migrateTo11(realm)
if (oldVersion <= 11) migrateTo12(realm)
} }
private fun migrateTo1Legacy(realm: DynamicRealm) { private fun migrateTo1Legacy(realm: DynamicRealm) {
@ -483,4 +485,16 @@ internal class RealmCryptoStoreMigration @Inject constructor(private val crossSi
realm.schema.get("CryptoMetadataEntity") realm.schema.get("CryptoMetadataEntity")
?.addField(CryptoMetadataEntityFields.DEVICE_KEYS_SENT_TO_SERVER, Boolean::class.java) ?.addField(CryptoMetadataEntityFields.DEVICE_KEYS_SENT_TO_SERVER, Boolean::class.java)
} }
// Version 12L added outbound group session persistence
private fun migrateTo12(realm: DynamicRealm) {
Timber.d("Step 11 -> 12")
val outboundEntitySchema = realm.schema.create("OutboundGroupSessionInfoEntity")
.addField(OutboundGroupSessionInfoEntityFields.SERIALIZED_OUTBOUND_SESSION_DATA, String::class.java)
.addField(OutboundGroupSessionInfoEntityFields.CREATION_TIME, Long::class.java)
.setNullable(OutboundGroupSessionInfoEntityFields.CREATION_TIME, true)
realm.schema.get("CryptoRoomEntity")
?.addRealmObjectField(CryptoRoomEntityFields.OUTBOUND_SESSION_INFO.`$`, outboundEntitySchema)
}
} }

View file

@ -33,6 +33,7 @@ import org.matrix.android.sdk.internal.crypto.store.db.model.TrustLevelEntity
import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntity import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntity
import org.matrix.android.sdk.internal.crypto.store.db.model.WithHeldSessionEntity import org.matrix.android.sdk.internal.crypto.store.db.model.WithHeldSessionEntity
import io.realm.annotations.RealmModule import io.realm.annotations.RealmModule
import org.matrix.android.sdk.internal.crypto.store.db.model.OutboundGroupSessionInfoEntity
/** /**
* Realm module for Crypto store classes * Realm module for Crypto store classes
@ -54,6 +55,7 @@ import io.realm.annotations.RealmModule
OutgoingGossipingRequestEntity::class, OutgoingGossipingRequestEntity::class,
MyDeviceLastSeenInfoEntity::class, MyDeviceLastSeenInfoEntity::class,
WithHeldSessionEntity::class, WithHeldSessionEntity::class,
SharedSessionEntity::class SharedSessionEntity::class,
OutboundGroupSessionInfoEntity::class
]) ])
internal class RealmCryptoStoreModule internal class RealmCryptoStoreModule

View file

@ -23,7 +23,12 @@ internal open class CryptoRoomEntity(
@PrimaryKey var roomId: String? = null, @PrimaryKey var roomId: String? = null,
var algorithm: String? = null, var algorithm: String? = null,
var shouldEncryptForInvitedMembers: Boolean? = null, var shouldEncryptForInvitedMembers: Boolean? = null,
var blacklistUnverifiedDevices: Boolean = false) var blacklistUnverifiedDevices: Boolean = false,
// Store the current outbound session for this room,
// to avoid re-create and re-share at each startup (if rotation not needed..)
// This is specific to megolm but not sure how to model it better
var outboundSessionInfo: OutboundGroupSessionInfoEntity? = null
)
: RealmObject() { : RealmObject() {
companion object companion object

View file

@ -0,0 +1,44 @@
/*
* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.crypto.store.db.model
import io.realm.RealmObject
import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm
import org.matrix.android.sdk.internal.crypto.store.db.serializeForRealm
import org.matrix.olm.OlmOutboundGroupSession
import timber.log.Timber
internal open class OutboundGroupSessionInfoEntity(
var serializedOutboundSessionData: String? = null,
var creationTime: Long? = null
) : RealmObject() {
fun getOutboundGroupSession(): OlmOutboundGroupSession? {
return try {
deserializeFromRealm(serializedOutboundSessionData)
} catch (failure: Throwable) {
Timber.e(failure, "## getOutboundGroupSession() Deserialization failure")
return null
}
}
fun putOutboundGroupSession(olmOutboundGroupSession: OlmOutboundGroupSession?) {
serializedOutboundSessionData = serializeForRealm(olmOutboundGroupSession)
}
companion object
}

View file

@ -21,9 +21,9 @@ import org.matrix.android.sdk.internal.crypto.model.MXKey
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.rest.KeysClaimBody import org.matrix.android.sdk.internal.crypto.model.rest.KeysClaimBody
import org.matrix.android.sdk.internal.crypto.model.rest.KeysClaimResponse import org.matrix.android.sdk.internal.crypto.model.rest.KeysClaimResponse
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
import org.greenrobot.eventbus.EventBus
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -36,13 +36,13 @@ internal interface ClaimOneTimeKeysForUsersDeviceTask : Task<ClaimOneTimeKeysFor
internal class DefaultClaimOneTimeKeysForUsersDevice @Inject constructor( internal class DefaultClaimOneTimeKeysForUsersDevice @Inject constructor(
private val cryptoApi: CryptoApi, private val cryptoApi: CryptoApi,
private val eventBus: EventBus private val globalErrorReceiver: GlobalErrorReceiver
) : ClaimOneTimeKeysForUsersDeviceTask { ) : ClaimOneTimeKeysForUsersDeviceTask {
override suspend fun execute(params: ClaimOneTimeKeysForUsersDeviceTask.Params): MXUsersDevicesMap<MXKey> { override suspend fun execute(params: ClaimOneTimeKeysForUsersDeviceTask.Params): MXUsersDevicesMap<MXKey> {
val body = KeysClaimBody(oneTimeKeys = params.usersDevicesKeyTypesMap.map) val body = KeysClaimBody(oneTimeKeys = params.usersDevicesKeyTypesMap.map)
val keysClaimResponse = executeRequest<KeysClaimResponse>(eventBus) { val keysClaimResponse = executeRequest<KeysClaimResponse>(globalErrorReceiver) {
apiCall = cryptoApi.claimOneTimeKeysForUsersDevices(body) apiCall = cryptoApi.claimOneTimeKeysForUsersDevices(body)
} }
val map = MXUsersDevicesMap<MXKey>() val map = MXUsersDevicesMap<MXKey>()

View file

@ -20,9 +20,9 @@ import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse
import org.matrix.android.sdk.internal.crypto.api.CryptoApi import org.matrix.android.sdk.internal.crypto.api.CryptoApi
import org.matrix.android.sdk.internal.crypto.model.rest.DeleteDeviceParams import org.matrix.android.sdk.internal.crypto.model.rest.DeleteDeviceParams
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
import org.greenrobot.eventbus.EventBus
import javax.inject.Inject import javax.inject.Inject
internal interface DeleteDeviceTask : Task<DeleteDeviceTask.Params, Unit> { internal interface DeleteDeviceTask : Task<DeleteDeviceTask.Params, Unit> {
@ -33,12 +33,12 @@ internal interface DeleteDeviceTask : Task<DeleteDeviceTask.Params, Unit> {
internal class DefaultDeleteDeviceTask @Inject constructor( internal class DefaultDeleteDeviceTask @Inject constructor(
private val cryptoApi: CryptoApi, private val cryptoApi: CryptoApi,
private val eventBus: EventBus private val globalErrorReceiver: GlobalErrorReceiver
) : DeleteDeviceTask { ) : DeleteDeviceTask {
override suspend fun execute(params: DeleteDeviceTask.Params) { override suspend fun execute(params: DeleteDeviceTask.Params) {
try { try {
executeRequest<Unit>(eventBus) { executeRequest<Unit>(globalErrorReceiver) {
apiCall = cryptoApi.deleteDevice(params.deviceId, DeleteDeviceParams()) apiCall = cryptoApi.deleteDevice(params.deviceId, DeleteDeviceParams())
} }
} catch (throwable: Throwable) { } catch (throwable: Throwable) {

View file

@ -21,9 +21,9 @@ import org.matrix.android.sdk.internal.crypto.api.CryptoApi
import org.matrix.android.sdk.internal.crypto.model.rest.DeleteDeviceParams import org.matrix.android.sdk.internal.crypto.model.rest.DeleteDeviceParams
import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
import org.greenrobot.eventbus.EventBus
import javax.inject.Inject import javax.inject.Inject
internal interface DeleteDeviceWithUserPasswordTask : Task<DeleteDeviceWithUserPasswordTask.Params, Unit> { internal interface DeleteDeviceWithUserPasswordTask : Task<DeleteDeviceWithUserPasswordTask.Params, Unit> {
@ -37,11 +37,11 @@ internal interface DeleteDeviceWithUserPasswordTask : Task<DeleteDeviceWithUserP
internal class DefaultDeleteDeviceWithUserPasswordTask @Inject constructor( internal class DefaultDeleteDeviceWithUserPasswordTask @Inject constructor(
private val cryptoApi: CryptoApi, private val cryptoApi: CryptoApi,
@UserId private val userId: String, @UserId private val userId: String,
private val eventBus: EventBus private val globalErrorReceiver: GlobalErrorReceiver
) : DeleteDeviceWithUserPasswordTask { ) : DeleteDeviceWithUserPasswordTask {
override suspend fun execute(params: DeleteDeviceWithUserPasswordTask.Params) { override suspend fun execute(params: DeleteDeviceWithUserPasswordTask.Params) {
return executeRequest(eventBus) { return executeRequest(globalErrorReceiver) {
apiCall = cryptoApi.deleteDevice(params.deviceId, apiCall = cryptoApi.deleteDevice(params.deviceId,
DeleteDeviceParams( DeleteDeviceParams(
userPasswordAuth = UserPasswordAuth( userPasswordAuth = UserPasswordAuth(

Some files were not shown because too many files have changed in this diff Show more