{
- return mapOf("key" to value)
+ return rawMap.filter {
+ it.key != "signatures" && it.key != "unsigned"
+ }
}
/**
@@ -82,6 +90,7 @@ data class MXKey(
*
* "signed_curve25519:AAAAFw": {
* "key": "IjwIcskng7YjYcn0tS8TUOT2OHHtBSfMpcfIczCgXj4",
+ * "fallback" : true|false
* "signatures": {
* "@userId:matrix.org": {
* "ed25519:GMJRREOASV": "EUjp6pXzK9u3SDFR\/qLbzpOi3bEREeI6qMnKzXu992HsfuDDZftfJfiUXv9b\/Hqq1og4qM\/vCQJGTHAWMmgkCg"
@@ -107,7 +116,8 @@ data class MXKey(
type = components[0],
keyId = components[1],
value = params["key"] as String,
- signatures = params["signatures"] as Map>
+ signatures = params["signatures"] as Map>,
+ rawMap = params
)
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/DefaultPushRuleService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/DefaultPushRuleService.kt
index 65974151c8..3e821b8956 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/DefaultPushRuleService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/DefaultPushRuleService.kt
@@ -19,6 +19,7 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.api.pushrules.Action
+import org.matrix.android.sdk.api.pushrules.PushEvents
import org.matrix.android.sdk.api.pushrules.PushRuleService
import org.matrix.android.sdk.api.pushrules.RuleKind
import org.matrix.android.sdk.api.pushrules.RuleScope
@@ -142,79 +143,6 @@ internal class DefaultPushRuleService @Inject constructor(
return pushRuleFinder.fulfilledBingRule(event, rules)?.getActions().orEmpty()
}
-// fun processEvents(events: List) {
-// var hasDoneSomething = false
-// events.forEach { event ->
-// fulfilledBingRule(event)?.let {
-// hasDoneSomething = true
-// dispatchBing(event, it)
-// }
-// }
-// if (hasDoneSomething)
-// dispatchFinish()
-// }
-
- fun dispatchBing(event: Event, rule: PushRule) {
- synchronized(listeners) {
- val actionsList = rule.getActions()
- listeners.forEach {
- try {
- it.onMatchRule(event, actionsList)
- } catch (e: Throwable) {
- Timber.e(e, "Error while dispatching bing")
- }
- }
- }
- }
-
- fun dispatchRoomJoined(roomId: String) {
- synchronized(listeners) {
- listeners.forEach {
- try {
- it.onRoomJoined(roomId)
- } catch (e: Throwable) {
- Timber.e(e, "Error while dispatching room joined")
- }
- }
- }
- }
-
- fun dispatchRoomLeft(roomId: String) {
- synchronized(listeners) {
- listeners.forEach {
- try {
- it.onRoomLeft(roomId)
- } catch (e: Throwable) {
- Timber.e(e, "Error while dispatching room left")
- }
- }
- }
- }
-
- fun dispatchRedactedEventId(redactedEventId: String) {
- synchronized(listeners) {
- listeners.forEach {
- try {
- it.onEventRedacted(redactedEventId)
- } catch (e: Throwable) {
- Timber.e(e, "Error while dispatching redacted event")
- }
- }
- }
- }
-
- fun dispatchFinish() {
- synchronized(listeners) {
- listeners.forEach {
- try {
- it.batchFinish()
- } catch (e: Throwable) {
- Timber.e(e, "Error while dispatching finish")
- }
- }
- }
- }
-
override fun getKeywords(): LiveData> {
// Keywords are all content rules that don't start with '.'
val liveData = monarchy.findAllMappedWithChanges(
@@ -229,4 +157,16 @@ internal class DefaultPushRuleService @Inject constructor(
results.firstOrNull().orEmpty().toSet()
}
}
+
+ fun dispatchEvents(pushEvents: PushEvents) {
+ synchronized(listeners) {
+ listeners.forEach {
+ try {
+ it.onEvents(pushEvents)
+ } catch (e: Throwable) {
+ Timber.e(e, "Error while dispatching push events")
+ }
+ }
+ }
+ }
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt
index 3c74888eda..0ac21b555e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt
@@ -16,6 +16,7 @@
package org.matrix.android.sdk.internal.session.notification
+import org.matrix.android.sdk.api.pushrules.PushEvents
import org.matrix.android.sdk.api.pushrules.rest.PushRule
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.isInvitation
@@ -39,14 +40,6 @@ internal class DefaultProcessEventForPushTask @Inject constructor(
) : ProcessEventForPushTask {
override suspend fun execute(params: ProcessEventForPushTask.Params) {
- // Handle left rooms
- params.syncResponse.leave.keys.forEach {
- defaultPushRuleService.dispatchRoomLeft(it)
- }
- // Handle joined rooms
- params.syncResponse.join.keys.forEach {
- defaultPushRuleService.dispatchRoomJoined(it)
- }
val newJoinEvents = params.syncResponse.join
.mapNotNull { (key, value) ->
value.timeline?.events?.mapNotNull {
@@ -74,10 +67,10 @@ internal class DefaultProcessEventForPushTask @Inject constructor(
}
Timber.v("[PushRules] Found ${allEvents.size} out of ${(newJoinEvents + inviteEvents).size}" +
" to check for push rules with ${params.rules.size} rules")
- allEvents.forEach { event ->
+ val matchedEvents = allEvents.mapNotNull { event ->
pushRuleFinder.fulfilledBingRule(event, params.rules)?.let {
Timber.v("[PushRules] Rule $it match for event ${event.eventId}")
- defaultPushRuleService.dispatchBing(event, it)
+ event to it
}
}
@@ -91,10 +84,13 @@ internal class DefaultProcessEventForPushTask @Inject constructor(
Timber.v("[PushRules] Found ${allRedactedEvents.size} redacted events")
- allRedactedEvents.forEach { redactedEventId ->
- defaultPushRuleService.dispatchRedactedEventId(redactedEventId)
- }
-
- defaultPushRuleService.dispatchFinish()
+ defaultPushRuleService.dispatchEvents(
+ PushEvents(
+ matchedEvents = matchedEvents,
+ roomsJoined = params.syncResponse.join.keys,
+ roomsLeft = params.syncResponse.leave.keys,
+ redactedEventIds = allRedactedEvents
+ )
+ )
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt
index a12587ac56..3e977b31fb 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt
@@ -16,9 +16,8 @@
package org.matrix.android.sdk.internal.util
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleObserver
-import androidx.lifecycle.OnLifecycleEvent
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
import org.matrix.android.sdk.internal.di.MatrixScope
import timber.log.Timber
import javax.inject.Inject
@@ -27,13 +26,12 @@ import javax.inject.Inject
* To be attached to ProcessLifecycleOwner lifecycle
*/
@MatrixScope
-internal class BackgroundDetectionObserver @Inject constructor() : LifecycleObserver {
+internal class BackgroundDetectionObserver @Inject constructor() : DefaultLifecycleObserver {
var isInBackground: Boolean = true
private set
- private
- val listeners = LinkedHashSet()
+ private val listeners = LinkedHashSet()
fun register(listener: Listener) {
listeners.add(listener)
@@ -43,15 +41,13 @@ internal class BackgroundDetectionObserver @Inject constructor() : LifecycleObse
listeners.remove(listener)
}
- @OnLifecycleEvent(Lifecycle.Event.ON_START)
- fun onMoveToForeground() {
+ override fun onStart(owner: LifecycleOwner) {
Timber.v("App returning to foreground…")
isInBackground = false
listeners.forEach { it.onMoveToForeground() }
}
- @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
- fun onMoveToBackground() {
+ override fun onStop(owner: LifecycleOwner) {
Timber.v("App going to background…")
isInBackground = true
listeners.forEach { it.onMoveToBackground() }
diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/ContactPicker.kt b/multipicker/src/main/java/im/vector/lib/multipicker/ContactPicker.kt
index 315fe6cbf2..2b8c1d11e6 100644
--- a/multipicker/src/main/java/im/vector/lib/multipicker/ContactPicker.kt
+++ b/multipicker/src/main/java/im/vector/lib/multipicker/ContactPicker.kt
@@ -21,6 +21,7 @@ import android.content.Context
import android.content.Intent
import android.provider.ContactsContract
import im.vector.lib.multipicker.entity.MultiPickerContactType
+import im.vector.lib.multipicker.utils.getColumnIndexOrNull
/**
* Contact Picker implementation
@@ -49,9 +50,9 @@ class ContactPicker : Picker() {
null
)?.use { cursor ->
if (cursor.moveToFirst()) {
- val idColumn = cursor.getColumnIndex(ContactsContract.Contacts._ID)
- val nameColumn = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)
- val photoUriColumn = cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_URI)
+ val idColumn = cursor.getColumnIndexOrNull(ContactsContract.Contacts._ID) ?: return@use
+ val nameColumn = cursor.getColumnIndexOrNull(ContactsContract.Contacts.DISPLAY_NAME) ?: return@use
+ val photoUriColumn = cursor.getColumnIndexOrNull(ContactsContract.Contacts.PHOTO_URI) ?: return@use
val contactId = cursor.getInt(idColumn)
var name = cursor.getString(nameColumn)
@@ -72,10 +73,13 @@ class ContactPicker : Picker() {
selection,
selectionArgs,
null
- )?.use { cursor ->
- while (cursor.moveToNext()) {
- val mimeType = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.MIMETYPE))
- val contactData = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DATA1))
+ )?.use inner@{ innerCursor ->
+ val mimeTypeColumnIndex = innerCursor.getColumnIndexOrNull(ContactsContract.Data.MIMETYPE) ?: return@inner
+ val data1ColumnIndex = innerCursor.getColumnIndexOrNull(ContactsContract.Data.DATA1) ?: return@inner
+
+ while (innerCursor.moveToNext()) {
+ val mimeType = innerCursor.getString(mimeTypeColumnIndex)
+ val contactData = innerCursor.getString(data1ColumnIndex)
if (mimeType == ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) {
name = contactData
@@ -115,7 +119,10 @@ class ContactPicker : Picker() {
selectionArgs,
null
)?.use { cursor ->
- return if (cursor.moveToFirst()) cursor.getInt(cursor.getColumnIndex(ContactsContract.RawContacts._ID)) else null
+ return if (cursor.moveToFirst()) {
+ cursor.getColumnIndexOrNull(ContactsContract.RawContacts._ID)
+ ?.let { cursor.getInt(it) }
+ } else null
}
}
diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/FilePicker.kt b/multipicker/src/main/java/im/vector/lib/multipicker/FilePicker.kt
index ec98152aa7..8e6c97f2f8 100644
--- a/multipicker/src/main/java/im/vector/lib/multipicker/FilePicker.kt
+++ b/multipicker/src/main/java/im/vector/lib/multipicker/FilePicker.kt
@@ -21,6 +21,7 @@ import android.content.Intent
import android.provider.OpenableColumns
import im.vector.lib.multipicker.entity.MultiPickerBaseType
import im.vector.lib.multipicker.entity.MultiPickerFileType
+import im.vector.lib.multipicker.utils.getColumnIndexOrNull
import im.vector.lib.multipicker.utils.isMimeTypeAudio
import im.vector.lib.multipicker.utils.isMimeTypeImage
import im.vector.lib.multipicker.utils.isMimeTypeVideo
@@ -49,8 +50,8 @@ class FilePicker : Picker() {
// Other files
context.contentResolver.query(selectedUri, null, null, null, null)
?.use { cursor ->
- val nameColumn = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
- val sizeColumn = cursor.getColumnIndex(OpenableColumns.SIZE)
+ val nameColumn = cursor.getColumnIndexOrNull(OpenableColumns.DISPLAY_NAME) ?: return@use null
+ val sizeColumn = cursor.getColumnIndexOrNull(OpenableColumns.SIZE) ?: return@use null
if (cursor.moveToFirst()) {
val name = cursor.getString(nameColumn)
val size = cursor.getLong(sizeColumn)
diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/utils/ContentResolverUtil.kt b/multipicker/src/main/java/im/vector/lib/multipicker/utils/ContentResolverUtil.kt
index 78136c274a..55c0010afd 100644
--- a/multipicker/src/main/java/im/vector/lib/multipicker/utils/ContentResolverUtil.kt
+++ b/multipicker/src/main/java/im/vector/lib/multipicker/utils/ContentResolverUtil.kt
@@ -37,8 +37,8 @@ internal fun Uri.toMultiPickerImageType(context: Context): MultiPickerImageType?
null,
null
)?.use { cursor ->
- val nameColumn = cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME)
- val sizeColumn = cursor.getColumnIndex(MediaStore.Images.Media.SIZE)
+ val nameColumn = cursor.getColumnIndexOrNull(MediaStore.Images.Media.DISPLAY_NAME) ?: return@use null
+ val sizeColumn = cursor.getColumnIndexOrNull(MediaStore.Images.Media.SIZE) ?: return@use null
if (cursor.moveToNext()) {
val name = cursor.getString(nameColumn)
@@ -75,8 +75,8 @@ internal fun Uri.toMultiPickerVideoType(context: Context): MultiPickerVideoType?
null,
null
)?.use { cursor ->
- val nameColumn = cursor.getColumnIndex(MediaStore.Video.Media.DISPLAY_NAME)
- val sizeColumn = cursor.getColumnIndex(MediaStore.Video.Media.SIZE)
+ val nameColumn = cursor.getColumnIndexOrNull(MediaStore.Video.Media.DISPLAY_NAME) ?: return@use null
+ val sizeColumn = cursor.getColumnIndexOrNull(MediaStore.Video.Media.SIZE) ?: return@use null
if (cursor.moveToNext()) {
val name = cursor.getString(nameColumn)
@@ -124,8 +124,8 @@ fun Uri.toMultiPickerAudioType(context: Context): MultiPickerAudioType? {
null,
null
)?.use { cursor ->
- val nameColumn = cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME)
- val sizeColumn = cursor.getColumnIndex(MediaStore.Audio.Media.SIZE)
+ val nameColumn = cursor.getColumnIndexOrNull(MediaStore.Audio.Media.DISPLAY_NAME) ?: return@use null
+ val sizeColumn = cursor.getColumnIndexOrNull(MediaStore.Audio.Media.SIZE) ?: return@use null
if (cursor.moveToNext()) {
val name = cursor.getString(nameColumn)
diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/utils/CursorExtensions.kt b/multipicker/src/main/java/im/vector/lib/multipicker/utils/CursorExtensions.kt
new file mode 100644
index 0000000000..87cf48d0a7
--- /dev/null
+++ b/multipicker/src/main/java/im/vector/lib/multipicker/utils/CursorExtensions.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2021 New Vector Ltd
+ *
+ * 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 im.vector.lib.multipicker.utils
+
+import android.database.Cursor
+
+fun Cursor.getColumnIndexOrNull(column: String): Int? {
+ return getColumnIndex(column).takeIf { it != -1 }
+}
diff --git a/tools/emojis/emoji_picker_datasource_formatted.json b/tools/emojis/emoji_picker_datasource_formatted.json
index 4f3038d53c..341cdc0c54 100644
--- a/tools/emojis/emoji_picker_datasource_formatted.json
+++ b/tools/emojis/emoji_picker_datasource_formatted.json
@@ -4191,7 +4191,8 @@
"call",
"hand",
"hands",
- "gesture"
+ "gesture",
+ "shaka"
]
},
"backhand-index-pointing-left": {
diff --git a/tools/release/sign_apk.sh b/tools/release/sign_apk.sh
index 7697f58ceb..aae9e1a378 100755
--- a/tools/release/sign_apk.sh
+++ b/tools/release/sign_apk.sh
@@ -17,7 +17,7 @@ PARAM_KEYSTORE_PATH=$1
PARAM_APK=$2
# Other params
-BUILD_TOOLS_VERSION="30.0.3"
+BUILD_TOOLS_VERSION="31.0.0-rc5"
MIN_SDK_VERSION=21
echo "Signing APK with build-tools version ${BUILD_TOOLS_VERSION} for min SDK version ${MIN_SDK_VERSION}..."
diff --git a/tools/release/sign_apk_unsafe.sh b/tools/release/sign_apk_unsafe.sh
index af5b0f0e32..5d209a4a2b 100755
--- a/tools/release/sign_apk_unsafe.sh
+++ b/tools/release/sign_apk_unsafe.sh
@@ -23,7 +23,7 @@ PARAM_KS_PASS=$3
PARAM_KEY_PASS=$4
# Other params
-BUILD_TOOLS_VERSION="30.0.3"
+BUILD_TOOLS_VERSION="31.0.0-rc5"
MIN_SDK_VERSION=21
echo "Signing APK with build-tools version ${BUILD_TOOLS_VERSION} for min SDK version ${MIN_SDK_VERSION}..."
diff --git a/vector/build.gradle b/vector/build.gradle
index f82aac9247..655cff2e73 100644
--- a/vector/build.gradle
+++ b/vector/build.gradle
@@ -210,6 +210,7 @@ android {
// This property does not affect tests that you run using Android Studio.”
animationsDisabled = true
+ // Comment to run on Android 12
execution 'ANDROIDX_TEST_ORCHESTRATOR'
}
@@ -356,8 +357,10 @@ dependencies {
implementation libs.squareup.moshi
kapt libs.squareup.moshiKotlin
- implementation libs.androidx.lifecycleExtensions
+
+ // Lifecycle
implementation libs.androidx.lifecycleLivedata
+ implementation libs.androidx.lifecycleProcess
implementation libs.androidx.datastore
implementation libs.androidx.datastorepreferences
@@ -370,7 +373,7 @@ dependencies {
implementation 'com.facebook.stetho:stetho:1.6.0'
// Phone number https://github.com/google/libphonenumber
- implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.36'
+ implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.37'
// FlowBinding
implementation libs.github.flowBinding
@@ -411,7 +414,7 @@ dependencies {
implementation 'com.github.Armen101:AudioRecordView:1.0.5'
// Custom Tab
- implementation 'androidx.browser:browser:1.3.0'
+ implementation 'androidx.browser:browser:1.4.0'
// Passphrase strength helper
implementation 'com.nulab-inc:zxcvbn:1.5.2'
diff --git a/vector/src/androidTest/java/im/vector/app/EspressoExt.kt b/vector/src/androidTest/java/im/vector/app/EspressoExt.kt
index 1cefa55e23..fbcb9b8cb3 100644
--- a/vector/src/androidTest/java/im/vector/app/EspressoExt.kt
+++ b/vector/src/androidTest/java/im/vector/app/EspressoExt.kt
@@ -19,6 +19,7 @@ package im.vector.app
import android.app.Activity
import android.view.View
import androidx.annotation.StringRes
+import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Observer
import androidx.test.espresso.Espresso
import androidx.test.espresso.IdlingRegistry
@@ -35,6 +36,11 @@ import androidx.test.runner.lifecycle.ActivityLifecycleCallback
import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry
import androidx.test.runner.lifecycle.Stage
import com.adevinta.android.barista.interaction.BaristaClickInteractions
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.bottomsheet.BottomSheetDialog
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment
+import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
+import im.vector.app.espresso.tools.waitUntilViewVisible
import org.hamcrest.Matcher
import org.hamcrest.Matchers
import org.hamcrest.StringDescription
@@ -52,6 +58,18 @@ object EspressoHelper {
}
return currentActivity
}
+
+ inline fun > getBottomSheetDialog(): BottomSheetDialogFragment? {
+ return (getCurrentActivity() as? FragmentActivity)
+ ?.supportFragmentManager
+ ?.fragments
+ ?.filterIsInstance()
+ ?.firstOrNull()
+ }
+}
+
+fun getString(@StringRes id: Int): String {
+ return EspressoHelper.getCurrentActivity()!!.resources.getString(id)
}
fun waitForView(viewMatcher: Matcher, timeout: Long = 10_000, waitForDisplayed: Boolean = true): ViewAction {
@@ -216,3 +234,46 @@ fun clickOnAndGoBack(@StringRes name: Int, block: () -> Unit) {
block()
Espresso.pressBack()
}
+
+inline fun > interactWithSheet(contentMatcher: Matcher, noinline block: () -> Unit = {}) {
+ waitUntilViewVisible(contentMatcher)
+ val behaviour = (EspressoHelper.getBottomSheetDialog()!!.dialog as BottomSheetDialog).behavior
+ withIdlingResource(BottomSheetResource(behaviour, BottomSheetBehavior.STATE_EXPANDED), block)
+ withIdlingResource(BottomSheetResource(behaviour, BottomSheetBehavior.STATE_HIDDEN)) {}
+}
+
+class BottomSheetResource(
+ private val bottomSheetBehavior: BottomSheetBehavior<*>,
+ @BottomSheetBehavior.State private val wantedState: Int
+) : IdlingResource, BottomSheetBehavior.BottomSheetCallback() {
+
+ private var isIdle: Boolean = false
+ private var resourceCallback: IdlingResource.ResourceCallback? = null
+
+ override fun onSlide(bottomSheet: View, slideOffset: Float) {}
+
+ override fun onStateChanged(bottomSheet: View, newState: Int) {
+ val wasIdle = isIdle
+ isIdle = newState == BottomSheetBehavior.STATE_EXPANDED
+ if (!wasIdle && isIdle) {
+ bottomSheetBehavior.removeBottomSheetCallback(this)
+ resourceCallback?.onTransitionToIdle()
+ }
+ }
+
+ override fun getName() = "BottomSheet awaiting state: $wantedState"
+
+ override fun isIdleNow() = isIdle
+
+ override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback) {
+ resourceCallback = callback
+
+ val state = bottomSheetBehavior.state
+ isIdle = state == wantedState
+ if (isIdle) {
+ resourceCallback!!.onTransitionToIdle()
+ } else {
+ bottomSheetBehavior.addBottomSheetCallback(this)
+ }
+ }
+}
diff --git a/vector/src/androidTest/java/im/vector/app/espresso/tools/ScreenshotFailureRule.kt b/vector/src/androidTest/java/im/vector/app/espresso/tools/ScreenshotFailureRule.kt
new file mode 100644
index 0000000000..2e329ebb6b
--- /dev/null
+++ b/vector/src/androidTest/java/im/vector/app/espresso/tools/ScreenshotFailureRule.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2021 New Vector Ltd
+ *
+ * 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 im.vector.app.espresso.tools
+
+import android.content.ContentResolver
+import android.content.ContentValues
+import android.graphics.Bitmap
+import android.net.Uri
+import android.os.Build
+import android.os.Environment
+import android.provider.MediaStore
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import org.junit.rules.TestWatcher
+import org.junit.runner.Description
+import timber.log.Timber
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.OutputStream
+import java.text.SimpleDateFormat
+import java.util.Date
+import java.util.Locale
+
+private val SCREENSHOT_FOLDER_LOCATION = "${Environment.DIRECTORY_PICTURES}/failure_screenshots"
+private val deviceLanguage = Locale.getDefault().language
+
+class ScreenshotFailureRule : TestWatcher() {
+ override fun failed(e: Throwable?, description: Description) {
+ val screenShotName = "$deviceLanguage-${description.methodName}-${SimpleDateFormat("EEE-MMMM-dd-HH:mm:ss").format(Date())}"
+ val bitmap = getInstrumentation().uiAutomation.takeScreenshot()
+ storeFailureScreenshot(bitmap, screenShotName)
+ }
+}
+
+/**
+ * Stores screenshots in sdcard/Pictures/failure_screenshots
+ */
+private fun storeFailureScreenshot(bitmap: Bitmap, screenshotName: String) {
+ val contentResolver = getInstrumentation().targetContext.applicationContext.contentResolver
+
+ val contentValues = ContentValues().apply {
+ put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
+ put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis())
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ useMediaStoreScreenshotStorage(
+ contentValues,
+ contentResolver,
+ screenshotName,
+ SCREENSHOT_FOLDER_LOCATION,
+ bitmap
+ )
+ } else {
+ usePublicExternalScreenshotStorage(
+ contentValues,
+ contentResolver,
+ screenshotName,
+ SCREENSHOT_FOLDER_LOCATION,
+ bitmap
+ )
+ }
+}
+
+private fun useMediaStoreScreenshotStorage(
+ contentValues: ContentValues,
+ contentResolver: ContentResolver,
+ screenshotName: String,
+ screenshotLocation: String,
+ bitmap: Bitmap
+) {
+ contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "$screenshotName.jpeg")
+ contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, screenshotLocation)
+ val uri: Uri? = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
+ if (uri != null) {
+ contentResolver.openOutputStream(uri)?.let { saveScreenshotToStream(bitmap, it) }
+ contentResolver.update(uri, contentValues, null, null)
+ }
+}
+
+@Suppress("DEPRECATION")
+private fun usePublicExternalScreenshotStorage(
+ contentValues: ContentValues,
+ contentResolver: ContentResolver,
+ screenshotName: String,
+ screenshotLocation: String,
+ bitmap: Bitmap
+) {
+ val directory = File(Environment.getExternalStoragePublicDirectory(screenshotLocation).toString())
+ if (!directory.exists()) {
+ directory.mkdirs()
+ }
+ val file = File(directory, "$screenshotName.jpeg")
+ saveScreenshotToStream(bitmap, FileOutputStream(file))
+ contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
+}
+
+private fun saveScreenshotToStream(bitmap: Bitmap, outputStream: OutputStream) {
+ outputStream.use {
+ try {
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 50, it)
+ } catch (e: IOException) {
+ Timber.e("Screenshot was not stored at this time")
+ }
+ }
+}
diff --git a/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt b/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt
index d80f11e975..f998a9f23c 100644
--- a/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt
+++ b/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt
@@ -19,10 +19,15 @@ package im.vector.app.ui
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
+import im.vector.app.R
+import im.vector.app.espresso.tools.ScreenshotFailureRule
import im.vector.app.features.MainActivity
+import im.vector.app.getString
import im.vector.app.ui.robot.ElementRobot
+import im.vector.app.ui.robot.withDeveloperMode
import org.junit.Rule
import org.junit.Test
+import org.junit.rules.RuleChain
import org.junit.runner.RunWith
import java.util.UUID
@@ -34,7 +39,9 @@ import java.util.UUID
class UiAllScreensSanityTest {
@get:Rule
- val activityRule = ActivityScenarioRule(MainActivity::class.java)
+ val testRule = RuleChain
+ .outerRule(ActivityScenarioRule(MainActivity::class.java))
+ .around(ScreenshotFailureRule())
private val elementRobot = ElementRobot()
@@ -69,13 +76,30 @@ class UiAllScreensSanityTest {
createNewRoom {
crawl()
createRoom {
- postMessage("Hello world!")
+ val message = "Hello world!"
+ postMessage(message)
crawl()
+ crawlMessage(message)
openSettings { crawl() }
}
}
}
+ elementRobot.withDeveloperMode {
+ settings {
+ advancedSettings { crawlDeveloperOptions() }
+ }
+ roomList {
+ openRoom(getString(R.string.room_displayname_empty_room)) {
+ val message = "Test view source"
+ postMessage(message)
+ openMessageMenu(message) {
+ viewSource()
+ }
+ }
+ }
+ }
+
elementRobot.roomList {
verifyCreatedRoom()
}
diff --git a/vector/src/androidTest/java/im/vector/app/ui/robot/ElementRobot.kt b/vector/src/androidTest/java/im/vector/app/ui/robot/ElementRobot.kt
index e904ce1c80..a3bc5b26fc 100644
--- a/vector/src/androidTest/java/im/vector/app/ui/robot/ElementRobot.kt
+++ b/vector/src/androidTest/java/im/vector/app/ui/robot/ElementRobot.kt
@@ -141,3 +141,9 @@ class ElementRobot {
}
private fun Boolean.toWarningType() = if (this) "shown" else "skipped"
+
+fun ElementRobot.withDeveloperMode(block: ElementRobot.() -> Unit) {
+ settings { toggleDeveloperMode() }
+ block()
+ settings { toggleDeveloperMode() }
+}
diff --git a/vector/src/androidTest/java/im/vector/app/ui/robot/MessageMenuRobot.kt b/vector/src/androidTest/java/im/vector/app/ui/robot/MessageMenuRobot.kt
new file mode 100644
index 0000000000..fd579c0d9f
--- /dev/null
+++ b/vector/src/androidTest/java/im/vector/app/ui/robot/MessageMenuRobot.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2021 New Vector Ltd
+ *
+ * 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 im.vector.app.ui.robot
+
+import androidx.test.espresso.Espresso.pressBack
+import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
+import com.adevinta.android.barista.interaction.BaristaListInteractions.clickListItem
+import im.vector.app.R
+import java.lang.Thread.sleep
+
+class MessageMenuRobot(
+ var autoClosed: Boolean = false
+) {
+
+ fun viewSource() {
+ clickOn(R.string.view_source)
+ // wait for library
+ sleep(1000)
+ pressBack()
+ autoClosed = true
+ }
+
+ fun editHistory() {
+ clickOn(R.string.message_view_edit_history)
+ pressBack()
+ autoClosed = true
+ }
+
+ fun addQuickReaction(quickReaction: String) {
+ clickOn(quickReaction)
+ autoClosed = true
+ }
+
+ fun addReactionFromEmojiPicker() {
+ clickOn(R.string.message_add_reaction)
+ // Wait for emoji to load, it's async now
+ sleep(2000)
+ clickListItem(R.id.emojiRecyclerView, 4)
+ autoClosed = true
+ }
+
+ fun edit() {
+ clickOn(R.string.edit)
+ autoClosed = true
+ }
+}
diff --git a/vector/src/androidTest/java/im/vector/app/ui/robot/RoomDetailRobot.kt b/vector/src/androidTest/java/im/vector/app/ui/robot/RoomDetailRobot.kt
index c77fcbfe35..24fe5adf64 100644
--- a/vector/src/androidTest/java/im/vector/app/ui/robot/RoomDetailRobot.kt
+++ b/vector/src/androidTest/java/im/vector/app/ui/robot/RoomDetailRobot.kt
@@ -17,21 +17,24 @@
package im.vector.app.ui.robot
import androidx.recyclerview.widget.RecyclerView
-import androidx.test.espresso.Espresso
+import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.Espresso.pressBack
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.contrib.RecyclerViewActions
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
import com.adevinta.android.barista.interaction.BaristaClickInteractions
import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
import com.adevinta.android.barista.interaction.BaristaClickInteractions.longClickOn
import com.adevinta.android.barista.interaction.BaristaEditTextInteractions.writeTo
-import com.adevinta.android.barista.interaction.BaristaListInteractions.clickListItem
import com.adevinta.android.barista.interaction.BaristaMenuClickInteractions.clickMenu
import com.adevinta.android.barista.interaction.BaristaMenuClickInteractions.openMenu
import im.vector.app.R
import im.vector.app.espresso.tools.waitUntilViewVisible
+import im.vector.app.features.home.room.detail.timeline.action.MessageActionsBottomSheet
+import im.vector.app.features.reactions.data.EmojiDataSource
+import im.vector.app.interactWithSheet
import im.vector.app.waitForView
import java.lang.Thread.sleep
@@ -39,7 +42,9 @@ class RoomDetailRobot {
fun postMessage(content: String) {
writeTo(R.id.composerEditText, content)
+ waitUntilViewVisible(withId(R.id.sendButton))
clickOn(R.id.sendButton)
+ waitUntilViewVisible(withText(content))
}
fun crawl() {
@@ -55,61 +60,54 @@ class RoomDetailRobot {
pressBack()
clickMenu(R.id.search)
pressBack()
- // Long click on the message
- longClickOnMessageTest()
}
- private fun longClickOnMessageTest() {
+ fun crawlMessage(message: String) {
// Test quick reaction
- longClickOnMessage()
- waitUntilViewVisible(withId(R.id.bottomSheetRecyclerView))
- // Add quick reaction
- clickOn("\uD83D\uDC4D️") // 👍
- waitUntilViewVisible(withId(R.id.composerEditText))
-
+ val quickReaction = EmojiDataSource.quickEmojis[0] // 👍
+ openMessageMenu(message) {
+ addQuickReaction(quickReaction)
+ }
// Open reactions
- longClickOn("\uD83D\uDC4D️") // 👍
+ longClickOn(quickReaction)
// wait for bottom sheet
pressBack()
-
// Test add reaction
- longClickOnMessage()
- waitUntilViewVisible(withId(R.id.bottomSheetRecyclerView))
- clickOn(R.string.message_add_reaction)
- // Filter
- // TODO clickMenu(R.id.search)
- // Wait for emoji to load, it's async now
- sleep(2000)
- clickListItem(R.id.emojiRecyclerView, 4)
- waitUntilViewVisible(withId(R.id.composerEditText))
-
+ openMessageMenu(message) {
+ addReactionFromEmojiPicker()
+ }
// Test Edit mode
- longClickOnMessage()
- waitUntilViewVisible(withId(R.id.bottomSheetRecyclerView))
- clickOn(R.string.edit)
- waitUntilViewVisible(withId(R.id.composerEditText))
+ openMessageMenu(message) {
+ edit()
+ }
// TODO Cancel action
writeTo(R.id.composerEditText, "Hello universe!")
// Wait a bit for the keyboard layout to update
- sleep(30)
+ waitUntilViewVisible(withId(R.id.sendButton))
clickOn(R.id.sendButton)
// Wait for the UI to update
- sleep(1000)
+ waitUntilViewVisible(withText("Hello universe! (edited)"))
// Open edit history
- longClickOnMessage("Hello universe! (edited)")
- waitUntilViewVisible(withId(R.id.bottomSheetRecyclerView))
- clickOn(R.string.message_view_edit_history)
- pressBack()
+ openMessageMenu("Hello universe! (edited)") {
+ editHistory()
+ }
}
- private fun longClickOnMessage(text: String = "Hello world!") {
- Espresso.onView(withId(R.id.timelineRecyclerView))
+ fun openMessageMenu(message: String, block: MessageMenuRobot.() -> Unit) {
+ onView(withId(R.id.timelineRecyclerView))
.perform(
RecyclerViewActions.actionOnItem(
- ViewMatchers.hasDescendant(ViewMatchers.withText(text)),
+ ViewMatchers.hasDescendant(ViewMatchers.withText(message)),
ViewActions.longClick()
)
)
+ interactWithSheet(contentMatcher = withId(R.id.bottomSheetRecyclerView)) {
+ val messageMenuRobot = MessageMenuRobot()
+ block(messageMenuRobot)
+ if (!messageMenuRobot.autoClosed) {
+ pressBack()
+ }
+ }
}
fun openSettings(block: RoomSettingsRobot.() -> Unit) {
diff --git a/vector/src/androidTest/java/im/vector/app/ui/robot/RoomListRobot.kt b/vector/src/androidTest/java/im/vector/app/ui/robot/RoomListRobot.kt
index bc7d4ac76b..dc07f06202 100644
--- a/vector/src/androidTest/java/im/vector/app/ui/robot/RoomListRobot.kt
+++ b/vector/src/androidTest/java/im/vector/app/ui/robot/RoomListRobot.kt
@@ -17,38 +17,46 @@
package im.vector.app.ui.robot
import androidx.recyclerview.widget.RecyclerView
-import androidx.test.espresso.Espresso
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.Espresso.pressBack
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.contrib.RecyclerViewActions
import androidx.test.espresso.matcher.ViewMatchers
+import androidx.test.espresso.matcher.ViewMatchers.withText
import com.adevinta.android.barista.assertion.BaristaVisibilityAssertions
-import com.adevinta.android.barista.interaction.BaristaClickInteractions
+import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
import im.vector.app.R
import im.vector.app.espresso.tools.waitUntilActivityVisible
import im.vector.app.features.roomdirectory.RoomDirectoryActivity
class RoomListRobot {
+ fun openRoom(roomName: String, block: RoomDetailRobot.() -> Unit) {
+ clickOn(roomName)
+ block(RoomDetailRobot())
+ pressBack()
+ }
+
fun verifyCreatedRoom() {
- Espresso.onView(ViewMatchers.withId(R.id.roomListView))
+ onView(ViewMatchers.withId(R.id.roomListView))
.perform(
RecyclerViewActions.actionOnItem(
- ViewMatchers.hasDescendant(ViewMatchers.withText(R.string.room_displayname_empty_room)),
+ ViewMatchers.hasDescendant(withText(R.string.room_displayname_empty_room)),
ViewActions.longClick()
)
)
- Espresso.pressBack()
+ pressBack()
}
fun newRoom(block: NewRoomRobot.() -> Unit) {
- BaristaClickInteractions.clickOn(R.id.createGroupRoomButton)
+ clickOn(R.id.createGroupRoomButton)
waitUntilActivityVisible {
BaristaVisibilityAssertions.assertDisplayed(R.id.publicRoomsList)
}
val newRoomRobot = NewRoomRobot()
block(newRoomRobot)
if (!newRoomRobot.createdRoom) {
- Espresso.pressBack()
+ pressBack()
}
}
}
diff --git a/vector/src/androidTest/java/im/vector/app/ui/robot/settings/SettingsAdvancedRobot.kt b/vector/src/androidTest/java/im/vector/app/ui/robot/settings/SettingsAdvancedRobot.kt
index ecce51f9bb..4aeb8903dd 100644
--- a/vector/src/androidTest/java/im/vector/app/ui/robot/settings/SettingsAdvancedRobot.kt
+++ b/vector/src/androidTest/java/im/vector/app/ui/robot/settings/SettingsAdvancedRobot.kt
@@ -17,8 +17,11 @@
package im.vector.app.ui.robot.settings
import androidx.test.espresso.Espresso.pressBack
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
import im.vector.app.R
import im.vector.app.espresso.tools.clickOnPreference
+import im.vector.app.espresso.tools.waitUntilViewVisible
class SettingsAdvancedRobot {
@@ -28,20 +31,19 @@ class SettingsAdvancedRobot {
clickOnPreference(R.string.settings_push_rules)
pressBack()
+ }
- /* TODO P2 test developer screens
- // Enable developer mode
- clickOnSwitchPreference("SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY")
+ fun toggleDeveloperMode() {
+ clickOn(R.string.settings_developer_mode_summary)
+ }
- clickOnPreference(R.string.settings_account_data)
- clickOn("m.push_rules")
- pressBack()
- pressBack()
- clickOnPreference(R.string.settings_key_requests)
- pressBack()
-
- // Disable developer mode
- clickOnSwitchPreference("SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY")
- */
+ fun crawlDeveloperOptions() {
+ clickOnPreference(R.string.settings_account_data)
+ waitUntilViewVisible(withText("m.push_rules"))
+ clickOn("m.push_rules")
+ pressBack()
+ pressBack()
+ clickOnPreference(R.string.settings_key_requests)
+ pressBack()
}
}
diff --git a/vector/src/androidTest/java/im/vector/app/ui/robot/settings/SettingsRobot.kt b/vector/src/androidTest/java/im/vector/app/ui/robot/settings/SettingsRobot.kt
index 3f37d9daf1..a9c053f6c3 100644
--- a/vector/src/androidTest/java/im/vector/app/ui/robot/settings/SettingsRobot.kt
+++ b/vector/src/androidTest/java/im/vector/app/ui/robot/settings/SettingsRobot.kt
@@ -21,6 +21,12 @@ import im.vector.app.clickOnAndGoBack
class SettingsRobot {
+ fun toggleDeveloperMode() {
+ advancedSettings {
+ toggleDeveloperMode()
+ }
+ }
+
fun general(block: SettingsGeneralRobot.() -> Unit) {
clickOnAndGoBack(R.string.settings_general_title) { block(SettingsGeneralRobot()) }
}
@@ -50,7 +56,9 @@ class SettingsRobot {
}
fun advancedSettings(block: SettingsAdvancedRobot.() -> Unit) {
- clickOnAndGoBack(R.string.settings_advanced_settings) { block(SettingsAdvancedRobot()) }
+ clickOnAndGoBack(R.string.settings_advanced_settings) {
+ block(SettingsAdvancedRobot())
+ }
}
fun helpAndAbout(block: SettingsHelpRobot.() -> Unit) {
diff --git a/vector/src/fdroid/AndroidManifest.xml b/vector/src/fdroid/AndroidManifest.xml
index 022b08f16d..ea9fa023ab 100644
--- a/vector/src/fdroid/AndroidManifest.xml
+++ b/vector/src/fdroid/AndroidManifest.xml
@@ -14,7 +14,9 @@
-
+
diff --git a/vector/src/fdroid/java/im/vector/app/fdroid/receiver/AlarmSyncBroadcastReceiver.kt b/vector/src/fdroid/java/im/vector/app/fdroid/receiver/AlarmSyncBroadcastReceiver.kt
index 0f375561b2..c1fda2d404 100644
--- a/vector/src/fdroid/java/im/vector/app/fdroid/receiver/AlarmSyncBroadcastReceiver.kt
+++ b/vector/src/fdroid/java/im/vector/app/fdroid/receiver/AlarmSyncBroadcastReceiver.kt
@@ -25,6 +25,7 @@ import android.os.Build
import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import im.vector.app.core.extensions.singletonEntryPoint
+import im.vector.app.core.platform.PendingIntentCompat
import im.vector.app.core.services.VectorSyncService
import org.matrix.android.sdk.internal.session.sync.job.SyncService
import timber.log.Timber
@@ -67,7 +68,12 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
putExtra(SyncService.EXTRA_SESSION_ID, sessionId)
putExtra(SyncService.EXTRA_PERIODIC, true)
}
- val pIntent = PendingIntent.getBroadcast(context, REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT)
+ val pIntent = PendingIntent.getBroadcast(
+ context,
+ REQUEST_CODE,
+ intent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
+ )
val firstMillis = System.currentTimeMillis() + delayInSeconds * 1000L
val alarmMgr = context.getSystemService()!!
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
@@ -80,7 +86,12 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
fun cancelAlarm(context: Context) {
Timber.v("## Sync: Cancel alarm for background sync")
val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java)
- val pIntent = PendingIntent.getBroadcast(context, REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT)
+ val pIntent = PendingIntent.getBroadcast(
+ context,
+ REQUEST_CODE,
+ intent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
+ )
val alarmMgr = context.getSystemService()!!
alarmMgr.cancel(pIntent)
diff --git a/vector/src/gplay/AndroidManifest.xml b/vector/src/gplay/AndroidManifest.xml
index d849d5fb2d..f541eebd83 100755
--- a/vector/src/gplay/AndroidManifest.xml
+++ b/vector/src/gplay/AndroidManifest.xml
@@ -9,7 +9,9 @@
android:name="firebase_analytics_collection_deactivated"
android:value="true" />
-
+
diff --git a/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt b/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt
index 63d50d4f97..e323506e9f 100755
--- a/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt
+++ b/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt
@@ -201,8 +201,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
resolvedEvent
?.also { Timber.tag(loggerTag.value).d("Fast lane: notify drawer") }
?.let {
- notificationDrawerManager.onNotifiableEventReceived(it)
- notificationDrawerManager.refreshNotificationDrawer()
+ notificationDrawerManager.updateEvents { it.onNotifiableEventReceived(resolvedEvent) }
}
}
}
diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml
index 01d7630678..e5d4753235 100644
--- a/vector/src/main/AndroidManifest.xml
+++ b/vector/src/main/AndroidManifest.xml
@@ -4,7 +4,10 @@
package="im.vector.app">
-
+
+
@@ -418,6 +421,22 @@
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/sdk_provider_paths" />
+
+
+
+
+
+
diff --git a/vector/src/main/java/im/vector/app/AppStateHandler.kt b/vector/src/main/java/im/vector/app/AppStateHandler.kt
index b6d41ce35d..9ed9dd5b23 100644
--- a/vector/src/main/java/im/vector/app/AppStateHandler.kt
+++ b/vector/src/main/java/im/vector/app/AppStateHandler.kt
@@ -16,9 +16,8 @@
package im.vector.app
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleObserver
-import androidx.lifecycle.OnLifecycleEvent
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
import arrow.core.Option
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.utils.BehaviorDataSource
@@ -57,7 +56,7 @@ class AppStateHandler @Inject constructor(
private val sessionDataSource: ActiveSessionDataSource,
private val uiStateRepository: UiStateRepository,
private val activeSessionHolder: ActiveSessionHolder
-) : LifecycleObserver {
+) : DefaultLifecycleObserver {
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
private val selectedSpaceDataSource = BehaviorDataSource