Merge pull request #7293 from vector-im/feature/bma/android13

Android 13
This commit is contained in:
Benoit Marty 2022-10-13 13:52:52 +02:00 committed by GitHub
commit 92a2420952
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
112 changed files with 618 additions and 237 deletions

View File

@ -1,8 +1,8 @@
ext.versions = [ ext.versions = [
'minSdk' : 21, 'minSdk' : 21,
'compileSdk' : 32, 'compileSdk' : 33,
'targetSdk' : 32, 'targetSdk' : 33,
'sourceCompat' : JavaVersion.VERSION_11, 'sourceCompat' : JavaVersion.VERSION_11,
'targetCompat' : JavaVersion.VERSION_11, 'targetCompat' : JavaVersion.VERSION_11,
] ]
@ -50,10 +50,10 @@ ext.libs = [
'coroutinesTest' : "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinCoroutines" 'coroutinesTest' : "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinCoroutines"
], ],
androidx : [ androidx : [
'activity' : "androidx.activity:activity:1.5.1", 'activity' : "androidx.activity:activity-ktx:1.6.0",
'appCompat' : "androidx.appcompat:appcompat:1.5.1", 'appCompat' : "androidx.appcompat:appcompat:1.5.1",
'biometric' : "androidx.biometric:biometric:1.1.0", 'biometric' : "androidx.biometric:biometric:1.1.0",
'core' : "androidx.core:core-ktx:1.8.0", 'core' : "androidx.core:core-ktx:1.9.0",
'recyclerview' : "androidx.recyclerview:recyclerview:1.2.1", 'recyclerview' : "androidx.recyclerview:recyclerview:1.2.1",
'exifinterface' : "androidx.exifinterface:exifinterface:1.3.4", 'exifinterface' : "androidx.exifinterface:exifinterface:1.3.4",
'fragmentKtx' : "androidx.fragment:fragment-ktx:$fragment", 'fragmentKtx' : "androidx.fragment:fragment-ktx:$fragment",

View File

@ -316,10 +316,6 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
} }
return false return false
} }
override fun onDoubleTap(e: MotionEvent?): Boolean {
return super.onDoubleTap(e)
}
}) })
override fun onEvent(event: AttachmentEvents) { override fun onEvent(event: AttachmentEvents) {

View File

@ -96,31 +96,27 @@ class SwipeToDismissHandler(
.setDuration(ANIMATION_DURATION) .setDuration(ANIMATION_DURATION)
.setInterpolator(AccelerateInterpolator()) .setInterpolator(AccelerateInterpolator())
.setUpdateListener { onSwipeViewMove(swipeView.translationY, translationLimit) } .setUpdateListener { onSwipeViewMove(swipeView.translationY, translationLimit) }
.setAnimatorListener(onAnimationEnd = { .setAnimatorEndListener {
if (translationTo != 0f) { if (translationTo != 0f) {
onDismiss() onDismiss()
} }
// remove the update listener, otherwise it will be saved on the next animation execution: // remove the update listener, otherwise it will be saved on the next animation execution:
swipeView.animate().setUpdateListener(null) swipeView.animate().setUpdateListener(null)
}) }
.start() .start()
} }
} }
internal fun ViewPropertyAnimator.setAnimatorListener( private fun ViewPropertyAnimator.setAnimatorEndListener(
onAnimationEnd: ((Animator?) -> Unit)? = null, onAnimationEnd: () -> Unit,
onAnimationStart: ((Animator?) -> Unit)? = null ) = setListener(
) = this.setListener(
object : AnimatorListenerAdapter() { object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) { override fun onAnimationEnd(animation: Animator) {
onAnimationEnd?.invoke(animation) onAnimationEnd()
} }
}
)
override fun onAnimationStart(animation: Animator?) { private val View.hitRect: Rect
onAnimationStart?.invoke(animation) get() = Rect().also { getHitRect(it) }
}
})
internal val View?.hitRect: Rect
get() = Rect().also { this?.getHitRect(it) }

View File

