1
0
mirror of https://github.com/vector-im/riotX-android synced 2025-10-05 15:52:47 +02:00

Merge pull request #9051 from element-hq/feature/bma/target35

Change targetApi to 35
This commit is contained in:
Benoit Marty
2025-07-17 15:11:31 +02:00
committed by GitHub
95 changed files with 585 additions and 195 deletions

View File

@@ -1,13 +1,13 @@
ext.versions = [
'minSdk' : 21,
'compileSdk' : 34,
'targetSdk' : 34,
'compileSdk' : 35,
'targetSdk' : 35,
'sourceCompat' : JavaVersion.VERSION_21,
'targetCompat' : JavaVersion.VERSION_21,
'jvmTarget' : "21",
]
def gradle = "8.4.2"
def gradle = "8.11.0"
// Ref: https://kotlinlang.org/releases.html
def kotlin = "1.9.24"
def kotlinCoroutines = "1.8.1"

View File

@@ -42,4 +42,4 @@ signing.element.nightly.keyPassword=Secret
# Customise the Lint version to use a more recent version than the one bundled with AGP
# https://googlesamples.github.io/android-custom-lint-rules/usage/newer-lint.md.html
android.experimental.lint.version=8.6.0-alpha08
android.experimental.lint.version=8.12.0-alpha08

Binary file not shown.

View File

@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=f8b4f4772d302c8ff580bc40d0f56e715de69b163546944f787c87abf209c961
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-all.zip
distributionSha256Sum=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

37
gradlew vendored
View File

@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -83,10 +85,8 @@ done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -114,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
@@ -133,10 +133,13 @@ location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
@@ -144,7 +147,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@@ -152,7 +155,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -197,16 +200,20 @@ if "$cygwin" || "$msys" ; then
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.

26
gradlew.bat vendored
View File

@@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@@ -57,22 +59,22 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell

View File

