diff --git a/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt b/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt
index 4ca6ced8fe..573138bf5c 100644
--- a/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt
+++ b/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt
@@ -17,6 +17,7 @@
 
 package im.vector.lib.attachmentviewer
 
+import android.annotation.SuppressLint
 import android.graphics.Color
 import android.os.Build
 import android.os.Bundle
@@ -141,7 +142,12 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
             // 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
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+                window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
+            } else {
+                @SuppressLint("WrongConstant")
+                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
@@ -347,7 +353,12 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
             // 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
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+                window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
+            } else {
+                @SuppressLint("WrongConstant")
+                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
diff --git a/dependencies.gradle b/dependencies.gradle
index db1e6523c3..85f00f95ea 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -1,8 +1,8 @@
 ext.versions = [
 
         'minSdk'            : 21,
-        'compileSdk'        : 30,
-        'targetSdk'         : 30,
+        'compileSdk'        : 31,
+        'targetSdk'         : 31,
         'sourceCompat'      : JavaVersion.VERSION_11,
         'targetCompat'      : JavaVersion.VERSION_11,
 ]
@@ -16,7 +16,7 @@ def retrofit = "2.9.0"
 def arrow = "0.8.2"
 def markwon = "4.6.2"
 def moshi = "1.12.0"
-def lifecycle = "2.2.0"
+def lifecycle = "2.4.0"
 def flowBinding = "1.2.0"
 def epoxy = "4.6.2"
 def mavericks = "2.4.0"
@@ -46,18 +46,18 @@ ext.libs = [
         ],
         androidx    : [
                 'appCompat'               : "androidx.appcompat:appcompat:1.3.1",
-                'core'                    : "androidx.core:core-ktx:1.6.0",
+                'core'                    : "androidx.core:core-ktx:1.7.0",
                 'recyclerview'            : "androidx.recyclerview:recyclerview:1.2.1",
                 'exifinterface'           : "androidx.exifinterface:exifinterface:1.3.3",
                 'fragmentKtx'             : "androidx.fragment:fragment-ktx:1.3.6",
                 'constraintLayout'        : "androidx.constraintlayout:constraintlayout:2.1.1",
-                'work'                    : "androidx.work:work-runtime-ktx:2.6.0",
+                'work'                    : "androidx.work:work-runtime-ktx:2.7.0",
                 'autoFill'                : "androidx.autofill:autofill:1.1.0",
                 'preferenceKtx'           : "androidx.preference:preference-ktx:1.1.1",
                 'junit'                   : "androidx.test.ext:junit:1.1.3",
-                'lifecycleExtensions'     : "androidx.lifecycle:lifecycle-extensions:$lifecycle",
-                'lifecycleJava8'          : "androidx.lifecycle:lifecycle-common-java8:$lifecycle",
-                'lifecycleLivedata'       : "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1",
+                'lifecycleCommon'         : "androidx.lifecycle:lifecycle-common:$lifecycle",
+                'lifecycleLivedata'       : "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle",
+                'lifecycleProcess'        : "androidx.lifecycle:lifecycle-process:$lifecycle",
                 'datastore'               : "androidx.datastore:datastore:1.0.0",
                 'datastorepreferences'    : "androidx.datastore:datastore-preferences:1.0.0",
                 'pagingRuntimeKtx'        : "androidx.paging:paging-runtime-ktx:2.1.2",
diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle
index 67e5eee74b..94fc221cf1 100644
--- a/matrix-sdk-android/build.gradle
+++ b/matrix-sdk-android/build.gradle
@@ -44,6 +44,7 @@ android {
     }
 
     testOptions {
+        // Comment to run on Android 12
         execution 'ANDROIDX_TEST_ORCHESTRATOR'
     }
 
@@ -106,8 +107,9 @@ dependencies {
     implementation libs.androidx.appCompat
     implementation libs.androidx.core
 
-    implementation libs.androidx.lifecycleExtensions
-    implementation  libs.androidx.lifecycleJava8
+    // Lifecycle
+    implementation libs.androidx.lifecycleCommon
+    implementation libs.androidx.lifecycleProcess
 
     // Network
     implementation libs.squareup.retrofit
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<Listener>()
+    private val listeners = LinkedHashSet<Listener>()
 
     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<MultiPickerContactType>() {
                     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<MultiPickerContactType>() {
                                 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<MultiPickerContactType>() {
                 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<MultiPickerBaseType>() {
                     // 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/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 298136129a..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
@@ -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/espresso/tools/ScreenshotFailureRule.kt b/vector/src/androidTest/java/im/vector/app/espresso/tools/ScreenshotFailureRule.kt
index bb1cb622c0..2e329ebb6b 100644
--- a/vector/src/androidTest/java/im/vector/app/espresso/tools/ScreenshotFailureRule.kt
+++ b/vector/src/androidTest/java/im/vector/app/espresso/tools/ScreenshotFailureRule.kt
@@ -20,6 +20,7 @@ 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
@@ -55,7 +56,7 @@ private fun storeFailureScreenshot(bitmap: Bitmap, screenshotName: String) {
         put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
         put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis())
     }
-    if (android.os.Build.VERSION.SDK_INT >= 29) {
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
         useMediaStoreScreenshotStorage(
                 contentValues,
                 contentResolver,
@@ -90,6 +91,7 @@ private fun useMediaStoreScreenshotStorage(
     }
 }
 
+@Suppress("DEPRECATION")
 private fun usePublicExternalScreenshotStorage(
         contentValues: ContentValues,
         contentResolver: ContentResolver,
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 @@
 
     <application>
 
-        <receiver android:name=".fdroid.receiver.OnApplicationUpgradeOrRebootReceiver">
+        <receiver
+            android:name=".fdroid.receiver.OnApplicationUpgradeOrRebootReceiver"
+            android:exported="false">
             <intent-filter>
                 <action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
                 <action android:name="android.intent.action.BOOT_COMPLETED" />
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<AlarmManager>()!!
             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<AlarmManager>()!!
             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" />
 
-        <service android:name=".gplay.push.fcm.VectorFirebaseMessagingService">
+        <service
+            android:name=".gplay.push.fcm.VectorFirebaseMessagingService"
+            android:exported="false">
             <intent-filter>
                 <action android:name="com.google.firebase.MESSAGING_EVENT" />
             </intent-filter>
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">
 
     <!-- Needed for VOIP call to detect and switch to headset-->
-    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission
+        android:name="android.permission.BLUETOOTH"
+        android:maxSdkVersion="30" />
+    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
@@ -418,6 +421,22 @@
                 android:name="android.support.FILE_PROVIDER_PATHS"
                 android:resource="@xml/sdk_provider_paths" />
         </provider>
+
+        <!-- Temporary fix for Android 12. android:exported has to be explicitly set when targeting Android 12
+        Do it for services coming from dependencies - BEGIN -->
+        <service
+            android:name="org.jitsi.meet.sdk.ConnectionService"
+            android:exported="false"
+            tools:node="merge" />
+        <service
+            android:name="org.jitsi.meet.sdk.JitsiMeetOngoingConferenceService"
+            android:exported="false"
+            tools:node="merge" />
+        <service
+            android:name="androidx.sharetarget.ChooserTargetServiceCompat"
+            android:exported="false"
+            tools:node="merge" />
+        <!-- Temporary fix for Android 12 change - END -->
     </application>
 
 </manifest>
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<Option<RoomGroupingMethod>>(Option.empty())
@@ -133,13 +132,11 @@ class AppStateHandler @Inject constructor(
         return (selectedSpaceDataSource.currentValue?.orNull() as? RoomGroupingMethod.ByLegacyGroup)?.groupSummary?.groupId
     }
 
-    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
-    fun entersForeground() {
+    override fun onResume(owner: LifecycleOwner) {
         observeActiveSession()
     }
 
-    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
-    fun entersBackground() {
+    override fun onPause(owner: LifecycleOwner) {
         coroutineScope.coroutineContext.cancelChildren()
         val session = activeSessionHolder.getSafeActiveSession() ?: return
         when (val currentMethod = selectedSpaceDataSource.currentValue?.orNull() ?: RoomGroupingMethod.BySpace(null)) {
diff --git a/vector/src/main/java/im/vector/app/VectorApplication.kt b/vector/src/main/java/im/vector/app/VectorApplication.kt
index 80b397231b..c1d9eef125 100644
--- a/vector/src/main/java/im/vector/app/VectorApplication.kt
+++ b/vector/src/main/java/im/vector/app/VectorApplication.kt
@@ -27,9 +27,8 @@ import android.os.HandlerThread
 import android.os.StrictMode
 import androidx.core.provider.FontRequest
 import androidx.core.provider.FontsContractCompat
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleObserver
-import androidx.lifecycle.OnLifecycleEvent
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
 import androidx.lifecycle.ProcessLifecycleOwner
 import androidx.multidex.MultiDex
 import com.airbnb.epoxy.EpoxyAsyncUtil
@@ -166,9 +165,8 @@ class VectorApplication :
 
         ProcessLifecycleOwner.get().lifecycle.addObserver(startSyncOnFirstStart)
 
-        ProcessLifecycleOwner.get().lifecycle.addObserver(object : LifecycleObserver {
-            @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
-            fun entersForeground() {
+        ProcessLifecycleOwner.get().lifecycle.addObserver(object : DefaultLifecycleObserver {
+            override fun onResume(owner: LifecycleOwner) {
                 Timber.i("App entered foreground")
                 FcmHelper.onEnterForeground(appContext, activeSessionHolder)
                 activeSessionHolder.getSafeActiveSession()?.also {
@@ -176,8 +174,7 @@ class VectorApplication :
                 }
             }
 
-            @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
-            fun entersBackground() {
+            override fun onPause(owner: LifecycleOwner) {
                 Timber.i("App entered background") // call persistInfo
                 notificationDrawerManager.persistInfo()
                 FcmHelper.onEnterBackground(appContext, vectorPreferences, activeSessionHolder)
@@ -198,9 +195,8 @@ class VectorApplication :
         EmojiManager.install(GoogleEmojiProvider())
     }
 
-    private val startSyncOnFirstStart = object : LifecycleObserver {
-        @OnLifecycleEvent(Lifecycle.Event.ON_START)
-        fun onStart() {
+    private val startSyncOnFirstStart = object : DefaultLifecycleObserver {
+        override fun onStart(owner: LifecycleOwner) {
             Timber.i("App process started")
             authenticationService.getLastAuthenticatedSession()?.startSyncing(appContext)
             ProcessLifecycleOwner.get().lifecycle.removeObserver(this)
diff --git a/vector/src/main/java/im/vector/app/core/contacts/ContactsDataSource.kt b/vector/src/main/java/im/vector/app/core/contacts/ContactsDataSource.kt
index f5a5e240c6..f99048501d 100644
--- a/vector/src/main/java/im/vector/app/core/contacts/ContactsDataSource.kt
+++ b/vector/src/main/java/im/vector/app/core/contacts/ContactsDataSource.kt
@@ -17,10 +17,10 @@
 package im.vector.app.core.contacts
 
 import android.content.Context
-import android.database.Cursor
 import android.net.Uri
 import android.provider.ContactsContract
 import androidx.annotation.WorkerThread
+import im.vector.lib.multipicker.utils.getColumnIndexOrNull
 import timber.log.Timber
 import javax.inject.Inject
 import kotlin.system.measureTimeMillis
@@ -57,16 +57,20 @@ class ContactsDataSource @Inject constructor(
             )
                     ?.use { cursor ->
                         if (cursor.count > 0) {
+                            val idColumnIndex = cursor.getColumnIndexOrNull(ContactsContract.Contacts._ID) ?: return@use
+                            val displayNameColumnIndex = cursor.getColumnIndexOrNull(ContactsContract.Contacts.DISPLAY_NAME) ?: return@use
+                            val photoUriColumnIndex = cursor.getColumnIndexOrNull(ContactsContract.Data.PHOTO_URI)
                             while (cursor.moveToNext()) {
-                                val id = cursor.getLong(ContactsContract.Contacts._ID) ?: continue
-                                val displayName = cursor.getString(ContactsContract.Contacts.DISPLAY_NAME) ?: continue
+                                val id = cursor.getLong(idColumnIndex)
+                                val displayName = cursor.getString(displayNameColumnIndex)
 
                                 val mappedContactBuilder = MappedContactBuilder(
                                         id = id,
                                         displayName = displayName
                                 )
 
-                                cursor.getString(ContactsContract.Data.PHOTO_URI)
+                                photoUriColumnIndex
+                                        ?.let { cursor.getString(it) }
                                         ?.let { Uri.parse(it) }
                                         ?.let { mappedContactBuilder.photoURI = it }
 
@@ -85,12 +89,15 @@ class ContactsDataSource @Inject constructor(
                         null,
                         null,
                         null)
-                        ?.use { innerCursor ->
-                            while (innerCursor.moveToNext()) {
-                                val mappedContactBuilder = innerCursor.getLong(ContactsContract.CommonDataKinds.Phone.CONTACT_ID)
-                                        ?.let { map[it] }
+                        ?.use { cursor ->
+                            val idColumnIndex = cursor.getColumnIndexOrNull(ContactsContract.CommonDataKinds.Phone.CONTACT_ID) ?: return@use
+                            val phoneNumberColumnIndex = cursor.getColumnIndexOrNull(ContactsContract.CommonDataKinds.Phone.NUMBER) ?: return@use
+
+                            while (cursor.moveToNext()) {
+                                val mappedContactBuilder = cursor.getLong(idColumnIndex)
+                                        .let { map[it] }
                                         ?: continue
-                                innerCursor.getString(ContactsContract.CommonDataKinds.Phone.NUMBER)
+                                cursor.getString(phoneNumberColumnIndex)
                                         ?.let {
                                             mappedContactBuilder.msisdns.add(
                                                     MappedMsisdn(
@@ -114,14 +121,17 @@ class ContactsDataSource @Inject constructor(
                         null,
                         null,
                         null)
-                        ?.use { innerCursor ->
-                            while (innerCursor.moveToNext()) {
+                        ?.use { cursor ->
+                            val idColumnIndex = cursor.getColumnIndexOrNull(ContactsContract.CommonDataKinds.Email.CONTACT_ID) ?: return@use
+                            val emailColumnIndex = cursor.getColumnIndexOrNull(ContactsContract.CommonDataKinds.Email.DATA) ?: return@use
+
+                            while (cursor.moveToNext()) {
                                 // This would allow you get several email addresses
                                 // if the email addresses were stored in an array
-                                val mappedContactBuilder = innerCursor.getLong(ContactsContract.CommonDataKinds.Email.CONTACT_ID)
-                                        ?.let { map[it] }
+                                val mappedContactBuilder = cursor.getLong(idColumnIndex)
+                                        .let { map[it] }
                                         ?: continue
-                                innerCursor.getString(ContactsContract.CommonDataKinds.Email.DATA)
+                                cursor.getString(emailColumnIndex)
                                         ?.let {
                                             mappedContactBuilder.emails.add(
                                                     MappedEmail(
@@ -140,16 +150,4 @@ class ContactsDataSource @Inject constructor(
                 .filter { it.emails.isNotEmpty() || it.msisdns.isNotEmpty() }
                 .map { it.build() }
     }
-
-    private fun Cursor.getString(column: String): String? {
-        return getColumnIndex(column)
-                .takeIf { it != -1 }
-                ?.let { getString(it) }
-    }
-
-    private fun Cursor.getLong(column: String): Long? {
-        return getColumnIndex(column)
-                .takeIf { it != -1 }
-                ?.let { getLong(it) }
-    }
 }
diff --git a/vector/src/main/java/im/vector/app/core/intent/Filename.kt b/vector/src/main/java/im/vector/app/core/intent/Filename.kt
index 5d118c19a1..a38602e4a5 100644
--- a/vector/src/main/java/im/vector/app/core/intent/Filename.kt
+++ b/vector/src/main/java/im/vector/app/core/intent/Filename.kt
@@ -19,15 +19,17 @@ package im.vector.app.core.intent
 import android.content.Context
 import android.net.Uri
 import android.provider.OpenableColumns
+import im.vector.lib.multipicker.utils.getColumnIndexOrNull
 
 fun getFilenameFromUri(context: Context?, uri: Uri): String? {
     if (context != null && uri.scheme == "content") {
-        val cursor = context.contentResolver.query(uri, null, null, null, null)
-        cursor?.use {
-            if (it.moveToFirst()) {
-                return it.getString(it.getColumnIndex(OpenableColumns.DISPLAY_NAME))
-            }
-        }
+        context.contentResolver.query(uri, null, null, null, null)
+                ?.use { cursor ->
+                    if (cursor.moveToFirst()) {
+                        return cursor.getColumnIndexOrNull(OpenableColumns.DISPLAY_NAME)
+                                ?.let { cursor.getString(it) }
+                    }
+                }
     }
     return uri.path?.substringAfterLast('/')
 }
diff --git a/vector/src/main/java/im/vector/app/core/platform/LifecycleAwareLazy.kt b/vector/src/main/java/im/vector/app/core/platform/LifecycleAwareLazy.kt
index 283106232e..54add00459 100644
--- a/vector/src/main/java/im/vector/app/core/platform/LifecycleAwareLazy.kt
+++ b/vector/src/main/java/im/vector/app/core/platform/LifecycleAwareLazy.kt
@@ -18,58 +18,56 @@ package im.vector.app.core.platform
 
 import androidx.annotation.MainThread
 import androidx.fragment.app.Fragment
+import androidx.lifecycle.DefaultLifecycleObserver
 import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleObserver
 import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.OnLifecycleEvent
 
 fun <T> LifecycleOwner.lifecycleAwareLazy(initializer: () -> T): Lazy<T> = LifecycleAwareLazy(this, initializer)
 
 private object UninitializedValue
 
 class LifecycleAwareLazy<out T>(
-  private val owner: LifecycleOwner,
-  initializer: () -> T
-) : Lazy<T>, LifecycleObserver {
+        private val owner: LifecycleOwner,
+        initializer: () -> T
+) : Lazy<T>, DefaultLifecycleObserver {
 
-  private var initializer: (() -> T)? = initializer
+    private var initializer: (() -> T)? = initializer
 
-  private var _value: Any? = UninitializedValue
+    private var _value: Any? = UninitializedValue
 
-  @Suppress("UNCHECKED_CAST")
-  override val value: T
-    @MainThread
-    get() {
-      if (_value === UninitializedValue) {
-        _value = initializer!!()
-        attachToLifecycle()
-      }
-      return _value as T
+    @Suppress("UNCHECKED_CAST")
+    override val value: T
+        @MainThread
+        get() {
+            if (_value === UninitializedValue) {
+                _value = initializer!!()
+                attachToLifecycle()
+            }
+            return _value as T
+        }
+
+    override fun onDestroy(owner: LifecycleOwner) {
+        _value = UninitializedValue
+        detachFromLifecycle()
     }
 
-  @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
-  fun resetValue() {
-    _value = UninitializedValue
-    detachFromLifecycle()
-  }
-
-  private fun attachToLifecycle() {
-    if (getLifecycleOwner().lifecycle.currentState == Lifecycle.State.DESTROYED) {
-      throw IllegalStateException("Initialization failed because lifecycle has been destroyed!")
+    private fun attachToLifecycle() {
+        if (getLifecycleOwner().lifecycle.currentState == Lifecycle.State.DESTROYED) {
+            throw IllegalStateException("Initialization failed because lifecycle has been destroyed!")
+        }
+        getLifecycleOwner().lifecycle.addObserver(this)
     }
-    getLifecycleOwner().lifecycle.addObserver(this)
-  }
 
-  private fun detachFromLifecycle() {
-    getLifecycleOwner().lifecycle.removeObserver(this)
-  }
+    private fun detachFromLifecycle() {
+        getLifecycleOwner().lifecycle.removeObserver(this)
+    }
 
-  private fun getLifecycleOwner() = when (owner) {
-    is Fragment -> owner.viewLifecycleOwner
-    else        -> owner
-  }
+    private fun getLifecycleOwner() = when (owner) {
+        is Fragment -> owner.viewLifecycleOwner
+        else        -> owner
+    }
 
-  override fun isInitialized(): Boolean = _value !== UninitializedValue
+    override fun isInitialized(): Boolean = _value !== UninitializedValue
 
-  override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
+    override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
 }
diff --git a/vector/src/main/java/im/vector/app/features/call/telecom/TelecomUtils.kt b/vector/src/main/java/im/vector/app/core/platform/PendingIntentCompat.kt
similarity index 55%
rename from vector/src/main/java/im/vector/app/features/call/telecom/TelecomUtils.kt
rename to vector/src/main/java/im/vector/app/core/platform/PendingIntentCompat.kt
index 819a1f6c0a..832c11888c 100644
--- a/vector/src/main/java/im/vector/app/features/call/telecom/TelecomUtils.kt
+++ b/vector/src/main/java/im/vector/app/core/platform/PendingIntentCompat.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 New Vector Ltd
+ * 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.
@@ -14,17 +14,21 @@
  * limitations under the License.
  */
 
-package im.vector.app.features.call.telecom
+package im.vector.app.core.platform
 
-import android.content.Context
-import android.telephony.TelephonyManager
-import androidx.core.content.getSystemService
+import android.app.PendingIntent
+import android.os.Build
 
-object TelecomUtils {
+object PendingIntentCompat {
+    val FLAG_IMMUTABLE = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+        PendingIntent.FLAG_IMMUTABLE
+    } else {
+        0
+    }
 
-    fun isLineBusy(context: Context): Boolean {
-        val telephonyManager = context.getSystemService<TelephonyManager>()
-                ?: return false
-        return telephonyManager.callState != TelephonyManager.CALL_STATE_IDLE
+    val FLAG_MUTABLE = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+        PendingIntent.FLAG_MUTABLE
+    } else {
+        0
     }
 }
diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
index 7fe939bef3..0fd660effb 100644
--- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
+++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
@@ -16,6 +16,7 @@
 
 package im.vector.app.core.platform
 
+import android.annotation.SuppressLint
 import android.app.Activity
 import android.content.Context
 import android.content.res.Configuration
@@ -403,7 +404,12 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
             // 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
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+                window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
+            } else {
+                @SuppressLint("WrongConstant")
+                window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE
+            }
             // New API instead of FLAG_TRANSLUCENT_STATUS
             window.statusBarColor = ContextCompat.getColor(this, im.vector.lib.attachmentviewer.R.color.half_transparent_status_bar)
             // New API instead of FLAG_TRANSLUCENT_NAVIGATION
diff --git a/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt b/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt
index 08db8227a6..a6f6d8d82f 100644
--- a/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt
+++ b/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt
@@ -32,6 +32,7 @@ import androidx.work.Worker
 import androidx.work.WorkerParameters
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
+import im.vector.app.core.platform.PendingIntentCompat
 import im.vector.app.features.notifications.NotificationUtils
 import im.vector.app.features.settings.BackgroundSyncMode
 import org.matrix.android.sdk.internal.session.sync.job.SyncService
@@ -199,9 +200,9 @@ private fun Context.rescheduleSyncService(sessionId: String,
         startService(intent)
     } else {
         val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            PendingIntent.getForegroundService(this, 0, intent, 0)
+            PendingIntent.getForegroundService(this, 0, intent, PendingIntentCompat.FLAG_IMMUTABLE)
         } else {
-            PendingIntent.getService(this, 0, intent, 0)
+            PendingIntent.getService(this, 0, intent, PendingIntentCompat.FLAG_IMMUTABLE)
         }
         val firstMillis = System.currentTimeMillis() + syncDelaySeconds * 1000L
         val alarmMgr = getSystemService<AlarmManager>()!!
diff --git a/vector/src/main/java/im/vector/app/features/call/conference/ConferenceEvent.kt b/vector/src/main/java/im/vector/app/features/call/conference/ConferenceEvent.kt
index 0a63ad6907..0d8e538eca 100644
--- a/vector/src/main/java/im/vector/app/features/call/conference/ConferenceEvent.kt
+++ b/vector/src/main/java/im/vector/app/features/call/conference/ConferenceEvent.kt
@@ -20,9 +20,8 @@ import android.content.BroadcastReceiver
 import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleObserver
-import androidx.lifecycle.OnLifecycleEvent
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
 import androidx.localbroadcastmanager.content.LocalBroadcastManager
 import com.facebook.react.bridge.JavaOnlyMap
 import org.jitsi.meet.sdk.BroadcastEmitter
@@ -42,7 +41,7 @@ sealed class ConferenceEvent(open val data: Map<String, Any>) {
     }
 }
 
-class ConferenceEventEmitter(private val context: Context)  {
+class ConferenceEventEmitter(private val context: Context) {
 
     fun emitConferenceEnded() {
         val broadcastEventData = JavaOnlyMap.of(CONFERENCE_URL_DATA_KEY, JitsiMeet.getCurrentConference())
@@ -52,7 +51,7 @@ class ConferenceEventEmitter(private val context: Context)  {
 
 class ConferenceEventObserver(private val context: Context,
                               private val onBroadcastEvent: (ConferenceEvent) -> Unit) :
-    LifecycleObserver {
+        DefaultLifecycleObserver {
 
     // See https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-android-sdk#listening-for-broadcasted-events
     private val broadcastReceiver = object : BroadcastReceiver() {
@@ -61,8 +60,7 @@ class ConferenceEventObserver(private val context: Context,
         }
     }
 
-    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
-    fun unregisterForBroadcastMessages() {
+    override fun onDestroy(owner: LifecycleOwner) {
         try {
             LocalBroadcastManager.getInstance(context).unregisterReceiver(broadcastReceiver)
         } catch (throwable: Throwable) {
@@ -70,8 +68,7 @@ class ConferenceEventObserver(private val context: Context,
         }
     }
 
-    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
-    fun registerForBroadcastMessages() {
+    override fun onCreate(owner: LifecycleOwner) {
         val intentFilter = IntentFilter()
         for (type in BroadcastEvent.Type.values()) {
             intentFilter.addAction(type.action)
diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt
index 9e620174f3..80390a7dfb 100644
--- a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt
+++ b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt
@@ -17,9 +17,8 @@
 package im.vector.app.features.call.webrtc
 
 import android.content.Context
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleObserver
-import androidx.lifecycle.OnLifecycleEvent
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
 import im.vector.app.ActiveSessionDataSource
 import im.vector.app.BuildConfig
 import im.vector.app.core.services.CallService
@@ -70,7 +69,8 @@ private val loggerTag = LoggerTag("WebRtcCallManager", LoggerTag.VOIP)
 class WebRtcCallManager @Inject constructor(
         private val context: Context,
         private val activeSessionDataSource: ActiveSessionDataSource
-) : CallListener, LifecycleObserver {
+) : CallListener,
+        DefaultLifecycleObserver {
 
     private val currentSession: Session?
         get() = activeSessionDataSource.currentValue?.orNull()
@@ -133,13 +133,11 @@ class WebRtcCallManager @Inject constructor(
 
     private var isInBackground: Boolean = true
 
-    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
-    fun entersForeground() {
+    override fun onResume(owner: LifecycleOwner) {
         isInBackground = false
     }
 
-    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
-    fun entersBackground() {
+    override fun onPause(owner: LifecycleOwner) {
         isInBackground = true
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt
index ddbe86305c..a0a6a138dc 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt
@@ -29,13 +29,8 @@ class KeysBackupRestoreFromKeyViewModel @Inject constructor(
         private val stringProvider: StringProvider
 ) : ViewModel() {
 
-    var recoveryCode: MutableLiveData<String> = MutableLiveData()
-    var recoveryCodeErrorText: MutableLiveData<String> = MutableLiveData()
-
-    init {
-        recoveryCode.value = null
-        recoveryCodeErrorText.value = null
-    }
+    var recoveryCode: MutableLiveData<String?> = MutableLiveData(null)
+    var recoveryCodeErrorText: MutableLiveData<String?> = MutableLiveData(null)
 
     // ========= Actions =========
     fun updateCode(newValue: String) {
diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt
index af5938e20a..81d3c64dec 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt
@@ -28,13 +28,8 @@ class KeysBackupRestoreFromPassphraseViewModel @Inject constructor(
         private val stringProvider: StringProvider
 ) : ViewModel() {
 
-    var passphrase: MutableLiveData<String> = MutableLiveData()
-    var passphraseErrorText: MutableLiveData<String> = MutableLiveData()
-
-    init {
-        passphrase.value = null
-        passphraseErrorText.value = null
-    }
+    var passphrase: MutableLiveData<String?> = MutableLiveData(null)
+    var passphraseErrorText: MutableLiveData<String?> = MutableLiveData(null)
 
     // ========= Actions =========
 
diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt
index 34a333d588..8362a3566e 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt
@@ -57,7 +57,7 @@ class KeysBackupRestoreSharedViewModel @Inject constructor(
 
     lateinit var session: Session
 
-    var keyVersionResult: MutableLiveData<KeysVersionResult> = MutableLiveData()
+    var keyVersionResult: MutableLiveData<KeysVersionResult?> = MutableLiveData(null)
 
     var keySourceModel: MutableLiveData<KeySource> = MutableLiveData()
 
@@ -69,17 +69,11 @@ class KeysBackupRestoreSharedViewModel @Inject constructor(
     val navigateEvent: LiveData<LiveEvent<String>>
         get() = _navigateEvent
 
-    var loadingEvent: MutableLiveData<WaitingViewData> = MutableLiveData()
+    var loadingEvent: MutableLiveData<WaitingViewData?> = MutableLiveData(null)
 
     var importKeyResult: ImportRoomKeysResult? = null
     var importRoomKeysFinishWithResult: MutableLiveData<LiveEvent<ImportRoomKeysResult>> = MutableLiveData()
 
-    init {
-        keyVersionResult.value = null
-        _keyVersionResultError.value = null
-        loadingEvent.value = null
-    }
-
     fun initSession(session: Session) {
         this.session = session
     }
diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt
index cd59a69a86..1141886689 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt
@@ -68,23 +68,15 @@ class KeysBackupSetupSharedViewModel @Inject constructor() : ViewModel() {
     // Step 3
     // Var to ignore events from previous request(s) to generate a recovery key
     private var currentRequestId: MutableLiveData<Long> = MutableLiveData()
-    var recoveryKey: MutableLiveData<String> = MutableLiveData()
-    var prepareRecoverFailError: MutableLiveData<Throwable> = MutableLiveData()
+    var recoveryKey: MutableLiveData<String?> = MutableLiveData(null)
+    var prepareRecoverFailError: MutableLiveData<Throwable?> = MutableLiveData(null)
     var megolmBackupCreationInfo: MegolmBackupCreationInfo? = null
     var copyHasBeenMade = false
-    var isCreatingBackupVersion: MutableLiveData<Boolean> = MutableLiveData()
-    var creatingBackupError: MutableLiveData<Throwable> = MutableLiveData()
+    var isCreatingBackupVersion: MutableLiveData<Boolean> = MutableLiveData(false)
+    var creatingBackupError: MutableLiveData<Throwable?> = MutableLiveData(null)
     var keysVersion: MutableLiveData<KeysVersion> = MutableLiveData()
 
-    var loadingStatus: MutableLiveData<WaitingViewData> = MutableLiveData()
-
-    init {
-        recoveryKey.value = null
-        isCreatingBackupVersion.value = false
-        prepareRecoverFailError.value = null
-        creatingBackupError.value = null
-        loadingStatus.value = null
-    }
+    var loadingStatus: MutableLiveData<WaitingViewData?> = MutableLiveData(null)
 
     fun initSession(session: Session) {
         this.session = session
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/ComposerEditText.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/ComposerEditText.kt
index 232323789c..03107fd3f7 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/ComposerEditText.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/ComposerEditText.kt
@@ -17,13 +17,15 @@
 
 package im.vector.app.features.home.room.detail.composer
 
+import android.content.ClipData
 import android.content.Context
 import android.net.Uri
-import android.os.Build
 import android.text.Editable
 import android.util.AttributeSet
 import android.view.inputmethod.EditorInfo
 import android.view.inputmethod.InputConnection
+import androidx.core.view.OnReceiveContentListener
+import androidx.core.view.ViewCompat
 import androidx.core.view.inputmethod.EditorInfoCompat
 import androidx.core.view.inputmethod.InputConnectionCompat
 import com.vanniktech.emoji.EmojiEditText
@@ -33,7 +35,7 @@ import im.vector.app.features.html.PillImageSpan
 import timber.log.Timber
 
 class ComposerEditText @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = android.R.attr.editTextStyle) :
-    EmojiEditText(context, attrs, defStyleAttr) {
+        EmojiEditText(context, attrs, defStyleAttr) {
 
     interface Callback {
         fun onRichContentSelected(contentUri: Uri): Boolean
@@ -43,23 +45,35 @@ class ComposerEditText @JvmOverloads constructor(context: Context, attrs: Attrib
     var callback: Callback? = null
 
     override fun onCreateInputConnection(editorInfo: EditorInfo): InputConnection? {
-        val ic = super.onCreateInputConnection(editorInfo) ?: return null
-        EditorInfoCompat.setContentMimeTypes(editorInfo, arrayOf("*/*"))
+        var ic = super.onCreateInputConnection(editorInfo) ?: return null
+        val mimeTypes = ViewCompat.getOnReceiveContentMimeTypes(this) ?: arrayOf("image/*")
 
-        val callback =
-                InputConnectionCompat.OnCommitContentListener { inputContentInfo, flags, _ ->
-                    val lacksPermission = (flags and
-                            InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0
-                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1 && lacksPermission) {
-                        try {
-                            inputContentInfo.requestPermission()
-                        } catch (e: Exception) {
-                            return@OnCommitContentListener false
-                        }
-                    }
-                    callback?.onRichContentSelected(inputContentInfo.contentUri) ?: false
+        EditorInfoCompat.setContentMimeTypes(editorInfo, mimeTypes)
+        ic = InputConnectionCompat.createWrapper(this, ic, editorInfo)
+
+        val onReceiveContentListener = OnReceiveContentListener { _, payload ->
+            val split = payload.partition { item -> item.uri != null }
+            val uriContent = split.first
+            val remaining = split.second
+
+            if (uriContent != null) {
+                val clip: ClipData = uriContent.clip
+                for (i in 0 until clip.itemCount) {
+                    val uri = clip.getItemAt(i).uri
+                    // ... app-specific logic to handle the URI ...
+                    callback?.onRichContentSelected(uri)
                 }
-        return InputConnectionCompat.createWrapper(ic, editorInfo, callback)
+            }
+            // Return anything that we didn't handle ourselves. This preserves the default platform
+            // behavior for text and anything else for which we are not implementing custom handling.
+            // Return anything that we didn't handle ourselves. This preserves the default platform
+            // behavior for text and anything else for which we are not implementing custom handling.
+            remaining
+        }
+
+        ViewCompat.setOnReceiveContentListener(this, mimeTypes, onReceiveContentListener)
+
+        return ic
     }
 
     init {
diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt
index 491302a225..1479e577e7 100755
--- a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt
+++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt
@@ -48,6 +48,7 @@ import androidx.fragment.app.Fragment
 import im.vector.app.BuildConfig
 import im.vector.app.R
 import im.vector.app.core.extensions.createIgnoredUri
+import im.vector.app.core.platform.PendingIntentCompat
 import im.vector.app.core.resources.StringProvider
 import im.vector.app.core.services.CallService
 import im.vector.app.core.utils.startNotificationChannelSettingsIntent
@@ -227,7 +228,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
         // build the pending intent go to the home screen if this is clicked.
         val i = HomeActivity.newIntent(context)
         i.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
-        val pi = PendingIntent.getActivity(context, 0, i, 0)
+        val pi = PendingIntent.getActivity(context, 0, i, PendingIntentCompat.FLAG_IMMUTABLE)
 
         val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
 
@@ -320,16 +321,23 @@ class NotificationUtils @Inject constructor(private val context: Context,
             flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
             data = createIgnoredUri(call.callId)
         }
-        val contentPendingIntent = PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), contentIntent, 0)
+        val contentPendingIntent = PendingIntent.getActivity(
+                context,
+                System.currentTimeMillis().toInt(),
+                contentIntent,
+                PendingIntentCompat.FLAG_IMMUTABLE
+        )
 
         val answerCallPendingIntent = TaskStackBuilder.create(context)
                 .addNextIntentWithParentStack(HomeActivity.newIntent(context))
-                .addNextIntent(VectorCallActivity.newIntent(
-                        context = context,
-                        call = call,
-                        mode = VectorCallActivity.INCOMING_ACCEPT)
+                .addNextIntent(
+                        VectorCallActivity.newIntent(
+                                context = context,
+                                call = call,
+                                mode = VectorCallActivity.INCOMING_ACCEPT
+                        )
                 )
-                .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
+                .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE)
 
         val rejectCallPendingIntent = buildRejectCallPendingIntent(call.callId)
 
@@ -338,7 +346,8 @@ class NotificationUtils @Inject constructor(private val context: Context,
                         IconCompat.createWithResource(context, R.drawable.ic_call_hangup)
                                 .setTint(ThemeUtils.getColor(context, R.attr.colorError)),
                         getActionText(R.string.call_notification_reject, R.attr.colorError),
-                        rejectCallPendingIntent)
+                        rejectCallPendingIntent
+                )
         )
 
         builder.addAction(
@@ -381,7 +390,12 @@ class NotificationUtils @Inject constructor(private val context: Context,
             flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
             data = createIgnoredUri(call.callId)
         }
-        val contentPendingIntent = PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), contentIntent, 0)
+        val contentPendingIntent = PendingIntent.getActivity(
+                context,
+                System.currentTimeMillis().toInt(),
+                contentIntent,
+                PendingIntentCompat.FLAG_IMMUTABLE
+        )
 
         val rejectCallPendingIntent = buildRejectCallPendingIntent(call.callId)
 
@@ -390,7 +404,8 @@ class NotificationUtils @Inject constructor(private val context: Context,
                         IconCompat.createWithResource(context, R.drawable.ic_call_hangup)
                                 .setTint(ThemeUtils.getColor(context, R.attr.colorError)),
                         getActionText(R.string.call_notification_hangup, R.attr.colorError),
-                        rejectCallPendingIntent)
+                        rejectCallPendingIntent
+                )
         )
         builder.setContentIntent(contentPendingIntent)
 
@@ -431,13 +446,14 @@ class NotificationUtils @Inject constructor(private val context: Context,
                         IconCompat.createWithResource(context, R.drawable.ic_call_hangup)
                                 .setTint(ThemeUtils.getColor(context, R.attr.colorError)),
                         getActionText(R.string.call_notification_hangup, R.attr.colorError),
-                        rejectCallPendingIntent)
+                        rejectCallPendingIntent
+                )
         )
 
         val contentPendingIntent = TaskStackBuilder.create(context)
                 .addNextIntentWithParentStack(HomeActivity.newIntent(context))
                 .addNextIntent(VectorCallActivity.newIntent(context, call, null))
-                .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
+                .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE)
 
         builder.setContentIntent(contentPendingIntent)
 
@@ -453,7 +469,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
                 context,
                 System.currentTimeMillis().toInt(),
                 rejectCallActionReceiver,
-                PendingIntent.FLAG_UPDATE_CURRENT
+                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
         )
     }
 
@@ -499,7 +515,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
         val contentPendingIntent = TaskStackBuilder.create(context)
                 .addNextIntentWithParentStack(HomeActivity.newIntent(context))
                 .addNextIntent(RoomDetailActivity.newIntent(context, RoomDetailArgs(callInformation.nativeRoomId)))
-                .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
+                .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE)
 
         builder.setContentIntent(contentPendingIntent)
         return builder.build()
@@ -517,7 +533,10 @@ class NotificationUtils @Inject constructor(private val context: Context,
                         addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                     }
                     PendingIntent.getActivity(
-                            context, System.currentTimeMillis().toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT
+                            context,
+                            System.currentTimeMillis().toInt(),
+                            intent,
+                            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
                     ).let {
                         setContentIntent(it)
                     }
@@ -587,8 +606,12 @@ class NotificationUtils @Inject constructor(private val context: Context,
                     markRoomReadIntent.action = MARK_ROOM_READ_ACTION
                     markRoomReadIntent.data = createIgnoredUri(roomInfo.roomId)
                     markRoomReadIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomInfo.roomId)
-                    val markRoomReadPendingIntent = PendingIntent.getBroadcast(context, System.currentTimeMillis().toInt(), markRoomReadIntent,
-                            PendingIntent.FLAG_UPDATE_CURRENT)
+                    val markRoomReadPendingIntent = PendingIntent.getBroadcast(
+                            context,
+                            System.currentTimeMillis().toInt(),
+                            markRoomReadIntent,
+                            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
+                    )
 
                     NotificationCompat.Action.Builder(R.drawable.ic_material_done_all_white,
                             stringProvider.getString(R.string.action_mark_room_read), markRoomReadPendingIntent)
@@ -624,8 +647,12 @@ class NotificationUtils @Inject constructor(private val context: Context,
                     val intent = Intent(context, NotificationBroadcastReceiver::class.java)
                     intent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomInfo.roomId)
                     intent.action = DISMISS_ROOM_NOTIF_ACTION
-                    val pendingIntent = PendingIntent.getBroadcast(context.applicationContext,
-                            System.currentTimeMillis().toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT)
+                    val pendingIntent = PendingIntent.getBroadcast(
+                            context.applicationContext,
+                            System.currentTimeMillis().toInt(),
+                            intent,
+                            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
+                    )
                     setDeleteIntent(pendingIntent)
                 }
                 .setTicker(tickerText)
@@ -655,31 +682,41 @@ class NotificationUtils @Inject constructor(private val context: Context,
                     rejectIntent.action = REJECT_ACTION
                     rejectIntent.data = createIgnoredUri("$roomId&$matrixId")
                     rejectIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId)
-                    val rejectIntentPendingIntent = PendingIntent.getBroadcast(context, System.currentTimeMillis().toInt(), rejectIntent,
-                            PendingIntent.FLAG_UPDATE_CURRENT)
+                    val rejectIntentPendingIntent = PendingIntent.getBroadcast(
+                            context,
+                            System.currentTimeMillis().toInt(),
+                            rejectIntent,
+                            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
+                    )
 
                     addAction(
                             R.drawable.vector_notification_reject_invitation,
                             stringProvider.getString(R.string.reject),
-                            rejectIntentPendingIntent)
+                            rejectIntentPendingIntent
+                    )
 
                     // offer to type a quick accept button
                     val joinIntent = Intent(context, NotificationBroadcastReceiver::class.java)
                     joinIntent.action = JOIN_ACTION
                     joinIntent.data = createIgnoredUri("$roomId&$matrixId")
                     joinIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId)
-                    val joinIntentPendingIntent = PendingIntent.getBroadcast(context, System.currentTimeMillis().toInt(), joinIntent,
-                            PendingIntent.FLAG_UPDATE_CURRENT)
+                    val joinIntentPendingIntent = PendingIntent.getBroadcast(
+                            context,
+                            System.currentTimeMillis().toInt(),
+                            joinIntent,
+                            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
+                    )
                     addAction(
                             R.drawable.vector_notification_accept_invitation,
                             stringProvider.getString(R.string.join),
-                            joinIntentPendingIntent)
+                            joinIntentPendingIntent
+                    )
 
                     val contentIntent = HomeActivity.newIntent(context, inviteNotificationRoomId = inviteNotifiableEvent.roomId)
                     contentIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
                     // pending intent get reused by system, this will mess up the extra params, so put unique info to avoid that
                     contentIntent.data = createIgnoredUri(inviteNotifiableEvent.eventId)
-                    setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
+                    setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, PendingIntentCompat.FLAG_IMMUTABLE))
 
                     if (inviteNotifiableEvent.noisy) {
                         // Compat
@@ -718,7 +755,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
                     contentIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
                     // pending intent get reused by system, this will mess up the extra params, so put unique info to avoid that
                     contentIntent.data = createIgnoredUri(simpleNotifiableEvent.eventId)
-                    setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
+                    setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, PendingIntentCompat.FLAG_IMMUTABLE))
 
                     if (simpleNotifiableEvent.noisy) {
                         // Compat
@@ -745,14 +782,22 @@ class NotificationUtils @Inject constructor(private val context: Context,
         return TaskStackBuilder.create(context)
                 .addNextIntentWithParentStack(HomeActivity.newIntent(context))
                 .addNextIntent(roomIntentTap)
-                .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
+                .getPendingIntent(
+                        System.currentTimeMillis().toInt(),
+                        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
+                )
     }
 
     private fun buildOpenHomePendingIntentForSummary(): PendingIntent {
         val intent = HomeActivity.newIntent(context, clearNotification = true)
         intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
         intent.data = createIgnoredUri("tapSummary")
-        return PendingIntent.getActivity(context, Random.nextInt(1000), intent, PendingIntent.FLAG_UPDATE_CURRENT)
+        return PendingIntent.getActivity(
+                context,
+                Random.nextInt(1000),
+                intent,
+                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
+        )
     }
 
     /*
@@ -769,8 +814,13 @@ class NotificationUtils @Inject constructor(private val context: Context,
             intent.action = SMART_REPLY_ACTION
             intent.data = createIgnoredUri(roomId)
             intent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId)
-            return PendingIntent.getBroadcast(context, System.currentTimeMillis().toInt(), intent,
-                    PendingIntent.FLAG_UPDATE_CURRENT)
+            return PendingIntent.getBroadcast(
+                    context,
+                    System.currentTimeMillis().toInt(),
+                    intent,
+                    // PendingIntents attached to actions with remote inputs must be mutable
+                    PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_MUTABLE
+            )
         } else {
             /*
             TODO
@@ -783,7 +833,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
                 // the action must be unique else the parameters are ignored
                 quickReplyIntent.action = QUICK_LAUNCH_ACTION
                 quickReplyIntent.data = createIgnoredUri($roomId")
-                return PendingIntent.getActivity(context, 0, quickReplyIntent, 0)
+                return PendingIntent.getActivity(context, 0, quickReplyIntent, PendingIntentCompat.FLAG_IMMUTABLE)
             }
              */
         }
@@ -837,8 +887,12 @@ class NotificationUtils @Inject constructor(private val context: Context,
         val intent = Intent(context, NotificationBroadcastReceiver::class.java)
         intent.action = DISMISS_SUMMARY_ACTION
         intent.data = createIgnoredUri("deleteSummary")
-        return PendingIntent.getBroadcast(context.applicationContext,
-                0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
+        return PendingIntent.getBroadcast(
+                context.applicationContext,
+                0,
+                intent,
+                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
+        )
     }
 
     fun showNotificationMessage(tag: String?, id: Int, notification: Notification) {
@@ -875,7 +929,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
                 context,
                 0,
                 testActionIntent,
-                PendingIntent.FLAG_UPDATE_CURRENT
+                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
         )
 
         notificationManager.notify(
diff --git a/vector/src/main/java/im/vector/app/features/pin/PinLocker.kt b/vector/src/main/java/im/vector/app/features/pin/PinLocker.kt
index 9c55b88805..f848a3174e 100644
--- a/vector/src/main/java/im/vector/app/features/pin/PinLocker.kt
+++ b/vector/src/main/java/im/vector/app/features/pin/PinLocker.kt
@@ -17,11 +17,10 @@
 package im.vector.app.features.pin
 
 import android.os.SystemClock
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleObserver
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.OnLifecycleEvent
 import im.vector.app.features.settings.VectorPreferences
 import kotlinx.coroutines.GlobalScope
 import kotlinx.coroutines.launch
@@ -41,7 +40,7 @@ private const val PERIOD_OF_GRACE_IN_MS = 2 * 60 * 1000L
 class PinLocker @Inject constructor(
         private val pinCodeStore: PinCodeStore,
         private val vectorPreferences: VectorPreferences
-) : LifecycleObserver {
+) : DefaultLifecycleObserver {
 
     enum class State {
         // App is locked, can be unlock
@@ -87,16 +86,14 @@ class PinLocker @Inject constructor(
         computeState()
     }
 
-    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
-    fun entersForeground() {
+    override fun onResume(owner: LifecycleOwner) {
         val timeElapsedSinceBackground = SystemClock.elapsedRealtime() - entersBackgroundTs
         shouldBeLocked = shouldBeLocked || timeElapsedSinceBackground >= getGracePeriod()
         Timber.v("App enters foreground after $timeElapsedSinceBackground ms spent in background shouldBeLocked: $shouldBeLocked")
         computeState()
     }
 
-    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
-    fun entersBackground() {
+    override fun onPause(owner: LifecycleOwner) {
         Timber.v("App enters background")
         entersBackgroundTs = SystemClock.elapsedRealtime()
     }
diff --git a/vector/src/main/java/im/vector/app/features/rageshake/RageShake.kt b/vector/src/main/java/im/vector/app/features/rageshake/RageShake.kt
index 93c72ea69e..b4dcb07349 100644
--- a/vector/src/main/java/im/vector/app/features/rageshake/RageShake.kt
+++ b/vector/src/main/java/im/vector/app/features/rageshake/RageShake.kt
@@ -46,7 +46,7 @@ class RageShake @Inject constructor(private val activity: FragmentActivity,
 
         shakeDetector = ShakeDetector(this).apply {
             setSensitivity(vectorPreferences.getRageshakeSensitivity())
-            start(sensorManager)
+            start(sensorManager, SensorManager.SENSOR_DELAY_GAME)
         }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorder.kt b/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorder.kt
index 786920aa22..1a91c00e11 100644
--- a/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorder.kt
+++ b/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorder.kt
@@ -23,7 +23,7 @@ import java.io.File
 import java.io.FileOutputStream
 
 abstract class AbstractVoiceRecorder(
-        context: Context,
+        private val context: Context,
         private val filenameExt: String
 ) : VoiceRecorder {
     private val outputDirectory: File by lazy {
@@ -39,7 +39,7 @@ abstract class AbstractVoiceRecorder(
     abstract fun convertFile(recordedFile: File?): File?
 
     private fun init() {
-        MediaRecorder().let {
+        createMediaRecorder().let {
             it.setAudioSource(MediaRecorder.AudioSource.DEFAULT)
             setOutputFormat(it)
             it.setAudioEncodingBitRate(24000)
@@ -48,6 +48,15 @@ abstract class AbstractVoiceRecorder(
         }
     }
 
+    private fun createMediaRecorder(): MediaRecorder {
+        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+            MediaRecorder(context)
+        } else {
+            @Suppress("DEPRECATION")
+            MediaRecorder()
+        }
+    }
+
     override fun startRecord() {
         init()
         outputFile = File(outputDirectory, "Voice message.$filenameExt")