@ -0,0 +1,70 @@
/*
* Copyright (c) 2022 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.core.utils.compat
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import java.io.Serializable
inline fun <reified T> Intent.getParcelableExtraCompat(key: String): T? = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> getParcelableExtra(key, T::class.java)
else -> @Suppress("DEPRECATION") getParcelableExtra(key) as? T?
}
inline fun <reified T : Parcelable> Intent.getParcelableArrayListExtraCompat(key: String): ArrayList<T>? = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> getParcelableArrayListExtra(key, T::class.java)
else -> @Suppress("DEPRECATION") getParcelableArrayListExtra<T>(key)
}
inline fun <reified T> Bundle.getParcelableCompat(key: String): T? = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> getParcelable(key, T::class.java)
else -> @Suppress("DEPRECATION") getParcelable(key) as? T?
}
inline fun <reified T : Serializable> Bundle.getSerializableCompat(key: String): T? = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> getSerializable(key, T::class.java)
else -> @Suppress("DEPRECATION") getSerializable(key) as? T?
}
inline fun <reified T : Serializable> Intent.getSerializableExtraCompat(key: String): T? = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> getSerializableExtra(key, T::class.java)
else -> @Suppress("DEPRECATION") getSerializableExtra(key) as? T?
}
fun PackageManager.queryIntentActivitiesCompat(data: Intent, flags: Int): List<ResolveInfo> {
return when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> queryIntentActivities(
data,
PackageManager.ResolveInfoFlags.of(flags.toLong())
)
else -> @Suppress("DEPRECATION") queryIntentActivities(data, flags)
}
}
fun PackageManager.resolveActivityCompat(data: Intent, flags: Int): ResolveInfo? {
return when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> resolveActivity(
data,
PackageManager.ResolveInfoFlags.of(flags.toLong())
)
else -> @Suppress("DEPRECATION") resolveActivity(data, flags)
}
}

View File

@ -103,6 +103,7 @@ public class DialpadView extends LinearLayout {
@Override @Override
protected void onFinishInflate() { protected void onFinishInflate() {
super.onFinishInflate();
setupKeypad(); setupKeypad();
mDigits = (EditText) findViewById(R.id.digits); mDigits = (EditText) findViewById(R.id.digits);
mDelete = (ImageButton) findViewById(R.id.deleteButton); mDelete = (ImageButton) findViewById(R.id.deleteButton);
@ -201,14 +202,6 @@ public class DialpadView extends LinearLayout {
zero.setLongHoverContentDescription(resources.getText(R.string.description_image_button_plus)); zero.setLongHoverContentDescription(resources.getText(R.string.description_image_button_plus));
} }
private Drawable getDrawableCompat(Context context, int id) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return context.getDrawable(id);
} else {
return context.getResources().getDrawable(id);
}
}
public void setShowVoicemailButton(boolean show) { public void setShowVoicemailButton(boolean show) {
View view = findViewById(R.id.dialpad_key_voicemail); View view = findViewById(R.id.dialpad_key_voicemail);
if (view != null) { if (view != null) {

View File

@ -23,6 +23,7 @@ import android.view.ViewGroup
import android.view.WindowManager import android.view.WindowManager
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.Mavericks
import im.vector.lib.core.utils.compat.getParcelableCompat
class JSonViewerDialog : DialogFragment() { class JSonViewerDialog : DialogFragment() {
@ -36,16 +37,17 @@ class JSonViewerDialog : DialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val args: JSonViewerFragmentArgs = arguments?.getParcelable(Mavericks.KEY_ARG) ?: return val args: JSonViewerFragmentArgs = arguments?.getParcelableCompat(Mavericks.KEY_ARG) ?: return
if (savedInstanceState == null) { if (savedInstanceState == null) {
childFragmentManager.beginTransaction() childFragmentManager.beginTransaction()
.replace( .replace(
R.id.fragmentContainer, JSonViewerFragment.newInstance( R.id.fragmentContainer,
args.jsonString, JSonViewerFragment.newInstance(
args.defaultOpenDepth, args.jsonString,
true, args.defaultOpenDepth,
args.styleProvider true,
) args.styleProvider
)
) )
.commitNow() .commitNow()
} }

View File

@ -28,6 +28,7 @@ import com.airbnb.mvrx.Mavericks
import com.airbnb.mvrx.MavericksView import com.airbnb.mvrx.MavericksView
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import im.vector.lib.core.utils.compat.getParcelableCompat
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@Parcelize @Parcelize
@ -53,7 +54,7 @@ class JSonViewerFragment : Fragment(), MavericksView {
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
val args: JSonViewerFragmentArgs? = arguments?.getParcelable(Mavericks.KEY_ARG) val args: JSonViewerFragmentArgs? = arguments?.getParcelableCompat(Mavericks.KEY_ARG)
val inflate = val inflate =
if (args?.wrap == true) { if (args?.wrap == true) {
inflater.inflate(R.layout.fragment_jv_recycler_view_wrap, container, false) inflater.inflate(R.layout.fragment_jv_recycler_view_wrap, container, false)

View File

@ -35,9 +35,19 @@ android {
} }
} }
compileOptions {
sourceCompatibility versions.sourceCompat
targetCompatibility versions.targetCompat
}
kotlinOptions {
jvmTarget = "11"
}
} }
dependencies { dependencies {
implementation project(":library:core-utils")
api libs.androidx.activity api libs.androidx.activity
implementation libs.androidx.exifinterface implementation libs.androidx.exifinterface
implementation libs.androidx.core implementation libs.androidx.core

View File

@ -22,6 +22,9 @@ import android.content.pm.PackageManager
import android.content.pm.ResolveInfo import android.content.pm.ResolveInfo
import android.net.Uri import android.net.Uri
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import im.vector.lib.core.utils.compat.getParcelableArrayListExtraCompat
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
import im.vector.lib.core.utils.compat.queryIntentActivitiesCompat
/** /**
* Abstract class to provide all types of Pickers. * Abstract class to provide all types of Pickers.
@ -45,13 +48,13 @@ abstract class Picker<T> {
val uriList = mutableListOf<Uri>() val uriList = mutableListOf<Uri>()
if (data.action == Intent.ACTION_SEND) { if (data.action == Intent.ACTION_SEND) {
(data.getParcelableExtra(Intent.EXTRA_STREAM) as? Uri)?.let { uriList.add(it) } data.getParcelableExtraCompat<Uri>(Intent.EXTRA_STREAM)?.let { uriList.add(it) }
} else if (data.action == Intent.ACTION_SEND_MULTIPLE) { } else if (data.action == Intent.ACTION_SEND_MULTIPLE) {
val extraUriList: List<Uri>? = data.getParcelableArrayListExtra(Intent.EXTRA_STREAM) val extraUriList: List<Uri>? = data.getParcelableArrayListExtraCompat(Intent.EXTRA_STREAM)
extraUriList?.let { uriList.addAll(it) } extraUriList?.let { uriList.addAll(it) }
} }
val resInfoList: List<ResolveInfo> = context.packageManager.queryIntentActivities(data, PackageManager.MATCH_DEFAULT_ONLY) val resInfoList: List<ResolveInfo> = context.packageManager.queryIntentActivitiesCompat(data, PackageManager.MATCH_DEFAULT_ONLY)
uriList.forEach { uriList.forEach {
for (resolveInfo in resInfoList) { for (resolveInfo in resInfoList) {
val packageName: String = resolveInfo.activityInfo.packageName val packageName: String = resolveInfo.activityInfo.packageName
@ -91,6 +94,7 @@ abstract class Picker<T> {
} else if (dataUri != null) { } else if (dataUri != null) {
selectedUriList.add(dataUri) selectedUriList.add(dataUri)
} else { } else {
@Suppress("DEPRECATION")
data?.extras?.get(Intent.EXTRA_STREAM)?.let { data?.extras?.get(Intent.EXTRA_STREAM)?.let {
(it as? List<*>)?.filterIsInstance<Uri>()?.let { uriList -> (it as? List<*>)?.filterIsInstance<Uri>()?.let { uriList ->
selectedUriList.addAll(uriList) selectedUriList.addAll(uriList)

View File

@ -638,6 +638,8 @@
<string name="permissions_rationale_msg_record_audio">${app_name} needs permission to access your microphone to perform audio calls.</string> <string name="permissions_rationale_msg_record_audio">${app_name} needs permission to access your microphone to perform audio calls.</string>
<!-- Note to translators: the translation MUST contain the string "${app_name}", which will be replaced by the application name --> <!-- Note to translators: the translation MUST contain the string "${app_name}", which will be replaced by the application name -->
<string name="permissions_rationale_msg_camera_and_audio">${app_name} needs permission to access your camera and your microphone to perform video calls.\n\nPlease allow access on the next pop-ups to be able to make the call.</string> <string name="permissions_rationale_msg_camera_and_audio">${app_name} needs permission to access your camera and your microphone to perform video calls.\n\nPlease allow access on the next pop-ups to be able to make the call.</string>
<!-- Note to translators: the translation MUST contain the string "${app_name}", which will be replaced by the application name -->
<string name="permissions_rationale_msg_notification">${app_name} needs permission to display notifications. Notifications can display your messages, your invitations, etc.\n\nPlease allow access on the next pop-ups to be able to view notification.</string>
<string name="permissions_denied_qr_code">To scan a QR code, you need to allow camera access.</string> <string name="permissions_denied_qr_code">To scan a QR code, you need to allow camera access.</string>
<string name="permissions_denied_add_contact">Allow permission to access your contacts.</string> <string name="permissions_denied_add_contact">Allow permission to access your contacts.</string>
@ -857,7 +859,9 @@
<string name="settings_troubleshoot_test_system_settings_title">System Settings.</string> <string name="settings_troubleshoot_test_system_settings_title">System Settings.</string>
<string name="settings_troubleshoot_test_system_settings_success">Notifications are enabled in the system settings.</string> <string name="settings_troubleshoot_test_system_settings_success">Notifications are enabled in the system settings.</string>
<string name="settings_troubleshoot_test_system_settings_failed">Notifications are disabled in the system settings.\nPlease check system settings.</string> <string name="settings_troubleshoot_test_system_settings_failed">Notifications are disabled in the system settings.\nPlease check system settings.</string>
<string name="settings_troubleshoot_test_system_settings_permission_failed">${app_name} needs the permission to show notifications.\nPlease grant the permission.</string>
<string name="open_settings">Open Settings</string> <string name="open_settings">Open Settings</string>
<string name="grant_permission">Grant Permission</string>
<string name="settings_troubleshoot_test_account_settings_title">Account Settings.</string> <string name="settings_troubleshoot_test_account_settings_title">Account Settings.</string>
<string name="settings_troubleshoot_test_account_settings_success">Notifications are enabled for your account.</string> <string name="settings_troubleshoot_test_account_settings_success">Notifications are enabled for your account.</string>

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.util
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.Build
fun PackageManager.getApplicationInfoCompat(packageName: String, flags: Int): ApplicationInfo {
return when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> getApplicationInfo(
packageName,
PackageManager.ApplicationInfoFlags.of(flags.toLong())
)
else -> @Suppress("DEPRECATION") getApplicationInfo(packageName, flags)
}
}
fun PackageManager.getPackageInfoCompat(packageName: String, flags: Int): PackageInfo {
return when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> getPackageInfo(
packageName,
PackageManager.PackageInfoFlags.of(flags.toLong())
)
else -> @Suppress("DEPRECATION") getPackageInfo(packageName, flags)
}
}

View File

@ -20,6 +20,8 @@ import android.content.Context
import android.os.Build import android.os.Build
import org.matrix.android.sdk.BuildConfig import org.matrix.android.sdk.BuildConfig
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.util.getApplicationInfoCompat
import org.matrix.android.sdk.api.util.getPackageInfoCompat
import javax.inject.Inject import javax.inject.Inject
class ComputeUserAgentUseCase @Inject constructor( class ComputeUserAgentUseCase @Inject constructor(
@ -36,7 +38,7 @@ class ComputeUserAgentUseCase @Inject constructor(
val appPackageName = context.applicationContext.packageName val appPackageName = context.applicationContext.packageName
val pm = context.packageManager val pm = context.packageManager
val appName = tryOrNull { pm.getApplicationLabel(pm.getApplicationInfo(appPackageName, 0)).toString() } val appName = tryOrNull { pm.getApplicationLabel(pm.getApplicationInfoCompat(appPackageName, 0)).toString() }
?.takeIf { ?.takeIf {
it.matches("\\A\\p{ASCII}*\\z".toRegex()) it.matches("\\A\\p{ASCII}*\\z".toRegex())
} }
@ -44,7 +46,7 @@ class ComputeUserAgentUseCase @Inject constructor(
// Use appPackageName instead of appName if appName is null or contains any non-ASCII character // Use appPackageName instead of appName if appName is null or contains any non-ASCII character
appPackageName appPackageName
} }
val appVersion = tryOrNull { pm.getPackageInfo(context.applicationContext.packageName, 0).versionName } ?: FALLBACK_APP_VERSION val appVersion = tryOrNull { pm.getPackageInfoCompat(context.applicationContext.packageName, 0).versionName } ?: FALLBACK_APP_VERSION
val deviceManufacturer = Build.MANUFACTURER val deviceManufacturer = Build.MANUFACTURER
val deviceModel = Build.MODEL val deviceModel = Build.MODEL

View File

@ -27,6 +27,8 @@ import org.amshove.kluent.shouldBeEqualTo
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.matrix.android.sdk.BuildConfig import org.matrix.android.sdk.BuildConfig
import org.matrix.android.sdk.api.util.getApplicationInfoCompat
import org.matrix.android.sdk.api.util.getPackageInfoCompat
import java.lang.Exception import java.lang.Exception
private const val A_PACKAGE_NAME = "org.matrix.sdk" private const val A_PACKAGE_NAME = "org.matrix.sdk"
@ -49,8 +51,8 @@ class ComputeUserAgentUseCaseTest {
every { context.applicationContext } returns context every { context.applicationContext } returns context
every { context.packageName } returns A_PACKAGE_NAME every { context.packageName } returns A_PACKAGE_NAME
every { context.packageManager } returns packageManager every { context.packageManager } returns packageManager
every { packageManager.getApplicationInfo(any(), any()) } returns applicationInfo every { packageManager.getApplicationInfoCompat(any(), any()) } returns applicationInfo
every { packageManager.getPackageInfo(any<String>(), any()) } returns packageInfo every { packageManager.getPackageInfoCompat(any(), any()) } returns packageInfo
} }
@Test @Test

25
tools/adb/notification.sh Executable file
View File

@ -0,0 +1,25 @@
#!/usr/bin/env bash
## From https://developer.android.com/develop/ui/views/notifications/notification-permission#test
PACKAGE_NAME=im.vector.app.debug
# App is newly installed on a device that runs Android 13 or higher:
adb shell pm revoke ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS
adb shell pm clear-permission-flags ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS user-set
adb shell pm clear-permission-flags ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS user-fixed
# The user keeps notifications enabled when the app is installed on a device that runs 12L or lower,
# then the device upgrades to Android 13 or higher:
# adb shell pm grant ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS
# adb shell pm set-permission-flags ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS user-set
# adb shell pm clear-permission-flags ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS user-fixed
# The user manually disables notifications when the app is installed on a device that runs 12L or lower,
# then the device upgrades to Android 13 or higher:
# adb shell pm revoke ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS
# adb shell pm set-permission-flags ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS user-set
# adb shell pm clear-permission-flags ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS user-fixed

View File

@ -22,6 +22,7 @@ import android.os.Build
import android.widget.Toast import android.widget.Toast
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.utils.checkPermissions import im.vector.app.core.utils.checkPermissions
@ -46,7 +47,15 @@ class DebugPermissionActivity : VectorBaseActivity<ActivityDebugPermissionBindin
Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.READ_CONTACTS Manifest.permission.READ_CONTACTS
) ) + getAndroid13Permissions()
private fun getAndroid13Permissions(): List<String> {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
listOf(Manifest.permission.POST_NOTIFICATIONS)
} else {
emptyList()
}
}
private var lastPermissions = emptyList<String>() private var lastPermissions = emptyList<String>()
@ -77,6 +86,14 @@ class DebugPermissionActivity : VectorBaseActivity<ActivityDebugPermissionBindin
lastPermissions = listOf(Manifest.permission.READ_CONTACTS) lastPermissions = listOf(Manifest.permission.READ_CONTACTS)
checkPerm() checkPerm()
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
views.notification.setOnClickListener {
lastPermissions = listOf(Manifest.permission.POST_NOTIFICATIONS)
checkPerm()
}
} else {
views.notification.isVisible = false
}
} }
private fun checkPerm() { private fun checkPerm() {

View File

@ -30,43 +30,43 @@
android:id="@+id/camera" android:id="@+id/camera"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="CAMERA" android:text="CAMERA" />
android:textAllCaps="false" />
<Button <Button
android:id="@+id/audio" android:id="@+id/audio"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="RECORD_AUDIO" android:text="RECORD_AUDIO" />
android:textAllCaps="false" />
<Button <Button
android:id="@+id/camera_audio" android:id="@+id/camera_audio"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="CAMERA + RECORD_AUDIO" android:text="CAMERA + RECORD_AUDIO" />
android:textAllCaps="false" />
<Button <Button
android:id="@+id/write" android:id="@+id/write"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="WRITE_EXTERNAL_STORAGE" android:text="WRITE_EXTERNAL_STORAGE" />
android:textAllCaps="false" />
<Button <Button
android:id="@+id/read" android:id="@+id/read"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="READ_EXTERNAL_STORAGE" android:text="READ_EXTERNAL_STORAGE" />
android:textAllCaps="false" />
<Button <Button
android:id="@+id/contact" android:id="@+id/contact"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="READ_CONTACTS" android:text="READ_CONTACTS" />
android:textAllCaps="false" />
<Button
android:id="@+id/notification"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="POST_NOTIFICATIONS" />
</LinearLayout> </LinearLayout>

View File

@ -15,8 +15,6 @@
*/ */
package im.vector.app.fdroid.features.settings.troubleshoot package im.vector.app.fdroid.features.settings.troubleshoot
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
@ -32,7 +30,7 @@ class TestAutoStartBoot @Inject constructor(
) : ) :
TroubleshootTest(R.string.settings_troubleshoot_test_service_boot_title) { TroubleshootTest(R.string.settings_troubleshoot_test_service_boot_title) {
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) { override fun perform(testParameters: TestParameters) {
if (vectorPreferences.autoStartOnBoot()) { if (vectorPreferences.autoStartOnBoot()) {
description = stringProvider.getString(R.string.settings_troubleshoot_test_service_boot_success) description = stringProvider.getString(R.string.settings_troubleshoot_test_service_boot_success)
status = TestStatus.SUCCESS status = TestStatus.SUCCESS
@ -42,7 +40,7 @@ class TestAutoStartBoot @Inject constructor(
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_service_boot_quickfix) { quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_service_boot_quickfix) {
override fun doFix() { override fun doFix() {
vectorPreferences.setAutoStartOnBoot(true) vectorPreferences.setAutoStartOnBoot(true)
manager?.retry(activityResultLauncher) manager?.retry(testParameters)
} }
} }
status = TestStatus.FAILED status = TestStatus.FAILED

View File

@ -15,9 +15,7 @@
*/ */
package im.vector.app.fdroid.features.settings.troubleshoot package im.vector.app.fdroid.features.settings.troubleshoot
import android.content.Intent
import android.net.ConnectivityManager import android.net.ConnectivityManager
import androidx.activity.result.ActivityResultLauncher
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.core.net.ConnectivityManagerCompat import androidx.core.net.ConnectivityManagerCompat
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
@ -32,7 +30,7 @@ class TestBackgroundRestrictions @Inject constructor(
) : ) :
TroubleshootTest(R.string.settings_troubleshoot_test_bg_restricted_title) { TroubleshootTest(R.string.settings_troubleshoot_test_bg_restricted_title) {
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) { override fun perform(testParameters: TestParameters) {
context.getSystemService<ConnectivityManager>()!!.apply { context.getSystemService<ConnectivityManager>()!!.apply {
// Checks if the device is on a metered network // Checks if the device is on a metered network
if (isActiveNetworkMetered) { if (isActiveNetworkMetered) {

View File

@ -15,8 +15,6 @@
*/ */
package im.vector.app.fdroid.features.settings.troubleshoot package im.vector.app.fdroid.features.settings.troubleshoot
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
@ -30,7 +28,7 @@ class TestBatteryOptimization @Inject constructor(
private val stringProvider: StringProvider private val stringProvider: StringProvider
) : TroubleshootTest(R.string.settings_troubleshoot_test_battery_title) { ) : TroubleshootTest(R.string.settings_troubleshoot_test_battery_title) {
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) { override fun perform(testParameters: TestParameters) {
if (context.isIgnoringBatteryOptimizations()) { if (context.isIgnoringBatteryOptimizations()) {
description = stringProvider.getString(R.string.settings_troubleshoot_test_battery_success) description = stringProvider.getString(R.string.settings_troubleshoot_test_battery_success)
status = TestStatus.SUCCESS status = TestStatus.SUCCESS
@ -39,7 +37,7 @@ class TestBatteryOptimization @Inject constructor(
description = stringProvider.getString(R.string.settings_troubleshoot_test_battery_failed) description = stringProvider.getString(R.string.settings_troubleshoot_test_battery_failed)
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_battery_quickfix) { quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_battery_quickfix) {
override fun doFix() { override fun doFix() {
requestDisablingBatteryOptimization(context, activityResultLauncher) requestDisablingBatteryOptimization(context, testParameters.activityResultLauncher)
} }
} }
status = TestStatus.FAILED status = TestStatus.FAILED

View File

@ -15,8 +15,6 @@
*/ */
package im.vector.app.gplay.features.settings.troubleshoot package im.vector.app.gplay.features.settings.troubleshoot
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import com.google.firebase.messaging.FirebaseMessaging import com.google.firebase.messaging.FirebaseMessaging
import im.vector.app.R import im.vector.app.R
@ -36,7 +34,7 @@ class TestFirebaseToken @Inject constructor(
private val fcmHelper: FcmHelper, private val fcmHelper: FcmHelper,
) : TroubleshootTest(R.string.settings_troubleshoot_test_fcm_title) { ) : TroubleshootTest(R.string.settings_troubleshoot_test_fcm_title) {
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) { override fun perform(testParameters: TestParameters) {
status = TestStatus.RUNNING status = TestStatus.RUNNING
try { try {
FirebaseMessaging.getInstance().token FirebaseMessaging.getInstance().token
@ -53,7 +51,7 @@ class TestFirebaseToken @Inject constructor(
"ACCOUNT_MISSING" -> { "ACCOUNT_MISSING" -> {
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_fcm_failed_account_missing_quick_fix) { quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_fcm_failed_account_missing_quick_fix) {
override fun doFix() { override fun doFix() {
startAddGoogleAccountIntent(context, activityResultLauncher) startAddGoogleAccountIntent(context, testParameters.activityResultLauncher)
} }
} }
stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_account_missing, errorMsg) stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_account_missing, errorMsg)

View File

@ -15,8 +15,6 @@
*/ */
package im.vector.app.gplay.features.settings.troubleshoot package im.vector.app.gplay.features.settings.troubleshoot
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability import com.google.android.gms.common.GoogleApiAvailability
@ -35,7 +33,7 @@ class TestPlayServices @Inject constructor(
) : ) :
TroubleshootTest(R.string.settings_troubleshoot_test_play_services_title) { TroubleshootTest(R.string.settings_troubleshoot_test_play_services_title) {
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) { override fun perform(testParameters: TestParameters) {
val apiAvailability = GoogleApiAvailability.getInstance() val apiAvailability = GoogleApiAvailability.getInstance()
val resultCode = apiAvailability.isGooglePlayServicesAvailable(context) val resultCode = apiAvailability.isGooglePlayServicesAvailable(context)
if (resultCode == ConnectionResult.SUCCESS) { if (resultCode == ConnectionResult.SUCCESS) {

View File

@ -15,8 +15,6 @@
*/ */
package im.vector.app.gplay.features.settings.troubleshoot package im.vector.app.gplay.features.settings.troubleshoot
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.work.WorkInfo import androidx.work.WorkInfo
@ -42,7 +40,7 @@ class TestTokenRegistration @Inject constructor(
) : ) :
TroubleshootTest(R.string.settings_troubleshoot_test_token_registration_title) { TroubleshootTest(R.string.settings_troubleshoot_test_token_registration_title) {
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) { override fun perform(testParameters: TestParameters) {
// Check if we have a registered pusher for this token // Check if we have a registered pusher for this token
val fcmToken = fcmHelper.getFcmToken() ?: run { val fcmToken = fcmHelper.getFcmToken() ?: run {
status = TestStatus.FAILED status = TestStatus.FAILED
@ -66,9 +64,9 @@ class TestTokenRegistration @Inject constructor(
WorkManager.getInstance(context).getWorkInfoByIdLiveData(workId).observe(context, Observer { workInfo -> WorkManager.getInstance(context).getWorkInfoByIdLiveData(workId).observe(context, Observer { workInfo ->
if (workInfo != null) { if (workInfo != null) {
if (workInfo.state == WorkInfo.State.SUCCEEDED) { if (workInfo.state == WorkInfo.State.SUCCEEDED) {
manager?.retry(activityResultLauncher) manager?.retry(testParameters)
} else if (workInfo.state == WorkInfo.State.FAILED) { } else if (workInfo.state == WorkInfo.State.FAILED) {
manager?.retry(activityResultLauncher) manager?.retry(testParameters)
} }
} }
}) })

View File

@ -17,6 +17,9 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- https://developer.android.com/develop/ui/views/notifications/notification-permission#exemptions -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- Call feature --> <!-- Call feature -->
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" /> <uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
<!-- Commented because Google PlayStore does not like we add permission if we are not requiring it. And it was added for future use --> <!-- Commented because Google PlayStore does not like we add permission if we are not requiring it. And it was added for future use -->

View File

@ -19,19 +19,19 @@ package im.vector.app.core.animations
import android.animation.Animator import android.animation.Animator
open class SimpleAnimatorListener : Animator.AnimatorListener { open class SimpleAnimatorListener : Animator.AnimatorListener {
override fun onAnimationRepeat(animation: Animator?) { override fun onAnimationRepeat(animation: Animator) {
// No op // No op
} }
override fun onAnimationEnd(animation: Animator?) { override fun onAnimationEnd(animation: Animator) {
// No op // No op
} }
override fun onAnimationCancel(animation: Animator?) { override fun onAnimationCancel(animation: Animator) {
// No op // No op
} }
override fun onAnimationStart(animation: Animator?) { override fun onAnimationStart(animation: Animator) {
// No op // No op
} }
} }

View File

@ -21,6 +21,7 @@ import android.os.Parcelable
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import im.vector.app.core.platform.DefaultListUpdateCallback import im.vector.app.core.platform.DefaultListUpdateCallback
import im.vector.app.core.platform.Restorable import im.vector.app.core.platform.Restorable
import im.vector.lib.core.utils.compat.getParcelableCompat
import java.util.concurrent.atomic.AtomicReference import java.util.concurrent.atomic.AtomicReference
private const val LAYOUT_MANAGER_STATE = "LAYOUT_MANAGER_STATE" private const val LAYOUT_MANAGER_STATE = "LAYOUT_MANAGER_STATE"
@ -44,7 +45,7 @@ class LayoutManagerStateRestorer(layoutManager: RecyclerView.LayoutManager) : Re
} }
override fun onRestoreInstanceState(savedInstanceState: Bundle?) { override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
val parcelable = savedInstanceState?.getParcelable<Parcelable>(LAYOUT_MANAGER_STATE) val parcelable = savedInstanceState?.getParcelableCompat<Parcelable>(LAYOUT_MANAGER_STATE)
layoutManagerState.set(parcelable) layoutManagerState.set(parcelable)
} }

View File

@ -25,6 +25,7 @@ import com.airbnb.mvrx.MavericksViewModelProvider
inline fun <reified VM : MavericksViewModel<S>, reified S : MavericksState> ComponentActivity.lazyViewModel(): Lazy<VM> { inline fun <reified VM : MavericksViewModel<S>, reified S : MavericksState> ComponentActivity.lazyViewModel(): Lazy<VM> {
return lazy(mode = LazyThreadSafetyMode.NONE) { return lazy(mode = LazyThreadSafetyMode.NONE) {
@Suppress("DEPRECATION")
MavericksViewModelProvider.get( MavericksViewModelProvider.get(
viewModelClass = VM::class.java, viewModelClass = VM::class.java,
stateClass = S::class.java, stateClass = S::class.java,

View File

@ -22,6 +22,7 @@ import android.animation.ValueAnimator
import android.view.View import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator import android.view.animation.AccelerateDecelerateInterpolator
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import im.vector.app.core.animations.SimpleAnimatorListener
fun ViewPager2.setCurrentItem( fun ViewPager2.setCurrentItem(
item: Int, item: Int,
@ -45,19 +46,16 @@ fun ViewPager2.setCurrentItem(
previousValue = currentValue previousValue = currentValue
}.onFailure { animator.cancel() } }.onFailure { animator.cancel() }
} }
animator.addListener(object : Animator.AnimatorListener { animator.addListener(object : SimpleAnimatorListener() {
override fun onAnimationStart(animation: Animator?) { override fun onAnimationStart(animation: Animator) {
isUserInputEnabled = false isUserInputEnabled = false
beginFakeDrag() beginFakeDrag()
} }
override fun onAnimationEnd(animation: Animator?) { override fun onAnimationEnd(animation: Animator) {
isUserInputEnabled = true isUserInputEnabled = true
endFakeDrag() endFakeDrag()
} }
override fun onAnimationCancel(animation: Animator?) = Unit
override fun onAnimationRepeat(animation: Animator?) = Unit
}) })
animator.interpolator = interpolator animator.interpolator = interpolator
animator.duration = duration animator.duration = duration

View File

@ -83,6 +83,7 @@ abstract class SimpleFragmentActivity : VectorBaseActivity<ActivityBinding>() {
// ignore // ignore
return return
} }
@Suppress("DEPRECATION")
super.onBackPressed() super.onBackPressed()
} }
} }

View File

@ -505,6 +505,7 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
private fun onBackPressed(fromToolbar: Boolean) { private fun onBackPressed(fromToolbar: Boolean) {
val handled = recursivelyDispatchOnBackPressed(supportFragmentManager, fromToolbar) val handled = recursivelyDispatchOnBackPressed(supportFragmentManager, fromToolbar)
if (!handled) { if (!handled) {
@Suppress("DEPRECATION")
super.onBackPressed() super.onBackPressed()
} }
} }

View File

@ -19,6 +19,7 @@ package im.vector.app.core.resources
import android.content.Context import android.content.Context
import android.os.Build import android.os.Build
import androidx.annotation.NonNull import androidx.annotation.NonNull
import org.matrix.android.sdk.api.util.getPackageInfoCompat
import javax.inject.Inject import javax.inject.Inject
class VersionCodeProvider @Inject constructor(private val context: Context) { class VersionCodeProvider @Inject constructor(private val context: Context) {
@ -28,7 +29,7 @@ class VersionCodeProvider @Inject constructor(private val context: Context) {
*/ */
@NonNull @NonNull
fun getVersionCode(): Long { fun getVersionCode(): Long {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0) val packageInfo = context.packageManager.getPackageInfoCompat(context.packageName, 0)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
packageInfo.longVersionCode packageInfo.longVersionCode

View File

@ -23,6 +23,7 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
class BluetoothHeadsetReceiver : BroadcastReceiver() { class BluetoothHeadsetReceiver : BroadcastReceiver() {
@ -59,7 +60,7 @@ class BluetoothHeadsetReceiver : BroadcastReceiver() {
else -> return // ignore intermediate states else -> return // ignore intermediate states
} }
val device = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE) val device = intent.getParcelableExtraCompat<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
val deviceName = device?.name val deviceName = device?.name
when (device?.bluetoothClass?.deviceClass) { when (device?.bluetoothClass?.deviceClass) {
BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE, BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE,

View File

@ -39,6 +39,8 @@ import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.notifications.NotificationUtils
import im.vector.app.features.popup.IncomingCallAlert import im.vector.app.features.popup.IncomingCallAlert
import im.vector.app.features.popup.PopupAlertManager import im.vector.app.features.popup.PopupAlertManager
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
import im.vector.lib.core.utils.compat.getSerializableExtraCompat
import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.logger.LoggerTag
import org.matrix.android.sdk.api.session.room.model.call.EndCallReason import org.matrix.android.sdk.api.session.room.model.call.EndCallReason
import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.MatrixItem
@ -71,7 +73,7 @@ class CallAndroidService : VectorAndroidService() {
private var mediaSession: MediaSessionCompat? = null private var mediaSession: MediaSessionCompat? = null
private val mediaSessionButtonCallback = object : MediaSessionCompat.Callback() { private val mediaSessionButtonCallback = object : MediaSessionCompat.Callback() {
override fun onMediaButtonEvent(mediaButtonEvent: Intent?): Boolean { override fun onMediaButtonEvent(mediaButtonEvent: Intent?): Boolean {
val keyEvent = mediaButtonEvent?.getParcelableExtra<KeyEvent>(Intent.EXTRA_KEY_EVENT) ?: return false val keyEvent = mediaButtonEvent?.getParcelableExtraCompat<KeyEvent>(Intent.EXTRA_KEY_EVENT) ?: return false
if (keyEvent.keyCode == KeyEvent.KEYCODE_HEADSETHOOK) { if (keyEvent.keyCode == KeyEvent.KEYCODE_HEADSETHOOK) {
callManager.headSetButtonTapped() callManager.headSetButtonTapped()
return true return true
@ -158,7 +160,7 @@ class CallAndroidService : VectorAndroidService() {
val incomingCallAlert = IncomingCallAlert(callId, val incomingCallAlert = IncomingCallAlert(callId,
shouldBeDisplayedIn = { activity -> shouldBeDisplayedIn = { activity ->
if (activity is VectorCallActivity) { if (activity is VectorCallActivity) {
activity.intent.getParcelableExtra<CallArgs>(Mavericks.KEY_ARG)?.callId != call.callId activity.intent.getParcelableExtraCompat<CallArgs>(Mavericks.KEY_ARG)?.callId != call.callId
} else true } else true
} }
).apply { ).apply {
@ -188,7 +190,7 @@ class CallAndroidService : VectorAndroidService() {
private fun handleCallTerminated(intent: Intent) { private fun handleCallTerminated(intent: Intent) {
val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: "" val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: ""
val endCallReason = intent.getSerializableExtra(EXTRA_END_CALL_REASON) as EndCallReason val endCallReason = intent.getSerializableExtraCompat<EndCallReason>(EXTRA_END_CALL_REASON)
val rejected = intent.getBooleanExtra(EXTRA_END_CALL_REJECTED, false) val rejected = intent.getBooleanExtra(EXTRA_END_CALL_REJECTED, false)
alertManager.cancelAlert(callId) alertManager.cancelAlert(callId)
val terminatedCall = knownCalls.remove(callId) val terminatedCall = knownCalls.remove(callId)
@ -202,7 +204,7 @@ class CallAndroidService : VectorAndroidService() {
startForeground(notificationId, notification) startForeground(notificationId, notification)
if (knownCalls.isEmpty()) { if (knownCalls.isEmpty()) {
Timber.tag(loggerTag.value).v("No more call, stop the service") Timber.tag(loggerTag.value).v("No more call, stop the service")
stopForeground(true) stopForegroundCompat()
mediaSession?.isActive = false mediaSession?.isActive = false
myStopSelf() myStopSelf()
} }

View File

@ -18,6 +18,7 @@ package im.vector.app.core.services
import android.app.Service import android.app.Service
import android.content.Intent import android.content.Intent
import android.os.Build
import android.os.IBinder import android.os.IBinder
import timber.log.Timber import timber.log.Timber
@ -55,4 +56,13 @@ abstract class VectorAndroidService : Service() {
override fun onBind(intent: Intent?): IBinder? { override fun onBind(intent: Intent?): IBinder? {
return null return null
} }
protected fun stopForegroundCompat() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
stopForeground(STOP_FOREGROUND_REMOVE)
} else {
@Suppress("DEPRECATION")
stopForeground(true)
}
}
} }

View File

@ -34,6 +34,7 @@ import androidx.core.content.getSystemService
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import im.vector.app.R import im.vector.app.R
import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.notifications.NotificationUtils
import org.matrix.android.sdk.api.util.getApplicationInfoCompat
/** /**
* Tells if the application ignores battery optimizations. * Tells if the application ignores battery optimizations.
@ -63,7 +64,7 @@ fun Context.isAnimationEnabled(): Boolean {
*/ */
fun Context.getApplicationLabel(packageName: String): String { fun Context.getApplicationLabel(packageName: String): String {
return try { return try {
val ai = packageManager.getApplicationInfo(packageName, 0) val ai = packageManager.getApplicationInfoCompat(packageName, 0)
packageManager.getApplicationLabel(ai).toString() packageManager.getApplicationLabel(ai).toString()
} catch (e: PackageManager.NameNotFoundException) { } catch (e: PackageManager.NameNotFoundException) {
packageName packageName

View File

@ -57,6 +57,7 @@ import im.vector.app.features.start.StartAppViewModel
import im.vector.app.features.start.StartAppViewState import im.vector.app.features.start.StartAppViewState
import im.vector.app.features.themes.ActivityOtherThemes import im.vector.app.features.themes.ActivityOtherThemes
import im.vector.app.features.ui.UiStateRepository import im.vector.app.features.ui.UiStateRepository
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@ -181,7 +182,7 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
private fun handleAppStarted() { private fun handleAppStarted() {
if (intent.hasExtra(EXTRA_NEXT_INTENT)) { if (intent.hasExtra(EXTRA_NEXT_INTENT)) {
// Start the next Activity // Start the next Activity
val nextIntent = intent.getParcelableExtra<Intent>(EXTRA_NEXT_INTENT) val nextIntent = intent.getParcelableExtraCompat<Intent>(EXTRA_NEXT_INTENT)
startIntentAndFinish(nextIntent) startIntentAndFinish(nextIntent)
} else if (intent.hasExtra(EXTRA_INIT_SESSION)) { } else if (intent.hasExtra(EXTRA_INIT_SESSION)) {
setResult(RESULT_OK) setResult(RESULT_OK)
@ -218,7 +219,7 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
} }
private fun parseArgs(): MainActivityArgs { private fun parseArgs(): MainActivityArgs {
val argsFromIntent: MainActivityArgs? = intent.getParcelableExtra(EXTRA_ARGS) val argsFromIntent: MainActivityArgs? = intent.getParcelableExtraCompat(EXTRA_ARGS)
Timber.w("Starting MainActivity with $argsFromIntent") Timber.w("Starting MainActivity with $argsFromIntent")
return MainActivityArgs( return MainActivityArgs(

View File

@ -26,6 +26,8 @@ import im.vector.app.core.dialogs.PhotoOrVideoDialog
import im.vector.app.core.platform.Restorable import im.vector.app.core.platform.Restorable
import im.vector.app.core.resources.BuildMeta import im.vector.app.core.resources.BuildMeta
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import im.vector.lib.core.utils.compat.getParcelableCompat
import im.vector.lib.core.utils.compat.getSerializableCompat
import im.vector.lib.multipicker.MultiPicker import im.vector.lib.multipicker.MultiPicker
import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.content.ContentAttachmentData
import timber.log.Timber import timber.log.Timber
@ -66,8 +68,8 @@ class AttachmentsHelper(
} }
override fun onRestoreInstanceState(savedInstanceState: Bundle?) { override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
captureUri = savedInstanceState?.getParcelable(CAPTURE_PATH_KEY) as? Uri captureUri = savedInstanceState?.getParcelableCompat(CAPTURE_PATH_KEY)
pendingType = savedInstanceState?.getSerializable(PENDING_TYPE_KEY) as? AttachmentTypeSelectorView.Type pendingType = savedInstanceState?.getSerializableCompat(PENDING_TYPE_KEY)
} }
// Public Methods // Public Methods

View File

@ -24,6 +24,8 @@ import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivitySimpleBinding import im.vector.app.databinding.ActivitySimpleBinding
import im.vector.app.features.themes.ActivityOtherThemes import im.vector.app.features.themes.ActivityOtherThemes
import im.vector.lib.core.utils.compat.getParcelableArrayListExtraCompat
import im.vector.lib.core.utils.compat.getParcelableCompat
import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.content.ContentAttachmentData
@AndroidEntryPoint @AndroidEntryPoint
@ -41,7 +43,7 @@ class AttachmentsPreviewActivity : VectorBaseActivity<ActivitySimpleBinding>() {
} }
fun getOutput(intent: Intent): List<ContentAttachmentData> { fun getOutput(intent: Intent): List<ContentAttachmentData> {
return intent.getParcelableArrayListExtra<ContentAttachmentData>(ATTACHMENTS_PREVIEW_RESULT).orEmpty() return intent.getParcelableArrayListExtraCompat<ContentAttachmentData>(ATTACHMENTS_PREVIEW_RESULT).orEmpty()
} }
fun getKeepOriginalSize(intent: Intent): Boolean { fun getKeepOriginalSize(intent: Intent): Boolean {
@ -57,7 +59,7 @@ class AttachmentsPreviewActivity : VectorBaseActivity<ActivitySimpleBinding>() {
override fun initUiAndData() { override fun initUiAndData() {
if (isFirstCreation()) { if (isFirstCreation()) {
val fragmentArgs: AttachmentsPreviewArgs = intent?.extras?.getParcelable(EXTRA_FRAGMENT_ARGS) ?: return val fragmentArgs: AttachmentsPreviewArgs = intent?.extras?.getParcelableCompat(EXTRA_FRAGMENT_ARGS) ?: return
addFragment(views.simpleFragmentContainer, AttachmentsPreviewFragment::class.java, fragmentArgs) addFragment(views.simpleFragmentContainer, AttachmentsPreviewFragment::class.java, fragmentArgs)
} }
} }

View File

@ -69,6 +69,7 @@ import im.vector.app.features.displayname.getBestName
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.home.room.detail.RoomDetailActivity
import im.vector.app.features.home.room.detail.arguments.TimelineArgs import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
import io.github.hyuwah.draggableviewlib.DraggableView import io.github.hyuwah.draggableviewlib.DraggableView
import io.github.hyuwah.draggableviewlib.setupDraggable import io.github.hyuwah.draggableviewlib.setupDraggable
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@ -178,7 +179,7 @@ class VectorCallActivity :
override fun onNewIntent(intent: Intent?) { override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent) super.onNewIntent(intent)
intent?.takeIf { it.hasExtra(Mavericks.KEY_ARG) } intent?.takeIf { it.hasExtra(Mavericks.KEY_ARG) }
?.let { intent.getParcelableExtra<CallArgs>(Mavericks.KEY_ARG) } ?.let { intent.getParcelableExtraCompat<CallArgs>(Mavericks.KEY_ARG) }
?.let { ?.let {
callViewModel.handle(VectorCallViewActions.SwitchCall(it)) callViewModel.handle(VectorCallViewActions.SwitchCall(it))
} }
@ -193,6 +194,7 @@ class VectorCallActivity :
override fun onBackPressed() { override fun onBackPressed() {
if (!enterPictureInPictureIfRequired()) { if (!enterPictureInPictureIfRequired()) {
@Suppress("DEPRECATION")
super.onBackPressed() super.onBackPressed()
} }
} }
@ -230,6 +232,7 @@ class VectorCallActivity :
} }
android.R.id.home -> { android.R.id.home -> {
// We check here as we want PiP in some cases // We check here as we want PiP in some cases
@Suppress("DEPRECATION")
onBackPressed() onBackPressed()
true true
} }

View File

@ -38,6 +38,7 @@ import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivityJitsiBinding import im.vector.app.databinding.ActivityJitsiBinding
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import org.jitsi.meet.sdk.JitsiMeet import org.jitsi.meet.sdk.JitsiMeet
import org.jitsi.meet.sdk.JitsiMeetActivityDelegate import org.jitsi.meet.sdk.JitsiMeetActivityDelegate
@ -200,7 +201,7 @@ class VectorJitsiActivity : VectorBaseActivity<ActivityJitsiBinding>(), JitsiMee
// Is it a switch to another conf? // Is it a switch to another conf?
intent?.takeIf { it.hasExtra(Mavericks.KEY_ARG) } intent?.takeIf { it.hasExtra(Mavericks.KEY_ARG) }
?.let { intent.getParcelableExtra<Args>(Mavericks.KEY_ARG) } ?.let { intent.getParcelableExtraCompat<Args>(Mavericks.KEY_ARG) }
?.let { ?.let {
jitsiViewModel.handle(JitsiCallViewActions.SwitchTo(it, true)) jitsiViewModel.handle(JitsiCallViewActions.SwitchTo(it, true))
} }

View File

@ -28,6 +28,7 @@ import im.vector.app.R
import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivityCallTransferBinding import im.vector.app.databinding.ActivityCallTransferBinding
import im.vector.lib.core.utils.compat.getParcelableCompat
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import javax.inject.Inject import javax.inject.Inject
@ -112,7 +113,7 @@ class CallTransferActivity : VectorBaseActivity<ActivityCallTransferBinding>() {
} }
fun getCallTransferResult(intent: Intent?): CallTransferResult? { fun getCallTransferResult(intent: Intent?): CallTransferResult? {
return intent?.extras?.getParcelable(EXTRA_TRANSFER_RESULT) return intent?.extras?.getParcelableCompat(EXTRA_TRANSFER_RESULT)
} }
} }
} }

View File

@ -78,6 +78,7 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() {
sharedActionViewModel sharedActionViewModel
.stream() .stream()
.onEach { action -> .onEach { action ->
@Suppress("DEPRECATION")
when (action) { when (action) {
UserListSharedAction.Close -> finish() UserListSharedAction.Close -> finish()
UserListSharedAction.GoBack -> onBackPressed() UserListSharedAction.GoBack -> onBackPressed()

View File

@ -52,6 +52,7 @@ class KeysBackupRestoreActivity : SimpleFragmentActivity() {
override fun onBackPressed() { override fun onBackPressed() {
hideWaitingView() hideWaitingView()
@Suppress("DEPRECATION")
super.onBackPressed() super.onBackPressed()
} }

View File

@ -114,6 +114,7 @@ class KeysBackupManageActivity : SimpleFragmentActivity() {
finish() finish()
return return
} }
@Suppress("DEPRECATION")
super.onBackPressed() super.onBackPressed()
} }
} }

View File

@ -183,6 +183,7 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() {
} }
.show() .show()
} else { } else {
@Suppress("DEPRECATION")
super.onBackPressed() super.onBackPressed()
} }
} }

View File

@ -26,6 +26,7 @@ import im.vector.app.features.home.room.detail.RoomDetailActivity
import im.vector.app.features.home.room.detail.arguments.TimelineArgs import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import im.vector.app.features.popup.PopupAlertManager import im.vector.app.features.popup.PopupAlertManager
import im.vector.app.features.popup.VerificationVectorAlert import im.vector.app.features.popup.VerificationVectorAlert
import im.vector.lib.core.utils.compat.getParcelableCompat
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
@ -147,7 +148,7 @@ class IncomingVerificationRequestHandler @Inject constructor(
R.drawable.ic_shield_black, R.drawable.ic_shield_black,
shouldBeDisplayedIn = { activity -> shouldBeDisplayedIn = { activity ->
if (activity is RoomDetailActivity) { if (activity is RoomDetailActivity) {
activity.intent?.extras?.getParcelable<TimelineArgs>(RoomDetailActivity.EXTRA_ROOM_DETAIL_ARGS)?.let { activity.intent?.extras?.getParcelableCompat<TimelineArgs>(RoomDetailActivity.EXTRA_ROOM_DETAIL_ARGS)?.let {
it.roomId != pr.roomId it.roomId != pr.roomId
} ?: true } ?: true
} else true } else true

View File

@ -27,6 +27,7 @@ import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding
import im.vector.lib.core.utils.compat.getParcelableCompat
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import javax.inject.Inject import javax.inject.Inject
@ -49,7 +50,7 @@ class VerificationQRWaitingFragment :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setupRecyclerView() setupRecyclerView()
(arguments?.getParcelable(Mavericks.KEY_ARG) as? Args)?.let { (arguments?.getParcelableCompat<Args>(Mavericks.KEY_ARG))?.let {
controller.update(it) controller.update(it)
} }
} }

View File

@ -48,6 +48,7 @@ import im.vector.app.core.platform.VectorMenuProvider
import im.vector.app.core.pushers.FcmHelper import im.vector.app.core.pushers.FcmHelper
import im.vector.app.core.pushers.PushersManager import im.vector.app.core.pushers.PushersManager
import im.vector.app.core.pushers.UnifiedPushHelper import im.vector.app.core.pushers.UnifiedPushHelper
import im.vector.app.core.utils.registerForPermissionsResult
import im.vector.app.core.utils.startSharePlainTextIntent import im.vector.app.core.utils.startSharePlainTextIntent
import im.vector.app.databinding.ActivityHomeBinding import im.vector.app.databinding.ActivityHomeBinding
import im.vector.app.features.MainActivity import im.vector.app.features.MainActivity
@ -86,6 +87,7 @@ import im.vector.app.features.spaces.share.ShareSpaceBottomSheet
import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.themes.ThemeUtils
import im.vector.app.features.usercode.UserCodeActivity import im.vector.app.features.usercode.UserCodeActivity
import im.vector.app.features.workers.signout.ServerBackupStatusViewModel import im.vector.app.features.workers.signout.ServerBackupStatusViewModel
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -142,6 +144,7 @@ class HomeActivity :
@Inject lateinit var fcmHelper: FcmHelper @Inject lateinit var fcmHelper: FcmHelper
@Inject lateinit var nightlyProxy: NightlyProxy @Inject lateinit var nightlyProxy: NightlyProxy
@Inject lateinit var disclaimerDialog: DisclaimerDialog @Inject lateinit var disclaimerDialog: DisclaimerDialog
@Inject lateinit var notificationPermissionManager: NotificationPermissionManager
private var isNewAppLayoutEnabled: Boolean = false // delete once old app layout is removed private var isNewAppLayoutEnabled: Boolean = false // delete once old app layout is removed
@ -171,6 +174,10 @@ class HomeActivity :
} }
} }
private val postPermissionLauncher = registerForPermissionsResult { _, _ ->
// Nothing to do with the result.
}
private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() { private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentResumed(fm: FragmentManager, f: Fragment) { override fun onFragmentResumed(fm: FragmentManager, f: Fragment) {
if (f is MatrixToBottomSheet) { if (f is MatrixToBottomSheet) {
@ -246,7 +253,7 @@ class HomeActivity :
} }
.launchIn(lifecycleScope) .launchIn(lifecycleScope)
val args = intent.getParcelableExtra<HomeActivityArgs>(Mavericks.KEY_ARG) val args = intent.getParcelableExtraCompat<HomeActivityArgs>(Mavericks.KEY_ARG)
if (args?.clearNotification == true) { if (args?.clearNotification == true) {
notificationDrawerManager.clearAllEvents() notificationDrawerManager.clearAllEvents()
@ -273,6 +280,7 @@ class HomeActivity :
} }
is HomeActivityViewEvents.OnCrossSignedInvalidated -> handleCrossSigningInvalidated(it) is HomeActivityViewEvents.OnCrossSignedInvalidated -> handleCrossSigningInvalidated(it)
HomeActivityViewEvents.ShowAnalyticsOptIn -> handleShowAnalyticsOptIn() HomeActivityViewEvents.ShowAnalyticsOptIn -> handleShowAnalyticsOptIn()
HomeActivityViewEvents.ShowNotificationDialog -> handleShowNotificationDialog()
HomeActivityViewEvents.ShowReleaseNotes -> handleShowReleaseNotes() HomeActivityViewEvents.ShowReleaseNotes -> handleShowReleaseNotes()
HomeActivityViewEvents.NotifyUserForThreadsMigration -> handleNotifyUserForThreadsMigration() HomeActivityViewEvents.NotifyUserForThreadsMigration -> handleNotifyUserForThreadsMigration()
is HomeActivityViewEvents.MigrateThreads -> migrateThreadsIfNeeded(it.checkSession) is HomeActivityViewEvents.MigrateThreads -> migrateThreadsIfNeeded(it.checkSession)
@ -288,6 +296,10 @@ class HomeActivity :
homeActivityViewModel.handle(HomeActivityViewActions.ViewStarted) homeActivityViewModel.handle(HomeActivityViewActions.ViewStarted)
} }
private fun handleShowNotificationDialog() {
notificationPermissionManager.eventuallyRequestPermission(this, postPermissionLauncher)
}
private fun handleShowReleaseNotes() { private fun handleShowReleaseNotes() {
startActivity(Intent(this, ReleaseNotesActivity::class.java)) startActivity(Intent(this, ReleaseNotesActivity::class.java))
} }
@ -328,7 +340,7 @@ class HomeActivity :
private fun migrateThreadsIfNeeded(checkSession: Boolean) { private fun migrateThreadsIfNeeded(checkSession: Boolean) {
if (checkSession) { if (checkSession) {
// We should check session to ensure we will only clear cache if needed // We should check session to ensure we will only clear cache if needed
val args = intent.getParcelableExtra<HomeActivityArgs>(Mavericks.KEY_ARG) val args = intent.getParcelableExtraCompat<HomeActivityArgs>(Mavericks.KEY_ARG)
if (args?.hasExistingSession == true) { if (args?.hasExistingSession == true) {
// existingSession --> Will be true only if we came from an existing active session // existingSession --> Will be true only if we came from an existing active session
Timber.i("----> Migrating threads from an existing session..") Timber.i("----> Migrating threads from an existing session..")
@ -547,7 +559,7 @@ class HomeActivity :
override fun onNewIntent(intent: Intent?) { override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent) super.onNewIntent(intent)
val parcelableExtra = intent?.getParcelableExtra<HomeActivityArgs>(Mavericks.KEY_ARG) val parcelableExtra = intent?.getParcelableExtraCompat<HomeActivityArgs>(Mavericks.KEY_ARG)
if (parcelableExtra?.clearNotification == true) { if (parcelableExtra?.clearNotification == true) {
notificationDrawerManager.clearAllEvents() notificationDrawerManager.clearAllEvents()
} }
@ -676,7 +688,10 @@ class HomeActivity :
if (views.drawerLayout.isDrawerOpen(GravityCompat.START)) { if (views.drawerLayout.isDrawerOpen(GravityCompat.START)) {
views.drawerLayout.closeDrawer(GravityCompat.START) views.drawerLayout.closeDrawer(GravityCompat.START)
} else { } else {
validateBackPressed { super.onBackPressed() } validateBackPressed {
@Suppress("DEPRECATION")
super.onBackPressed()
}
} }
} }

View File

@ -31,6 +31,7 @@ sealed interface HomeActivityViewEvents : VectorViewEvents {
data class OnCrossSignedInvalidated(val userItem: MatrixItem.UserItem) : HomeActivityViewEvents data class OnCrossSignedInvalidated(val userItem: MatrixItem.UserItem) : HomeActivityViewEvents
object PromptToEnableSessionPush : HomeActivityViewEvents object PromptToEnableSessionPush : HomeActivityViewEvents
object ShowAnalyticsOptIn : HomeActivityViewEvents object ShowAnalyticsOptIn : HomeActivityViewEvents
object ShowNotificationDialog : HomeActivityViewEvents
object ShowReleaseNotes : HomeActivityViewEvents object ShowReleaseNotes : HomeActivityViewEvents
object NotifyUserForThreadsMigration : HomeActivityViewEvents object NotifyUserForThreadsMigration : HomeActivityViewEvents
data class MigrateThreads(val checkSession: Boolean) : HomeActivityViewEvents data class MigrateThreads(val checkSession: Boolean) : HomeActivityViewEvents

View File

@ -42,6 +42,7 @@ import im.vector.app.features.raw.wellknown.isSecureBackupRequired
import im.vector.app.features.raw.wellknown.withElementWellKnown import im.vector.app.features.raw.wellknown.withElementWellKnown
import im.vector.app.features.session.coroutineScope import im.vector.app.features.session.coroutineScope
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
@ -101,7 +102,7 @@ class HomeActivityViewModel @AssistedInject constructor(
companion object : MavericksViewModelFactory<HomeActivityViewModel, HomeActivityViewState> by hiltMavericksViewModelFactory() { companion object : MavericksViewModelFactory<HomeActivityViewModel, HomeActivityViewState> by hiltMavericksViewModelFactory() {
override fun initialState(viewModelContext: ViewModelContext): HomeActivityViewState? { override fun initialState(viewModelContext: ViewModelContext): HomeActivityViewState? {
val activity: HomeActivity = viewModelContext.activity() val activity: HomeActivity = viewModelContext.activity()
val args: HomeActivityArgs? = activity.intent.getParcelableExtra(Mavericks.KEY_ARG) val args: HomeActivityArgs? = activity.intent.getParcelableExtraCompat(Mavericks.KEY_ARG)
return args?.let { HomeActivityViewState(authenticationDescription = it.authenticationDescription) } return args?.let { HomeActivityViewState(authenticationDescription = it.authenticationDescription) }
?: super.initialState(viewModelContext) ?: super.initialState(viewModelContext)
} }
@ -160,6 +161,8 @@ class HomeActivityViewModel @AssistedInject constructor(
.onEach { didAskUser -> .onEach { didAskUser ->
if (!didAskUser) { if (!didAskUser) {
_viewEvents.post(HomeActivityViewEvents.ShowAnalyticsOptIn) _viewEvents.post(HomeActivityViewEvents.ShowAnalyticsOptIn)
} else {
_viewEvents.post(HomeActivityViewEvents.ShowNotificationDialog)
} }
} }
.launchIn(viewModelScope) .launchIn(viewModelScope)
@ -179,6 +182,8 @@ class HomeActivityViewModel @AssistedInject constructor(
// do nothing // do nothing
} }
} }
} else {
_viewEvents.post(HomeActivityViewEvents.ShowNotificationDialog)
} }
} }

View File

@ -0,0 +1,80 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.home
import android.Manifest
import android.app.Activity
import android.content.pm.PackageManager
import android.os.Build
import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.ChecksSdkIntAtLeast
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import im.vector.app.R
import im.vector.app.core.utils.checkPermissions
import im.vector.app.features.settings.VectorPreferences
import org.matrix.android.sdk.api.util.BuildVersionSdkIntProvider
import javax.inject.Inject
class NotificationPermissionManager @Inject constructor(
private val sdkIntProvider: BuildVersionSdkIntProvider,
private val vectorPreferences: VectorPreferences,
) {
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.TIRAMISU)
fun isPermissionGranted(activity: Activity): Boolean {
return if (sdkIntProvider.isAtLeast(Build.VERSION_CODES.TIRAMISU)) {
ContextCompat.checkSelfPermission(
activity,
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
} else {
// No notification permission management before API 33.
true
}
}
fun eventuallyRequestPermission(
activity: Activity,
requestPermissionLauncher: ActivityResultLauncher<Array<String>>,
showRationale: Boolean = true,
ignorePreference: Boolean = false,
) {
if (!sdkIntProvider.isAtLeast(Build.VERSION_CODES.TIRAMISU)) return
if (!vectorPreferences.areNotificationEnabledForDevice() && !ignorePreference) return
checkPermissions(
listOf(Manifest.permission.POST_NOTIFICATIONS),
activity,
activityResultLauncher = requestPermissionLauncher,
if (showRationale) R.string.permissions_rationale_msg_notification else 0
)
}
@RequiresApi(33)
fun askPermission(requestPermissionLauncher: ActivityResultLauncher<Array<String>>) {
requestPermissionLauncher.launch(
arrayOf(Manifest.permission.POST_NOTIFICATIONS)
)
}
fun eventuallyRevokePermission(
activity: Activity,
) {
if (!sdkIntProvider.isAtLeast(Build.VERSION_CODES.TIRAMISU)) return
activity.revokeSelfPermissionOnKill(Manifest.permission.POST_NOTIFICATIONS)
}
}

View File

@ -46,6 +46,7 @@ import im.vector.app.features.navigation.Navigator
import im.vector.app.features.room.RequireActiveMembershipAction import im.vector.app.features.room.RequireActiveMembershipAction
import im.vector.app.features.room.RequireActiveMembershipViewEvents import im.vector.app.features.room.RequireActiveMembershipViewEvents
import im.vector.app.features.room.RequireActiveMembershipViewModel import im.vector.app.features.room.RequireActiveMembershipViewModel
import im.vector.lib.core.utils.compat.getParcelableCompat
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import javax.inject.Inject import javax.inject.Inject
@ -99,7 +100,7 @@ class RoomDetailActivity :
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, false) supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, false)
waitingView = views.waitingView.waitingView waitingView = views.waitingView.waitingView
val timelineArgs: TimelineArgs = intent?.extras?.getParcelable(EXTRA_ROOM_DETAIL_ARGS) ?: return val timelineArgs: TimelineArgs = intent?.extras?.getParcelableCompat(EXTRA_ROOM_DETAIL_ARGS) ?: return
intent.putExtra(Mavericks.KEY_ARG, timelineArgs) intent.putExtra(Mavericks.KEY_ARG, timelineArgs)
currentRoomId = timelineArgs.roomId currentRoomId = timelineArgs.roomId
@ -177,6 +178,7 @@ class RoomDetailActivity :
if (views.drawerLayout.isDrawerOpen(GravityCompat.START)) { if (views.drawerLayout.isDrawerOpen(GravityCompat.START)) {
views.drawerLayout.closeDrawer(GravityCompat.START) views.drawerLayout.closeDrawer(GravityCompat.START)
} else { } else {
@Suppress("DEPRECATION")
super.onBackPressed() super.onBackPressed()
} }
} }

View File

@ -772,7 +772,7 @@ class TimelineFragment :
} }
// We use a custom layout for this menu item, so we need to set a ClickListener // We use a custom layout for this menu item, so we need to set a ClickListener
menu.findItem(R.id.open_matrix_apps)?.let { menuItem -> menu.findItem(R.id.open_matrix_apps)?.let { menuItem ->
menuItem.actionView.debouncedClicks { menuItem.actionView?.debouncedClicks {
handleMenuItemSelected(menuItem) handleMenuItemSelected(menuItem)
} }
} }
@ -783,7 +783,7 @@ class TimelineFragment :
// Custom thread notification menu item // Custom thread notification menu item
menu.findItem(R.id.menu_timeline_thread_list)?.let { menuItem -> menu.findItem(R.id.menu_timeline_thread_list)?.let { menuItem ->
menuItem.actionView.debouncedClicks { menuItem.actionView?.debouncedClicks {
handleMenuItemSelected(menuItem) handleMenuItemSelected(menuItem)
} }
} }
@ -812,16 +812,16 @@ class TimelineFragment :
// icon should be default color no badge // icon should be default color no badge
val actionView = matrixAppsMenuItem.actionView val actionView = matrixAppsMenuItem.actionView
actionView actionView
.findViewById<ImageView>(R.id.action_view_icon_image) ?.findViewById<ImageView>(R.id.action_view_icon_image)
.setColorFilter(ThemeUtils.getColor(requireContext(), R.attr.vctr_content_secondary)) ?.setColorFilter(ThemeUtils.getColor(requireContext(), R.attr.vctr_content_secondary))
actionView.findViewById<TextView>(R.id.cart_badge).isVisible = false actionView?.findViewById<TextView>(R.id.cart_badge)?.isVisible = false
matrixAppsMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER) matrixAppsMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
} else { } else {
val actionView = matrixAppsMenuItem.actionView val actionView = matrixAppsMenuItem.actionView
actionView actionView
.findViewById<ImageView>(R.id.action_view_icon_image) ?.findViewById<ImageView>(R.id.action_view_icon_image)
.setColorFilter(colorProvider.getColorFromAttribute(R.attr.colorPrimary)) ?.setColorFilter(colorProvider.getColorFromAttribute(R.attr.colorPrimary))
actionView.findViewById<TextView>(R.id.cart_badge).setTextOrHide("$widgetsCount") actionView?.findViewById<TextView>(R.id.cart_badge)?.setTextOrHide("$widgetsCount")
@Suppress("AlwaysShowAction") @Suppress("AlwaysShowAction")
matrixAppsMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS) matrixAppsMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
} }
@ -893,7 +893,7 @@ class TimelineFragment :
*/ */
private fun updateMenuThreadNotificationBadge(menu: Menu, state: RoomDetailViewState) { private fun updateMenuThreadNotificationBadge(menu: Menu, state: RoomDetailViewState) {
val menuThreadList = menu.findItem(R.id.menu_timeline_thread_list).actionView val menuThreadList = menu.findItem(R.id.menu_timeline_thread_list).actionView
val badgeFrameLayout = menuThreadList.findViewById<FrameLayout>(R.id.threadNotificationBadgeFrameLayout) val badgeFrameLayout = menuThreadList?.findViewById<FrameLayout>(R.id.threadNotificationBadgeFrameLayout) ?: return
val badgeTextView = menuThreadList.findViewById<TextView>(R.id.threadNotificationBadgeTextView) val badgeTextView = menuThreadList.findViewById<TextView>(R.id.threadNotificationBadgeTextView)
val unreadThreadMessages = state.threadNotificationBadgeState.numberOfLocalUnreadThreads val unreadThreadMessages = state.threadNotificationBadgeState.numberOfLocalUnreadThreads

View File

@ -25,6 +25,7 @@ import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivitySearchBinding import im.vector.app.databinding.ActivitySearchBinding
import im.vector.lib.core.utils.compat.getParcelableCompat
@AndroidEntryPoint @AndroidEntryPoint
class SearchActivity : VectorBaseActivity<ActivitySearchBinding>() { class SearchActivity : VectorBaseActivity<ActivitySearchBinding>() {
@ -46,7 +47,7 @@ class SearchActivity : VectorBaseActivity<ActivitySearchBinding>() {
override fun initUiAndData() { override fun initUiAndData() {
if (isFirstCreation()) { if (isFirstCreation()) {
val fragmentArgs: SearchArgs = intent?.extras?.getParcelable(Mavericks.KEY_ARG) ?: return val fragmentArgs: SearchArgs = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG) ?: return
addFragment(views.searchFragmentContainer, SearchFragment::class.java, fragmentArgs, FRAGMENT_TAG) addFragment(views.searchFragmentContainer, SearchFragment::class.java, fragmentArgs, FRAGMENT_TAG)
} }
views.searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { views.searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {

View File

@ -34,6 +34,7 @@ import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import im.vector.app.features.home.room.threads.arguments.ThreadListArgs import im.vector.app.features.home.room.threads.arguments.ThreadListArgs
import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
import im.vector.app.features.home.room.threads.list.views.ThreadListFragment import im.vector.app.features.home.room.threads.list.views.ThreadListFragment
import im.vector.lib.core.utils.compat.getParcelableCompat
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
@ -129,8 +130,8 @@ class ThreadsActivity : VectorBaseActivity<ActivityThreadsBinding>() {
return DisplayFragment.ErrorFragment return DisplayFragment.ErrorFragment
} }
private fun getThreadTimelineArgs(): ThreadTimelineArgs? = intent?.extras?.getParcelable(THREAD_TIMELINE_ARGS) private fun getThreadTimelineArgs(): ThreadTimelineArgs? = intent?.extras?.getParcelableCompat(THREAD_TIMELINE_ARGS)
private fun getThreadListArgs(): ThreadListArgs? = intent?.extras?.getParcelable(THREAD_LIST_ARGS) private fun getThreadListArgs(): ThreadListArgs? = intent?.extras?.getParcelableCompat(THREAD_LIST_ARGS)
private fun getEventIdToNavigate(): String? = intent?.extras?.getString(THREAD_EVENT_ID_TO_NAVIGATE) private fun getEventIdToNavigate(): String? = intent?.extras?.getString(THREAD_EVENT_ID_TO_NAVIGATE)
companion object { companion object {

View File

@ -78,7 +78,7 @@ class ThreadListFragment :
override fun handlePostCreateMenu(menu: Menu) { override fun handlePostCreateMenu(menu: Menu) {
// We use a custom layout for this menu item, so we need to set a ClickListener // We use a custom layout for this menu item, so we need to set a ClickListener
menu.findItem(R.id.menu_thread_list_filter)?.let { menuItem -> menu.findItem(R.id.menu_thread_list_filter)?.let { menuItem ->
menuItem.actionView.debouncedClicks { menuItem.actionView?.debouncedClicks {
handleMenuItemSelected(menuItem) handleMenuItemSelected(menuItem)
} }
} }
@ -96,7 +96,7 @@ class ThreadListFragment :
override fun handlePrepareMenu(menu: Menu) { override fun handlePrepareMenu(menu: Menu) {
withState(threadListViewModel) { state -> withState(threadListViewModel) { state ->
val filterIcon = menu.findItem(R.id.menu_thread_list_filter).actionView val filterIcon = menu.findItem(R.id.menu_thread_list_filter).actionView ?: return@withState
val filterBadge = filterIcon.findViewById<View>(R.id.threadListFilterBadge) val filterBadge = filterIcon.findViewById<View>(R.id.threadListFilterBadge)
filterBadge.isVisible = state.shouldFilterThreads filterBadge.isVisible = state.shouldFilterThreads
when (threadListViewModel.canHomeserverUseThreading()) { when (threadListViewModel.canHomeserverUseThreading()) {

View File

@ -68,6 +68,7 @@ class InviteUsersToRoomActivity : SimpleFragmentActivity() {
sharedActionViewModel sharedActionViewModel
.stream() .stream()
.onEach { sharedAction -> .onEach { sharedAction ->
@Suppress("DEPRECATION")
when (sharedAction) { when (sharedAction) {
UserListSharedAction.Close -> finish() UserListSharedAction.Close -> finish()
UserListSharedAction.GoBack -> onBackPressed() UserListSharedAction.GoBack -> onBackPressed()

View File

@ -34,6 +34,7 @@ import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.util.getPackageInfoCompat
import timber.log.Timber import timber.log.Timber
class VectorActivityLifecycleCallbacks constructor(private val popupAlertManager: PopupAlertManager) : Application.ActivityLifecycleCallbacks { class VectorActivityLifecycleCallbacks constructor(private val popupAlertManager: PopupAlertManager) : Application.ActivityLifecycleCallbacks {
@ -64,16 +65,16 @@ class VectorActivityLifecycleCallbacks constructor(private val popupAlertManager
val packageManager: PackageManager = context.packageManager val packageManager: PackageManager = context.packageManager
// Get all activities from element android // Get all activities from element android
activitiesInfo = packageManager.getPackageInfo(context.packageName, PackageManager.GET_ACTIVITIES).activities activitiesInfo = packageManager.getPackageInfoCompat(context.packageName, PackageManager.GET_ACTIVITIES).activities
// Get all activities from PermissionController module // Get all activities from PermissionController module
// See https://source.android.com/docs/core/architecture/modular-system/permissioncontroller#package-format // See https://source.android.com/docs/core/architecture/modular-system/permissioncontroller#package-format
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S_V2) { if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S_V2) {
activitiesInfo += tryOrNull { activitiesInfo += tryOrNull {
packageManager.getPackageInfo("com.google.android.permissioncontroller", PackageManager.GET_ACTIVITIES).activities packageManager.getPackageInfoCompat("com.google.android.permissioncontroller", PackageManager.GET_ACTIVITIES).activities
} ?: tryOrNull { } ?: tryOrNull {
packageManager.getModuleInfo("com.google.android.permission", 1).packageName?.let { packageManager.getModuleInfo("com.google.android.permission", 1).packageName?.let {
packageManager.getPackageInfo(it, PackageManager.GET_ACTIVITIES or PackageManager.MATCH_APEX).activities packageManager.getPackageInfoCompat(it, PackageManager.GET_ACTIVITIES or PackageManager.MATCH_APEX).activities
} }
}.orEmpty() }.orEmpty()
} }

View File

@ -24,6 +24,7 @@ import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivityLocationSharingBinding import im.vector.app.databinding.ActivityLocationSharingBinding
import im.vector.app.features.location.preview.LocationPreviewFragment import im.vector.app.features.location.preview.LocationPreviewFragment
import im.vector.lib.core.utils.compat.getParcelableCompat
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@Parcelize @Parcelize
@ -40,7 +41,7 @@ class LocationSharingActivity : VectorBaseActivity<ActivityLocationSharingBindin
override fun getBinding() = ActivityLocationSharingBinding.inflate(layoutInflater) override fun getBinding() = ActivityLocationSharingBinding.inflate(layoutInflater)
override fun initUiAndData() { override fun initUiAndData() {
val locationSharingArgs: LocationSharingArgs? = intent?.extras?.getParcelable(EXTRA_LOCATION_SHARING_ARGS) val locationSharingArgs: LocationSharingArgs? = intent?.extras?.getParcelableCompat(EXTRA_LOCATION_SHARING_ARGS)
if (locationSharingArgs == null) { if (locationSharingArgs == null) {
finish() finish()
return return

View File

@ -25,6 +25,7 @@ import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivityLocationSharingBinding import im.vector.app.databinding.ActivityLocationSharingBinding
import im.vector.app.features.MainActivity import im.vector.app.features.MainActivity
import im.vector.lib.core.utils.compat.getParcelableCompat
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@Parcelize @Parcelize
@ -38,7 +39,7 @@ class LiveLocationMapViewActivity : VectorBaseActivity<ActivityLocationSharingBi
override fun getBinding() = ActivityLocationSharingBinding.inflate(layoutInflater) override fun getBinding() = ActivityLocationSharingBinding.inflate(layoutInflater)
override fun initUiAndData() { override fun initUiAndData() {
val mapViewArgs: LiveLocationMapViewArgs? = intent?.extras?.getParcelable(EXTRA_LIVE_LOCATION_MAP_VIEW_ARGS) val mapViewArgs: LiveLocationMapViewArgs? = intent?.extras?.getParcelableCompat(EXTRA_LIVE_LOCATION_MAP_VIEW_ARGS)
if (mapViewArgs == null) { if (mapViewArgs == null) {
finish() finish()
return return

View File

@ -29,6 +29,7 @@ import im.vector.app.features.location.LocationTracker
import im.vector.app.features.location.live.GetLiveLocationShareSummaryUseCase import im.vector.app.features.location.live.GetLiveLocationShareSummaryUseCase
import im.vector.app.features.redaction.CheckIfEventIsRedactedUseCase import im.vector.app.features.redaction.CheckIfEventIsRedactedUseCase
import im.vector.app.features.session.coroutineScope import im.vector.app.features.session.coroutineScope
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
@ -95,7 +96,7 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
startInProgress = true startInProgress = true
val roomArgs = intent?.getParcelableExtra(EXTRA_ROOM_ARGS) as? RoomArgs val roomArgs = intent?.getParcelableExtraCompat(EXTRA_ROOM_ARGS) as? RoomArgs
Timber.i("onStartCommand. sessionId - roomId ${roomArgs?.sessionId} - ${roomArgs?.roomId}") Timber.i("onStartCommand. sessionId - roomId ${roomArgs?.sessionId} - ${roomArgs?.roomId}")
@ -191,7 +192,7 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
} }
override fun onNoLocationProviderAvailable() { override fun onNoLocationProviderAvailable() {
stopForeground(true) stopForegroundCompat()
stopSelf() stopSelf()
} }

View File

@ -133,6 +133,7 @@ abstract class AbstractLoginFragment<VB : ViewBinding> : VectorBaseFragment<VB>(
.setMessage(R.string.login_signup_cancel_confirmation_content) .setMessage(R.string.login_signup_cancel_confirmation_content)
.setPositiveButton(R.string.yes) { _, _ -> .setPositiveButton(R.string.yes) { _, _ ->
displayCancelDialog = false displayCancelDialog = false
@Suppress("DEPRECATION")
vectorBaseActivity.onBackPressed() vectorBaseActivity.onBackPressed()
} }
.setNegativeButton(R.string.no, null) .setNegativeButton(R.string.no, null)
@ -147,6 +148,7 @@ abstract class AbstractLoginFragment<VB : ViewBinding> : VectorBaseFragment<VB>(
.setMessage(R.string.login_reset_password_cancel_confirmation_content) .setMessage(R.string.login_reset_password_cancel_confirmation_content)
.setPositiveButton(R.string.yes) { _, _ -> .setPositiveButton(R.string.yes) { _, _ ->
displayCancelDialog = false displayCancelDialog = false
@Suppress("DEPRECATION")
vectorBaseActivity.onBackPressed() vectorBaseActivity.onBackPressed()
} }
.setNegativeButton(R.string.no, null) .setNegativeButton(R.string.no, null)

View File

@ -45,6 +45,7 @@ import im.vector.app.features.login.terms.LoginTermsFragment
import im.vector.app.features.login.terms.LoginTermsFragmentArgument import im.vector.app.features.login.terms.LoginTermsFragmentArgument
import im.vector.app.features.onboarding.AuthenticationDescription import im.vector.app.features.onboarding.AuthenticationDescription
import im.vector.app.features.pin.UnlockedActivity import im.vector.app.features.pin.UnlockedActivity
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
import org.matrix.android.sdk.api.auth.registration.FlowResult import org.matrix.android.sdk.api.auth.registration.FlowResult
import org.matrix.android.sdk.api.auth.registration.Stage import org.matrix.android.sdk.api.auth.registration.Stage
import org.matrix.android.sdk.api.auth.toLocalizedLoginTerms import org.matrix.android.sdk.api.auth.toLocalizedLoginTerms
@ -96,7 +97,7 @@ open class LoginActivity : VectorBaseActivity<ActivityLoginBinding>(), UnlockedA
loginViewModel.observeViewEvents { handleLoginViewEvents(it) } loginViewModel.observeViewEvents { handleLoginViewEvents(it) }
// Get config extra // Get config extra
val loginConfig = intent.getParcelableExtra<LoginConfig?>(EXTRA_CONFIG) val loginConfig = intent.getParcelableExtraCompat<LoginConfig?>(EXTRA_CONFIG)
if (isFirstCreation()) { if (isFirstCreation()) {
loginViewModel.handle(LoginAction.InitWith(loginConfig)) loginViewModel.handle(LoginAction.InitWith(loginConfig))
} }
@ -316,7 +317,10 @@ open class LoginActivity : VectorBaseActivity<ActivityLoginBinding>(), UnlockedA
} }
override fun onBackPressed() { override fun onBackPressed() {
validateBackPressed { super.onBackPressed() } validateBackPressed {
@Suppress("DEPRECATION")
super.onBackPressed()
}
} }
private fun onRegistrationStageNotSupported() { private fun onRegistrationStageNotSupported() {

View File

@ -47,6 +47,8 @@ import im.vector.app.features.themes.ActivityOtherThemes
import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.themes.ThemeUtils
import im.vector.lib.attachmentviewer.AttachmentCommands import im.vector.lib.attachmentviewer.AttachmentCommands
import im.vector.lib.attachmentviewer.AttachmentViewerActivity import im.vector.lib.attachmentviewer.AttachmentViewerActivity
import im.vector.lib.core.utils.compat.getParcelableArrayListExtraCompat
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@ -105,7 +107,7 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), AttachmentInt
transitionImageContainer.isVisible = true transitionImageContainer.isVisible = true
// Postpone transaction a bit until thumbnail is loaded // Postpone transaction a bit until thumbnail is loaded
val mediaData: Parcelable? = intent.getParcelableExtra(EXTRA_IMAGE_DATA) val mediaData: Parcelable? = intent.getParcelableExtraCompat(EXTRA_IMAGE_DATA)
if (mediaData is ImageContentRenderer.Data) { if (mediaData is ImageContentRenderer.Data) {
// will be shown at end of transition // will be shown at end of transition
pager2.isInvisible = true pager2.isInvisible = true
@ -130,7 +132,7 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), AttachmentInt
val room = args.roomId?.let { session.getRoom(it) } val room = args.roomId?.let { session.getRoom(it) }
val inMemoryData = intent.getParcelableArrayListExtra<AttachmentData>(EXTRA_IN_MEMORY_DATA) val inMemoryData = intent.getParcelableArrayListExtraCompat<AttachmentData>(EXTRA_IN_MEMORY_DATA)
val sourceProvider = if (inMemoryData != null) { val sourceProvider = if (inMemoryData != null) {
initialIndex = inMemoryData.indexOfFirst { it.eventId == args.eventId }.coerceAtLeast(0) initialIndex = inMemoryData.indexOfFirst { it.eventId == args.eventId }.coerceAtLeast(0)
dataSourceFactory.createProvider(inMemoryData, room, lifecycleScope) dataSourceFactory.createProvider(inMemoryData, room, lifecycleScope)
@ -173,6 +175,7 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), AttachmentInt
transitionImageContainer.isVisible = true transitionImageContainer.isVisible = true
} }
isAnimatingOut = true isAnimatingOut = true
@Suppress("DEPRECATION")
super.onBackPressed() super.onBackPressed()
} }
@ -227,7 +230,7 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), AttachmentInt
return false return false
} }
private fun args() = intent.getParcelableExtra<Args>(EXTRA_ARGS) private fun args() = intent.getParcelableExtraCompat<Args>(EXTRA_ARGS)
private fun scheduleStartPostponedTransition(sharedElement: View) { private fun scheduleStartPostponedTransition(sharedElement: View) {
sharedElement.viewTreeObserver.addOnPreDrawListener( sharedElement.viewTreeObserver.addOnPreDrawListener(

View File

@ -48,7 +48,10 @@ class OnboardingActivity : VectorBaseActivity<ActivityLoginBinding>(), UnlockedA
} }
override fun onBackPressed() { override fun onBackPressed() {
validateBackPressed { super.onBackPressed() } validateBackPressed {
@Suppress("DEPRECATION")
super.onBackPressed()
}
} }
override fun initUiAndData() { override fun initUiAndData() {

View File

@ -123,6 +123,7 @@ abstract class AbstractFtueAuthFragment<VB : ViewBinding> : VectorBaseFragment<V
.setMessage(R.string.login_signup_cancel_confirmation_content) .setMessage(R.string.login_signup_cancel_confirmation_content)
.setPositiveButton(R.string.yes) { _, _ -> .setPositiveButton(R.string.yes) { _, _ ->
displayCancelDialog = false displayCancelDialog = false
@Suppress("DEPRECATION")
vectorBaseActivity.onBackPressed() vectorBaseActivity.onBackPressed()
} }
.setNegativeButton(R.string.no, null) .setNegativeButton(R.string.no, null)
@ -137,6 +138,7 @@ abstract class AbstractFtueAuthFragment<VB : ViewBinding> : VectorBaseFragment<V
.setMessage(R.string.login_reset_password_cancel_confirmation_content) .setMessage(R.string.login_reset_password_cancel_confirmation_content)
.setPositiveButton(R.string.yes) { _, _ -> .setPositiveButton(R.string.yes) { _, _ ->
displayCancelDialog = false displayCancelDialog = false
@Suppress("DEPRECATION")
vectorBaseActivity.onBackPressed() vectorBaseActivity.onBackPressed()
} }
.setNegativeButton(R.string.no, null) .setNegativeButton(R.string.no, null)

View File

@ -54,6 +54,7 @@ import im.vector.app.features.onboarding.OnboardingViewState
import im.vector.app.features.onboarding.ftueauth.terms.FtueAuthLegacyStyleTermsFragment import im.vector.app.features.onboarding.ftueauth.terms.FtueAuthLegacyStyleTermsFragment
import im.vector.app.features.onboarding.ftueauth.terms.FtueAuthTermsFragment import im.vector.app.features.onboarding.ftueauth.terms.FtueAuthTermsFragment
import im.vector.app.features.onboarding.ftueauth.terms.FtueAuthTermsLegacyStyleFragmentArgument import im.vector.app.features.onboarding.ftueauth.terms.FtueAuthTermsLegacyStyleFragmentArgument
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
import org.matrix.android.sdk.api.auth.registration.Stage import org.matrix.android.sdk.api.auth.registration.Stage
import org.matrix.android.sdk.api.auth.toLocalizedLoginTerms import org.matrix.android.sdk.api.auth.toLocalizedLoginTerms
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
@ -107,7 +108,7 @@ class FtueAuthVariant(
} }
// Get config extra // Get config extra
val loginConfig = activity.intent.getParcelableExtra<LoginConfig?>(OnboardingActivity.EXTRA_CONFIG) val loginConfig = activity.intent.getParcelableExtraCompat<LoginConfig?>(OnboardingActivity.EXTRA_CONFIG)
if (isFirstCreation) { if (isFirstCreation) {
onboardingViewModel.handle(OnboardingAction.InitWith(loginConfig)) onboardingViewModel.handle(OnboardingAction.InitWith(loginConfig))
} }

View File

@ -23,6 +23,7 @@ import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivitySimpleBinding import im.vector.app.databinding.ActivitySimpleBinding
import im.vector.lib.core.utils.compat.getParcelableCompat
@AndroidEntryPoint @AndroidEntryPoint
class PinActivity : VectorBaseActivity<ActivitySimpleBinding>(), UnlockedActivity { class PinActivity : VectorBaseActivity<ActivitySimpleBinding>(), UnlockedActivity {
@ -41,7 +42,7 @@ class PinActivity : VectorBaseActivity<ActivitySimpleBinding>(), UnlockedActivit
override fun initUiAndData() { override fun initUiAndData() {
if (isFirstCreation()) { if (isFirstCreation()) {
val fragmentArgs: PinArgs = intent?.extras?.getParcelable(Mavericks.KEY_ARG) ?: return val fragmentArgs: PinArgs = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG) ?: return
addFragment(views.simpleFragmentContainer, PinFragment::class.java, fragmentArgs) addFragment(views.simpleFragmentContainer, PinFragment::class.java, fragmentArgs)
} }
} }

View File

@ -142,7 +142,8 @@ class LockScreenCodeView @JvmOverloads constructor(
var codeLength: Int = 0 var codeLength: Int = 0
constructor(source: Parcel) : super(source) { constructor(source: Parcel) : super(source) {
source.readList(code, null) val codeStr = source.readString().orEmpty()
code = codeStr.toMutableList()
codeLength = source.readInt() codeLength = source.readInt()
} }
@ -150,7 +151,7 @@ class LockScreenCodeView @JvmOverloads constructor(
override fun writeToParcel(out: Parcel, flags: Int) { override fun writeToParcel(out: Parcel, flags: Int) {
super.writeToParcel(out, flags) super.writeToParcel(out, flags)
out.writeList(code) out.writeString(String(code.toCharArray()))
out.writeInt(codeLength) out.writeInt(codeLength)
} }

View File

@ -23,6 +23,7 @@ import android.view.View
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.SimpleFragmentActivity import im.vector.app.core.platform.SimpleFragmentActivity
import im.vector.lib.core.utils.compat.getParcelableCompat
@AndroidEntryPoint @AndroidEntryPoint
class CreatePollActivity : SimpleFragmentActivity() { class CreatePollActivity : SimpleFragmentActivity() {
@ -31,7 +32,7 @@ class CreatePollActivity : SimpleFragmentActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
views.toolbar.visibility = View.GONE views.toolbar.visibility = View.GONE
val createPollArgs: CreatePollArgs? = intent?.extras?.getParcelable(EXTRA_CREATE_POLL_ARGS) val createPollArgs: CreatePollArgs? = intent?.extras?.getParcelableCompat(EXTRA_CREATE_POLL_ARGS)
if (isFirstCreation()) { if (isFirstCreation()) {
addFragment( addFragment(

View File

@ -128,7 +128,7 @@ class BugReportActivity :
val isValid = !views.bugReportMaskView.isVisible val isValid = !views.bugReportMaskView.isVisible
it.isEnabled = isValid it.isEnabled = isValid
it.icon.alpha = if (isValid) 255 else 100 it.icon?.alpha = if (isValid) 255 else 100
} }
} }
@ -267,6 +267,7 @@ class BugReportActivity :
// Ensure there is no crash status remaining, which will be sent later on by mistake // Ensure there is no crash status remaining, which will be sent later on by mistake
bugReporter.deleteCrashFile() bugReporter.deleteCrashFile()
@Suppress("DEPRECATION")
super.onBackPressed() super.onBackPressed()
} }

View File

@ -146,7 +146,7 @@ class EmojiReactionPickerActivity :
val searchItem = menu.findItem(R.id.search) val searchItem = menu.findItem(R.id.search)
(searchItem.actionView as? SearchView)?.let { searchView -> (searchItem.actionView as? SearchView)?.let { searchView ->
searchItem.setOnActionExpandListener(object : MenuItem.OnActionExpandListener { searchItem.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
override fun onMenuItemActionExpand(p0: MenuItem?): Boolean { override fun onMenuItemActionExpand(p0: MenuItem): Boolean {
searchView.isIconified = false searchView.isIconified = false
searchView.requestFocusFromTouch() searchView.requestFocusFromTouch()
// we want to force the tool bar as visible even if hidden with scroll flags // we want to force the tool bar as visible even if hidden with scroll flags
@ -154,7 +154,7 @@ class EmojiReactionPickerActivity :
return true return true
} }
override fun onMenuItemActionCollapse(p0: MenuItem?): Boolean { override fun onMenuItemActionCollapse(p0: MenuItem): Boolean {
// when back, clear all search // when back, clear all search
views.emojiPickerToolbar.minimumHeight = 0 views.emojiPickerToolbar.minimumHeight = 0
searchView.setQuery("", true) searchView.setQuery("", true)

View File

@ -29,6 +29,7 @@ import im.vector.app.databinding.ActivitySimpleBinding
import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.analytics.plan.MobileScreen
import im.vector.app.features.roomdirectory.RoomDirectorySharedAction import im.vector.app.features.roomdirectory.RoomDirectorySharedAction
import im.vector.app.features.roomdirectory.RoomDirectorySharedActionViewModel import im.vector.app.features.roomdirectory.RoomDirectorySharedActionViewModel
import im.vector.lib.core.utils.compat.getParcelableCompat
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@ -46,7 +47,7 @@ class CreateRoomActivity : VectorBaseActivity<ActivitySimpleBinding>() {
override fun initUiAndData() { override fun initUiAndData() {
if (isFirstCreation()) { if (isFirstCreation()) {
val fragmentArgs: CreateRoomArgs = intent?.extras?.getParcelable(Mavericks.KEY_ARG) ?: return val fragmentArgs: CreateRoomArgs = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG) ?: return
addFragment( addFragment(
views.simpleFragmentContainer, views.simpleFragmentContainer,
CreateRoomFragment::class.java, CreateRoomFragment::class.java,

View File

@ -100,7 +100,10 @@ class CreateRoomFragment :
.allowBack(useCross = true) .allowBack(useCross = true)
viewModel.observeViewEvents { viewModel.observeViewEvents {
when (it) { when (it) {
CreateRoomViewEvents.Quit -> vectorBaseActivity.onBackPressed() CreateRoomViewEvents.Quit -> {
@Suppress("DEPRECATION")
vectorBaseActivity.onBackPressed()
}
is CreateRoomViewEvents.Failure -> showFailure(it.throwable) is CreateRoomViewEvents.Failure -> showFailure(it.throwable)
} }
} }

View File

@ -24,6 +24,7 @@ import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivitySimpleBinding import im.vector.app.databinding.ActivitySimpleBinding
import im.vector.app.features.roomdirectory.RoomDirectoryData import im.vector.app.features.roomdirectory.RoomDirectoryData
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import org.matrix.android.sdk.api.session.permalinks.PermalinkData import org.matrix.android.sdk.api.session.permalinks.PermalinkData
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom
@ -83,7 +84,7 @@ class RoomPreviewActivity : VectorBaseActivity<ActivitySimpleBinding>() {
override fun initUiAndData() { override fun initUiAndData() {
if (isFirstCreation()) { if (isFirstCreation()) {
val args = intent.getParcelableExtra<RoomPreviewData>(ARG) val args = intent.getParcelableExtraCompat<RoomPreviewData>(ARG)
if (args?.worldReadable == true) { if (args?.worldReadable == true) {
// TODO Room preview: Note: M does not recommend to use /events anymore, so for now we just display the room preview // TODO Room preview: Note: M does not recommend to use /events anymore, so for now we just display the room preview

View File

@ -28,6 +28,7 @@ import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivitySimpleBinding import im.vector.app.databinding.ActivitySimpleBinding
import im.vector.app.features.room.RequireActiveMembershipViewEvents import im.vector.app.features.room.RequireActiveMembershipViewEvents
import im.vector.app.features.room.RequireActiveMembershipViewModel import im.vector.app.features.room.RequireActiveMembershipViewModel
import im.vector.lib.core.utils.compat.getParcelableCompat
@AndroidEntryPoint @AndroidEntryPoint
class RoomMemberProfileActivity : VectorBaseActivity<ActivitySimpleBinding>() { class RoomMemberProfileActivity : VectorBaseActivity<ActivitySimpleBinding>() {
@ -48,7 +49,7 @@ class RoomMemberProfileActivity : VectorBaseActivity<ActivitySimpleBinding>() {
override fun initUiAndData() { override fun initUiAndData() {
if (isFirstCreation()) { if (isFirstCreation()) {
val fragmentArgs: RoomMemberProfileArgs = intent?.extras?.getParcelable(Mavericks.KEY_ARG) ?: return val fragmentArgs: RoomMemberProfileArgs = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG) ?: return
addFragment(views.simpleFragmentContainer, RoomMemberProfileFragment::class.java, fragmentArgs) addFragment(views.simpleFragmentContainer, RoomMemberProfileFragment::class.java, fragmentArgs)
} }

View File

@ -38,6 +38,7 @@ import im.vector.app.features.roomprofile.notifications.RoomNotificationSettings
import im.vector.app.features.roomprofile.permissions.RoomPermissionsFragment import im.vector.app.features.roomprofile.permissions.RoomPermissionsFragment
import im.vector.app.features.roomprofile.settings.RoomSettingsFragment import im.vector.app.features.roomprofile.settings.RoomSettingsFragment
import im.vector.app.features.roomprofile.uploads.RoomUploadsFragment import im.vector.app.features.roomprofile.uploads.RoomUploadsFragment
import im.vector.lib.core.utils.compat.getParcelableCompat
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import javax.inject.Inject import javax.inject.Inject
@ -77,7 +78,7 @@ class RoomProfileActivity :
override fun initUiAndData() { override fun initUiAndData() {
sharedActionViewModel = viewModelProvider.get(RoomProfileSharedActionViewModel::class.java) sharedActionViewModel = viewModelProvider.get(RoomProfileSharedActionViewModel::class.java)
roomProfileArgs = intent?.extras?.getParcelable(Mavericks.KEY_ARG) ?: return roomProfileArgs = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG) ?: return
if (isFirstCreation()) { if (isFirstCreation()) {
when (intent?.extras?.getInt(EXTRA_DIRECT_ACCESS, EXTRA_DIRECT_ACCESS_ROOM_ROOT)) { when (intent?.extras?.getInt(EXTRA_DIRECT_ACCESS, EXTRA_DIRECT_ACCESS_ROOM_ROOT)) {
EXTRA_DIRECT_ACCESS_ROOM_SETTINGS -> { EXTRA_DIRECT_ACCESS_ROOM_SETTINGS -> {

View File

@ -106,6 +106,7 @@ class RoomSettingsFragment :
RoomSettingsViewEvents.Success -> showSuccess() RoomSettingsViewEvents.Success -> showSuccess()
RoomSettingsViewEvents.GoBack -> { RoomSettingsViewEvents.GoBack -> {
ignoreChanges = true ignoreChanges = true
@Suppress("DEPRECATION")
vectorBaseActivity.onBackPressed() vectorBaseActivity.onBackPressed()
} }
} }

View File

@ -43,6 +43,7 @@ import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRul
import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedFragment import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedFragment
import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedState import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedState
import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedViewModel import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedViewModel
import im.vector.lib.core.utils.compat.getParcelableCompat
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
@ -58,7 +59,7 @@ class RoomJoinRuleActivity : VectorBaseActivity<ActivitySimpleBinding>() {
val viewModel: RoomJoinRuleChooseRestrictedViewModel by viewModel() val viewModel: RoomJoinRuleChooseRestrictedViewModel by viewModel()
override fun initUiAndData() { override fun initUiAndData() {
roomProfileArgs = intent?.extras?.getParcelable(Mavericks.KEY_ARG) ?: return roomProfileArgs = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG) ?: return
if (isFirstCreation()) { if (isFirstCreation()) {
addFragment( addFragment(
views.simpleFragmentContainer, views.simpleFragmentContainer,

View File

@ -34,6 +34,7 @@ import im.vector.app.features.navigation.SettingsActivityPayload
import im.vector.app.features.settings.devices.VectorSettingsDevicesFragment import im.vector.app.features.settings.devices.VectorSettingsDevicesFragment
import im.vector.app.features.settings.notifications.VectorSettingsNotificationPreferenceFragment import im.vector.app.features.settings.notifications.VectorSettingsNotificationPreferenceFragment
import im.vector.app.features.settings.threepids.ThreePidsSettingsFragment import im.vector.app.features.settings.threepids.ThreePidsSettingsFragment
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
import org.matrix.android.sdk.api.failure.GlobalError import org.matrix.android.sdk.api.failure.GlobalError
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import timber.log.Timber import timber.log.Timber
@ -194,8 +195,8 @@ class VectorSettingsActivity : VectorBaseActivity<ActivityVectorSettingsBinding>
} }
} }
private fun <T : Parcelable> Activity.readPayload(default: T): T { private inline fun <reified T : Parcelable> Activity.readPayload(default: T): T {
return intent.getParcelableExtra(KEY_ACTIVITY_PAYLOAD) ?: default return intent.getParcelableExtraCompat<T>(KEY_ACTIVITY_PAYLOAD) ?: default
} }
private fun <T : Parcelable> Intent.applyPayload(payload: T): Intent { private fun <T : Parcelable> Intent.applyPayload(payload: T): Intent {

View File

@ -29,6 +29,7 @@ import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.preference.VectorPreference import im.vector.app.core.preference.VectorPreference
import im.vector.app.core.utils.RingtoneUtils import im.vector.app.core.utils.RingtoneUtils
import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.analytics.plan.MobileScreen
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
@ -69,7 +70,7 @@ class VectorSettingsVoiceVideoFragment : VectorSettingsBaseFragment() {
private val ringtoneStartForActivityResult = registerStartForActivityResult { activityResult -> private val ringtoneStartForActivityResult = registerStartForActivityResult { activityResult ->
if (activityResult.resultCode == Activity.RESULT_OK) { if (activityResult.resultCode == Activity.RESULT_OK) {
val callRingtoneUri: Uri? = activityResult.data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI) val callRingtoneUri: Uri? = activityResult.data?.getParcelableExtraCompat(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
if (callRingtoneUri != null) { if (callRingtoneUri != null) {
ringtoneUtils.setCallRingtoneUri(callRingtoneUri) ringtoneUtils.setCallRingtoneUri(callRingtoneUri)
mCallRingtonePreference.summary = ringtoneUtils.getCallRingtoneName() mCallRingtonePreference.summary = ringtoneUtils.getCallRingtoneName()

View File

@ -23,6 +23,7 @@ import com.airbnb.mvrx.Mavericks
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.SimpleFragmentActivity import im.vector.app.core.platform.SimpleFragmentActivity
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
/** /**
* Display the details info about a Session. * Display the details info about a Session.
@ -37,7 +38,7 @@ class SessionDetailsActivity : SimpleFragmentActivity() {
addFragment( addFragment(
container = views.container, container = views.container,
fragmentClass = SessionDetailsFragment::class.java, fragmentClass = SessionDetailsFragment::class.java,
params = intent.getParcelableExtra(Mavericks.KEY_ARG) params = intent.getParcelableExtraCompat(Mavericks.KEY_ARG)
) )
} }
} }

View File

@ -26,6 +26,7 @@ import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.SimpleFragmentActivity import im.vector.app.core.platform.SimpleFragmentActivity
import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
@AndroidEntryPoint @AndroidEntryPoint
class OtherSessionsActivity : SimpleFragmentActivity() { class OtherSessionsActivity : SimpleFragmentActivity() {
@ -39,7 +40,7 @@ class OtherSessionsActivity : SimpleFragmentActivity() {
addFragment( addFragment(
container = views.container, container = views.container,
fragmentClass = OtherSessionsFragment::class.java, fragmentClass = OtherSessionsFragment::class.java,
params = intent.getParcelableExtra(Mavericks.KEY_ARG) params = intent.getParcelableExtraCompat(Mavericks.KEY_ARG)
) )
} }
} }

View File

@ -23,6 +23,7 @@ import com.airbnb.mvrx.Mavericks
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.SimpleFragmentActivity import im.vector.app.core.platform.SimpleFragmentActivity
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
/** /**
* Display the overview info about a Session. * Display the overview info about a Session.
@ -37,7 +38,7 @@ class SessionOverviewActivity : SimpleFragmentActivity() {
addFragment( addFragment(
container = views.container, container = views.container,
fragmentClass = SessionOverviewFragment::class.java, fragmentClass = SessionOverviewFragment::class.java,
params = intent.getParcelableExtra(Mavericks.KEY_ARG) params = intent.getParcelableExtraCompat(Mavericks.KEY_ARG)
) )
} }
} }

View File

@ -25,6 +25,7 @@ import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivitySimpleBinding import im.vector.app.databinding.ActivitySimpleBinding
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
/** /**
* Display the screen to rename a Session. * Display the screen to rename a Session.
@ -42,7 +43,7 @@ class RenameSessionActivity : VectorBaseActivity<ActivitySimpleBinding>() {
addFragment( addFragment(
container = views.simpleFragmentContainer, container = views.simpleFragmentContainer,
fragmentClass = RenameSessionFragment::class.java, fragmentClass = RenameSessionFragment::class.java,
params = intent.getParcelableExtra(Mavericks.KEY_ARG) params = intent.getParcelableExtraCompat(Mavericks.KEY_ARG)
) )
} }
} }

View File

@ -22,7 +22,6 @@ import android.content.Intent
import android.media.RingtoneManager import android.media.RingtoneManager
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable
import android.widget.Toast import android.widget.Toast
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.distinctUntilChanged import androidx.lifecycle.distinctUntilChanged
@ -44,15 +43,19 @@ import im.vector.app.core.pushers.UnifiedPushHelper
import im.vector.app.core.services.GuardServiceStarter import im.vector.app.core.services.GuardServiceStarter
import im.vector.app.core.utils.combineLatest import im.vector.app.core.utils.combineLatest
import im.vector.app.core.utils.isIgnoringBatteryOptimizations import im.vector.app.core.utils.isIgnoringBatteryOptimizations
import im.vector.app.core.utils.registerForPermissionsResult
import im.vector.app.core.utils.requestDisablingBatteryOptimization import im.vector.app.core.utils.requestDisablingBatteryOptimization
import im.vector.app.core.utils.startNotificationSettingsIntent
import im.vector.app.features.VectorFeatures import im.vector.app.features.VectorFeatures
import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.analytics.plan.MobileScreen
import im.vector.app.features.home.NotificationPermissionManager
import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.notifications.NotificationUtils
import im.vector.app.features.settings.BackgroundSyncMode import im.vector.app.features.settings.BackgroundSyncMode
import im.vector.app.features.settings.BackgroundSyncModeChooserDialog import im.vector.app.features.settings.BackgroundSyncModeChooserDialog
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.settings.VectorSettingsBaseFragment import im.vector.app.features.settings.VectorSettingsBaseFragment
import im.vector.app.features.settings.VectorSettingsFragmentInteractionListener import im.vector.app.features.settings.VectorSettingsFragmentInteractionListener
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -77,12 +80,24 @@ class VectorSettingsNotificationPreferenceFragment :
@Inject lateinit var vectorPreferences: VectorPreferences @Inject lateinit var vectorPreferences: VectorPreferences
@Inject lateinit var guardServiceStarter: GuardServiceStarter @Inject lateinit var guardServiceStarter: GuardServiceStarter
@Inject lateinit var vectorFeatures: VectorFeatures @Inject lateinit var vectorFeatures: VectorFeatures
@Inject lateinit var notificationPermissionManager: NotificationPermissionManager
override var titleRes: Int = R.string.settings_notifications override var titleRes: Int = R.string.settings_notifications
override val preferenceXmlRes = R.xml.vector_settings_notifications override val preferenceXmlRes = R.xml.vector_settings_notifications
private var interactionListener: VectorSettingsFragmentInteractionListener? = null private var interactionListener: VectorSettingsFragmentInteractionListener? = null
private val notificationStartForActivityResult = registerStartForActivityResult { _ ->
// No op
}
private val postPermissionLauncher = registerForPermissionsResult { _, deniedPermanently ->
if (deniedPermanently) {
// Open System setting, to give a chance to the user to enable notification. Sometimes the permission dialog is not displayed
startNotificationSettingsIntent(requireContext(), notificationStartForActivityResult)
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
analyticsScreenName = MobileScreen.ScreenName.SettingsNotifications analyticsScreenName = MobileScreen.ScreenName.SettingsNotifications
@ -133,9 +148,16 @@ class VectorSettingsNotificationPreferenceFragment :
} }
} }
} }
notificationPermissionManager.eventuallyRequestPermission(
requireActivity(),
postPermissionLauncher,
showRationale = false,
ignorePreference = true
)
} else { } else {
unifiedPushHelper.unregister(pushersManager) unifiedPushHelper.unregister(pushersManager)
session.pushersService().refreshPushers() session.pushersService().refreshPushers()
notificationPermissionManager.eventuallyRevokePermission(requireActivity())
} }
} }
} }
@ -357,7 +379,7 @@ class VectorSettingsNotificationPreferenceFragment :
private val ringtoneStartForActivityResult = registerStartForActivityResult { activityResult -> private val ringtoneStartForActivityResult = registerStartForActivityResult { activityResult ->
if (activityResult.resultCode == Activity.RESULT_OK) { if (activityResult.resultCode == Activity.RESULT_OK) {
vectorPreferences.setNotificationRingTone(activityResult.data?.getParcelableExtra<Parcelable>(RingtoneManager.EXTRA_RINGTONE_PICKED_URI) as Uri?) vectorPreferences.setNotificationRingTone(activityResult.data?.getParcelableExtraCompat<Uri>(RingtoneManager.EXTRA_RINGTONE_PICKED_URI))
// test if the selected ring tone can be played // test if the selected ring tone can be played
val notificationRingToneName = vectorPreferences.getNotificationRingToneName() val notificationRingToneName = vectorPreferences.getNotificationRingToneName()

View File

@ -34,6 +34,8 @@ import im.vector.app.R
import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.registerForPermissionsResult
import im.vector.app.core.utils.startNotificationSettingsIntent
import im.vector.app.databinding.FragmentSettingsNotificationsTroubleshootBinding import im.vector.app.databinding.FragmentSettingsNotificationsTroubleshootBinding
import im.vector.app.features.notifications.NotificationActionIds import im.vector.app.features.notifications.NotificationActionIds
import im.vector.app.features.push.NotificationTroubleshootTestManagerFactory import im.vector.app.features.push.NotificationTroubleshootTestManagerFactory
@ -76,7 +78,7 @@ class VectorSettingsNotificationsTroubleshootFragment :
} }
views.troubleshootRunButton.debouncedClicks { views.troubleshootRunButton.debouncedClicks {
testManager?.retry(testStartForActivityResult) testManager?.retry(TroubleshootTest.TestParameters(testStartForActivityResult, testStartForPermissionResult))
} }
startUI() startUI()
} }
@ -125,7 +127,7 @@ class VectorSettingsNotificationsTroubleshootFragment :
} }
} }
views.troubleshootTestRecyclerView.adapter = testManager?.adapter views.troubleshootTestRecyclerView.adapter = testManager?.adapter
testManager?.runDiagnostic(testStartForActivityResult) testManager?.runDiagnostic(TroubleshootTest.TestParameters(testStartForActivityResult, testStartForPermissionResult))
} }
override fun onDestroyView() { override fun onDestroyView() {
@ -139,8 +141,17 @@ class VectorSettingsNotificationsTroubleshootFragment :
} }
} }
private val testStartForPermissionResult = registerForPermissionsResult { allGranted, deniedPermanently ->
if (allGranted) {
retry()
} else if (deniedPermanently) {
// Open System setting
startNotificationSettingsIntent(requireContext(), testStartForActivityResult)
}
}
private fun retry() { private fun retry() {
testManager?.retry(testStartForActivityResult) testManager?.retry(TroubleshootTest.TestParameters(testStartForActivityResult, testStartForPermissionResult))
} }
override fun onDetach() { override fun onDetach() {

View File

@ -15,10 +15,8 @@
*/ */
package im.vector.app.features.settings.troubleshoot package im.vector.app.features.settings.troubleshoot
import android.content.Intent
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import androidx.activity.result.ActivityResultLauncher
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import kotlin.properties.Delegates import kotlin.properties.Delegates
@ -50,7 +48,7 @@ class NotificationTroubleshootTestManager(val fragment: Fragment) {
test.manager = this test.manager = this
} }
fun runDiagnostic(activityResultLauncher: ActivityResultLauncher<Intent>) { fun runDiagnostic(testParameters: TroubleshootTest.TestParameters) {
if (isCancelled) return if (isCancelled) return
currentTestIndex = 0 currentTestIndex = 0
val handler = Handler(Looper.getMainLooper()) val handler = Handler(Looper.getMainLooper())
@ -69,7 +67,7 @@ class NotificationTroubleshootTestManager(val fragment: Fragment) {
// Cosmetic: Start with a small delay for UI/UX reason (better animation effect) for non async tests // Cosmetic: Start with a small delay for UI/UX reason (better animation effect) for non async tests
handler.postDelayed({ handler.postDelayed({
if (fragment.isAdded) { if (fragment.isAdded) {
troubleshootTest.perform(activityResultLauncher) troubleshootTest.perform(testParameters)
} }
}, 600) }, 600)
} else { } else {
@ -81,18 +79,18 @@ class NotificationTroubleshootTestManager(val fragment: Fragment) {
} }
} }
if (fragment.isAdded) { if (fragment.isAdded) {
testList.firstOrNull()?.perform(activityResultLauncher) testList.firstOrNull()?.perform(testParameters)
} }
} }
fun retry(activityResultLauncher: ActivityResultLauncher<Intent>) { fun retry(testParameters: TroubleshootTest.TestParameters) {
testList.forEach { testList.forEach {
it.cancel() it.cancel()
it.description = null it.description = null
it.quickFix = null it.quickFix = null
it.status = TroubleshootTest.TestStatus.NOT_STARTED it.status = TroubleshootTest.TestStatus.NOT_STARTED
} }
runDiagnostic(activityResultLauncher) runDiagnostic(testParameters)
} }
fun hasQuickFix(): Boolean { fun hasQuickFix(): Boolean {

View File

@ -15,8 +15,6 @@
*/ */
package im.vector.app.features.settings.troubleshoot package im.vector.app.features.settings.troubleshoot
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
@ -38,7 +36,7 @@ class TestAccountSettings @Inject constructor(
) : ) :
TroubleshootTest(R.string.settings_troubleshoot_test_account_settings_title) { TroubleshootTest(R.string.settings_troubleshoot_test_account_settings_title) {
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) { override fun perform(testParameters: TestParameters) {
val session = activeSessionHolder.getSafeActiveSession() ?: return val session = activeSessionHolder.getSafeActiveSession() ?: return
val defaultRule = session.pushRuleService().getPushRules().getAllRules() val defaultRule = session.pushRuleService().getPushRules().getAllRules()
.find { it.ruleId == RuleIds.RULE_ID_DISABLE_ALL } .find { it.ruleId == RuleIds.RULE_ID_DISABLE_ALL }
@ -59,7 +57,7 @@ class TestAccountSettings @Inject constructor(
session.pushRuleService().updatePushRuleEnableStatus(RuleKind.OVERRIDE, defaultRule, !defaultRule.enabled) session.pushRuleService().updatePushRuleEnableStatus(RuleKind.OVERRIDE, defaultRule, !defaultRule.enabled)
} }
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
manager?.retry(activityResultLauncher) manager?.retry(testParameters)
} }
} }
} }

View File

@ -16,8 +16,6 @@
package im.vector.app.features.settings.troubleshoot package im.vector.app.features.settings.troubleshoot
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.pushers.FcmHelper import im.vector.app.core.pushers.FcmHelper
import im.vector.app.core.pushers.UnifiedPushHelper import im.vector.app.core.pushers.UnifiedPushHelper
@ -30,7 +28,7 @@ class TestAvailableUnifiedPushDistributors @Inject constructor(
private val fcmHelper: FcmHelper, private val fcmHelper: FcmHelper,
) : TroubleshootTest(R.string.settings_troubleshoot_test_distributors_title) { ) : TroubleshootTest(R.string.settings_troubleshoot_test_distributors_title) {
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) { override fun perform(testParameters: TestParameters) {
val distributors = unifiedPushHelper.getExternalDistributors() val distributors = unifiedPushHelper.getExternalDistributors()
description = if (distributors.isEmpty()) { description = if (distributors.isEmpty()) {
stringProvider.getString( stringProvider.getString(

View File

@ -16,8 +16,6 @@
package im.vector.app.features.settings.troubleshoot package im.vector.app.features.settings.troubleshoot
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.pushers.UnifiedPushHelper import im.vector.app.core.pushers.UnifiedPushHelper
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
@ -28,7 +26,7 @@ class TestCurrentUnifiedPushDistributor @Inject constructor(
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
) : TroubleshootTest(R.string.settings_troubleshoot_test_current_distributor_title) { ) : TroubleshootTest(R.string.settings_troubleshoot_test_current_distributor_title) {
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) { override fun perform(testParameters: TestParameters) {
description = stringProvider.getString( description = stringProvider.getString(
R.string.settings_troubleshoot_test_current_distributor, R.string.settings_troubleshoot_test_current_distributor,
unifiedPushHelper.getCurrentDistributorName() unifiedPushHelper.getCurrentDistributorName()

View File

@ -15,8 +15,6 @@
*/ */
package im.vector.app.features.settings.troubleshoot package im.vector.app.features.settings.troubleshoot
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
@ -31,7 +29,7 @@ class TestDeviceSettings @Inject constructor(
) : ) :
TroubleshootTest(R.string.settings_troubleshoot_test_device_settings_title) { TroubleshootTest(R.string.settings_troubleshoot_test_device_settings_title) {
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) { override fun perform(testParameters: TestParameters) {
if (vectorPreferences.areNotificationEnabledForDevice()) { if (vectorPreferences.areNotificationEnabledForDevice()) {
description = stringProvider.getString(R.string.settings_troubleshoot_test_device_settings_success) description = stringProvider.getString(R.string.settings_troubleshoot_test_device_settings_success)
quickFix = null quickFix = null
@ -40,7 +38,7 @@ class TestDeviceSettings @Inject constructor(
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_device_settings_quickfix) { quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_device_settings_quickfix) {
override fun doFix() { override fun doFix() {
vectorPreferences.setNotificationEnabledForDevice(true) vectorPreferences.setNotificationEnabledForDevice(true)
manager?.retry(activityResultLauncher) manager?.retry(testParameters)
} }
} }
description = stringProvider.getString(R.string.settings_troubleshoot_test_device_settings_failed) description = stringProvider.getString(R.string.settings_troubleshoot_test_device_settings_failed)

View File

@ -16,8 +16,6 @@
package im.vector.app.features.settings.troubleshoot package im.vector.app.features.settings.troubleshoot
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.work.WorkInfo import androidx.work.WorkInfo
@ -38,7 +36,7 @@ class TestEndpointAsTokenRegistration @Inject constructor(
private val unifiedPushHelper: UnifiedPushHelper, private val unifiedPushHelper: UnifiedPushHelper,
) : TroubleshootTest(R.string.settings_troubleshoot_test_endpoint_registration_title) { ) : TroubleshootTest(R.string.settings_troubleshoot_test_endpoint_registration_title) {
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) { override fun perform(testParameters: TestParameters) {
// Check if we have a registered pusher for this token // Check if we have a registered pusher for this token
val endpoint = unifiedPushHelper.getEndpointOrToken() ?: run { val endpoint = unifiedPushHelper.getEndpointOrToken() ?: run {
status = TestStatus.FAILED status = TestStatus.FAILED
@ -66,9 +64,9 @@ class TestEndpointAsTokenRegistration @Inject constructor(
WorkManager.getInstance(context).getWorkInfoByIdLiveData(workId).observe(context, Observer { workInfo -> WorkManager.getInstance(context).getWorkInfoByIdLiveData(workId).observe(context, Observer { workInfo ->
if (workInfo != null) { if (workInfo != null) {
if (workInfo.state == WorkInfo.State.SUCCEEDED) { if (workInfo.state == WorkInfo.State.SUCCEEDED) {
manager?.retry(activityResultLauncher) manager?.retry(testParameters)
} else if (workInfo.state == WorkInfo.State.FAILED) { } else if (workInfo.state == WorkInfo.State.FAILED) {
manager?.retry(activityResultLauncher) manager?.retry(testParameters)
} }
} }
}) })

View File

@ -16,8 +16,6 @@
package im.vector.app.features.settings.troubleshoot package im.vector.app.features.settings.troubleshoot
import android.content.Context import android.content.Context
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.startNotificationSettingsIntent import im.vector.app.core.utils.startNotificationSettingsIntent
@ -34,14 +32,14 @@ class TestNotification @Inject constructor(
) : ) :
TroubleshootTest(R.string.settings_troubleshoot_test_notification_title) { TroubleshootTest(R.string.settings_troubleshoot_test_notification_title) {
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) { override fun perform(testParameters: TestParameters) {
// Display the notification right now // Display the notification right now
notificationUtils.displayDiagnosticNotification() notificationUtils.displayDiagnosticNotification()
description = stringProvider.getString(R.string.settings_troubleshoot_test_notification_notice) description = stringProvider.getString(R.string.settings_troubleshoot_test_notification_notice)
quickFix = object : TroubleshootQuickFix(R.string.open_settings) { quickFix = object : TroubleshootQuickFix(R.string.open_settings) {
override fun doFix() { override fun doFix() {
startNotificationSettingsIntent(context, activityResultLauncher) startNotificationSettingsIntent(context, testParameters.activityResultLauncher)
} }
} }

View File

@ -15,8 +15,6 @@
*/ */
package im.vector.app.features.settings.troubleshoot package im.vector.app.features.settings.troubleshoot
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.error.ErrorFormatter
@ -43,7 +41,7 @@ class TestPushFromPushGateway @Inject constructor(
private var action: Job? = null private var action: Job? = null
private var pushReceived: Boolean = false private var pushReceived: Boolean = false
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) { override fun perform(testParameters: TestParameters) {
pushReceived = false pushReceived = false
action = activeSessionHolder.getActiveSession().coroutineScope.launch { action = activeSessionHolder.getActiveSession().coroutineScope.launch {
val result = runCatching { pushersManager.testPush() } val result = runCatching { pushersManager.testPush() }

View File

@ -15,8 +15,6 @@
*/ */
package im.vector.app.features.settings.troubleshoot package im.vector.app.features.settings.troubleshoot
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
@ -39,7 +37,7 @@ class TestPushRulesSettings @Inject constructor(
RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS
) )
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) { override fun perform(testParameters: TestParameters) {
val session = activeSessionHolder.getSafeActiveSession() ?: return val session = activeSessionHolder.getSafeActiveSession() ?: return
val pushRules = session.pushRuleService().getPushRules().getAllRules() val pushRules = session.pushRuleService().getPushRules().getAllRules()
var oneOrMoreRuleIsOff = false var oneOrMoreRuleIsOff = false

View File

@ -15,34 +15,44 @@
*/ */
package im.vector.app.features.settings.troubleshoot package im.vector.app.features.settings.troubleshoot
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.startNotificationSettingsIntent import im.vector.app.core.utils.startNotificationSettingsIntent
import im.vector.app.features.home.NotificationPermissionManager
import javax.inject.Inject import javax.inject.Inject
/** /**
* Checks if notifications are enable in the system settings for this app. * Checks if notifications are enable in the system settings for this app.
* On Android 13, it will check for the notification permission.
*/ */
class TestSystemSettings @Inject constructor( class TestSystemSettings @Inject constructor(
private val context: FragmentActivity, private val context: FragmentActivity,
private val stringProvider: StringProvider private val stringProvider: StringProvider,
) : private val notificationPermissionManager: NotificationPermissionManager,
TroubleshootTest(R.string.settings_troubleshoot_test_system_settings_title) { ) : TroubleshootTest(R.string.settings_troubleshoot_test_system_settings_title) {
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) { override fun perform(testParameters: TestParameters) {
if (NotificationManagerCompat.from(context).areNotificationsEnabled()) { if (NotificationManagerCompat.from(context).areNotificationsEnabled()) {
description = stringProvider.getString(R.string.settings_troubleshoot_test_system_settings_success) description = stringProvider.getString(R.string.settings_troubleshoot_test_system_settings_success)
quickFix = null quickFix = null
status = TestStatus.SUCCESS status = TestStatus.SUCCESS
} else { } else {
description = stringProvider.getString(R.string.settings_troubleshoot_test_system_settings_failed) if (notificationPermissionManager.isPermissionGranted(context)) {
quickFix = object : TroubleshootQuickFix(R.string.open_settings) { description = stringProvider.getString(R.string.settings_troubleshoot_test_system_settings_failed)
override fun doFix() { quickFix = object : TroubleshootQuickFix(R.string.open_settings) {
startNotificationSettingsIntent(context, activityResultLauncher) override fun doFix() {
startNotificationSettingsIntent(context, testParameters.activityResultLauncher)
}
}
} else {
// In this case, we can ask for user permission
description = stringProvider.getString(R.string.settings_troubleshoot_test_system_settings_permission_failed)
quickFix = object : TroubleshootQuickFix(R.string.grant_permission) {
override fun doFix() {
notificationPermissionManager.askPermission(testParameters.permissionResultLauncher)
}
} }
} }
status = TestStatus.FAILED status = TestStatus.FAILED

View File

@ -16,8 +16,6 @@
package im.vector.app.features.settings.troubleshoot package im.vector.app.features.settings.troubleshoot
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.pushers.UnifiedPushHelper import im.vector.app.core.pushers.UnifiedPushHelper
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
@ -28,7 +26,7 @@ class TestUnifiedPushEndpoint @Inject constructor(
private val unifiedPushHelper: UnifiedPushHelper, private val unifiedPushHelper: UnifiedPushHelper,
) : TroubleshootTest(R.string.settings_troubleshoot_test_current_endpoint_title) { ) : TroubleshootTest(R.string.settings_troubleshoot_test_current_endpoint_title) {
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) { override fun perform(testParameters: TestParameters) {
val endpoint = unifiedPushHelper.getPrivacyFriendlyUpEndpoint() val endpoint = unifiedPushHelper.getPrivacyFriendlyUpEndpoint()
if (endpoint != null) { if (endpoint != null) {
description = stringProvider.getString(R.string.settings_troubleshoot_test_current_endpoint_success, endpoint) description = stringProvider.getString(R.string.settings_troubleshoot_test_current_endpoint_success, endpoint)

View File

@ -16,8 +16,6 @@
package im.vector.app.features.settings.troubleshoot package im.vector.app.features.settings.troubleshoot
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.pushers.UnifiedPushHelper import im.vector.app.core.pushers.UnifiedPushHelper
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
@ -28,7 +26,7 @@ class TestUnifiedPushGateway @Inject constructor(
private val stringProvider: StringProvider private val stringProvider: StringProvider
) : TroubleshootTest(R.string.settings_troubleshoot_test_current_gateway_title) { ) : TroubleshootTest(R.string.settings_troubleshoot_test_current_gateway_title) {
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) { override fun perform(testParameters: TestParameters) {
description = stringProvider.getString( description = stringProvider.getString(
R.string.settings_troubleshoot_test_current_gateway, R.string.settings_troubleshoot_test_current_gateway,
unifiedPushHelper.getPushGateway() unifiedPushHelper.getPushGateway()

View File

@ -22,6 +22,11 @@ import kotlin.properties.Delegates
abstract class TroubleshootTest(@StringRes val titleResId: Int) { abstract class TroubleshootTest(@StringRes val titleResId: Int) {
data class TestParameters(
val activityResultLauncher: ActivityResultLauncher<Intent>,
val permissionResultLauncher: ActivityResultLauncher<Array<String>>
)
enum class TestStatus { enum class TestStatus {
NOT_STARTED, NOT_STARTED,
RUNNING, RUNNING,
@ -40,7 +45,7 @@ abstract class TroubleshootTest(@StringRes val titleResId: Int) {
var manager: NotificationTroubleshootTestManager? = null var manager: NotificationTroubleshootTestManager? = null
abstract fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) abstract fun perform(testParameters: TestParameters)
fun isFinished(): Boolean = (status == TestStatus.FAILED || status == TestStatus.SUCCESS) fun isFinished(): Boolean = (status == TestStatus.FAILED || status == TestStatus.SUCCESS)

View File

@ -40,6 +40,7 @@ import im.vector.app.features.spaces.explore.SpaceDirectoryFragment
import im.vector.app.features.spaces.explore.SpaceDirectoryViewAction import im.vector.app.features.spaces.explore.SpaceDirectoryViewAction
import im.vector.app.features.spaces.explore.SpaceDirectoryViewEvents import im.vector.app.features.spaces.explore.SpaceDirectoryViewEvents
import im.vector.app.features.spaces.explore.SpaceDirectoryViewModel import im.vector.app.features.spaces.explore.SpaceDirectoryViewModel
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
@AndroidEntryPoint @AndroidEntryPoint
class SpaceExploreActivity : VectorBaseActivity<ActivitySimpleBinding>(), MatrixToBottomSheet.InteractionListener { class SpaceExploreActivity : VectorBaseActivity<ActivitySimpleBinding>(), MatrixToBottomSheet.InteractionListener {
@ -80,7 +81,7 @@ class SpaceExploreActivity : VectorBaseActivity<ActivitySimpleBinding>(), Matrix
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, false) supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, false)
if (isFirstCreation()) { if (isFirstCreation()) {
val args = intent?.getParcelableExtra<SpaceDirectoryArgs>(Mavericks.KEY_ARG) val args = intent?.getParcelableExtraCompat<SpaceDirectoryArgs>(Mavericks.KEY_ARG)
replaceFragment( replaceFragment(
views.simpleFragmentContainer, views.simpleFragmentContainer,
SpaceDirectoryFragment::class.java, SpaceDirectoryFragment::class.java,

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