@@ -131,12 +131,15 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
// the touch coordinates
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
@Suppress("DEPRECATION")
window.setDecorFitsSystemWindows(false)
// New API instead of SYSTEM_UI_FLAG_IMMERSIVE
window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
// New API instead of FLAG_TRANSLUCENT_STATUS
@Suppress("DEPRECATION")
window.statusBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
// new API instead of FLAG_TRANSLUCENT_NAVIGATION
@Suppress("DEPRECATION")
window.navigationBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
} else {
@Suppress("DEPRECATION")
@@ -318,6 +321,7 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
protected open fun shouldAnimateDismiss(): Boolean = true
protected open fun animateClose() {
@Suppress("DEPRECATION")
window.statusBarColor = Color.TRANSPARENT
finish()
}
@@ -334,14 +338,17 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
// Or for "sticky immersive," replace it with SYSTEM_UI_FLAG_IMMERSIVE_STICKY
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
@Suppress("DEPRECATION")
window.setDecorFitsSystemWindows(false)
// new API instead of SYSTEM_UI_FLAG_HIDE_NAVIGATION
window.decorView.windowInsetsController?.hide(WindowInsets.Type.navigationBars())
// New API instead of SYSTEM_UI_FLAG_IMMERSIVE
window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
// New API instead of FLAG_TRANSLUCENT_STATUS
@Suppress("DEPRECATION")
window.statusBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
// New API instead of FLAG_TRANSLUCENT_NAVIGATION
@Suppress("DEPRECATION")
window.navigationBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
} else {
@Suppress("DEPRECATION")
@@ -363,6 +370,7 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
systemUiVisibility = true
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
@Suppress("DEPRECATION")
window.setDecorFitsSystemWindows(false)
} else {
@Suppress("DEPRECATION")

View File

@@ -64,6 +64,9 @@
<!-- Material color -->
<item name="colorPrimary">@color/element_accent_light</item>
<!-- Fix background color of status bar in home and room detail activity -->
<!--item name="colorPrimaryDark">@color/element_background_light</item-->
<item name="colorPrimaryDark">?vctr_toolbar_background</item>
<item name="colorPrimaryVariant">@color/element_accent_light</item>
<item name="colorOnPrimary">@android:color/white</item>
<item name="colorSecondary">@color/element_accent_light</item>

View File

@@ -38,7 +38,8 @@ internal class FormattedJsonHttpLogger(
*/
@Synchronized
override fun log(message: String) {
Timber.v(message)
Timber.v(message.take(20_000))
if (message.length > 20_000) return
// Try to log formatted Json only if there is a chance that [message] contains Json.
// It can be only the case if we log the bodies of Http requests.

View File

@@ -12,6 +12,7 @@ import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Intent
import android.os.Build
import android.view.View
import androidx.core.app.NotificationCompat
import androidx.core.app.Person
import androidx.core.content.getSystemService
@@ -49,7 +50,9 @@ import javax.inject.Inject
class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
override fun getBinding() = ActivityDebugMenuBinding.inflate(layoutInflater)
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
@Inject lateinit var clock: Clock
private lateinit var buffer: ByteArray

View File

@@ -10,6 +10,7 @@ package im.vector.app.features.debug
import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import android.view.View
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
@@ -32,6 +33,9 @@ class DebugPermissionActivity : VectorBaseActivity<ActivityDebugPermissionBindin
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
// For debug
private val allPermissions = listOf(
Manifest.permission.CAMERA,

View File

@@ -7,6 +7,7 @@
package im.vector.app.features.debug.analytics
import android.view.View
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.VectorBaseActivity
@@ -17,6 +18,10 @@ class DebugAnalyticsActivity : VectorBaseActivity<ActivitySimpleBinding>() {
override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater)
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun initUiAndData() {
if (isFirstCreation()) {
addFragment(

View File

@@ -8,6 +8,7 @@
package im.vector.app.features.debug.features
import android.os.Bundle
import android.view.View
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
@@ -24,6 +25,9 @@ class DebugFeaturesSettingsActivity : VectorBaseActivity<FragmentGenericRecycler
override fun getBinding() = FragmentGenericRecyclerBinding.inflate(layoutInflater)
override val rootView: View
get() = views.mainRoot
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
controller.listener = object : FeaturesController.Listener {

View File

@@ -8,6 +8,7 @@
package im.vector.app.features.debug.jitsi
import android.annotation.SuppressLint
import android.view.View
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.application.databinding.ActivityDebugJitsiBinding
@@ -19,6 +20,8 @@ class DebugJitsiActivity : VectorBaseActivity<ActivityDebugJitsiBinding>() {
override fun getBinding() = ActivityDebugJitsiBinding.inflate(layoutInflater)
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
@SuppressLint("SetTextI18n")
override fun initUiAndData() {

View File

@@ -7,6 +7,7 @@
package im.vector.app.features.debug.leak
import android.view.View
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.VectorBaseActivity
@@ -17,6 +18,10 @@ class DebugMemoryLeaksActivity : VectorBaseActivity<ActivitySimpleBinding>() {
override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater)
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun initUiAndData() {
if (isFirstCreation()) {
addFragment(

View File

@@ -7,6 +7,7 @@
package im.vector.app.features.debug.settings
import android.view.View
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.VectorBaseActivity
@@ -17,6 +18,10 @@ class DebugPrivateSettingsActivity : VectorBaseActivity<ActivitySimpleBinding>()
override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater)
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun initUiAndData() {
if (isFirstCreation()) {
addFragment(

View File

@@ -167,8 +167,7 @@
<activity
android:name=".features.home.room.detail.RoomDetailActivity"
android:parentActivityName=".features.home.HomeActivity"
android:windowSoftInputMode="adjustResize">
android:parentActivityName=".features.home.HomeActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".features.home.HomeActivity" />
@@ -369,8 +368,8 @@
<service
android:name=".core.services.CallAndroidService"
android:foregroundServiceType="phoneCall"
android:exported="false">
android:exported="false"
android:foregroundServiceType="phoneCall">
<!-- in order to get headset button events -->
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
@@ -387,7 +386,8 @@
<service
android:name=".features.call.telecom.VectorConnectionAndroidService"
android:exported="false"
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
tools:targetApi="M">
<intent-filter>
<action android:name="android.telecom.ConnectionService" />
</intent-filter>
@@ -413,8 +413,7 @@
android:name=".features.call.audio.MicrophoneAccessService"
android:exported="false"
android:foregroundServiceType="microphone"
android:permission="android.permission.FOREGROUND_SERVICE_MICROPHONE">
</service>
android:permission="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<!-- Receivers -->

View File

@@ -137,7 +137,7 @@ class SpaceStateHandlerImpl @Inject constructor(
override fun popSpaceBackstack(): String? {
vectorPreferences.getSpaceBackstack().toMutableList().apply {
val poppedSpaceId = removeLast()
val poppedSpaceId = removeAt(lastIndex)
vectorPreferences.setSpaceBackstack(this)
return poppedSpaceId
}

View File

@@ -6,6 +6,7 @@
*/
package im.vector.app.core.platform
import android.view.View
import androidx.core.view.isGone
import androidx.core.view.isVisible
import im.vector.app.core.extensions.hideKeyboard
@@ -20,6 +21,9 @@ abstract class SimpleFragmentActivity : VectorBaseActivity<ActivityBinding>() {
final override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun initUiAndData() {
setupToolbar(views.toolbar)
.allowBack(true)

View File

@@ -10,25 +10,26 @@ package im.vector.app.core.platform
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.os.Build
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.WindowInsetsController
import android.view.WindowManager
import android.widget.TextView
import androidx.activity.enableEdgeToEdge
import androidx.annotation.CallSuper
import androidx.annotation.MainThread
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.app.MultiWindowModeChangedInfo
import androidx.core.content.ContextCompat
import androidx.core.util.Consumer
import androidx.core.view.MenuProvider
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
@@ -208,6 +209,7 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
val activityEntryPoint = EntryPointAccessors.fromActivity(this, ActivityEntryPoint::class.java)
ThemeUtils.setActivityTheme(this, getOtherThemes())
viewModelFactory = activityEntryPoint.viewModelFactory()
enableEdgeToEdge()
super.onCreate(savedInstanceState)
addOnMultiWindowModeChangedListener(onMultiWindowModeChangedListener)
setupMenu()
@@ -247,7 +249,9 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
if (vectorPreferences.isNewAppLayoutEnabled()) {
tryOrNull { // Add to XML theme when feature flag is removed
val toolbarBackground = MaterialColors.getColor(views.root, im.vector.lib.ui.styles.R.attr.vctr_toolbar_background)
@Suppress("DEPRECATION")
window.statusBarColor = toolbarBackground
@Suppress("DEPRECATION")
window.navigationBarColor = toolbarBackground
}
}
@@ -334,7 +338,8 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
private fun handleCertificateError(certificateError: GlobalError.CertificateError) {
singletonEntryPoint()
.unrecognizedCertificateDialog()
.show(this,
.show(
this,
certificateError.fingerprint,
object : UnrecognizedCertificateDialog.Callback {
override fun onAccept() {
@@ -411,6 +416,20 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
// Just log that a change occurred.
Timber.w("MDM data has been updated")
}
ViewCompat.setOnApplyWindowInsetsListener(rootView) { v, insets ->
val systemBars = insets.getInsets(
WindowInsetsCompat.Type.systemBars() or
WindowInsetsCompat.Type.displayCutout() or
WindowInsetsCompat.Type.ime()
)
v.updatePadding(
systemBars.left,
systemBars.top,
systemBars.right,
systemBars.bottom,
)
insets
}
}
private val postResumeScheduledActions = mutableListOf<() -> Unit>()
@@ -444,14 +463,6 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
mdmService.unregisterListener(this)
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (hasFocus && displayInFullscreen()) {
setFullScreen()
}
}
private val onMultiWindowModeChangedListener = Consumer<MultiWindowModeChangedInfo> {
Timber.w("onMultiWindowModeChanged. isInMultiWindowMode: ${it.isInMultiWindowMode}")
bugReporter.inMultiWindowMode = it.isInMultiWindowMode
@@ -461,30 +472,6 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
* PRIVATE METHODS
* ========================================================================================== */
/**
* Force to render the activity in fullscreen.
*/
private fun setFullScreen() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
window.setDecorFitsSystemWindows(false)
// New API instead of SYSTEM_UI_FLAG_IMMERSIVE
window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
// New API instead of FLAG_TRANSLUCENT_STATUS
window.statusBarColor = ContextCompat.getColor(this, im.vector.lib.ui.styles.R.color.half_transparent_status_bar)
// New API instead of FLAG_TRANSLUCENT_NAVIGATION
window.navigationBarColor = ContextCompat.getColor(this, im.vector.lib.ui.styles.R.color.half_transparent_status_bar)
} else {
@Suppress("DEPRECATION")
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_FULLSCREEN
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
}
}
private fun handleMenuItemHome(item: MenuItem): Boolean {
return when (item.itemId) {
android.R.id.home -> {
@@ -586,8 +573,6 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
abstract fun getBinding(): VB
open fun displayInFullscreen() = false
open fun doBeforeSetContentView() = Unit
open fun initUiAndData() = Unit
@@ -626,6 +611,8 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
open fun getCoordinatorLayout(): CoordinatorLayout? = null
abstract val rootView: View
/* ==========================================================================================
* User Consent
* ========================================================================================== */

View File

@@ -18,6 +18,10 @@ import androidx.core.content.ContextCompat
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
import java.lang.ref.WeakReference
/**
* It's only used in API 21 and 22 so we will not have security exception on these OS,
* so it's safe to use @Suppress("MissingPermission").
*/
class BluetoothHeadsetReceiver : BroadcastReceiver() {
interface EventListener {
@@ -53,12 +57,15 @@ class BluetoothHeadsetReceiver : BroadcastReceiver() {
}
val device = intent.getParcelableExtraCompat<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
@Suppress("MissingPermission")
val deviceName = device?.name
@Suppress("MissingPermission")
when (device?.bluetoothClass?.deviceClass) {
BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE,
BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO,
BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET -> {
// filter only device that we care about for
@Suppress("MissingPermission")
delegate?.get()?.onBTHeadsetEvent(
BTHeadsetPlugEvent(
plugged = headsetConnected,

View File

@@ -8,11 +8,14 @@
package im.vector.app.core.services
import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Binder
import android.support.v4.media.session.MediaSessionCompat
import android.view.KeyEvent
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import androidx.media.session.MediaButtonReceiver
@@ -150,7 +153,8 @@ class CallAndroidService : VectorAndroidService() {
val isVideoCall = call.mxCall.isVideoCall
val fromBg = intent.getBooleanExtra(EXTRA_IS_IN_BG, false)
Timber.tag(loggerTag.value).v("displayIncomingCallNotification : display the dedicated notification")
val incomingCallAlert = IncomingCallAlert(callId,
val incomingCallAlert = IncomingCallAlert(
callId,
shouldBeDisplayedIn = { activity ->
if (activity is VectorCallActivity) {
activity.intent.getParcelableExtraCompat<CallArgs>(Mavericks.KEY_ARG)?.callId != call.callId
@@ -176,7 +180,11 @@ class CallAndroidService : VectorAndroidService() {
if (knownCalls.isEmpty()) {
startForegroundCompat(callId.hashCode(), notification)
} else {
notificationManager.notify(callId.hashCode(), notification)
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
Timber.w("Not allowed to notify.")
} else {
notificationManager.notify(callId.hashCode(), notification)
}
}
knownCalls[callId] = callInformation
}
@@ -234,7 +242,11 @@ class CallAndroidService : VectorAndroidService() {
if (knownCalls.isEmpty()) {
startForegroundCompat(callId.hashCode(), notification)
} else {
notificationManager.notify(callId.hashCode(), notification)
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
Timber.w("Not allowed to notify.")
} else {
notificationManager.notify(callId.hashCode(), notification)
}
}
knownCalls[callId] = callInformation
}
@@ -258,7 +270,11 @@ class CallAndroidService : VectorAndroidService() {
if (knownCalls.isEmpty()) {
startForegroundCompat(callId.hashCode(), notification)
} else {
notificationManager.notify(callId.hashCode(), notification)
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
Timber.w("Not allowed to notify.")
} else {
notificationManager.notify(callId.hashCode(), notification)
}
}
knownCalls[callId] = callInformation
}

View File

@@ -658,7 +658,8 @@ class ExpandingBottomSheetBehavior<V : View> : CoordinatorLayout.Behavior<V> {
val insetsType = WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.ime()
val imeInsets = insets.getInsets(insetsType)
insetTop = imeInsets.top
insetBottom = imeInsets.bottom
// Now that edgeToEdge is enabled, disable the bottom padding.
insetBottom = 0
insetLeft = imeInsets.left
insetRight = imeInsets.right

View File

@@ -0,0 +1,23 @@
/*
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package im.vector.app.core.utils
import android.content.Context
import android.content.pm.PackageManager
import androidx.core.app.ActivityCompat
import javax.inject.Inject
class PermissionChecker @Inject constructor(
private val applicationContext: Context,
) {
fun checkPermission(vararg permissions: String): Boolean {
return permissions.any { permission ->
ActivityCompat.checkSelfPermission(applicationContext, permission) != PackageManager.PERMISSION_GRANTED
}
}
}

View File

@@ -12,6 +12,7 @@ import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Parcelable
import android.view.View
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
@@ -392,4 +393,7 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
val className = componentName.className
return packageName == buildMeta.applicationId && className in allowList
}
override val rootView: View
get() = views.mainRoot
}

View File

@@ -7,6 +7,7 @@
package im.vector.app.features.analytics.ui.consent
import android.view.View
import com.airbnb.mvrx.viewModel
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment
@@ -29,6 +30,9 @@ class AnalyticsOptInActivity : VectorBaseActivity<ActivitySimpleBinding>() {
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun initUiAndData() {
orientationLocker.lockPhonesToPortrait(this)
if (isFirstCreation()) {

View File

@@ -9,6 +9,7 @@ package im.vector.app.features.attachments.preview
import android.content.Context
import android.content.Intent
import android.view.View
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.VectorBaseActivity
@@ -47,6 +48,9 @@ class AttachmentsPreviewActivity : VectorBaseActivity<ActivitySimpleBinding>() {
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun initUiAndData() {
if (isFirstCreation()) {
val fragmentArgs: AttachmentsPreviewArgs = intent?.extras?.getParcelableCompat(EXTRA_FRAGMENT_ARGS) ?: return

View File

@@ -163,6 +163,7 @@ class AttachmentsPreviewFragment :
private fun applyInsets() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
@Suppress("DEPRECATION")
activity?.window?.setDecorFitsSystemWindows(false)
} else {
@Suppress("DEPRECATION")

View File

@@ -128,7 +128,9 @@ class VectorCallActivity :
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
@Suppress("DEPRECATION")
window.statusBarColor = Color.TRANSPARENT
@Suppress("DEPRECATION")
window.navigationBarColor = Color.BLACK
super.onCreate(savedInstanceState)
addOnPictureInPictureModeChangedListener(pictureInPictureModeChangedInfoConsumer)
@@ -185,6 +187,9 @@ class VectorCallActivity :
override fun getMenuRes() = R.menu.vector_call
override val rootView: View
get() = views.constraintLayout
override fun onUserLeaveHint() {
super.onUserLeaveHint()
enterPictureInPictureIfRequired()

View File

@@ -43,6 +43,11 @@ internal class API21AudioDeviceDetector(
return HashSet<CallAudioManager.Device>().apply {
if (isBluetoothHeadsetOn()) {
connectedBlueToothHeadset?.connectedDevices?.forEach {
// Call requires permission which may be rejected by user: code should explicitly
// check to see if permission is available (with checkPermission) or explicitly
// handle a potential SecurityException
// But it should not happen on API 21/22.
@Suppress("MissingPermission")
add(CallAudioManager.Device.WirelessHeadset(it.name))
}
}

View File

@@ -15,6 +15,7 @@ import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import android.view.View
import android.widget.FrameLayout
import android.widget.Toast
import androidx.core.app.PictureInPictureModeChangedInfo
@@ -58,6 +59,9 @@ class VectorJitsiActivity : VectorBaseActivity<ActivityJitsiBinding>(), JitsiMee
override fun getBinding() = ActivityJitsiBinding.inflate(layoutInflater)
override val rootView: View
get() = views.jitsiLayout
private var jitsiMeetView: JitsiMeetView? = null
private val jitsiViewModel: JitsiCallViewModel by viewModel()

View File

@@ -12,7 +12,6 @@ import android.content.ClipboardManager
import android.content.Context
import android.content.res.ColorStateList
import android.os.Bundle
import android.telephony.PhoneNumberFormattingTextWatcher
import android.telephony.PhoneNumberUtils
import android.text.Editable
import android.text.InputType
@@ -78,7 +77,8 @@ class DialPadFragment : Fragment(), TextWatcher {
digits.inputType = InputType.TYPE_CLASS_PHONE
digits.keyListener = DialerKeyListener.getInstance()
digits.setTextColor(ThemeUtils.getColor(requireContext(), im.vector.lib.ui.styles.R.attr.vctr_content_primary))
digits.addTextChangedListener(PhoneNumberFormattingTextWatcher(if (formatAsYouType) regionCode else ""))
@Suppress("DEPRECATION")
digits.addTextChangedListener(android.telephony.PhoneNumberFormattingTextWatcher(if (formatAsYouType) regionCode else ""))
digits.addTextChangedListener(this)
dialpadView.findViewById<View>(R.id.zero).setOnClickListener { keyPressed(KeyEvent.KEYCODE_0, "0") }
dialpadView.findViewById<View>(R.id.one).setOnClickListener { keyPressed(KeyEvent.KEYCODE_1, "1") }

View File

@@ -11,6 +11,7 @@ import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Parcelable
import android.view.View
import com.airbnb.mvrx.Mavericks
import com.airbnb.mvrx.viewModel
import com.google.android.material.tabs.TabLayoutMediator
@@ -37,6 +38,9 @@ class CallTransferActivity : VectorBaseActivity<ActivityCallTransferBinding>() {
override fun getCoordinatorLayout() = views.vectorCoordinatorLayout
override val rootView: View
get() = views.vectorCoordinatorLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
waitingView = views.waitingView.waitingView

View File

@@ -122,6 +122,7 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetBoot
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val rootView = super.onCreateView(inflater, container, savedInstanceState)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
@Suppress("DEPRECATION")
dialog?.window?.setDecorFitsSystemWindows(false)
} else {
@Suppress("DEPRECATION")

View File

@@ -192,6 +192,9 @@ class HomeActivity :
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun getBinding() = ActivityHomeBinding.inflate(layoutInflater)
override fun onCreate(savedInstanceState: Bundle?) {

View File

@@ -14,7 +14,6 @@ import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.core.view.GravityCompat
import androidx.core.view.WindowCompat
import androidx.drawerlayout.widget.DrawerLayout
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
@@ -82,6 +81,9 @@ class RoomDetailActivity :
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
@Inject lateinit var playbackTracker: AudioMessagePlaybackTracker
private lateinit var sharedActionViewModel: RoomDetailSharedActionViewModel
private val requireActiveMembershipViewModel: RequireActiveMembershipViewModel by viewModel()
@@ -93,7 +95,7 @@ class RoomDetailActivity :
super.onCreate(savedInstanceState)
// For dealing with insets and status bar background color
WindowCompat.setDecorFitsSystemWindows(window, false)
@Suppress("DEPRECATION")
window.statusBarColor = Color.TRANSPARENT
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, false)

View File

@@ -32,11 +32,9 @@ import androidx.core.net.toUri
import androidx.core.text.toSpannable
import androidx.core.util.Pair
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.forEach
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.fragment.app.setFragmentResultListener
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.withResumed
@@ -409,13 +407,6 @@ class TimelineFragment :
is RoomDetailViewEvents.RevokeFilePermission -> revokeFilePermission(it)
}
}
ViewCompat.setOnApplyWindowInsetsListener(views.coordinatorLayout) { _, insets ->
val imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime() or WindowInsetsCompat.Type.systemBars())
views.appBarLayout.updatePadding(top = imeInsets.top)
views.voiceMessageRecorderContainer.updatePadding(bottom = imeInsets.bottom)
insets
}
}
private fun setupBackPressHandling() {

View File

@@ -10,6 +10,7 @@ package im.vector.app.features.home.room.detail.search
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.appcompat.widget.SearchView
import com.airbnb.mvrx.Mavericks
import dagger.hilt.android.AndroidEntryPoint
@@ -30,6 +31,9 @@ class SearchActivity : VectorBaseActivity<ActivitySearchBinding>() {
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupToolbar(views.searchToolbar)

View File

@@ -10,6 +10,7 @@ package im.vector.app.features.home.room.filtered
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.appcompat.widget.SearchView
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.replaceFragment
@@ -32,6 +33,9 @@ class FilteredRoomsActivity : VectorBaseActivity<ActivityFilteredRoomsBinding>()
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
analyticsScreenName = MobileScreen.ScreenName.RoomFilter

View File

@@ -7,6 +7,7 @@
package im.vector.app.features.home.room.list.home.invites
import android.view.View
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.VectorBaseActivity
@@ -22,4 +23,8 @@ class InvitesActivity : VectorBaseActivity<ActivitySimpleBinding>() {
addFragment(views.simpleFragmentContainer, InvitesFragment::class.java)
}
}
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
}

View File

@@ -7,6 +7,7 @@
package im.vector.app.features.home.room.list.home.release
import android.view.View
import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment
@@ -26,6 +27,9 @@ class ReleaseNotesActivity : VectorBaseActivity<ActivitySimpleBinding>() {
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun initUiAndData() {
orientationLocker.lockPhonesToPortrait(this)
if (isFirstCreation()) {

View File

@@ -10,6 +10,7 @@ package im.vector.app.features.home.room.threads
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.fragment.app.FragmentTransaction
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragmentToBackstack
@@ -42,6 +43,9 @@ class ThreadsActivity : VectorBaseActivity<ActivityThreadsBinding>() {
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initFragment()

View File

@@ -32,7 +32,7 @@ class VectorActivityLifecycleCallbacks constructor(private val popupAlertManager
/**
* The activities information collected from the app manifest.
*/
private var activitiesInfo: Array<ActivityInfo> = emptyArray()
private var activitiesInfo: List<ActivityInfo>? = null
private val coroutineScope = CoroutineScope(SupervisorJob())
@@ -51,24 +51,32 @@ class VectorActivityLifecycleCallbacks constructor(private val popupAlertManager
override fun onActivityStopped(activity: Activity) {}
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
if (activitiesInfo.isEmpty()) {
if (activitiesInfo == null) {
val context = activity.applicationContext
val packageManager: PackageManager = context.packageManager
// Get all activities from element android
activitiesInfo = packageManager.getPackageInfoCompat(context.packageName, PackageManager.GET_ACTIVITIES).activities
val activities = packageManager
.getPackageInfoCompat(context.packageName, PackageManager.GET_ACTIVITIES)
.activities
.orEmpty()
.toList()
// Get all activities from PermissionController module
// See https://source.android.com/docs/core/architecture/modular-system/permissioncontroller#package-format
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S_V2) {
activitiesInfo += tryOrNull {
val otherActivities = if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S_V2) {
(tryOrNull {
packageManager.getPackageInfoCompat("com.google.android.permissioncontroller", PackageManager.GET_ACTIVITIES).activities
} ?: tryOrNull {
packageManager.getModuleInfo("com.google.android.permission", 1).packageName?.let {
packageManager.getPackageInfoCompat(it, PackageManager.GET_ACTIVITIES or PackageManager.MATCH_APEX).activities
}
}.orEmpty()
})
.orEmpty()
.toList()
} else {
emptyList()
}
activitiesInfo = activities + otherActivities
}
// restart the app if the task contains an unknown activity
@@ -144,5 +152,5 @@ class VectorActivityLifecycleCallbacks constructor(private val popupAlertManager
* @param activity the activity of the task
* @return true if the activity is potentially malicious
*/
private fun isPotentialMaliciousActivity(activity: ComponentName): Boolean = activitiesInfo.none { it.name == activity.className }
private fun isPotentialMaliciousActivity(activity: ComponentName): Boolean = activitiesInfo.orEmpty().none { it.name == activity.className }
}

View File

@@ -10,6 +10,7 @@ package im.vector.app.features.link
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.View
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.viewModel
import com.google.android.material.dialog.MaterialAlertDialogBuilder
@@ -41,6 +42,9 @@ class LinkHandlerActivity : VectorBaseActivity<ActivityProgressBinding>() {
override fun getBinding() = ActivityProgressBinding.inflate(layoutInflater)
override val rootView: View
get() = views.mainRoot
override fun initUiAndData() {
handleIntent()
}

View File

@@ -10,6 +10,7 @@ package im.vector.app.features.location
import android.content.Context
import android.content.Intent
import android.os.Parcelable
import android.view.View
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.VectorBaseActivity
@@ -31,6 +32,9 @@ class LocationSharingActivity : VectorBaseActivity<ActivityLocationSharingBindin
override fun getBinding() = ActivityLocationSharingBinding.inflate(layoutInflater)
override val rootView: View
get() = views.mainRoot
override fun initUiAndData() {
val locationSharingArgs: LocationSharingArgs? = intent?.extras?.getParcelableCompat(EXTRA_LOCATION_SHARING_ARGS)
if (locationSharingArgs == null) {

View File

@@ -7,6 +7,7 @@
package im.vector.app.features.location
import android.Manifest
import android.graphics.drawable.Drawable
import com.airbnb.mvrx.MavericksViewModelFactory
import dagger.assisted.Assisted
@@ -15,6 +16,7 @@ import dagger.assisted.AssistedInject
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.utils.PermissionChecker
import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
import im.vector.app.features.location.domain.usecase.CompareLocationsUseCase
import im.vector.app.features.powerlevel.PowerLevelsFlowFactory
@@ -48,6 +50,7 @@ class LocationSharingViewModel @AssistedInject constructor(
private val session: Session,
private val compareLocationsUseCase: CompareLocationsUseCase,
private val vectorPreferences: VectorPreferences,
private val permissionChecker: PermissionChecker,
) : VectorViewModel<LocationSharingViewState, LocationSharingAction, LocationSharingViewEvents>(initialState), LocationTracker.Callback {
private val room = session.getRoom(initialState.roomId)!!
@@ -88,7 +91,15 @@ class LocationSharingViewModel @AssistedInject constructor(
locationTracker.locations
.onEach(::onLocationUpdate)
.launchIn(viewModelScope)
locationTracker.start()
if (permissionChecker.checkPermission(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
)
) {
locationTracker.start()
} else {
Timber.w("Not allowed to use location api.")
}
}
private fun setUserItem() {

View File

@@ -17,6 +17,7 @@ import androidx.core.content.getSystemService
import androidx.core.location.LocationListenerCompat
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.resources.BuildMeta
import im.vector.app.core.utils.PermissionChecker
import im.vector.app.features.session.coroutineScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
@@ -37,6 +38,7 @@ class LocationTracker @Inject constructor(
context: Context,
private val activeSessionHolder: ActiveSessionHolder,
private val buildMeta: BuildMeta,
private val permissionChecker: PermissionChecker,
) : LocationListenerCompat {
private val locationManager = context.getSystemService<LocationManager>()
@@ -173,7 +175,15 @@ class LocationTracker @Inject constructor(
fun removeCallback(callback: Callback) {
callbacks.remove(callback)
if (callbacks.size == 0) {
stop()
if (permissionChecker.checkPermission(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
)
) {
stop()
} else {
Timber.w("Not allowed to use location api.")
}
}
}

View File

@@ -10,6 +10,7 @@ package im.vector.app.features.location.live.map
import android.content.Context
import android.content.Intent
import android.os.Parcelable
import android.view.View
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.VectorBaseActivity
@@ -29,6 +30,9 @@ class LiveLocationMapViewActivity : VectorBaseActivity<ActivityLocationSharingBi
override fun getBinding() = ActivityLocationSharingBinding.inflate(layoutInflater)
override val rootView: View
get() = views.mainRoot
override fun initUiAndData() {
val mapViewArgs: LiveLocationMapViewArgs? = intent?.extras?.getParcelableCompat(EXTRA_LIVE_LOCATION_MAP_VIEW_ARGS)
if (mapViewArgs == null) {

View File

@@ -7,6 +7,7 @@
package im.vector.app.features.location.live.map
import android.Manifest
import com.airbnb.mvrx.MavericksViewModelFactory
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -14,6 +15,7 @@ import dagger.assisted.AssistedInject
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.utils.PermissionChecker
import im.vector.app.features.location.LocationData
import im.vector.app.features.location.LocationTracker
import im.vector.app.features.location.live.StopLiveLocationShareUseCase
@@ -23,6 +25,7 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult
import timber.log.Timber
class LiveLocationMapViewModel @AssistedInject constructor(
@Assisted private val initialState: LiveLocationMapViewState,
@@ -31,6 +34,7 @@ class LiveLocationMapViewModel @AssistedInject constructor(
private val locationSharingServiceConnection: LocationSharingServiceConnection,
private val stopLiveLocationShareUseCase: StopLiveLocationShareUseCase,
private val locationTracker: LocationTracker,
private val permissionChecker: PermissionChecker,
) :
VectorViewModel<LiveLocationMapViewState, LiveLocationMapAction, LiveLocationMapViewEvents>(initialState),
LocationSharingServiceConnection.Callback,
@@ -123,7 +127,15 @@ class LiveLocationMapViewModel @AssistedInject constructor(
copy(isLoadingUserLocation = true)
}
viewModelScope.launch(session.coroutineDispatchers.main) {
locationTracker.start()
if (permissionChecker.checkPermission(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
)
) {
locationTracker.start()
} else {
Timber.w("Not allowed to use location api.")
}
locationTracker.requestLastKnownLocation()
}
}

View File

@@ -7,14 +7,18 @@
package im.vector.app.features.location.live.tracking
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.os.IBinder
import android.os.Parcelable
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationManagerCompat
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.extensions.startForegroundCompat
import im.vector.app.core.services.VectorAndroidService
import im.vector.app.core.utils.PermissionChecker
import im.vector.app.features.location.LocationData
import im.vector.app.features.location.LocationTracker
import im.vector.app.features.location.live.GetLiveLocationShareSummaryUseCase
@@ -52,6 +56,7 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
@Inject lateinit var getLiveLocationShareSummaryUseCase: GetLiveLocationShareSummaryUseCase
@Inject lateinit var checkIfEventIsRedactedUseCase: CheckIfEventIsRedactedUseCase
@Inject lateinit var permissionChecker: PermissionChecker
private var binder: LocationSharingAndroidServiceBinder? = null
@@ -74,7 +79,15 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
private fun initLocationTracking() {
// Start tracking location
locationTracker.addCallback(this)
locationTracker.start()
if (permissionChecker.checkPermission(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
)
) {
locationTracker.start()
} else {
Timber.w("Not allowed to use location api.")
}
launchWithActiveSession { session ->
val job = locationTracker.locations
@@ -95,7 +108,11 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
// Show a sticky notification
val notification = liveLocationNotificationBuilder.buildLiveLocationSharingNotification(roomArgs.roomId)
if (foregroundModeStarted) {
NotificationManagerCompat.from(this).notify(FOREGROUND_SERVICE_NOTIFICATION_ID, notification)
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
Timber.w("Not allowed to notify.")
} else {
NotificationManagerCompat.from(this).notify(FOREGROUND_SERVICE_NOTIFICATION_ID, notification)
}
} else {
startForegroundCompat(FOREGROUND_SERVICE_NOTIFICATION_ID, notification)
foregroundModeStarted = true
@@ -146,10 +163,14 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
}
private fun updateNotification() {
if (liveInfoSet.isNotEmpty()) {
val roomId = liveInfoSet.last().roomArgs.roomId
val notification = liveLocationNotificationBuilder.buildLiveLocationSharingNotification(roomId)
NotificationManagerCompat.from(this).notify(FOREGROUND_SERVICE_NOTIFICATION_ID, notification)
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
Timber.w("Not allowed to notify.")
} else {
if (liveInfoSet.isNotEmpty()) {
val roomId = liveInfoSet.last().roomArgs.roomId
val notification = liveLocationNotificationBuilder.buildLiveLocationSharingNotification(roomId)
NotificationManagerCompat.from(this).notify(FOREGROUND_SERVICE_NOTIFICATION_ID, notification)
}
}
}

View File

@@ -7,6 +7,7 @@
package im.vector.app.features.location.preview
import android.Manifest
import com.airbnb.mvrx.MavericksViewModelFactory
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -14,6 +15,7 @@ import dagger.assisted.AssistedInject
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.utils.PermissionChecker
import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
import im.vector.app.features.location.LocationData
import im.vector.app.features.location.LocationTracker
@@ -23,12 +25,14 @@ import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.util.MatrixItem
import org.matrix.android.sdk.api.util.toMatrixItem
import timber.log.Timber
class LocationPreviewViewModel @AssistedInject constructor(
@Assisted private val initialState: LocationPreviewViewState,
private val session: Session,
private val locationPinProvider: LocationPinProvider,
private val locationTracker: LocationTracker,
private val permissionChecker: PermissionChecker,
) : VectorViewModel<LocationPreviewViewState, LocationPreviewAction, LocationPreviewViewEvents>(initialState), LocationTracker.Callback {
@AssistedFactory
@@ -89,7 +93,15 @@ class LocationPreviewViewModel @AssistedInject constructor(
copy(isLoadingUserLocation = true)
}
viewModelScope.launch(session.coroutineDispatchers.main) {
locationTracker.start()
if (permissionChecker.checkPermission(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
)
) {
locationTracker.start()
} else {
Timber.w("Not allowed to use location api.")
}
locationTracker.requestLastKnownLocation()
}
}

View File

@@ -76,6 +76,9 @@ open class LoginActivity : VectorBaseActivity<ActivityLoginBinding>(), UnlockedA
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun initUiAndData() {
analyticsScreenName = MobileScreen.ScreenName.Login

View File

@@ -10,6 +10,7 @@ package im.vector.app.features.media
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.core.net.toUri
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.di.ActiveSessionHolder
@@ -26,6 +27,9 @@ class BigImageViewerActivity : VectorBaseActivity<ActivityBigImageViewerBinding>
override fun getBinding() = ActivityBigImageViewerBinding.inflate(layoutInflater)
override val rootView: View
get() = views.mainRoot
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View File

@@ -138,7 +138,9 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), AttachmentInt
}
}
@Suppress("DEPRECATION")
window.statusBarColor = ContextCompat.getColor(this, im.vector.lib.ui.styles.R.color.black_alpha)
@Suppress("DEPRECATION")
window.navigationBarColor = ContextCompat.getColor(this, im.vector.lib.ui.styles.R.color.black_alpha)
observeViewEvents()

View File

@@ -7,18 +7,27 @@
package im.vector.app.features.notifications
import android.Manifest
import android.app.Notification
import android.content.Context
import android.content.pm.PackageManager
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationManagerCompat
import timber.log.Timber
import javax.inject.Inject
class NotificationDisplayer @Inject constructor(context: Context) {
class NotificationDisplayer @Inject constructor(
private val context: Context,
) {
private val notificationManager = NotificationManagerCompat.from(context)
fun showNotificationMessage(tag: String?, id: Int, notification: Notification) {
notificationManager.notify(tag, id, notification)
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
Timber.w("Not allowed to notify.")
} else {
notificationManager.notify(tag, id, notification)
}
}
fun cancelNotificationMessage(tag: String?, id: Int) {

View File

@@ -9,6 +9,7 @@
package im.vector.app.features.notifications
import android.Manifest
import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationChannel
@@ -16,6 +17,7 @@ import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Canvas
import android.net.Uri
@@ -27,6 +29,7 @@ import androidx.annotation.AttrRes
import androidx.annotation.ChecksSdkIntAtLeast
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.RemoteInput
@@ -153,55 +156,59 @@ class NotificationUtils @Inject constructor(
* Default notification importance: shows everywhere, makes noise, but does not visually
* intrude.
*/
notificationManager.createNotificationChannel(NotificationChannel(
NOISY_NOTIFICATION_CHANNEL_ID,
stringProvider.getString(CommonStrings.notification_noisy_notifications).ifEmpty { "Noisy notifications" },
NotificationManager.IMPORTANCE_DEFAULT
)
.apply {
description = stringProvider.getString(CommonStrings.notification_noisy_notifications)
enableVibration(true)
enableLights(true)
lightColor = accentColor
})
notificationManager.createNotificationChannel(
NotificationChannel(
NOISY_NOTIFICATION_CHANNEL_ID,
stringProvider.getString(CommonStrings.notification_noisy_notifications).ifEmpty { "Noisy notifications" },
NotificationManager.IMPORTANCE_DEFAULT
)
.apply {
description = stringProvider.getString(CommonStrings.notification_noisy_notifications)
enableVibration(true)
enableLights(true)
lightColor = accentColor
})
/**
* Low notification importance: shows everywhere, but is not intrusive.
*/
notificationManager.createNotificationChannel(NotificationChannel(
SILENT_NOTIFICATION_CHANNEL_ID,
stringProvider.getString(CommonStrings.notification_silent_notifications).ifEmpty { "Silent notifications" },
NotificationManager.IMPORTANCE_LOW
)
.apply {
description = stringProvider.getString(CommonStrings.notification_silent_notifications)
setSound(null, null)
enableLights(true)
lightColor = accentColor
})
notificationManager.createNotificationChannel(
NotificationChannel(
SILENT_NOTIFICATION_CHANNEL_ID,
stringProvider.getString(CommonStrings.notification_silent_notifications).ifEmpty { "Silent notifications" },
NotificationManager.IMPORTANCE_LOW
)
.apply {
description = stringProvider.getString(CommonStrings.notification_silent_notifications)
setSound(null, null)
enableLights(true)
lightColor = accentColor
})
notificationManager.createNotificationChannel(NotificationChannel(
LISTENING_FOR_EVENTS_NOTIFICATION_CHANNEL_ID,
stringProvider.getString(CommonStrings.notification_listening_for_events).ifEmpty { "Listening for events" },
NotificationManager.IMPORTANCE_MIN
)
.apply {
description = stringProvider.getString(CommonStrings.notification_listening_for_events)
setSound(null, null)
setShowBadge(false)
})
notificationManager.createNotificationChannel(
NotificationChannel(
LISTENING_FOR_EVENTS_NOTIFICATION_CHANNEL_ID,
stringProvider.getString(CommonStrings.notification_listening_for_events).ifEmpty { "Listening for events" },
NotificationManager.IMPORTANCE_MIN
)
.apply {
description = stringProvider.getString(CommonStrings.notification_listening_for_events)
setSound(null, null)
setShowBadge(false)
})
notificationManager.createNotificationChannel(NotificationChannel(
CALL_NOTIFICATION_CHANNEL_ID,
stringProvider.getString(CommonStrings.call).ifEmpty { "Call" },
NotificationManager.IMPORTANCE_HIGH
)
.apply {
description = stringProvider.getString(CommonStrings.call)
setSound(null, null)
enableLights(true)
lightColor = accentColor
})
notificationManager.createNotificationChannel(
NotificationChannel(
CALL_NOTIFICATION_CHANNEL_ID,
stringProvider.getString(CommonStrings.call).ifEmpty { "Call" },
NotificationManager.IMPORTANCE_HIGH
)
.apply {
description = stringProvider.getString(CommonStrings.call)
setSound(null, null)
enableLights(true)
lightColor = accentColor
})
}
fun getChannel(channelId: String): NotificationChannel? {
@@ -997,7 +1004,11 @@ class NotificationUtils @Inject constructor(
}
fun showNotificationMessage(tag: String?, id: Int, notification: Notification) {
notificationManager.notify(tag, id, notification)
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
Timber.w("Not allowed to notify.")
} else {
notificationManager.notify(tag, id, notification)
}
}
fun cancelNotificationMessage(tag: String?, id: Int) {
@@ -1025,30 +1036,34 @@ class NotificationUtils @Inject constructor(
@SuppressLint("LaunchActivityFromNotification")
fun displayDiagnosticNotification() {
val testActionIntent = Intent(context, TestNotificationReceiver::class.java)
testActionIntent.action = actionIds.diagnostic
val testPendingIntent = PendingIntent.getBroadcast(
context,
0,
testActionIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
)
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
Timber.w("Not allowed to notify.")
} else {
val testActionIntent = Intent(context, TestNotificationReceiver::class.java)
testActionIntent.action = actionIds.diagnostic
val testPendingIntent = PendingIntent.getBroadcast(
context,
0,
testActionIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
)
notificationManager.notify(
"DIAGNOSTIC",
888,
NotificationCompat.Builder(context, NOISY_NOTIFICATION_CHANNEL_ID)
.setContentTitle(buildMeta.applicationName)
.setContentText(stringProvider.getString(CommonStrings.settings_troubleshoot_test_push_notification_content))
.setSmallIcon(R.drawable.ic_notification)
.setLargeIcon(getBitmap(context, im.vector.lib.ui.styles.R.drawable.element_logo_green))
.setColor(ContextCompat.getColor(context, im.vector.lib.ui.styles.R.color.notification_accent_color))
.setPriority(NotificationCompat.PRIORITY_MAX)
.setCategory(NotificationCompat.CATEGORY_STATUS)
.setAutoCancel(true)
.setContentIntent(testPendingIntent)
.build()
)
notificationManager.notify(
"DIAGNOSTIC",
888,
NotificationCompat.Builder(context, NOISY_NOTIFICATION_CHANNEL_ID)
.setContentTitle(buildMeta.applicationName)
.setContentText(stringProvider.getString(CommonStrings.settings_troubleshoot_test_push_notification_content))
.setSmallIcon(R.drawable.ic_notification)
.setLargeIcon(getBitmap(context, im.vector.lib.ui.styles.R.drawable.element_logo_green))
.setColor(ContextCompat.getColor(context, im.vector.lib.ui.styles.R.color.notification_accent_color))
.setPriority(NotificationCompat.PRIORITY_MAX)
.setCategory(NotificationCompat.CATEGORY_STATUS)
.setAutoCancel(true)
.setContentIntent(testPendingIntent)
.build()
)
}
}
private fun getBitmap(context: Context, @DrawableRes drawableRes: Int): Bitmap? {

View File

@@ -10,6 +10,7 @@ package im.vector.app.features.onboarding
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.view.View
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.lazyViewModel
import im.vector.app.core.extensions.validateBackPressed
@@ -33,6 +34,9 @@ class OnboardingActivity : VectorBaseActivity<ActivityLoginBinding>(), UnlockedA
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
onboardingVariant.onNewIntent(intent)

View File

@@ -9,6 +9,7 @@ package im.vector.app.features.pin
import android.content.Context
import android.content.Intent
import android.view.View
import com.airbnb.mvrx.Mavericks
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment
@@ -31,6 +32,9 @@ class PinActivity : VectorBaseActivity<ActivitySimpleBinding>(), UnlockedActivit
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun initUiAndData() {
if (isFirstCreation()) {
val fragmentArgs: PinArgs = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG) ?: return

View File

@@ -92,7 +92,7 @@ class LockScreenCodeView @JvmOverloads constructor(
*/
fun deleteLast(): Int {
if (code.size == 0) return code.size
code.removeLast()
code.removeAt(code.lastIndex)
getCodeView(code.size)?.toggle()
return code.size
}

View File

@@ -10,6 +10,7 @@ package im.vector.app.features.qrcode
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import com.airbnb.mvrx.viewModel
@@ -26,6 +27,9 @@ class QrCodeScannerActivity : VectorBaseActivity<ActivitySimpleBinding>() {
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
private val qrViewModel: QrCodeScannerViewModel by viewModel()
override fun onCreate(savedInstanceState: Bundle?) {

View File

@@ -11,6 +11,7 @@ import android.content.Context
import android.content.Intent
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.Toast
import androidx.core.view.isVisible
import androidx.core.widget.doOnTextChanged
@@ -37,6 +38,9 @@ class BugReportActivity :
private val viewModel: BugReportViewModel by viewModel()
override val rootView: View
get() = views.mainRoot
private var reportType: ReportType = ReportType.BUG_REPORT
override fun initUiAndData() {

View File

@@ -13,6 +13,7 @@ import android.graphics.Typeface
import android.util.TypedValue
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.SearchView
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
@@ -55,6 +56,9 @@ class EmojiReactionPickerActivity :
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun getTitleRes() = CommonStrings.title_activity_emoji_reaction_picker
@Inject lateinit var emojiCompatFontProvider: EmojiCompatFontProvider

View File

@@ -10,6 +10,7 @@ package im.vector.app.features.roomdirectory
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.viewModel
import com.airbnb.mvrx.withState
@@ -41,6 +42,9 @@ class RoomDirectoryActivity : VectorBaseActivity<ActivitySimpleBinding>(), Matri
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
analyticsScreenName = MobileScreen.ScreenName.RoomDirectory

View File

@@ -11,6 +11,7 @@ import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.Mavericks
import dagger.hilt.android.AndroidEntryPoint
@@ -36,6 +37,9 @@ class CreateRoomActivity : VectorBaseActivity<ActivitySimpleBinding>() {
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun initUiAndData() {
if (isFirstCreation()) {
val fragmentArgs: CreateRoomArgs = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG) ?: return

View File

@@ -10,6 +10,7 @@ package im.vector.app.features.roomdirectory.roompreview
import android.content.Context
import android.content.Intent
import android.os.Parcelable
import android.view.View
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.VectorBaseActivity
@@ -73,6 +74,9 @@ class RoomPreviewActivity : VectorBaseActivity<ActivitySimpleBinding>() {
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.simpleFragmentContainer
override fun initUiAndData() {
if (isFirstCreation()) {
val args = intent.getParcelableExtraCompat<RoomPreviewData>(ARG)

View File

@@ -9,6 +9,7 @@ package im.vector.app.features.roommemberprofile
import android.content.Context
import android.content.Intent
import android.view.View
import android.widget.Toast
import com.airbnb.mvrx.Mavericks
import com.airbnb.mvrx.viewModel
@@ -37,6 +38,11 @@ class RoomMemberProfileActivity : VectorBaseActivity<ActivitySimpleBinding>() {
return ActivitySimpleBinding.inflate(layoutInflater)
}
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun initUiAndData() {
if (isFirstCreation()) {
val fragmentArgs: RoomMemberProfileArgs = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG) ?: return

View File

@@ -9,6 +9,7 @@ package im.vector.app.features.roomprofile
import android.content.Context
import android.content.Intent
import android.view.View
import android.widget.Toast
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.Mavericks
@@ -68,6 +69,9 @@ class RoomProfileActivity :
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun initUiAndData() {
sharedActionViewModel = viewModelProvider.get(RoomProfileSharedActionViewModel::class.java)
roomProfileArgs = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG) ?: return

View File

@@ -10,6 +10,7 @@ package im.vector.app.features.roomprofile.polls.detail.ui
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import com.airbnb.mvrx.Mavericks
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment
@@ -25,6 +26,11 @@ class RoomPollDetailActivity : VectorBaseActivity<ActivitySimpleBinding>() {
override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater)
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View File

@@ -10,6 +10,7 @@ package im.vector.app.features.roomprofile.settings.joinrule
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.core.view.isVisible
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
@@ -40,6 +41,11 @@ class RoomJoinRuleActivity : VectorBaseActivity<ActivitySimpleBinding>() {
override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater)
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
private lateinit var roomProfileArgs: RoomProfileArgs
val viewModel: RoomJoinRuleChooseRestrictedViewModel by viewModel()

View File

@@ -11,6 +11,7 @@ import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Parcelable
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.preference.Preference
@@ -47,6 +48,9 @@ class VectorSettingsActivity : VectorBaseActivity<ActivityVectorSettingsBinding>
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun getTitleRes() = CommonStrings.title_activity_settings
private var keyToHighlight: String? = null

View File

@@ -10,6 +10,7 @@ package im.vector.app.features.settings.devices.v2.rename
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.view.WindowManager
import com.airbnb.mvrx.Mavericks
import dagger.hilt.android.AndroidEntryPoint
@@ -26,6 +27,11 @@ class RenameSessionActivity : VectorBaseActivity<ActivitySimpleBinding>() {
override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater)
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View File

@@ -7,6 +7,7 @@
package im.vector.app.features.settings.font
import android.view.View
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.VectorBaseActivity
@@ -17,6 +18,11 @@ class FontScaleSettingActivity : VectorBaseActivity<ActivitySimpleBinding>() {
override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater)
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun initUiAndData() {
if (isFirstCreation()) {
addFragment(views.simpleFragmentContainer, FontScaleSettingFragment::class.java)

View File

@@ -9,6 +9,7 @@ package im.vector.app.features.share
import android.content.Intent
import android.os.Bundle
import android.view.View
import com.airbnb.mvrx.viewModel
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment
@@ -46,6 +47,9 @@ class IncomingShareActivity : VectorBaseActivity<ActivitySimpleBinding>() {
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
private fun handleAppStarted() {
// If we are not logged in, stop the sharing process and open login screen.
// In the future, we might want to relaunch the sharing process after login.

View File

@@ -10,6 +10,7 @@ package im.vector.app.features.signout.hard
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivitySignedOutBinding
@@ -26,6 +27,9 @@ class SignedOutActivity : VectorBaseActivity<ActivitySignedOutBinding>() {
override fun getBinding() = ActivitySignedOutBinding.inflate(layoutInflater)
override val rootView: View
get() = views.signedOut
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View File

@@ -11,6 +11,7 @@ import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import com.airbnb.mvrx.Mavericks
@@ -38,6 +39,11 @@ class SpaceExploreActivity : VectorBaseActivity<ActivitySimpleBinding>(), Matrix
override fun getBinding(): ActivitySimpleBinding = ActivitySimpleBinding.inflate(layoutInflater)
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun getTitleRes(): Int = CommonStrings.space_explore_activity_title
val sharedViewModel: SpaceDirectoryViewModel by viewModel()

View File

@@ -10,6 +10,7 @@ package im.vector.app.features.spaces
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.Mavericks
import dagger.hilt.android.AndroidEntryPoint
@@ -29,6 +30,11 @@ class SpacePreviewActivity : VectorBaseActivity<ActivitySimpleBinding>() {
override fun getBinding(): ActivitySimpleBinding = ActivitySimpleBinding.inflate(layoutInflater)
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sharedActionViewModel = viewModelProvider.get(SpacePreviewSharedActionViewModel::class.java)

View File

@@ -10,6 +10,7 @@ package im.vector.app.features.spaces.leave
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.core.view.isGone
import androidx.core.view.isVisible
import com.airbnb.mvrx.Fail
@@ -33,6 +34,11 @@ class SpaceLeaveAdvancedActivity : VectorBaseActivity<ActivitySimpleLoadingBindi
override fun getBinding(): ActivitySimpleLoadingBinding = ActivitySimpleLoadingBinding.inflate(layoutInflater)
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
private val leaveViewModel: SpaceLeaveAdvancedViewModel by viewModel()
override fun showWaitingView(text: String?) {

View File

@@ -11,6 +11,7 @@ import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Parcelable
import android.view.View
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
@@ -49,6 +50,11 @@ class SpaceManageActivity : VectorBaseActivity<ActivitySimpleLoadingBinding>() {
override fun getBinding(): ActivitySimpleLoadingBinding = ActivitySimpleLoadingBinding.inflate(layoutInflater)
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
override fun getTitleRes(): Int = CommonStrings.space_add_existing_rooms
val sharedViewModel: SpaceManageSharedViewModel by viewModel()

View File

@@ -10,6 +10,7 @@ package im.vector.app.features.spaces.people
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
@@ -32,6 +33,11 @@ class SpacePeopleActivity : VectorBaseActivity<ActivitySimpleLoadingBinding>() {
override fun getBinding() = ActivitySimpleLoadingBinding.inflate(layoutInflater)
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
private lateinit var sharedActionViewModel: SpacePeopleSharedActionViewModel
override fun initUiAndData() {

View File

@@ -11,6 +11,7 @@ import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Parcelable
import android.view.View
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.view.isVisible
@@ -51,6 +52,9 @@ class UserCodeActivity : VectorBaseActivity<ActivitySimpleBinding>(),
override fun getCoordinatorLayout() = views.coordinatorLayout
override val rootView: View
get() = views.coordinatorLayout
private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentResumed(fm: FragmentManager, f: Fragment) {
if (f is MatrixToBottomSheet) {

View File

@@ -7,6 +7,7 @@
package im.vector.app.features.voice
import android.Manifest
import android.content.Context
import android.media.AudioFormat
import android.media.AudioRecord
@@ -15,6 +16,7 @@ import android.media.audiofx.AutomaticGainControl
import android.media.audiofx.NoiseSuppressor
import android.os.Build
import android.widget.Toast
import im.vector.app.core.utils.PermissionChecker
import io.element.android.opusencoder.OggOpusEncoder
import io.element.android.opusencoder.configuration.SampleRate
import kotlinx.coroutines.CoroutineScope
@@ -22,6 +24,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.extensions.tryOrNull
import timber.log.Timber
import kotlin.coroutines.CoroutineContext
/**
@@ -31,6 +34,7 @@ class VoiceRecorderL(
private val context: Context,
coroutineContext: CoroutineContext,
private val codec: OggOpusEncoder,
private val permissionChecker: PermissionChecker,
) : AbstractVoiceRecorder(context) {
companion object {
@@ -127,7 +131,11 @@ class VoiceRecorderL(
bufferSizeInShorts = AudioRecord.getMinBufferSize(SAMPLE_RATE.value, channelConfig, format)
// Buffer is created as a ShortArray, but AudioRecord needs the size in bytes
val bufferSizeInBytes = bufferSizeInShorts * 2
audioRecorder = AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLE_RATE.value, channelConfig, format, bufferSizeInBytes)
if (permissionChecker.checkPermission(Manifest.permission.RECORD_AUDIO)) {
audioRecorder = AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLE_RATE.value, channelConfig, format, bufferSizeInBytes)
} else {
Timber.w("Not allowed to record audio.")
}
}
private fun calculateMaxAmplitude(buffer: ShortArray) {

View File

@@ -13,6 +13,7 @@ import android.media.MediaFormat
import android.os.Build
import androidx.annotation.ChecksSdkIntAtLeast
import androidx.annotation.VisibleForTesting
import im.vector.app.core.utils.PermissionChecker
import im.vector.app.features.VectorFeatures
import io.element.android.opusencoder.OggOpusEncoder
import kotlinx.coroutines.Dispatchers
@@ -23,12 +24,13 @@ class VoiceRecorderProvider @Inject constructor(
private val context: Context,
private val vectorFeatures: VectorFeatures,
private val buildVersionSdkIntProvider: BuildVersionSdkIntProvider,
private val permissionChecker: PermissionChecker,
) {
fun provideVoiceRecorder(): VoiceRecorder {
return if (useNativeRecorder()) {
VoiceRecorderQ(context)
} else {
VoiceRecorderL(context, Dispatchers.IO, OggOpusEncoder.create())
VoiceRecorderL(context, Dispatchers.IO, OggOpusEncoder.create(), permissionChecker)
}
}

View File

@@ -9,6 +9,7 @@ package im.vector.app.features.webview
import android.content.Context
import android.content.Intent
import android.view.View
import android.webkit.WebChromeClient
import android.webkit.WebView
import dagger.hilt.android.AndroidEntryPoint
@@ -28,6 +29,9 @@ class VectorWebViewActivity : VectorBaseActivity<ActivityVectorWebViewBinding>()
override fun getBinding() = ActivityVectorWebViewBinding.inflate(layoutInflater)
override val rootView: View
get() = views.mainRoot
val session: Session by lazy {
activeSessionHolder.getActiveSession()
}

View File

@@ -20,6 +20,7 @@ import android.content.IntentFilter
import android.graphics.drawable.Icon
import android.os.Build
import android.util.Rational
import android.view.View
import androidx.annotation.RequiresApi
import androidx.core.app.PictureInPictureModeChangedInfo
import androidx.core.content.ContextCompat
@@ -77,6 +78,9 @@ class WidgetActivity : VectorBaseActivity<ActivityWidgetBinding>() {
override fun getBinding() = ActivityWidgetBinding.inflate(layoutInflater)
override val rootView: View
get() = views.mainRoot
override fun getTitleRes() = CommonStrings.room_widget_activity_title
override fun initUiAndData() {

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/mainRoot"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -28,4 +29,4 @@
app:layout_constraintTop_toBottomOf="@id/appBarLayout"
app:optimizeDisplay="true" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -2,6 +2,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/mainRoot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

View File

@@ -97,7 +97,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:fitsSystemWindows="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mainRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
@@ -21,4 +22,4 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</LinearLayout>

View File

@@ -3,6 +3,7 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/mainRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:background="@drawable/splash"

View File

@@ -2,6 +2,7 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/mainRoot"
android:background="?vctr_waiting_background_color">
<ProgressBar

View File

@@ -2,6 +2,7 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/mainRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".features.webview.VectorWebViewActivity">

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/mainRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
@@ -23,4 +24,4 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</LinearLayout>

View File

@@ -2,6 +2,7 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/mainRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:colorBackground">

View File

@@ -45,7 +45,6 @@
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:layout_constraintTop_toBottomOf="@id/syncStateView">
<com.google.android.material.appbar.CollapsingToolbarLayout

View File

@@ -25,7 +25,6 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="4dp"
android:layout_marginTop="20dp"
android:background="?android:colorBackground"
app:layout_constraintBottom_toTopOf="@id/roomPollsViewPager"
app:layout_constraintEnd_toEndOf="parent"