mirror of
https://github.com/vector-im/riotX-android
synced 2025-10-06 00:02:48 +02:00
Compare commits
344 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ce6d4c4a64 | ||
|
061f3c6919 | ||
|
9b13381938 | ||
|
bc22647b48 | ||
|
73fccdd6de | ||
|
ad9873c565 | ||
|
e976100f1a | ||
|
289a3ab21f | ||
|
286f00396d | ||
|
2a12cf22f5 | ||
|
97daf57b0a | ||
|
697b551b9b | ||
|
0211197c47 | ||
|
6ac2717d8b | ||
|
49aed39711 | ||
|
a820443c56 | ||
|
67aa239398 | ||
|
2a3962265b | ||
|
e6fc605b08 | ||
|
8129cd0cd3 | ||
|
0bc203e0d5 | ||
|
6c9b16088f | ||
|
76b425ee8a | ||
|
5b8215a356 | ||
|
9c7df25862 | ||
|
da16ec0af3 | ||
|
1244d00b31 | ||
|
98054815a4 | ||
|
06be3e691d | ||
|
c794843bb2 | ||
|
afa3149504 | ||
|
f2f4d325eb | ||
|
fef0404ac7 | ||
|
d6a5b9fb48 | ||
|
57dba2f29a | ||
|
f64db7f5f3 | ||
|
128d3845b9 | ||
|
602ea3327b | ||
|
e55178612c | ||
|
5a0d62db6f | ||
|
ed62e6a390 | ||
|
25a6b0ddfb | ||
|
89cf1f3237 | ||
|
b65fc4f46b | ||
|
401b5e2b7a | ||
|
a44d00a31c | ||
|
267ae457ee | ||
|
887da0a3d6 | ||
|
07ffd3ded3 | ||
|
25dbb3e9ea | ||
|
68177a02e1 | ||
|
0a96841336 | ||
|
4dcd7846b0 | ||
|
dc48cd4d16 | ||
|
883a7cecf0 | ||
|
daf019b288 | ||
|
5eeb545ae2 | ||
|
618d1f5de6 | ||
|
277fc4bf61 | ||
|
c75eb050df | ||
|
304b489470 | ||
|
4709002429 | ||
|
f36bab0a7a | ||
|
68f422e498 | ||
|
2c0b7ce0f4 | ||
|
72ee1ace48 | ||
|
7c013de7b9 | ||
|
979ffcf227 | ||
|
f0f0aafa1b | ||
|
c2d5ded335 | ||
|
1a5291537d | ||
|
d6ef3ea08e | ||
|
fac9a65f54 | ||
|
93fc64ff33 | ||
|
e097c92458 | ||
|
e91dbfbf2c | ||
|
801f2860e2 | ||
|
1ffc3a4f53 | ||
|
844fe2c4cd | ||
|
69efb45fb7 | ||
|
1103d7c112 | ||
|
c88072e55f | ||
|
e041d404f3 | ||
|
5b07af2c12 | ||
|
de73539d2b | ||
|
f7ed94c6e9 | ||
|
36b1a1471a | ||
|
1f1eeccc32 | ||
|
1d449c84fe | ||
|
0750c7763c | ||
|
dbaaa07dad | ||
|
7446b12827 | ||
|
d4c8f56c6e | ||
|
2ea45185d4 | ||
|
50ba131350 | ||
|
b2df107f17 | ||
|
2b60affd9a | ||
|
e771b21ea3 | ||
|
b20bbc1295 | ||
|
609ceb7fa4 | ||
|
aac3f379a7 | ||
|
d46ae83dc4 | ||
|
561b89830a | ||
|
b69d8ad71a | ||
|
ec0a04e893 | ||
|
8307245120 | ||
|
5431584b3c | ||
|
426782a001 | ||
|
7eb9941f8c | ||
|
f585deb1d7 | ||
|
26c4c7e467 | ||
|
b2b1bebc32 | ||
|
793c0ababb | ||
|
cf1628228a | ||
|
7f433ac4a0 | ||
|
48f03c0953 | ||
|
e75d25f00c | ||
|
e22e70c5ba | ||
|
a8e4be9146 | ||
|
a9ee94e3f0 | ||
|
cdaddb15af | ||
|
a724f844f7 | ||
|
2546061933 | ||
|
f4db593009 | ||
|
ce92533525 | ||
|
6d5e1b2285 | ||
|
6388d6b91b | ||
|
54e501127c | ||
|
6bcd96d629 | ||
|
996fe71de9 | ||
|
08b38cd6ad | ||
|
d649cb4aa5 | ||
|
61be2dd3df | ||
|
b6dda73cb1 | ||
|
0c77f49ffd | ||
|
76f4a1b4f1 | ||
|
466f9bd532 | ||
|
992edb2ee2 | ||
|
8de4b0bb20 | ||
|
e21c8792e6 | ||
|
54f6440bcf | ||
|
819f0c021e | ||
|
bc2f49fa31 | ||
|
d3a3ba8079 | ||
|
1540f13444 | ||
|
245d580825 | ||
|
2578423c91 | ||
|
1762451bc0 | ||
|
38e4984a4a | ||
|
9d7c49306a | ||
|
6b605b2e98 | ||
|
cf0242f23a | ||
|
56e8325efe | ||
|
d91ed2985d | ||
|
5f9f69b4dc | ||
|
da33cbedda | ||
|
2d91865277 | ||
|
003f5fab1f | ||
|
2a365d6776 | ||
|
55f5f90c45 | ||
|
bd3bdd6996 | ||
|
0cf485d873 | ||
|
75e06d43c5 | ||
|
afa1cf7d6c | ||
|
a6f909b942 | ||
|
ae55ee82a7 | ||
|
037e53f385 | ||
|
a28dfdc48e | ||
|
81bdf506bc | ||
|
f253aa6b37 | ||
|
0702eee179 | ||
|
c4a019f0d3 | ||
|
e948e9d85a | ||
|
22c10f5ada | ||
|
869eb262f3 | ||
|
7249c7d25a | ||
|
6e8d93bc6f | ||
|
963c30a275 | ||
|
474ade01cf | ||
|
18d4b66c97 | ||
|
fa311f4ce2 | ||
|
3a9b80127f | ||
|
78c1a0acf4 | ||
|
4ee34126bd | ||
|
4f59ec37ca | ||
|
c34fea2932 | ||
|
c11a50f7ff | ||
|
23623b8895 | ||
|
b475f36b5a | ||
|
11367488e6 | ||
|
a7d8e74468 | ||
|
a25b93197d | ||
|
83f2ff5d82 | ||
|
868c056ddd | ||
|
0106adf36c | ||
|
b6a93be611 | ||
|
61291f7337 | ||
|
05013d2559 | ||
|
3240cadb94 | ||
|
594ee61a99 | ||
|
c85b0124e3 | ||
|
61bc19fc96 | ||
|
3ea3d0fc91 | ||
|
f1f1613f00 | ||
|
68dd206140 | ||
|
17e8581ef0 | ||
|
7ff45738e0 | ||
|
c9eacec449 | ||
|
3e78098c43 | ||
|
cbdacc199a | ||
|
cc01f25d8f | ||
|
9f3176c49c | ||
|
7ae2b34a9e | ||
|
d1bec21759 | ||
|
073e6227d6 | ||
|
761d8d89b0 | ||
|
f203fa5c58 | ||
|
9ccb22015a | ||
|
6d1c53c7ed | ||
|
855e7e02ce | ||
|
df3bb5b212 | ||
|
5768e45809 | ||
|
83584fab44 | ||
|
c234e12302 | ||
|
4645a8c633 | ||
|
cfb54f7608 | ||
|
7195ee9a69 | ||
|
ba6dffc0bf | ||
|
d3acf7e862 | ||
|
5c6d710f2d | ||
|
fa22a4a747 | ||
|
94aa0a02fe | ||
|
ab23d9b715 | ||
|
c5ce208d07 | ||
|
e7f7c0ba78 | ||
|
b082a009a7 | ||
|
cdeb2663fe | ||
|
a5e979b763 | ||
|
be0cadb3c7 | ||
|
6c4836e27e | ||
|
36a553a886 | ||
|
13938f2ab3 | ||
|
a755536a2f | ||
|
4a5dbde8d3 | ||
|
5744939c05 | ||
|
8c68126ed9 | ||
|
3c37d4c1b0 | ||
|
a9316b2f7e | ||
|
3e420033c5 | ||
|
571f05bcfb | ||
|
4e73705378 | ||
|
611e1185c8 | ||
|
b90d8e6238 | ||
|
ad3573226c | ||
|
1cc0825b54 | ||
|
87ac4ec5d4 | ||
|
8344506b05 | ||
|
9da3eec64f | ||
|
621c6c8773 | ||
|
d5e76c515e | ||
|
9fcf7263b5 | ||
|
0059fdf174 | ||
|
3d291c04c9 | ||
|
ca4b91a98f | ||
|
b0ba62aa31 | ||
|
abf763f454 | ||
|
15597eb041 | ||
|
00b16db7cc | ||
|
ff8a208012 | ||
|
7732bd47ce | ||
|
42a5680374 | ||
|
5d8f365520 | ||
|
938cd32ddd | ||
|
80396fcd39 | ||
|
7b97981bb5 | ||
|
b263273c87 | ||
|
427dc784fe | ||
|
7e4725c091 | ||
|
9b332f7a32 | ||
|
2b780a8b76 | ||
|
0518e5f18d | ||
|
0412b87ad1 | ||
|
2fe7caa580 | ||
|
d53650c8ae | ||
|
930b8da3b3 | ||
|
c48f153b0d | ||
|
68cd06f1fb | ||
|
c7afcf4ff2 | ||
|
3491774e7b | ||
|
ea6fde3ed0 | ||
|
267a7a36ff | ||
|
9a69bb9656 | ||
|
28aef1067b | ||
|
690a9eaa21 | ||
|
24d76b1d32 | ||
|
173458ed6c | ||
|
cebef970d3 | ||
|
4aa349ddd6 | ||
|
a918d28ded | ||
|
b1660c077e | ||
|
38075b4e8d | ||
|
174ecb10d9 | ||
|
5ef1fd62a7 | ||
|
d095784bfc | ||
|
1ac1d32b3a | ||
|
518b207f63 | ||
|
16ba5d2416 | ||
|
ac6e7652bd | ||
|
cc3e090855 | ||
|
30e4fdebd7 | ||
|
27e5626fcf | ||
|
8878cb41ee | ||
|
10575698de | ||
|
451750f08a | ||
|
6f04f4109d | ||
|
929d711149 | ||
|
b52f8b1dbf | ||
|
aa0a851b35 | ||
|
4ddc8e706d | ||
|
da2a0abf45 | ||
|
53a0e0ce10 | ||
|
7f02c0596e | ||
|
e081c3b249 | ||
|
8db4da5473 | ||
|
d389581d96 | ||
|
018574a21e | ||
|
9e3eb993ee | ||
|
6c64fb2169 | ||
|
2e70808bbd | ||
|
18bf9856fe | ||
|
f09ee5016a | ||
|
4d3c4b5afc | ||
|
7de2494af2 | ||
|
409d7e50bb | ||
|
c1222737d6 | ||
|
d02da1b97e | ||
|
a8c6b1cdf7 | ||
|
dba65dcd22 | ||
|
706736273c | ||
|
838340bbc8 | ||
|
75ec9ba3d9 | ||
|
876359539f | ||
|
6cdb192955 | ||
|
2927f1ff1c |
70
CHANGES.md
70
CHANGES.md
@@ -1,3 +1,73 @@
|
||||
Changes in Element 1.0.15 (2020-02-03)
|
||||
===================================================
|
||||
|
||||
Features ✨:
|
||||
- Social Login support
|
||||
|
||||
Improvements 🙌:
|
||||
- SSO support for cross signing (#1062)
|
||||
- Deactivate account when logged in with SSO (#1264)
|
||||
- SSO UIA doesn't work (#2754)
|
||||
|
||||
Bugfix 🐛:
|
||||
- Fix clear cache issue: sometimes, after a clear cache, there is still a token, so the init sync service is not started.
|
||||
- Sidebar too large in horizontal orientation or tablets (#475)
|
||||
- UrlPreview should be updated when the url is edited and changed (#2678)
|
||||
- When receiving a new pepper from identity server, use it on the next hash lookup (#2708)
|
||||
- Crashes reported by PlayStore (new in 1.0.14) (#2707)
|
||||
- Widgets: Support $matrix_widget_id parameter (#2748)
|
||||
- Data for Worker overload (#2721)
|
||||
- Fix multiple tasks
|
||||
|
||||
SDK API changes ⚠️:
|
||||
- Increase targetSdkVersion to 30 (#2600)
|
||||
|
||||
Build 🧱:
|
||||
- Compile with Android SDK 30 (Android 11)
|
||||
|
||||
Other changes:
|
||||
- Update Dagger to 2.31 version so we can use the embedded AssistedInject feature
|
||||
|
||||
Changes in Element 1.0.14 (2020-01-15)
|
||||
===================================================
|
||||
|
||||
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)
|
||||
===================================================
|
||||
|
||||
Bugfix 🐛:
|
||||
- Fix MSC2858 implementation details (#2540)
|
||||
|
||||
Changes in Element 1.0.12 (2020-12-15)
|
||||
===================================================
|
||||
|
||||
|
@@ -16,7 +16,6 @@
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
@@ -33,11 +32,11 @@ buildscript {
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
compileSdkVersion 30
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
targetSdkVersion 30
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
@@ -55,6 +54,10 @@ android {
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@@ -17,19 +17,17 @@
|
||||
package im.vector.lib.attachmentviewer
|
||||
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import im.vector.lib.attachmentviewer.databinding.ItemAnimatedImageAttachmentBinding
|
||||
|
||||
class AnimatedImageViewHolder constructor(itemView: View) :
|
||||
BaseViewHolder(itemView) {
|
||||
|
||||
val touchImageView: ImageView = itemView.findViewById(R.id.imageView)
|
||||
val imageLoaderProgress: ProgressBar = itemView.findViewById(R.id.imageLoaderProgress)
|
||||
val views = ItemAnimatedImageAttachmentBinding.bind(itemView)
|
||||
|
||||
internal val target = DefaultImageLoaderTarget(this, this.touchImageView)
|
||||
internal val target = DefaultImageLoaderTarget(this, views.imageView)
|
||||
|
||||
override fun onRecycled() {
|
||||
super.onRecycled()
|
||||
touchImageView.setImageDrawable(null)
|
||||
views.imageView.setImageDrawable(null)
|
||||
}
|
||||
}
|
||||
|
@@ -18,58 +18,70 @@
|
||||
package im.vector.lib.attachmentviewer
|
||||
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.GestureDetector
|
||||
import android.view.MotionEvent
|
||||
import android.view.ScaleGestureDetector
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowInsets
|
||||
import android.view.WindowInsetsController
|
||||
import android.view.WindowManager
|
||||
import android.widget.ImageView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.GestureDetectorCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.transition.TransitionManager
|
||||
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 kotlin.math.abs
|
||||
|
||||
abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventListener {
|
||||
|
||||
lateinit var pager2: ViewPager2
|
||||
lateinit var imageTransitionView: ImageView
|
||||
lateinit var transitionImageContainer: ViewGroup
|
||||
protected val pager2: ViewPager2
|
||||
get() = views.attachmentPager
|
||||
protected val imageTransitionView: ImageView
|
||||
get() = views.transitionImageView
|
||||
protected val transitionImageContainer: ViewGroup
|
||||
get() = views.transitionImageContainer
|
||||
|
||||
var topInset = 0
|
||||
var bottomInset = 0
|
||||
var systemUiVisibility = true
|
||||
private var topInset = 0
|
||||
private var bottomInset = 0
|
||||
private var systemUiVisibility = true
|
||||
|
||||
private var overlayView: View? = null
|
||||
set(value) {
|
||||
if (value == overlayView) return
|
||||
overlayView?.let { rootContainer.removeView(it) }
|
||||
rootContainer.addView(value)
|
||||
overlayView?.let { views.rootContainer.removeView(it) }
|
||||
views.rootContainer.addView(value)
|
||||
value?.updatePadding(top = topInset, bottom = bottomInset)
|
||||
field = value
|
||||
}
|
||||
|
||||
private lateinit var views: ActivityAttachmentViewerBinding
|
||||
|
||||
private lateinit var swipeDismissHandler: SwipeToDismissHandler
|
||||
private lateinit var directionDetector: SwipeDirectionDetector
|
||||
private lateinit var scaleDetector: ScaleGestureDetector
|
||||
private lateinit var gestureDetector: GestureDetectorCompat
|
||||
|
||||
var currentPosition = 0
|
||||
private set
|
||||
|
||||
private var swipeDirection: SwipeDirection? = null
|
||||
|
||||
private fun isScaled() = attachmentsAdapter.isScaled(currentPosition)
|
||||
|
||||
private val attachmentsAdapter = AttachmentsAdapter()
|
||||
|
||||
private var wasScaled: Boolean = false
|
||||
private var isSwipeToDismissAllowed: Boolean = true
|
||||
private lateinit var attachmentsAdapter: AttachmentsAdapter
|
||||
private var isOverlayWasClicked = false
|
||||
|
||||
// private val shouldDismissToBottom: Boolean
|
||||
@@ -86,26 +98,16 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// This is important for the dispatchTouchEvent, if not we must correct
|
||||
// the touch coordinates
|
||||
window.decorView.systemUiVisibility = (
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
or View.SYSTEM_UI_FLAG_IMMERSIVE)
|
||||
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
|
||||
setDecorViewFullScreen()
|
||||
|
||||
setContentView(R.layout.activity_attachment_viewer)
|
||||
attachmentPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL
|
||||
attachmentsAdapter = AttachmentsAdapter()
|
||||
attachmentPager.adapter = attachmentsAdapter
|
||||
imageTransitionView = transitionImageView
|
||||
transitionImageContainer = findViewById(R.id.transitionImageContainer)
|
||||
pager2 = attachmentPager
|
||||
views = ActivityAttachmentViewerBinding.inflate(layoutInflater)
|
||||
setContentView(views.root)
|
||||
views.attachmentPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL
|
||||
views.attachmentPager.adapter = attachmentsAdapter
|
||||
directionDetector = createSwipeDirectionDetector()
|
||||
gestureDetector = createGestureDetector()
|
||||
|
||||
attachmentPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
views.attachmentPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageScrollStateChanged(state: Int) {
|
||||
isImagePagerIdle = state == ViewPager2.SCROLL_STATE_IDLE
|
||||
}
|
||||
@@ -116,12 +118,12 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
||||
})
|
||||
|
||||
swipeDismissHandler = createSwipeToDismissHandler()
|
||||
rootContainer.setOnTouchListener(swipeDismissHandler)
|
||||
rootContainer.viewTreeObserver.addOnGlobalLayoutListener { swipeDismissHandler.translationLimit = dismissContainer.height / 4 }
|
||||
views.rootContainer.setOnTouchListener(swipeDismissHandler)
|
||||
views.rootContainer.viewTreeObserver.addOnGlobalLayoutListener { swipeDismissHandler.translationLimit = views.dismissContainer.height / 4 }
|
||||
|
||||
scaleDetector = createScaleGestureDetector()
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(rootContainer) { _, insets ->
|
||||
ViewCompat.setOnApplyWindowInsetsListener(views.rootContainer) { _, insets ->
|
||||
overlayView?.updatePadding(top = insets.systemWindowInsetTop, bottom = insets.systemWindowInsetBottom)
|
||||
topInset = insets.systemWindowInsetTop
|
||||
bottomInset = insets.systemWindowInsetBottom
|
||||
@@ -129,6 +131,29 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun setDecorViewFullScreen() {
|
||||
// This is important for the dispatchTouchEvent, if not we must correct
|
||||
// the touch coordinates
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
// New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
window.setDecorFitsSystemWindows(false)
|
||||
// New API instead of SYSTEM_UI_FLAG_IMMERSIVE
|
||||
window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE
|
||||
// New API instead of FLAG_TRANSLUCENT_STATUS
|
||||
window.statusBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
|
||||
// new API instead of FLAG_TRANSLUCENT_NAVIGATION
|
||||
window.navigationBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
|
||||
} else {
|
||||
window.decorView.systemUiVisibility = (
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
or View.SYSTEM_UI_FLAG_IMMERSIVE)
|
||||
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
|
||||
}
|
||||
}
|
||||
|
||||
fun onSelectedPositionChanged(position: Int) {
|
||||
attachmentsAdapter.recyclerView?.findViewHolderForAdapterPosition(currentPosition)?.let {
|
||||
(it as? BaseViewHolder)?.onSelected(false)
|
||||
@@ -170,7 +195,7 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
||||
if (swipeDirection == null && (scaleDetector.isInProgress || ev.pointerCount > 1 || wasScaled)) {
|
||||
wasScaled = true
|
||||
// Log.v("ATTACHEMENTS", "dispatch to pager")
|
||||
return attachmentPager.dispatchTouchEvent(ev)
|
||||
return views.attachmentPager.dispatchTouchEvent(ev)
|
||||
}
|
||||
|
||||
// Log.v("ATTACHEMENTS", "is current item scaled ${isScaled()}")
|
||||
@@ -196,16 +221,16 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
||||
private fun handleEventActionDown(event: MotionEvent) {
|
||||
swipeDirection = null
|
||||
wasScaled = false
|
||||
attachmentPager.dispatchTouchEvent(event)
|
||||
views.attachmentPager.dispatchTouchEvent(event)
|
||||
|
||||
swipeDismissHandler.onTouch(rootContainer, event)
|
||||
swipeDismissHandler.onTouch(views.rootContainer, event)
|
||||
isOverlayWasClicked = dispatchOverlayTouch(event)
|
||||
}
|
||||
|
||||
private fun handleEventActionUp(event: MotionEvent) {
|
||||
// wasDoubleTapped = false
|
||||
swipeDismissHandler.onTouch(rootContainer, event)
|
||||
attachmentPager.dispatchTouchEvent(event)
|
||||
swipeDismissHandler.onTouch(views.rootContainer, event)
|
||||
views.attachmentPager.dispatchTouchEvent(event)
|
||||
isOverlayWasClicked = dispatchOverlayTouch(event)
|
||||
}
|
||||
|
||||
@@ -220,12 +245,12 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
||||
private fun toggleOverlayViewVisibility() {
|
||||
if (systemUiVisibility) {
|
||||
// we hide
|
||||
TransitionManager.beginDelayedTransition(rootContainer)
|
||||
TransitionManager.beginDelayedTransition(views.rootContainer)
|
||||
hideSystemUI()
|
||||
overlayView?.isVisible = false
|
||||
} else {
|
||||
// we show
|
||||
TransitionManager.beginDelayedTransition(rootContainer)
|
||||
TransitionManager.beginDelayedTransition(views.rootContainer)
|
||||
showSystemUI()
|
||||
overlayView?.isVisible = true
|
||||
}
|
||||
@@ -238,11 +263,11 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
||||
return when (swipeDirection) {
|
||||
SwipeDirection.Up, SwipeDirection.Down -> {
|
||||
if (isSwipeToDismissAllowed && !wasScaled && isImagePagerIdle) {
|
||||
swipeDismissHandler.onTouch(rootContainer, event)
|
||||
swipeDismissHandler.onTouch(views.rootContainer, event)
|
||||
} else true
|
||||
}
|
||||
SwipeDirection.Left, SwipeDirection.Right -> {
|
||||
attachmentPager.dispatchTouchEvent(event)
|
||||
views.attachmentPager.dispatchTouchEvent(event)
|
||||
}
|
||||
else -> true
|
||||
}
|
||||
@@ -250,8 +275,8 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
||||
|
||||
private fun handleSwipeViewMove(translationY: Float, translationLimit: Int) {
|
||||
val alpha = calculateTranslationAlpha(translationY, translationLimit)
|
||||
backgroundView.alpha = alpha
|
||||
dismissContainer.alpha = alpha
|
||||
views.backgroundView.alpha = alpha
|
||||
views.dismissContainer.alpha = alpha
|
||||
overlayView?.alpha = alpha
|
||||
}
|
||||
|
||||
@@ -265,7 +290,7 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
||||
|
||||
private fun createSwipeToDismissHandler()
|
||||
: SwipeToDismissHandler = SwipeToDismissHandler(
|
||||
swipeView = dismissContainer,
|
||||
swipeView = views.dismissContainer,
|
||||
shouldAnimateDismiss = { shouldAnimateDismiss() },
|
||||
onDismiss = { animateClose() },
|
||||
onSwipeViewMove = ::handleSwipeViewMove)
|
||||
@@ -308,28 +333,48 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
||||
?.handleCommand(commands)
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun hideSystemUI() {
|
||||
systemUiVisibility = false
|
||||
// Enables regular immersive mode.
|
||||
// For "lean back" mode, remove SYSTEM_UI_FLAG_IMMERSIVE.
|
||||
// Or for "sticky immersive," replace it with SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE
|
||||
// Set the content to appear under the system bars so that the
|
||||
// content doesn't resize when the system bars hide and show.
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
// Hide the nav bar and status bar
|
||||
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
or View.SYSTEM_UI_FLAG_FULLSCREEN)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
// New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
window.setDecorFitsSystemWindows(false)
|
||||
// new API instead of SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
window.decorView.windowInsetsController?.hide(WindowInsets.Type.navigationBars())
|
||||
// New API instead of SYSTEM_UI_FLAG_IMMERSIVE
|
||||
window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE
|
||||
// New API instead of FLAG_TRANSLUCENT_STATUS
|
||||
window.statusBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
|
||||
// New API instead of FLAG_TRANSLUCENT_NAVIGATION
|
||||
window.navigationBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
|
||||
} else {
|
||||
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE
|
||||
// Set the content to appear under the system bars so that the
|
||||
// content doesn't resize when the system bars hide and show.
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
// Hide the nav bar and status bar
|
||||
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
or View.SYSTEM_UI_FLAG_FULLSCREEN)
|
||||
}
|
||||
}
|
||||
|
||||
// Shows the system bars by removing all the flags
|
||||
// except for the ones that make the content appear under the system bars.
|
||||
@Suppress("DEPRECATION")
|
||||
private fun showSystemUI() {
|
||||
systemUiVisibility = true
|
||||
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
// New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
window.setDecorFitsSystemWindows(false)
|
||||
} else {
|
||||
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -98,7 +98,7 @@ class AttachmentsAdapter : RecyclerView.Adapter<BaseViewHolder>() {
|
||||
fun isScaled(position: Int): Boolean {
|
||||
val holder = recyclerView?.findViewHolderForAdapterPosition(position)
|
||||
if (holder is ZoomableImageViewHolder) {
|
||||
return holder.touchImageView.attacher.scale > 1f
|
||||
return holder.views.touchImageView.attacher.scale > 1f
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@@ -44,29 +44,29 @@ internal class DefaultImageLoaderTarget(val holder: AnimatedImageViewHolder, pri
|
||||
|
||||
override fun onResourceLoading(uid: String, placeholder: Drawable?) {
|
||||
if (holder.boundResourceUid != uid) return
|
||||
holder.imageLoaderProgress.isVisible = true
|
||||
holder.views.imageLoaderProgress.isVisible = true
|
||||
}
|
||||
|
||||
override fun onLoadFailed(uid: String, errorDrawable: Drawable?) {
|
||||
if (holder.boundResourceUid != uid) return
|
||||
holder.imageLoaderProgress.isVisible = false
|
||||
holder.touchImageView.setImageDrawable(errorDrawable)
|
||||
holder.views.imageLoaderProgress.isVisible = false
|
||||
holder.views.imageView.setImageDrawable(errorDrawable)
|
||||
}
|
||||
|
||||
override fun onResourceCleared(uid: String, placeholder: Drawable?) {
|
||||
if (holder.boundResourceUid != uid) return
|
||||
holder.touchImageView.setImageDrawable(placeholder)
|
||||
holder.views.imageView.setImageDrawable(placeholder)
|
||||
}
|
||||
|
||||
override fun onResourceReady(uid: String, resource: Drawable) {
|
||||
if (holder.boundResourceUid != uid) return
|
||||
holder.imageLoaderProgress.isVisible = false
|
||||
holder.views.imageLoaderProgress.isVisible = false
|
||||
// Glide mess up the view size :/
|
||||
holder.touchImageView.updateLayoutParams {
|
||||
holder.views.imageView.updateLayoutParams {
|
||||
width = LinearLayout.LayoutParams.MATCH_PARENT
|
||||
height = LinearLayout.LayoutParams.MATCH_PARENT
|
||||
}
|
||||
holder.touchImageView.setImageDrawable(resource)
|
||||
holder.views.imageView.setImageDrawable(resource)
|
||||
if (resource is Animatable) {
|
||||
resource.start()
|
||||
}
|
||||
@@ -77,30 +77,30 @@ internal class DefaultImageLoaderTarget(val holder: AnimatedImageViewHolder, pri
|
||||
|
||||
override fun onResourceLoading(uid: String, placeholder: Drawable?) {
|
||||
if (holder.boundResourceUid != uid) return
|
||||
holder.imageLoaderProgress.isVisible = true
|
||||
holder.touchImageView.setImageDrawable(placeholder)
|
||||
holder.views.imageLoaderProgress.isVisible = true
|
||||
holder.views.touchImageView.setImageDrawable(placeholder)
|
||||
}
|
||||
|
||||
override fun onLoadFailed(uid: String, errorDrawable: Drawable?) {
|
||||
if (holder.boundResourceUid != uid) return
|
||||
holder.imageLoaderProgress.isVisible = false
|
||||
holder.touchImageView.setImageDrawable(errorDrawable)
|
||||
holder.views.imageLoaderProgress.isVisible = false
|
||||
holder.views.touchImageView.setImageDrawable(errorDrawable)
|
||||
}
|
||||
|
||||
override fun onResourceCleared(uid: String, placeholder: Drawable?) {
|
||||
if (holder.boundResourceUid != uid) return
|
||||
holder.touchImageView.setImageDrawable(placeholder)
|
||||
holder.views.touchImageView.setImageDrawable(placeholder)
|
||||
}
|
||||
|
||||
override fun onResourceReady(uid: String, resource: Drawable) {
|
||||
if (holder.boundResourceUid != uid) return
|
||||
holder.imageLoaderProgress.isVisible = false
|
||||
holder.views.imageLoaderProgress.isVisible = false
|
||||
// Glide mess up the view size :/
|
||||
holder.touchImageView.updateLayoutParams {
|
||||
holder.views.touchImageView.updateLayoutParams {
|
||||
width = LinearLayout.LayoutParams.MATCH_PARENT
|
||||
height = LinearLayout.LayoutParams.MATCH_PARENT
|
||||
}
|
||||
holder.touchImageView.setImageDrawable(resource)
|
||||
holder.views.touchImageView.setImageDrawable(resource)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -49,19 +49,19 @@ internal class DefaultVideoLoaderTarget(val holder: VideoViewHolder, private val
|
||||
|
||||
override fun onThumbnailResourceCleared(uid: String, placeholder: Drawable?) {
|
||||
if (holder.boundResourceUid != uid) return
|
||||
holder.thumbnailImage.setImageDrawable(placeholder)
|
||||
holder.views.videoThumbnailImage.setImageDrawable(placeholder)
|
||||
}
|
||||
|
||||
override fun onThumbnailResourceReady(uid: String, resource: Drawable) {
|
||||
if (holder.boundResourceUid != uid) return
|
||||
holder.thumbnailImage.setImageDrawable(resource)
|
||||
holder.views.videoThumbnailImage.setImageDrawable(resource)
|
||||
}
|
||||
|
||||
override fun onVideoFileLoading(uid: String) {
|
||||
if (holder.boundResourceUid != uid) return
|
||||
holder.thumbnailImage.isVisible = true
|
||||
holder.loaderProgressBar.isVisible = true
|
||||
holder.videoView.isVisible = false
|
||||
holder.views.videoThumbnailImage.isVisible = true
|
||||
holder.views.videoLoaderProgress.isVisible = true
|
||||
holder.views.videoView.isVisible = false
|
||||
}
|
||||
|
||||
override fun onVideoFileLoadFailed(uid: String) {
|
||||
@@ -82,8 +82,8 @@ internal class DefaultVideoLoaderTarget(val holder: VideoViewHolder, private val
|
||||
}
|
||||
|
||||
private fun arrangeForVideoReady() {
|
||||
holder.thumbnailImage.isVisible = false
|
||||
holder.loaderProgressBar.isVisible = false
|
||||
holder.videoView.isVisible = true
|
||||
holder.views.videoThumbnailImage.isVisible = false
|
||||
holder.views.videoLoaderProgress.isVisible = false
|
||||
holder.views.videoView.isVisible = true
|
||||
}
|
||||
}
|
||||
|
@@ -18,11 +18,8 @@ package im.vector.lib.attachmentviewer
|
||||
|
||||
import android.util.Log
|
||||
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 im.vector.lib.attachmentviewer.databinding.ItemVideoAttachmentBinding
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
@@ -44,13 +41,9 @@ class VideoViewHolder constructor(itemView: View) :
|
||||
|
||||
var eventListener: WeakReference<AttachmentEventListener>? = null
|
||||
|
||||
val thumbnailImage: ImageView = itemView.findViewById(R.id.videoThumbnailImage)
|
||||
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)
|
||||
val views = ItemVideoAttachmentBinding.bind(itemView)
|
||||
|
||||
internal val target = DefaultVideoLoaderTarget(this, thumbnailImage)
|
||||
internal val target = DefaultVideoLoaderTarget(this, views.videoThumbnailImage)
|
||||
|
||||
override fun onRecycled() {
|
||||
super.onRecycled()
|
||||
@@ -77,12 +70,12 @@ class VideoViewHolder constructor(itemView: View) :
|
||||
}
|
||||
|
||||
override fun entersBackground() {
|
||||
if (videoView.isPlaying) {
|
||||
progress = videoView.currentPosition
|
||||
if (views.videoView.isPlaying) {
|
||||
progress = views.videoView.currentPosition
|
||||
progressDisposable?.dispose()
|
||||
progressDisposable = null
|
||||
videoView.stopPlayback()
|
||||
videoView.pause()
|
||||
views.videoView.stopPlayback()
|
||||
views.videoView.pause()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,9 +85,9 @@ class VideoViewHolder constructor(itemView: View) :
|
||||
|
||||
override fun onSelected(selected: Boolean) {
|
||||
if (!selected) {
|
||||
if (videoView.isPlaying) {
|
||||
progress = videoView.currentPosition
|
||||
videoView.stopPlayback()
|
||||
if (views.videoView.isPlaying) {
|
||||
progress = views.videoView.currentPosition
|
||||
views.videoView.stopPlayback()
|
||||
} else {
|
||||
progress = 0
|
||||
}
|
||||
@@ -109,34 +102,34 @@ class VideoViewHolder constructor(itemView: View) :
|
||||
}
|
||||
|
||||
private fun startPlaying() {
|
||||
thumbnailImage.isVisible = false
|
||||
loaderProgressBar.isVisible = false
|
||||
videoView.isVisible = true
|
||||
views.videoThumbnailImage.isVisible = false
|
||||
views.videoLoaderProgress.isVisible = false
|
||||
views.videoView.isVisible = true
|
||||
|
||||
videoView.setOnPreparedListener {
|
||||
views.videoView.setOnPreparedListener {
|
||||
progressDisposable?.dispose()
|
||||
progressDisposable = Observable.interval(100, TimeUnit.MILLISECONDS)
|
||||
.timeInterval()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
val duration = videoView.duration
|
||||
val progress = videoView.currentPosition
|
||||
val isPlaying = videoView.isPlaying
|
||||
val duration = views.videoView.duration
|
||||
val progress = views.videoView.currentPosition
|
||||
val isPlaying = views.videoView.isPlaying
|
||||
// Log.v("FOO", "isPlaying $isPlaying $progress/$duration")
|
||||
eventListener?.get()?.onEvent(AttachmentEvents.VideoEvent(isPlaying, progress, duration))
|
||||
}
|
||||
}
|
||||
try {
|
||||
videoView.setVideoPath(mVideoPath)
|
||||
views.videoView.setVideoPath(mVideoPath)
|
||||
} catch (failure: Throwable) {
|
||||
// Couldn't open
|
||||
Log.v(VideoViewHolder::class.java.name, "Failed to start video")
|
||||
}
|
||||
|
||||
if (!wasPaused) {
|
||||
videoView.start()
|
||||
views.videoView.start()
|
||||
if (progress > 0) {
|
||||
videoView.seekTo(progress)
|
||||
views.videoView.seekTo(progress)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,17 +139,17 @@ class VideoViewHolder constructor(itemView: View) :
|
||||
when (commands) {
|
||||
AttachmentCommands.StartVideo -> {
|
||||
wasPaused = false
|
||||
videoView.start()
|
||||
views.videoView.start()
|
||||
}
|
||||
AttachmentCommands.PauseVideo -> {
|
||||
wasPaused = true
|
||||
videoView.pause()
|
||||
views.videoView.pause()
|
||||
}
|
||||
is AttachmentCommands.SeekTo -> {
|
||||
val duration = videoView.duration
|
||||
val duration = views.videoView.duration
|
||||
if (duration > 0) {
|
||||
val seekDuration = duration * (commands.percentProgress / 100f)
|
||||
videoView.seekTo(seekDuration.toInt())
|
||||
views.videoView.seekTo(seekDuration.toInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -17,31 +17,29 @@
|
||||
package im.vector.lib.attachmentviewer
|
||||
|
||||
import android.view.View
|
||||
import android.widget.ProgressBar
|
||||
import com.github.chrisbanes.photoview.PhotoView
|
||||
import im.vector.lib.attachmentviewer.databinding.ItemImageAttachmentBinding
|
||||
|
||||
class ZoomableImageViewHolder constructor(itemView: View) :
|
||||
BaseViewHolder(itemView) {
|
||||
|
||||
val touchImageView: PhotoView = itemView.findViewById(R.id.touchImageView)
|
||||
val imageLoaderProgress: ProgressBar = itemView.findViewById(R.id.imageLoaderProgress)
|
||||
val views = ItemImageAttachmentBinding.bind(itemView)
|
||||
|
||||
init {
|
||||
touchImageView.setAllowParentInterceptOnEdge(false)
|
||||
touchImageView.setOnScaleChangeListener { scaleFactor, _, _ ->
|
||||
views.touchImageView.setAllowParentInterceptOnEdge(false)
|
||||
views.touchImageView.setOnScaleChangeListener { scaleFactor, _, _ ->
|
||||
// Log.v("ATTACHEMENTS", "scaleFactor $scaleFactor")
|
||||
// It's a bit annoying but when you pitch down the scaling
|
||||
// is not exactly one :/
|
||||
touchImageView.setAllowParentInterceptOnEdge(scaleFactor <= 1.0008f)
|
||||
views.touchImageView.setAllowParentInterceptOnEdge(scaleFactor <= 1.0008f)
|
||||
}
|
||||
touchImageView.setScale(1.0f, true)
|
||||
touchImageView.setAllowParentInterceptOnEdge(true)
|
||||
views.touchImageView.setScale(1.0f, true)
|
||||
views.touchImageView.setAllowParentInterceptOnEdge(true)
|
||||
}
|
||||
|
||||
internal val target = DefaultImageLoaderTarget.ZoomableImageTarget(this, touchImageView)
|
||||
internal val target = DefaultImageLoaderTarget.ZoomableImageTarget(this, views.touchImageView)
|
||||
|
||||
override fun onRecycled() {
|
||||
super.onRecycled()
|
||||
touchImageView.setImageDrawable(null)
|
||||
views.touchImageView.setImageDrawable(null)
|
||||
}
|
||||
}
|
||||
|
6
attachment-viewer/src/main/res/values/colors.xml
Normal file
6
attachment-viewer/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<resources>
|
||||
|
||||
<color name="half_transparent_status_bar">#80000000</color>
|
||||
|
||||
</resources>
|
@@ -165,7 +165,7 @@ In this case, the user can click on "Sign in with SSO" and the native web browse
|
||||
|
||||
> https://homeserver.with.sso/_matrix/client/r0/login/sso/redirect?redirectUrl=element%3A%2F%element
|
||||
|
||||
The parameter `redirectUrl` is set to `element://element`.
|
||||
The parameter `redirectUrl` is set to `element://connect`.
|
||||
|
||||
ChromeCustomTabs are an intermediate way to display a WebPage, between a WebView and using the external browser. More info can be found [here](https://developer.chrome.com/multidevice/android/customtabs)
|
||||
|
||||
@@ -175,7 +175,7 @@ During the process, user may be asked to validate an email by clicking on a link
|
||||
|
||||
Once the process is finished, the web page will call the `redirectUrl` with an extra parameter `loginToken`
|
||||
|
||||
> element://element?loginToken=MDAxOWxvY2F0aW9uIG1vemlsbGEub3JnCjAwMTNpZGVudGlmaWVy
|
||||
> element://connect?loginToken=MDAxOWxvY2F0aW9uIG1vemlsbGEub3JnCjAwMTNpZGVudGlmaWVy
|
||||
|
||||
This navigation is intercepted by Element by the `LoginActivity`, which will then ask the homeserver to convert this `loginToken` to an access token
|
||||
|
||||
|
@@ -27,7 +27,6 @@ $ source env/bin/activate
|
||||
Every time you want to launch these test homeservers, type:
|
||||
|
||||
```shell script
|
||||
$ virtualenv -p python3 env
|
||||
$ source env/bin/activate
|
||||
(env) $ demo/start.sh --no-rate-limit
|
||||
```
|
||||
|
@@ -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
|
||||
|
2
fastlane/metadata/android/de/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/de/changelogs/40100110.txt
Normal 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
|
2
fastlane/metadata/android/en-US/changelogs/40100130.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40100130.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
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.13
|
2
fastlane/metadata/android/en-US/changelogs/40100140.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40100140.txt
Normal 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
|
2
fastlane/metadata/android/en-US/changelogs/40100150.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40100150.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Main changes in this version: Social Login support.
|
||||
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.0.15
|
2
fastlane/metadata/android/et/changelogs/40100100.txt
Normal file
2
fastlane/metadata/android/et/changelogs/40100100.txt
Normal 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
|
2
fastlane/metadata/android/et/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/et/changelogs/40100110.txt
Normal 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
|
@@ -1 +1,2 @@
|
||||
// برای انجام
|
||||
این نگارش جدید به طور عمده شامل رفع اشکالها و بهبودها است. ارسال پیام اکنون بسیار سریعتر است.
|
||||
گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.0.10
|
||||
|
2
fastlane/metadata/android/fa/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/fa/changelogs/40100110.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
این نگارش جدید به طور عمده شامل رابط کاربری و بهبود تجربه کاربر است. اکنون میتوانید با پویش کدهای QR دوستانتان را دعوت کرده و بسیار سریع پیام مستقیم ایجاد کنید.
|
||||
گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.0.11
|
@@ -1 +1 @@
|
||||
گپ و تماس نامتمرکز امن. دادههایتان را از شرکتها امن نگه دارید.
|
||||
گپ و تماس نامتمرکز امن. دادههایتان را از اشخاص سوم امن نگه دارید.
|
||||
|
@@ -1 +1 @@
|
||||
المنت (ریوت سابق)
|
||||
Element (پیشتر Riot.im)
|
||||
|
2
fastlane/metadata/android/fi/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/fi/changelogs/40100110.txt
Normal 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
|
@@ -1,30 +1,30 @@
|
||||
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
|
||||
3. Suojaa sinua mainonnalta, tietojen keräämiseltä ja suljetuilta alustoilta
|
||||
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 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 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.
|
||||
2. Isännöi tiliäsi itse suorittamalla palvelinta omalla laitteellasi
|
||||
3. Luo tili mukautetulla palvelimella yksinkertaisesti tilaamalla Element Matrix Services -palvelu
|
||||
2. Isännöi tiliäsi itse ylläpitämällä palvelinta omalla laitteellasi
|
||||
3. Luo tili sinua varten tehdyllä palvelimella tilaamalla Element Matrix Services -palvelu
|
||||
|
||||
<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>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.
|
||||
|
@@ -1 +1 @@
|
||||
Turvallista, hajautettua, keskusteluja ja VoIP-puheluita. Pidä tietosi turvassa.
|
||||
Turvallista, hajautettua keskustelua ja VoIP-puheluita. Pidä tietosi turvassa.
|
||||
|
2
fastlane/metadata/android/fr/changelogs/40100100.txt
Normal file
2
fastlane/metadata/android/fr/changelogs/40100100.txt
Normal 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
|
2
fastlane/metadata/android/hu/changelogs/40100100.txt
Normal file
2
fastlane/metadata/android/hu/changelogs/40100100.txt
Normal 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
|
2
fastlane/metadata/android/hu/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/hu/changelogs/40100110.txt
Normal 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
|
@@ -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
|
||||
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.
|
||||
|
||||
|
2
fastlane/metadata/android/it/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/it/changelogs/40100110.txt
Normal 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
|
2
fastlane/metadata/android/nb_NO/changelogs/40100100.txt
Normal file
2
fastlane/metadata/android/nb_NO/changelogs/40100100.txt
Normal 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
|
2
fastlane/metadata/android/nb_NO/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/nb_NO/changelogs/40100110.txt
Normal 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
|
1
fastlane/metadata/android/nb_NO/short_description.txt
Normal file
1
fastlane/metadata/android/nb_NO/short_description.txt
Normal file
@@ -0,0 +1 @@
|
||||
Sikker desentralisert chat & VoIP. Beskytt dataene dine fra tredjeparter.
|
1
fastlane/metadata/android/nb_NO/title.txt
Normal file
1
fastlane/metadata/android/nb_NO/title.txt
Normal file
@@ -0,0 +1 @@
|
||||
Element (tidligere Riot.im)
|
2
fastlane/metadata/android/pt_BR/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/pt_BR/changelogs/40100110.txt
Normal 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
|
2
fastlane/metadata/android/ru/changelogs/40100100.txt
Normal file
2
fastlane/metadata/android/ru/changelogs/40100100.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Эта новая версия в основном содержит исправления ошибок и улучшения. Отправка сообщения стала намного быстрее.
|
||||
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.0.10
|
2
fastlane/metadata/android/ru/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/ru/changelogs/40100110.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Эта новая версия в основном содержит улучшения пользовательского интерфейса и взаимодействия с пользователем. Теперь вы можете приглашать друзей и очень быстро создавать чаты, сканируя QR-коды.
|
||||
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.0.11
|
2
fastlane/metadata/android/sk/changelogs/40100100.txt
Normal file
2
fastlane/metadata/android/sk/changelogs/40100100.txt
Normal 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
|
2
fastlane/metadata/android/sk/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/sk/changelogs/40100110.txt
Normal 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
|
2
fastlane/metadata/android/sv/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/sv/changelogs/40100110.txt
Normal 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
|
2
fastlane/metadata/android/uk/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/uk/changelogs/40100110.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Ця нова версія містить переважно поліпшення інтерфейсу та зручності користування. Тепер ви можете запросити друзів і створити прямі повідомлення дуже швидко, скануючи QR-коди.
|
||||
Повний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.0.11
|
2
fastlane/metadata/android/vi/changelogs/40100100.txt
Normal file
2
fastlane/metadata/android/vi/changelogs/40100100.txt
Normal 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
|
2
fastlane/metadata/android/vi/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/vi/changelogs/40100110.txt
Normal 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
|
1
fastlane/metadata/android/vi/short_description.txt
Normal file
1
fastlane/metadata/android/vi/short_description.txt
Normal 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.
|
1
fastlane/metadata/android/vi/title.txt
Normal file
1
fastlane/metadata/android/vi/title.txt
Normal file
@@ -0,0 +1 @@
|
||||
Element (trước là Riot.im)
|
@@ -0,0 +1,2 @@
|
||||
這個新版本主要包含使用者介面與使用者體驗改善。現在您可以邀請朋友,並透過掃描 QR code 來快速建立直接訊息了。
|
||||
完整變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.0.11
|
@@ -0,0 +1,2 @@
|
||||
此版本中的主要變更:URL 預覽、新的表情符號鍵盤、新的聊天室設定功能以及聖誕節降雪!
|
||||
完整變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.0.12
|
@@ -0,0 +1,2 @@
|
||||
此版本中的主要變更:URL 預覽、新的表情符號鍵盤、新的聊天室設定功能以及聖誕節降雪!
|
||||
完整變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.0.12
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=22449f5231796abd892c98b2a07c9ceebe4688d192cd2d6763f8e3bf8acbedeb
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
|
||||
distributionSha256Sum=3db89524a3981819ff28c3f979236c1274a726e146ced0c8a2020417f9bc0782
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
@@ -3,11 +3,11 @@ apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
compileSdkVersion 30
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
targetSdkVersion 30
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-parcelize'
|
||||
apply plugin: 'realm-android'
|
||||
|
||||
buildscript {
|
||||
@@ -13,17 +13,13 @@ buildscript {
|
||||
}
|
||||
}
|
||||
|
||||
androidExtensions {
|
||||
experimental = true
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
compileSdkVersion 30
|
||||
testOptions.unitTests.includeAndroidResources = true
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
targetSdkVersion 30
|
||||
versionCode 1
|
||||
versionName "0.0.1"
|
||||
// Multidex is useful for tests
|
||||
@@ -116,7 +112,7 @@ dependencies {
|
||||
def lifecycle_version = '2.2.0'
|
||||
def arch_version = '2.1.0'
|
||||
def markwon_version = '3.1.0'
|
||||
def daggerVersion = '2.29.1'
|
||||
def daggerVersion = '2.31'
|
||||
def work_version = '2.4.0'
|
||||
def retrofit_version = '2.6.2'
|
||||
|
||||
@@ -164,16 +160,11 @@ dependencies {
|
||||
// DI
|
||||
implementation "com.google.dagger:dagger:$daggerVersion"
|
||||
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||
compileOnly 'com.squareup.inject:assisted-inject-annotations-dagger2:0.5.0'
|
||||
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.5.0'
|
||||
|
||||
// Logging
|
||||
implementation 'com.jakewharton.timber:timber:4.7.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
|
||||
implementation 'com.googlecode.libphonenumber:libphonenumber:8.10.23'
|
||||
|
||||
|
8
matrix-sdk-android/proguard-rules.pro
vendored
8
matrix-sdk-android/proguard-rules.pro
vendored
@@ -20,14 +20,8 @@
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
||||
|
||||
### EVENT BUS ###
|
||||
|
||||
# BMA: Not sure I can delete this one without side effect
|
||||
-keepattributes *Annotation*
|
||||
-keepclassmembers class * {
|
||||
@org.greenrobot.eventbus.Subscribe <methods>;
|
||||
}
|
||||
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
|
||||
|
||||
### MOSHI ###
|
||||
|
||||
|
@@ -16,8 +16,18 @@
|
||||
|
||||
package org.matrix.android.sdk.account
|
||||
|
||||
import org.junit.Assert.assertTrue
|
||||
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.auth.UIABaseAuth
|
||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||
import org.matrix.android.sdk.api.auth.UserPasswordAuth
|
||||
import org.matrix.android.sdk.api.auth.data.LoginFlowResult
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationResult
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
import org.matrix.android.sdk.api.failure.MatrixError
|
||||
@@ -25,12 +35,8 @@ import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import org.matrix.android.sdk.common.TestMatrixCallback
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
import org.junit.runners.MethodSorters
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
@RunWith(JUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
@@ -44,7 +50,18 @@ class DeactivateAccountTest : InstrumentedTest {
|
||||
|
||||
// Deactivate the account
|
||||
commonTestHelper.runBlockingTest {
|
||||
session.deactivateAccount(TestConstants.PASSWORD, false)
|
||||
session.deactivateAccount(
|
||||
object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(
|
||||
UserPasswordAuth(
|
||||
user = session.myUserId,
|
||||
password = TestConstants.PASSWORD,
|
||||
session = flowResponse.session
|
||||
)
|
||||
)
|
||||
}
|
||||
}, false)
|
||||
}
|
||||
|
||||
// Try to login on the previous account, it will fail (M_USER_DEACTIVATED)
|
||||
|
@@ -86,7 +86,7 @@ class CommonTestHelper(context: Context) {
|
||||
*
|
||||
* @param session the session to sync
|
||||
*/
|
||||
fun syncSession(session: Session) {
|
||||
fun syncSession(session: Session, timeout: Long = TestConstants.timeOutMillis) {
|
||||
val lock = CountDownLatch(1)
|
||||
|
||||
val job = GlobalScope.launch(Dispatchers.Main) {
|
||||
@@ -109,7 +109,7 @@ class CommonTestHelper(context: Context) {
|
||||
}
|
||||
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 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 sentEvents = ArrayList<TimelineEvent>(nbOfMessages)
|
||||
val latch = CountDownLatch(1)
|
||||
@@ -151,7 +151,7 @@ class CommonTestHelper(context: Context) {
|
||||
room.sendTextMessage(message + " #" + (i + 1))
|
||||
}
|
||||
// Wait 3 second more per message
|
||||
await(latch, timeout = TestConstants.timeOutMillis + 3_000L * nbOfMessages)
|
||||
await(latch, timeout = timeout + 3_000L * nbOfMessages)
|
||||
timeline.dispose()
|
||||
|
||||
// Check that all events has been created
|
||||
@@ -215,14 +215,14 @@ class CommonTestHelper(context: Context) {
|
||||
.getLoginFlow(hs, it)
|
||||
}
|
||||
|
||||
doSync<RegistrationResult> {
|
||||
doSync<RegistrationResult>(timeout = 60_000) {
|
||||
matrix.authenticationService
|
||||
.getRegistrationWizard()
|
||||
.createAccount(userName, password, null, it)
|
||||
}
|
||||
|
||||
// Perform dummy step
|
||||
val registrationResult = doSync<RegistrationResult> {
|
||||
val registrationResult = doSync<RegistrationResult>(timeout = 60_000) {
|
||||
matrix.authenticationService
|
||||
.getRegistrationWizard()
|
||||
.dummy(it)
|
||||
@@ -231,7 +231,7 @@ class CommonTestHelper(context: Context) {
|
||||
assertTrue(registrationResult is RegistrationResult.Success)
|
||||
val session = (registrationResult as RegistrationResult.Success).session
|
||||
if (sessionTestParams.withInitialSync) {
|
||||
syncSession(session)
|
||||
syncSession(session, 60_000)
|
||||
}
|
||||
|
||||
return session
|
||||
@@ -378,7 +378,9 @@ class CommonTestHelper(context: Context) {
|
||||
fun Iterable<Session>.signOutAndClose() = forEach { signOutAndClose(it) }
|
||||
|
||||
fun signOutAndClose(session: Session) {
|
||||
doSync<Unit>(60_000) { session.signOut(true, it) }
|
||||
runBlockingTest(timeout = 60_000) {
|
||||
session.signOut(true)
|
||||
}
|
||||
// no need signout will close
|
||||
// session.close()
|
||||
}
|
||||
|
@@ -18,14 +18,21 @@ package org.matrix.android.sdk.common
|
||||
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
|
||||
data class CryptoTestData(val firstSession: Session,
|
||||
val roomId: String,
|
||||
val secondSession: Session? = null,
|
||||
val thirdSession: Session? = null) {
|
||||
data class CryptoTestData(val roomId: String,
|
||||
val sessions: List<Session>) {
|
||||
|
||||
val firstSession: Session
|
||||
get() = sessions.first()
|
||||
|
||||
val secondSession: Session?
|
||||
get() = sessions.getOrNull(1)
|
||||
|
||||
val thirdSession: Session?
|
||||
get() = sessions.getOrNull(2)
|
||||
|
||||
fun cleanUp(testHelper: CommonTestHelper) {
|
||||
testHelper.signOutAndClose(firstSession)
|
||||
secondSession?.let { testHelper.signOutAndClose(it) }
|
||||
thirdSession?.let { testHelper.signOutAndClose(it) }
|
||||
sessions.forEach {
|
||||
testHelper.signOutAndClose(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -19,6 +19,18 @@ package org.matrix.android.sdk.common
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.Observer
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||
import org.matrix.android.sdk.api.auth.UserPasswordAuth
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.IncomingSasVerificationTransaction
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.OutgoingSasVerificationTransaction
|
||||
@@ -36,17 +48,10 @@ import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP
|
||||
import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupAuthData
|
||||
import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreationInfo
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||
|
||||
@@ -73,7 +78,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||
}
|
||||
}
|
||||
|
||||
return CryptoTestData(aliceSession, roomId)
|
||||
return CryptoTestData(roomId, listOf(aliceSession))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,7 +144,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||
// assertNotNull(roomFromBobPOV.powerLevels)
|
||||
// assertTrue(roomFromBobPOV.powerLevels.maySendMessage(bobSession.myUserId))
|
||||
|
||||
return CryptoTestData(aliceSession, aliceRoomId, bobSession)
|
||||
return CryptoTestData(aliceRoomId, listOf(aliceSession, bobSession))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,7 +162,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||
// wait the initial sync
|
||||
SystemClock.sleep(1000)
|
||||
|
||||
return CryptoTestData(aliceSession, aliceRoomId, cryptoTestData.secondSession, samSession)
|
||||
return CryptoTestData(aliceRoomId, listOf(aliceSession, cryptoTestData.secondSession!!, samSession))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -304,10 +309,18 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||
fun initializeCrossSigning(session: Session) {
|
||||
mTestHelper.doSync<Unit> {
|
||||
session.cryptoService().crossSigningService()
|
||||
.initializeCrossSigning(UserPasswordAuth(
|
||||
user = session.myUserId,
|
||||
password = TestConstants.PASSWORD
|
||||
), it)
|
||||
.initializeCrossSigning(
|
||||
object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(
|
||||
UserPasswordAuth(
|
||||
user = session.myUserId,
|
||||
password = TestConstants.PASSWORD,
|
||||
session = flowResponse.session
|
||||
)
|
||||
)
|
||||
}
|
||||
}, it)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,4 +394,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)
|
||||
}
|
||||
}
|
||||
|
@@ -17,7 +17,18 @@
|
||||
package org.matrix.android.sdk.internal.crypto
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.amshove.kluent.shouldBe
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||
import org.matrix.android.sdk.api.auth.UserPasswordAuth
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
@@ -30,19 +41,13 @@ import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
|
||||
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.serializeForRealm
|
||||
import org.amshove.kluent.shouldBe
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.olm.OlmSession
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
/**
|
||||
* Ref:
|
||||
@@ -202,10 +207,18 @@ class UnwedgingTest : InstrumentedTest {
|
||||
// It's a trick to force key request on fail to decrypt
|
||||
mTestHelper.doSync<Unit> {
|
||||
bobSession.cryptoService().crossSigningService()
|
||||
.initializeCrossSigning(UserPasswordAuth(
|
||||
user = bobSession.myUserId,
|
||||
password = TestConstants.PASSWORD
|
||||
), it)
|
||||
.initializeCrossSigning(
|
||||
object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(
|
||||
UserPasswordAuth(
|
||||
user = bobSession.myUserId,
|
||||
password = TestConstants.PASSWORD,
|
||||
session = flowResponse.session
|
||||
)
|
||||
)
|
||||
}
|
||||
}, it)
|
||||
}
|
||||
|
||||
// Wait until we received back the key
|
||||
|
@@ -17,14 +17,6 @@
|
||||
package org.matrix.android.sdk.internal.crypto.crosssigning
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
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.rest.UserPasswordAuth
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNotNull
|
||||
@@ -35,6 +27,19 @@ import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||
import org.matrix.android.sdk.api.auth.UserPasswordAuth
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
|
||||
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
@@ -49,10 +54,17 @@ class XSigningTest : InstrumentedTest {
|
||||
|
||||
mTestHelper.doSync<Unit> {
|
||||
aliceSession.cryptoService().crossSigningService()
|
||||
.initializeCrossSigning(UserPasswordAuth(
|
||||
user = aliceSession.myUserId,
|
||||
password = TestConstants.PASSWORD
|
||||
), it)
|
||||
.initializeCrossSigning(object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(
|
||||
UserPasswordAuth(
|
||||
user = aliceSession.myUserId,
|
||||
password = TestConstants.PASSWORD,
|
||||
session = flowResponse.session
|
||||
)
|
||||
)
|
||||
}
|
||||
}, it)
|
||||
}
|
||||
|
||||
val myCrossSigningKeys = aliceSession.cryptoService().crossSigningService().getMyCrossSigningKeys()
|
||||
@@ -86,8 +98,18 @@ class XSigningTest : InstrumentedTest {
|
||||
password = TestConstants.PASSWORD
|
||||
)
|
||||
|
||||
mTestHelper.doSync<Unit> { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(aliceAuthParams, it) }
|
||||
mTestHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(bobAuthParams, it) }
|
||||
mTestHelper.doSync<Unit> {
|
||||
aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(aliceAuthParams)
|
||||
}
|
||||
}, it)
|
||||
}
|
||||
mTestHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(bobAuthParams)
|
||||
}
|
||||
}, it) }
|
||||
|
||||
// Check that alice can see bob keys
|
||||
mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { aliceSession.cryptoService().downloadKeys(listOf(bobSession.myUserId), true, it) }
|
||||
@@ -122,8 +144,16 @@ class XSigningTest : InstrumentedTest {
|
||||
password = TestConstants.PASSWORD
|
||||
)
|
||||
|
||||
mTestHelper.doSync<Unit> { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(aliceAuthParams, it) }
|
||||
mTestHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(bobAuthParams, it) }
|
||||
mTestHelper.doSync<Unit> { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(aliceAuthParams)
|
||||
}
|
||||
}, it) }
|
||||
mTestHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(bobAuthParams)
|
||||
}
|
||||
}, it) }
|
||||
|
||||
// Check that alice can see bob keys
|
||||
val bobUserId = bobSession.myUserId
|
||||
|
@@ -18,7 +18,21 @@ package org.matrix.android.sdk.internal.crypto.gossiping
|
||||
|
||||
import android.util.Log
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import junit.framework.TestCase.assertNotNull
|
||||
import junit.framework.TestCase.assertTrue
|
||||
import junit.framework.TestCase.fail
|
||||
import org.junit.Assert
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||
import org.matrix.android.sdk.api.auth.UserPasswordAuth
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.IncomingSasVerificationTransaction
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
|
||||
@@ -28,6 +42,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxStat
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
@@ -40,17 +55,9 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion
|
||||
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.event.EncryptedEventContent
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import junit.framework.TestCase.assertNotNull
|
||||
import junit.framework.TestCase.assertTrue
|
||||
import junit.framework.TestCase.fail
|
||||
import org.junit.Assert
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.MethodSorters
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
@@ -198,10 +205,17 @@ class KeyShareTests : InstrumentedTest {
|
||||
|
||||
mTestHelper.doSync<Unit> {
|
||||
aliceSession1.cryptoService().crossSigningService()
|
||||
.initializeCrossSigning(UserPasswordAuth(
|
||||
user = aliceSession1.myUserId,
|
||||
password = TestConstants.PASSWORD
|
||||
), it)
|
||||
.initializeCrossSigning(
|
||||
object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(
|
||||
UserPasswordAuth(
|
||||
user = aliceSession1.myUserId,
|
||||
password = TestConstants.PASSWORD
|
||||
)
|
||||
)
|
||||
}
|
||||
}, it)
|
||||
}
|
||||
|
||||
// Also bootstrap keybackup on first session
|
||||
@@ -296,4 +310,93 @@ class KeyShareTests : InstrumentedTest {
|
||||
mTestHelper.signOutAndClose(aliceSession1)
|
||||
mTestHelper.signOutAndClose(aliceSession2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_ImproperKeyShareBug() {
|
||||
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||
|
||||
mTestHelper.doSync<Unit> {
|
||||
aliceSession.cryptoService().crossSigningService()
|
||||
.initializeCrossSigning(
|
||||
object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(
|
||||
UserPasswordAuth(
|
||||
user = aliceSession.myUserId,
|
||||
password = TestConstants.PASSWORD,
|
||||
session = flowResponse.session
|
||||
)
|
||||
)
|
||||
}
|
||||
}, 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(
|
||||
object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(
|
||||
UserPasswordAuth(
|
||||
user = bobSession.myUserId,
|
||||
password = TestConstants.PASSWORD,
|
||||
session = flowResponse.session
|
||||
)
|
||||
)
|
||||
}
|
||||
}, 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)
|
||||
}
|
||||
}
|
||||
|
@@ -17,20 +17,25 @@
|
||||
package org.matrix.android.sdk.internal.crypto.verification.qrcode
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
|
||||
import org.amshove.kluent.shouldBe
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||
import org.matrix.android.sdk.api.auth.UserPasswordAuth
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
@@ -157,18 +162,34 @@ class VerificationTest : InstrumentedTest {
|
||||
|
||||
mTestHelper.doSync<Unit> { callback ->
|
||||
aliceSession.cryptoService().crossSigningService()
|
||||
.initializeCrossSigning(UserPasswordAuth(
|
||||
user = aliceSession.myUserId,
|
||||
password = TestConstants.PASSWORD
|
||||
), callback)
|
||||
.initializeCrossSigning(
|
||||
object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(
|
||||
UserPasswordAuth(
|
||||
user = aliceSession.myUserId,
|
||||
password = TestConstants.PASSWORD,
|
||||
session = flowResponse.session
|
||||
)
|
||||
)
|
||||
}
|
||||
}, callback)
|
||||
}
|
||||
|
||||
mTestHelper.doSync<Unit> { callback ->
|
||||
bobSession.cryptoService().crossSigningService()
|
||||
.initializeCrossSigning(UserPasswordAuth(
|
||||
user = bobSession.myUserId,
|
||||
password = TestConstants.PASSWORD
|
||||
), callback)
|
||||
.initializeCrossSigning(
|
||||
object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(
|
||||
UserPasswordAuth(
|
||||
user = bobSession.myUserId,
|
||||
password = TestConstants.PASSWORD,
|
||||
session = flowResponse.session
|
||||
)
|
||||
)
|
||||
}
|
||||
}, callback)
|
||||
}
|
||||
|
||||
val aliceVerificationService = aliceSession.cryptoService().verificationService()
|
||||
|
@@ -26,6 +26,8 @@ import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
||||
import org.matrix.android.sdk.api.session.room.sender.SenderInfo
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
internal class UrlsExtractorTest : InstrumentedTest {
|
||||
@@ -36,6 +38,7 @@ internal class UrlsExtractorTest : InstrumentedTest {
|
||||
fun wrongEventTypeTest() {
|
||||
createEvent(body = "https://matrix.org")
|
||||
.copy(type = EventType.STATE_ROOM_GUEST_ACCESS)
|
||||
.toFakeTimelineEvent()
|
||||
.let { urlsExtractor.extract(it) }
|
||||
.size shouldBeEqualTo 0
|
||||
}
|
||||
@@ -43,6 +46,7 @@ internal class UrlsExtractorTest : InstrumentedTest {
|
||||
@Test
|
||||
fun oneUrlTest() {
|
||||
createEvent(body = "https://matrix.org")
|
||||
.toFakeTimelineEvent()
|
||||
.let { urlsExtractor.extract(it) }
|
||||
.let { result ->
|
||||
result.size shouldBeEqualTo 1
|
||||
@@ -53,6 +57,7 @@ internal class UrlsExtractorTest : InstrumentedTest {
|
||||
@Test
|
||||
fun withoutProtocolTest() {
|
||||
createEvent(body = "www.matrix.org")
|
||||
.toFakeTimelineEvent()
|
||||
.let { urlsExtractor.extract(it) }
|
||||
.size shouldBeEqualTo 0
|
||||
}
|
||||
@@ -60,6 +65,7 @@ internal class UrlsExtractorTest : InstrumentedTest {
|
||||
@Test
|
||||
fun oneUrlWithParamTest() {
|
||||
createEvent(body = "https://matrix.org?foo=bar")
|
||||
.toFakeTimelineEvent()
|
||||
.let { urlsExtractor.extract(it) }
|
||||
.let { result ->
|
||||
result.size shouldBeEqualTo 1
|
||||
@@ -70,6 +76,7 @@ internal class UrlsExtractorTest : InstrumentedTest {
|
||||
@Test
|
||||
fun oneUrlWithParamsTest() {
|
||||
createEvent(body = "https://matrix.org?foo=bar&bar=foo")
|
||||
.toFakeTimelineEvent()
|
||||
.let { urlsExtractor.extract(it) }
|
||||
.let { result ->
|
||||
result.size shouldBeEqualTo 1
|
||||
@@ -80,16 +87,18 @@ internal class UrlsExtractorTest : InstrumentedTest {
|
||||
@Test
|
||||
fun oneUrlInlinedTest() {
|
||||
createEvent(body = "Hello https://matrix.org, how are you?")
|
||||
.toFakeTimelineEvent()
|
||||
.let { urlsExtractor.extract(it) }
|
||||
.let { result ->
|
||||
result.size shouldBeEqualTo 1
|
||||
result[0] shouldBeEqualTo "https://matrix.org"
|
||||
result[0] shouldBeEqualTo "https://matrix.org"
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun twoUrlsTest() {
|
||||
createEvent(body = "https://matrix.org https://example.org")
|
||||
.toFakeTimelineEvent()
|
||||
.let { urlsExtractor.extract(it) }
|
||||
.let { result ->
|
||||
result.size shouldBeEqualTo 2
|
||||
@@ -99,10 +108,26 @@ internal class UrlsExtractorTest : InstrumentedTest {
|
||||
}
|
||||
|
||||
private fun createEvent(body: String): Event = Event(
|
||||
eventId = "!fake",
|
||||
type = EventType.MESSAGE,
|
||||
content = MessageTextContent(
|
||||
msgType = MessageType.MSGTYPE_TEXT,
|
||||
body = body
|
||||
).toContent()
|
||||
)
|
||||
|
||||
private fun Event.toFakeTimelineEvent(): TimelineEvent {
|
||||
return TimelineEvent(
|
||||
root = this,
|
||||
localId = 0L,
|
||||
eventId = eventId!!,
|
||||
displayIndex = 0,
|
||||
senderInfo = SenderInfo(
|
||||
userId = "",
|
||||
displayName = null,
|
||||
isUniqueDisplayName = true,
|
||||
avatarUrl = null
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@@ -66,8 +66,8 @@ class TimelineForwardPaginationTest : InstrumentedTest {
|
||||
numberOfMessagesToSend)
|
||||
|
||||
// Alice clear the cache
|
||||
commonTestHelper.doSync<Unit> {
|
||||
aliceSession.clearCache(it)
|
||||
commonTestHelper.runBlockingTest {
|
||||
aliceSession.clearCache()
|
||||
}
|
||||
|
||||
// And restarts the sync
|
||||
|
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
@@ -41,6 +41,16 @@ interface AuthenticationService {
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
|
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.auth
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
|
||||
|
||||
/**
|
||||
* This class provides the authentication data by using user and password
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class TokenBasedAuth(
|
||||
|
||||
/**
|
||||
* This is a session identifier that the client must pass back to the homeserver,
|
||||
* if one is provided, in subsequent attempts to authenticate in the same API call.
|
||||
*/
|
||||
@Json(name = "session")
|
||||
override val session: String? = null,
|
||||
|
||||
/**
|
||||
* A client may receive a login token via some external service, such as email or SMS.
|
||||
* Note that a login token is separate from an access token, the latter providing general authentication to various API endpoints.
|
||||
*/
|
||||
@Json(name = "token")
|
||||
val token: String? = null,
|
||||
|
||||
/**
|
||||
* The txn_id should be a random string generated by the client for the request.
|
||||
* The same txn_id should be used if retrying the request.
|
||||
* The txn_id may be used by the server to disallow other devices from using the token,
|
||||
* thus providing "single use" tokens while still allowing the device to retry the request.
|
||||
* This would be done by tying the token to the txn_id server side, as well as potentially invalidating
|
||||
* the token completely once the device has successfully logged in
|
||||
* (e.g. when we receive a request from the newly provisioned access_token).
|
||||
*/
|
||||
@Json(name = "txn_id")
|
||||
val transactionId: String? = null,
|
||||
|
||||
// registration information
|
||||
@Json(name = "type")
|
||||
val type: String? = LoginFlowTypes.TOKEN
|
||||
|
||||
) : UIABaseAuth {
|
||||
override fun hasAuthInfo() = token != null
|
||||
|
||||
override fun copyWithSession(session: String) = this.copy(session = session)
|
||||
|
||||
override fun asMap(): Map<String, *> = mapOf(
|
||||
"session" to session,
|
||||
"token" to token,
|
||||
"transactionId" to transactionId,
|
||||
"type" to type
|
||||
)
|
||||
}
|
@@ -14,18 +14,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.eventbus
|
||||
package org.matrix.android.sdk.api.auth
|
||||
|
||||
import org.greenrobot.eventbus.Logger
|
||||
import timber.log.Timber
|
||||
import java.util.logging.Level
|
||||
interface UIABaseAuth {
|
||||
/**
|
||||
* This is a session identifier that the client must pass back to the homeserver,
|
||||
* if one is provided, in subsequent attempts to authenticate in the same API call.
|
||||
*/
|
||||
val session: String?
|
||||
|
||||
class EventBusTimberLogger : Logger {
|
||||
override fun log(level: Level, msg: String) {
|
||||
Timber.d(msg)
|
||||
}
|
||||
fun hasAuthInfo(): Boolean
|
||||
|
||||
override fun log(level: Level, msg: String, th: Throwable) {
|
||||
Timber.e(th, msg)
|
||||
}
|
||||
fun copyWithSession(session: String): UIABaseAuth
|
||||
|
||||
fun asMap() : Map<String, *>
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.auth
|
||||
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import kotlin.coroutines.Continuation
|
||||
|
||||
/**
|
||||
* Some API endpoints require authentication that interacts with the user.
|
||||
* The homeserver may provide many different ways of authenticating, such as user/password auth, login via a social network (OAuth2),
|
||||
* login by confirming a token sent to their email address, etc.
|
||||
*
|
||||
* The process takes the form of one or more 'stages'.
|
||||
* At each stage the client submits a set of data for a given authentication type and awaits a response from the server,
|
||||
* which will either be a final success or a request to perform an additional stage.
|
||||
* This exchange continues until the final success.
|
||||
*
|
||||
* For each endpoint, a server offers one or more 'flows' that the client can use to authenticate itself.
|
||||
* Each flow comprises a series of stages, as described above.
|
||||
* The client is free to choose which flow it follows, however the flow's stages must be completed in order.
|
||||
* Failing to follow the flows in order must result in an HTTP 401 response.
|
||||
* When all stages in a flow are complete, authentication is complete and the API call succeeds.
|
||||
*/
|
||||
interface UserInteractiveAuthInterceptor {
|
||||
|
||||
/**
|
||||
* When the API needs additional auth, this will be called.
|
||||
* Implementation should check the flows from flow response and act accordingly.
|
||||
* Updated auth should be provided using promise.resume, this allow implementation to perform
|
||||
* an async operation (prompt for user password, open sso fallback) and then resume initial API call when done.
|
||||
*/
|
||||
fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>)
|
||||
}
|
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.matrix.android.sdk.internal.crypto.model.rest
|
||||
package org.matrix.android.sdk.api.auth
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
@@ -27,7 +27,7 @@ data class UserPasswordAuth(
|
||||
|
||||
// device device session id
|
||||
@Json(name = "session")
|
||||
val session: String? = null,
|
||||
override val session: String? = null,
|
||||
|
||||
// registration information
|
||||
@Json(name = "type")
|
||||
@@ -38,4 +38,16 @@ data class UserPasswordAuth(
|
||||
|
||||
@Json(name = "password")
|
||||
val password: String? = null
|
||||
)
|
||||
) : UIABaseAuth {
|
||||
|
||||
override fun hasAuthInfo() = password != null
|
||||
|
||||
override fun copyWithSession(session: String) = this.copy(session = session)
|
||||
|
||||
override fun asMap(): Map<String, *> = mapOf(
|
||||
"session" to session,
|
||||
"user" to user,
|
||||
"password" to password,
|
||||
"type" to type
|
||||
)
|
||||
}
|
@@ -19,7 +19,7 @@ package org.matrix.android.sdk.api.auth.data
|
||||
import android.os.Parcelable
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@Parcelize
|
||||
@@ -38,15 +38,24 @@ data class SsoIdentityProvider(
|
||||
* If present then it must be an HTTPS URL to an image resource.
|
||||
* This should be hosted by the homeserver service provider to not leak the client's IP address unnecessarily.
|
||||
*/
|
||||
@Json(name = "icon") val iconUrl: String?
|
||||
@Json(name = "icon") val iconUrl: String?,
|
||||
|
||||
/**
|
||||
* The `brand` field is **optional**. It allows the client to style the login
|
||||
* button to suit a particular brand. It should be a string matching the
|
||||
* "Common namespaced identifier grammar" as defined in
|
||||
* [MSC2758](https://github.com/matrix-org/matrix-doc/pull/2758).
|
||||
*/
|
||||
@Json(name = "brand") val brand: String?
|
||||
|
||||
) : Parcelable {
|
||||
|
||||
companion object {
|
||||
// Not really defined by the spec, but we may define some ids here
|
||||
const val ID_GOOGLE = "google"
|
||||
const val ID_GITHUB = "github"
|
||||
const val ID_APPLE = "apple"
|
||||
const val ID_FACEBOOK = "facebook"
|
||||
const val ID_TWITTER = "twitter"
|
||||
const val BRAND_GOOGLE = "org.matrix.google"
|
||||
const val BRAND_GITHUB = "org.matrix.github"
|
||||
const val BRAND_APPLE = "org.matrix.apple"
|
||||
const val BRAND_FACEBOOK = "org.matrix.facebook"
|
||||
const val BRAND_TWITTER = "org.matrix.twitter"
|
||||
const val BRAND_GITLAB = "org.matrix.gitlab"
|
||||
}
|
||||
}
|
||||
|
@@ -14,14 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.auth.registration
|
||||
package org.matrix.android.sdk.api.auth.registration
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
|
||||
import org.matrix.android.sdk.api.auth.registration.FlowResult
|
||||
import org.matrix.android.sdk.api.auth.registration.Stage
|
||||
import org.matrix.android.sdk.api.auth.registration.TermPolicies
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
import org.matrix.android.sdk.internal.auth.data.InteractiveAuthenticationFlow
|
||||
|
||||
@@ -109,3 +106,8 @@ fun RegistrationFlowResponse.toFlowResult(): FlowResult {
|
||||
|
||||
return FlowResult(missingStage, completedStage)
|
||||
}
|
||||
|
||||
fun RegistrationFlowResponse.nextUncompletedStage(flowIndex: Int = 0): String? {
|
||||
val completed = completedStages ?: emptyList()
|
||||
return flows?.getOrNull(flowIndex)?.stages?.firstOrNull { completed.contains(it).not() }
|
||||
}
|
@@ -16,8 +16,8 @@
|
||||
|
||||
package org.matrix.android.sdk.api.failure
|
||||
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||
import java.io.IOException
|
||||
import javax.net.ssl.HttpsURLConnection
|
||||
@@ -43,6 +43,12 @@ fun Throwable.isInvalidPassword(): Boolean {
|
||||
&& error.message == "Invalid password"
|
||||
}
|
||||
|
||||
fun Throwable.isInvalidUIAAuth(): Boolean {
|
||||
return this is Failure.ServerError
|
||||
&& error.code == MatrixError.M_FORBIDDEN
|
||||
&& error.flows != null
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to convert to a RegistrationFlowResponse. Return null in the cases it's not possible
|
||||
*/
|
||||
@@ -53,6 +59,16 @@ fun Throwable.toRegistrationFlowResponse(): RegistrationFlowResponse? {
|
||||
.adapter(RegistrationFlowResponse::class.java)
|
||||
.fromJson(this.errorBody)
|
||||
}
|
||||
} else if (this is Failure.ServerError && this.httpCode == 401 && this.error.code == MatrixError.M_FORBIDDEN) {
|
||||
// This happens when the submission for this stage was bad (like bad password)
|
||||
if (this.error.session != null && this.error.flows != null) {
|
||||
RegistrationFlowResponse(
|
||||
flows = this.error.flows,
|
||||
session = this.error.session,
|
||||
completedStages = this.error.completedStages,
|
||||
params = this.error.params
|
||||
)
|
||||
} else null
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
@@ -16,8 +16,8 @@
|
||||
|
||||
package org.matrix.android.sdk.api.failure
|
||||
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||
import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.internal.network.ssl.Fingerprint
|
||||
import java.io.IOException
|
||||
|
||||
|
@@ -18,6 +18,8 @@ package org.matrix.android.sdk.api.failure
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
import org.matrix.android.sdk.internal.auth.data.InteractiveAuthenticationFlow
|
||||
|
||||
/**
|
||||
* This data class holds the error defined by the matrix specifications.
|
||||
@@ -42,7 +44,17 @@ data class MatrixError(
|
||||
@Json(name = "soft_logout") val isSoftLogout: Boolean = false,
|
||||
// For M_INVALID_PEPPER
|
||||
// {"error": "pepper does not match 'erZvr'", "lookup_pepper": "pQgMS", "algorithm": "sha256", "errcode": "M_INVALID_PEPPER"}
|
||||
@Json(name = "lookup_pepper") val newLookupPepper: String? = null
|
||||
@Json(name = "lookup_pepper") val newLookupPepper: String? = null,
|
||||
|
||||
// For M_FORBIDDEN UIA
|
||||
@Json(name = "session")
|
||||
val session: String? = null,
|
||||
@Json(name = "completed")
|
||||
val completedStages: List<String>? = null,
|
||||
@Json(name = "flows")
|
||||
val flows: List<InteractiveAuthenticationFlow>? = null,
|
||||
@Json(name = "params")
|
||||
val params: JsonDict? = null
|
||||
) {
|
||||
|
||||
companion object {
|
||||
|
@@ -37,6 +37,6 @@ class SenderNotificationPermissionCondition(
|
||||
|
||||
fun isSatisfied(event: Event, powerLevels: PowerLevelsContent): Boolean {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@@ -245,6 +245,8 @@ interface Session :
|
||||
|
||||
val sharedSecretStorageService: SharedSecretStorageService
|
||||
|
||||
fun getUiaSsoFallbackUrl(authenticationSessionId: String): String
|
||||
|
||||
/**
|
||||
* Maintenance API, allows to print outs info on DB size to logcat
|
||||
*/
|
||||
|
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.matrix.android.sdk.api.session.account
|
||||
|
||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||
|
||||
/**
|
||||
* This interface defines methods to manage the account. It's implemented at the session level.
|
||||
*/
|
||||
@@ -43,5 +45,5 @@ interface AccountService {
|
||||
* @param eraseAllData set to true to forget all messages that have been sent. Warning: this will cause future users to see
|
||||
* an incomplete view of conversations
|
||||
*/
|
||||
suspend fun deactivateAccount(password: String, eraseAllData: Boolean)
|
||||
suspend fun deactivateAccount(userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor, eraseAllData: Boolean)
|
||||
}
|
||||
|
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.matrix.android.sdk.api.session.cache
|
||||
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
|
||||
/**
|
||||
* This interface defines a method to clear the cache. It's implemented at the session level.
|
||||
*/
|
||||
@@ -26,5 +24,5 @@ interface CacheService {
|
||||
/**
|
||||
* Clear the whole cached data, except credentials. Once done, the sync has to be restarted by the sdk user.
|
||||
*/
|
||||
fun clearCache(callback: MatrixCallback<Unit>)
|
||||
suspend fun clearCache()
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@ import android.net.Uri
|
||||
import android.os.Parcelable
|
||||
import androidx.exifinterface.media.ExifInterface
|
||||
import com.squareup.moshi.JsonClass
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.matrix.android.sdk.api.util.MimeTypes.normalizeMimeType
|
||||
|
||||
@Parcelize
|
||||
|
@@ -20,6 +20,7 @@ import android.content.Context
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.paging.PagedList
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||
import org.matrix.android.sdk.api.listeners.ProgressListener
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupService
|
||||
@@ -53,7 +54,7 @@ interface CryptoService {
|
||||
|
||||
fun setDeviceName(deviceId: String, deviceName: String, callback: MatrixCallback<Unit>)
|
||||
|
||||
fun deleteDevice(deviceId: String, callback: MatrixCallback<Unit>)
|
||||
fun deleteDevice(deviceId: String, userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor, callback: MatrixCallback<Unit>)
|
||||
|
||||
fun deleteDeviceWithUserPassword(deviceId: String, authSession: String?, password: String, callback: MatrixCallback<Unit>)
|
||||
|
||||
|
@@ -18,10 +18,10 @@ package org.matrix.android.sdk.api.session.crypto.crosssigning
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustResult
|
||||
import org.matrix.android.sdk.internal.crypto.crosssigning.UserTrustResult
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
|
||||
import org.matrix.android.sdk.internal.crypto.store.PrivateKeysInfo
|
||||
|
||||
interface CrossSigningService {
|
||||
@@ -40,7 +40,7 @@ interface CrossSigningService {
|
||||
* Initialize cross signing for this user.
|
||||
* Users needs to enter credentials
|
||||
*/
|
||||
fun initializeCrossSigning(authParams: UserPasswordAuth?,
|
||||
fun initializeCrossSigning(uiaInterceptor: UserInteractiveAuthInterceptor?,
|
||||
callback: MatrixCallback<Unit>)
|
||||
|
||||
fun isCrossSigningInitialized(): Boolean = getMyCrossSigningKeys() != null
|
||||
|
@@ -17,15 +17,16 @@
|
||||
package org.matrix.android.sdk.api.session.media
|
||||
|
||||
import org.matrix.android.sdk.api.cache.CacheStrategy
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
|
||||
interface MediaService {
|
||||
/**
|
||||
* Extract URLs from an Event.
|
||||
* @return the list of URLs contains in the body of the Event. It does not mean that URLs in this list have UrlPreview data
|
||||
* Extract URLs from a TimelineEvent.
|
||||
* @param event TimelineEvent to extract the URL from.
|
||||
* @return the list of URLs contains in the body of the TimelineEvent. It does not mean that URLs in this list have UrlPreview data
|
||||
*/
|
||||
fun extractUrls(event: Event): List<String>
|
||||
fun extractUrls(event: TimelineEvent): List<String>
|
||||
|
||||
/**
|
||||
* Get Raw Url Preview data from the homeserver. There is no cache management for this request
|
||||
|
@@ -25,7 +25,6 @@ interface PermalinkService {
|
||||
|
||||
companion object {
|
||||
const val MATRIX_TO_URL_BASE = "https://matrix.to/#/"
|
||||
const val MATRIX_TO_CUSTOM_SCHEME_URL_BASE = "element://"
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -20,6 +20,7 @@ package org.matrix.android.sdk.api.session.profile
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.LiveData
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||
import org.matrix.android.sdk.api.session.identity.ThreePid
|
||||
import org.matrix.android.sdk.api.util.Cancelable
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
@@ -107,8 +108,7 @@ interface ProfileService {
|
||||
* Finalize adding a 3Pids. Call this method once the user has validated that he owns the ThreePid
|
||||
*/
|
||||
fun finalizeAddingThreePid(threePid: ThreePid,
|
||||
uiaSession: String?,
|
||||
accountPassword: String?,
|
||||
userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor,
|
||||
matrixCallback: MatrixCallback<Unit>): Cancelable
|
||||
|
||||
/**
|
||||
|
@@ -25,28 +25,85 @@ import org.matrix.android.sdk.api.session.room.powerlevels.Role
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class PowerLevelsContent(
|
||||
/**
|
||||
* The level required to ban a user. Defaults to 50 if unspecified.
|
||||
*/
|
||||
@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,
|
||||
/**
|
||||
* The level required to invite a user. Defaults to 50 if unspecified.
|
||||
*/
|
||||
@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,
|
||||
/**
|
||||
* 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") 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") 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 = "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 powerLevel the new power level, or null to set the default value.
|
||||
*/
|
||||
fun setUserPowerLevel(userId: String, powerLevel: Int?) {
|
||||
if (powerLevel == null || powerLevel == usersDefault) {
|
||||
users.remove(userId)
|
||||
} else {
|
||||
users[userId] = powerLevel
|
||||
fun setUserPowerLevel(userId: String, powerLevel: Int?): PowerLevelsContent {
|
||||
return copy(
|
||||
users = users.toMutableMap().apply {
|
||||
if (powerLevel == null || powerLevel == usersDefault) {
|
||||
remove(userId)
|
||||
} else {
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@ data class MessageStickerContent(
|
||||
/**
|
||||
* Set in local, not from server
|
||||
*/
|
||||
@Transient
|
||||
override val msgType: String = MessageType.MSGTYPE_STICKER_LOCAL,
|
||||
|
||||
/**
|
||||
|
@@ -108,19 +108,4 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) {
|
||||
val powerLevel = getUserPowerLevelValue(userId)
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
@@ -89,6 +89,17 @@ data class TimelineEvent(
|
||||
*/
|
||||
fun TimelineEvent.hasBeenEdited() = annotations?.editSummary != null
|
||||
|
||||
/**
|
||||
* Get the latest known eventId for an edited event, or the eventId for an Event which has not been edited
|
||||
*/
|
||||
fun TimelineEvent.getLatestEventId(): String {
|
||||
return annotations
|
||||
?.editSummary
|
||||
?.sourceEvents
|
||||
?.lastOrNull()
|
||||
?: eventId
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the relation content if any
|
||||
*/
|
||||
|
@@ -16,9 +16,7 @@
|
||||
|
||||
package org.matrix.android.sdk.api.session.signout
|
||||
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.auth.data.Credentials
|
||||
import org.matrix.android.sdk.api.util.Cancelable
|
||||
|
||||
/**
|
||||
* This interface defines a method to sign out, or to renew the token. It's implemented at the session level.
|
||||
@@ -29,19 +27,16 @@ interface SignOutService {
|
||||
* Ask the homeserver for a new access token.
|
||||
* The same deviceId will be used
|
||||
*/
|
||||
fun signInAgain(password: String,
|
||||
callback: MatrixCallback<Unit>): Cancelable
|
||||
suspend fun signInAgain(password: String)
|
||||
|
||||
/**
|
||||
* Update the session with credentials received after SSO
|
||||
*/
|
||||
fun updateCredentials(credentials: Credentials,
|
||||
callback: MatrixCallback<Unit>): Cancelable
|
||||
suspend fun updateCredentials(credentials: Credentials)
|
||||
|
||||
/**
|
||||
* Sign out, and release the session, clear all the session data, including crypto data
|
||||
* @param signOutFromHomeserver true if the sign out request has to be done
|
||||
*/
|
||||
fun signOut(signOutFromHomeserver: Boolean,
|
||||
callback: MatrixCallback<Unit>): Cancelable
|
||||
suspend fun signOut(signOutFromHomeserver: Boolean)
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
@@ -14,24 +14,28 @@
|
||||
* 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
|
||||
* 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
|
||||
* 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
|
||||
* 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"
|
||||
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"
|
||||
|
||||
// Ref: https://matrix.org/docs/spec/client_server/r0.6.1#single-sign-on
|
||||
internal const val SSO_UIA_FALLBACK_PATH = "/_matrix/client/r0/auth/m.login.sso/fallback/web"
|
@@ -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.util.Cancelable
|
||||
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.auth.data.LoginFlowResponse
|
||||
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 {
|
||||
pendingSessionData = null
|
||||
|
||||
|
@@ -42,6 +42,7 @@ internal data class LoginFlow(
|
||||
* the client can show a button for each of the supported providers
|
||||
* See MSC #2858
|
||||
*/
|
||||
@Json(name = "identity_providers")
|
||||
val ssoIdentityProvider: List<SsoIdentityProvider>?
|
||||
@Json(name = "org.matrix.msc2858.identity_providers")
|
||||
val ssoIdentityProvider: List<SsoIdentityProvider>? = null
|
||||
|
||||
)
|
||||
|
@@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
|
||||
import org.matrix.android.sdk.api.auth.registration.RegisterThreePid
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationResult
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
|
||||
import org.matrix.android.sdk.api.auth.registration.toFlowResult
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
import org.matrix.android.sdk.api.failure.Failure.RegistrationFlowError
|
||||
import org.matrix.android.sdk.api.util.Cancelable
|
||||
|
@@ -17,7 +17,7 @@
|
||||
package org.matrix.android.sdk.internal.auth.registration
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
/**
|
||||
* This class represent a localized privacy policy for registration Flow.
|
||||
|
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.auth.registration
|
||||
|
||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse
|
||||
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
||||
import timber.log.Timber
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
internal suspend fun handleUIA(failure: Throwable, interceptor: UserInteractiveAuthInterceptor, retryBlock: suspend (UIABaseAuth) -> Unit): Boolean {
|
||||
Timber.d("## UIA: check error ${failure.message}")
|
||||
val flowResponse = failure.toRegistrationFlowResponse()
|
||||
?: return false.also {
|
||||
Timber.d("## UIA: not a UIA error")
|
||||
}
|
||||
|
||||
Timber.d("## UIA: error can be passed to interceptor")
|
||||
Timber.d("## UIA: type = ${flowResponse.flows}")
|
||||
|
||||
Timber.d("## UIA: delegate to interceptor...")
|
||||
val authUpdate = try {
|
||||
suspendCoroutine<UIABaseAuth> { continuation ->
|
||||
interceptor.performStage(flowResponse, (failure as? Failure.ServerError)?.error?.code, continuation)
|
||||
}
|
||||
} catch (failure: Throwable) {
|
||||
Timber.w(failure, "## UIA: failed to participate")
|
||||
return false
|
||||
}
|
||||
|
||||
Timber.d("## UIA: updated auth $authUpdate")
|
||||
return try {
|
||||
retryBlock(authUpdate)
|
||||
true
|
||||
} catch (failure: Throwable) {
|
||||
handleUIA(failure, interceptor, retryBlock)
|
||||
}
|
||||
}
|
@@ -19,7 +19,6 @@ package org.matrix.android.sdk.internal.crypto
|
||||
import android.content.Context
|
||||
import androidx.work.WorkerParameters
|
||||
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.failure.shouldBeRetried
|
||||
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 cryptoStore: IMXCryptoStore
|
||||
@Inject lateinit var eventBus: EventBus
|
||||
@Inject lateinit var credentials: Credentials
|
||||
|
||||
override fun injectWith(injector: SessionComponent) {
|
||||
|
@@ -30,6 +30,7 @@ import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.NoOpMatrixCallback
|
||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||
import org.matrix.android.sdk.api.crypto.MXCryptoConfig
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
@@ -207,9 +208,9 @@ internal class DefaultCryptoService @Inject constructor(
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
override fun deleteDevice(deviceId: String, callback: MatrixCallback<Unit>) {
|
||||
override fun deleteDevice(deviceId: String, userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor, callback: MatrixCallback<Unit>) {
|
||||
deleteDeviceTask
|
||||
.configureWith(DeleteDeviceTask.Params(deviceId)) {
|
||||
.configureWith(DeleteDeviceTask.Params(deviceId, userInteractiveAuthInterceptor, null)) {
|
||||
this.executionThread = TaskThread.CRYPTO
|
||||
this.callback = callback
|
||||
}
|
||||
|
@@ -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.model.rest.GossipingDefaultContent
|
||||
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.di.SessionId
|
||||
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}")
|
||||
if (credentials.userId != userId) {
|
||||
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)
|
||||
handleKeyRequestFromOtherUser(body, request, alg, roomId, userId, deviceId)
|
||||
return
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
|
||||
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) {
|
||||
val secretName = request.secretName ?: return Unit.also {
|
||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user