mirror of
https://github.com/element-hq/element-android
synced 2024-11-23 18:05:36 +03:00
Merge pull request #4433 from vector-im/feature/bma/android12
Android12
This commit is contained in:
commit
be3aafeef2
35 changed files with 389 additions and 263 deletions
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() }
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 }
|
||||
}
|
|
@ -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}..."
|
||||
|
|
|
@ -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}..."
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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('/')
|
||||
}
|
||||
|
|
|
@ -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."
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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>()!!
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 =========
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
Loading…
Reference in a new issue