Use Clock interface app side

This commit is contained in:
Benoit Marty 2022-05-03 10:32:48 +02:00 committed by Benoit Marty
parent 45526c0e3a
commit 40d3203297
45 changed files with 320 additions and 174 deletions

View file

@ -43,6 +43,7 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.core.time.DefaultClock
import im.vector.app.espresso.tools.waitUntilViewVisible import im.vector.app.espresso.tools.waitUntilViewVisible
import org.hamcrest.Matcher import org.hamcrest.Matcher
import org.hamcrest.Matchers import org.hamcrest.Matchers
@ -52,6 +53,7 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.PrivateKeysInfo
import org.matrix.android.sdk.api.session.sync.SyncState import org.matrix.android.sdk.api.session.sync.SyncState
import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.Optional
import java.util.concurrent.TimeoutException import java.util.concurrent.TimeoutException
import kotlin.random.Random
object EspressoHelper { object EspressoHelper {
fun getCurrentActivity(): Activity? { fun getCurrentActivity(): Activity? {
@ -89,6 +91,8 @@ fun getString(@StringRes id: Int): String {
fun waitForView(viewMatcher: Matcher<View>, timeout: Long = 10_000, waitForDisplayed: Boolean = true): ViewAction { fun waitForView(viewMatcher: Matcher<View>, timeout: Long = 10_000, waitForDisplayed: Boolean = true): ViewAction {
return object : ViewAction { return object : ViewAction {
private val clock = DefaultClock()
override fun getConstraints(): Matcher<View> { override fun getConstraints(): Matcher<View> {
return Matchers.any(View::class.java) return Matchers.any(View::class.java)
} }
@ -102,14 +106,14 @@ fun waitForView(viewMatcher: Matcher<View>, timeout: Long = 10_000, waitForDispl
override fun perform(uiController: UiController, view: View) { override fun perform(uiController: UiController, view: View) {
println("*** waitForView 1 $view") println("*** waitForView 1 $view")
uiController.loopMainThreadUntilIdle() uiController.loopMainThreadUntilIdle()
val startTime = System.currentTimeMillis() val startTime = clock.epochMillis()
val endTime = startTime + timeout val endTime = startTime + timeout
val visibleMatcher = isDisplayed() val visibleMatcher = isDisplayed()
uiController.loopMainThreadForAtLeast(100) uiController.loopMainThreadForAtLeast(100)
do { do {
println("*** waitForView loop $view end:$endTime current:${System.currentTimeMillis()}") println("*** waitForView loop $view end:$endTime current:${clock.epochMillis()}")
val viewVisible = TreeIterables.breadthFirstViewTraversal(view) val viewVisible = TreeIterables.breadthFirstViewTraversal(view)
.any { viewMatcher.matches(it) && visibleMatcher.matches(it) } .any { viewMatcher.matches(it) && visibleMatcher.matches(it) }
@ -118,7 +122,7 @@ fun waitForView(viewMatcher: Matcher<View>, timeout: Long = 10_000, waitForDispl
println("*** waitForView loop loopMainThreadForAtLeast...") println("*** waitForView loop loopMainThreadForAtLeast...")
uiController.loopMainThreadForAtLeast(50) uiController.loopMainThreadForAtLeast(50)
println("*** waitForView loop ...loopMainThreadForAtLeast") println("*** waitForView loop ...loopMainThreadForAtLeast")
} while (System.currentTimeMillis() < endTime) } while (clock.epochMillis() < endTime)
println("*** waitForView timeout $view") println("*** waitForView timeout $view")
// Timeout happens. // Timeout happens.
@ -168,7 +172,7 @@ fun activityIdlingResource(activityClass: Class<*>): IdlingResource {
val res = object : IdlingResource, ActivityLifecycleCallback { val res = object : IdlingResource, ActivityLifecycleCallback {
private var callback: IdlingResource.ResourceCallback? = null private var callback: IdlingResource.ResourceCallback? = null
private var resumedActivity: Activity? = null private var resumedActivity: Activity? = null
private val uniqTS = System.currentTimeMillis() private val uniqTS = Random.nextLong()
override fun getName() = "activityIdlingResource_${activityClass.name}_$uniqTS" override fun getName() = "activityIdlingResource_${activityClass.name}_$uniqTS"

View file

@ -34,6 +34,7 @@ import org.hamcrest.CoreMatchers.not
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import kotlin.random.Random
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@LargeTest @LargeTest
@ -44,7 +45,7 @@ class RegistrationTest {
@Test @Test
fun simpleRegister() { fun simpleRegister() {
val userId: String = "UiAutoTest_${System.currentTimeMillis()}" val userId: String = "UiAutoTest_${Random.nextLong()}"
val password: String = "password" val password: String = "password"
val homeServerUrl: String = "http://10.0.2.2:8080" val homeServerUrl: String = "http://10.0.2.2:8080"

View file

@ -48,6 +48,7 @@ import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import kotlin.random.Random
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@LargeTest @LargeTest
@ -61,7 +62,7 @@ class SecurityBootstrapTest : VerificationTestBase() {
@Before @Before
fun createSessionWithCrossSigning() { fun createSessionWithCrossSigning() {
val matrix = getMatrixInstance() val matrix = getMatrixInstance()
val userName = "foobar_${System.currentTimeMillis()}" val userName = "foobar_${Random.nextLong()}"
existingSession = createAccountAndSync(matrix, userName, password, true) existingSession = createAccountAndSync(matrix, userName, password, true)
stubAllExternalIntents() stubAllExternalIntents()
} }

View file

@ -53,6 +53,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransa
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
import kotlin.coroutines.Continuation import kotlin.coroutines.Continuation
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.random.Random
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@LargeTest @LargeTest
@ -66,7 +67,7 @@ class VerifySessionInteractiveTest : VerificationTestBase() {
@Before @Before
fun createSessionWithCrossSigning() { fun createSessionWithCrossSigning() {
val matrix = getMatrixInstance() val matrix = getMatrixInstance()
val userName = "foobar_${System.currentTimeMillis()}" val userName = "foobar_${Random.nextLong()}"
existingSession = createAccountAndSync(matrix, userName, password, true) existingSession = createAccountAndSync(matrix, userName, password, true)
doSync<Unit> { doSync<Unit> {
existingSession!!.cryptoService().crossSigningService() existingSession!!.cryptoService().crossSigningService()

View file

@ -53,6 +53,7 @@ import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import kotlin.coroutines.Continuation import kotlin.coroutines.Continuation
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.random.Random
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@LargeTest @LargeTest
@ -68,7 +69,7 @@ class VerifySessionPassphraseTest : VerificationTestBase() {
fun createSessionWithCrossSigningAnd4S() { fun createSessionWithCrossSigningAnd4S() {
val context = InstrumentationRegistry.getInstrumentation().targetContext val context = InstrumentationRegistry.getInstrumentation().targetContext
val matrix = getMatrixInstance() val matrix = getMatrixInstance()
val userName = "foobar_${System.currentTimeMillis()}" val userName = "foobar_${Random.nextLong()}"
existingSession = createAccountAndSync(matrix, userName, password, true) existingSession = createAccountAndSync(matrix, userName, password, true)
doSync<Unit> { doSync<Unit> {
existingSession!!.cryptoService().crossSigningService() existingSession!!.cryptoService().crossSigningService()

View file

@ -24,6 +24,7 @@ import android.os.Build
import android.os.Environment import android.os.Environment
import android.provider.MediaStore import android.provider.MediaStore
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import im.vector.app.core.time.DefaultClock
import org.junit.rules.TestWatcher import org.junit.rules.TestWatcher
import org.junit.runner.Description import org.junit.runner.Description
import timber.log.Timber import timber.log.Timber
@ -54,7 +55,7 @@ private fun storeFailureScreenshot(bitmap: Bitmap, screenshotName: String) {
val contentValues = ContentValues().apply { val contentValues = ContentValues().apply {
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg") put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis()) put(MediaStore.Images.Media.DATE_TAKEN, DefaultClock().epochMillis())
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
useMediaStoreScreenshotStorage( useMediaStoreScreenshotStorage(

View file

@ -29,6 +29,7 @@ import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.time.Clock
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
import im.vector.app.core.utils.checkPermissions import im.vector.app.core.utils.checkPermissions
import im.vector.app.core.utils.registerForPermissionsResult import im.vector.app.core.utils.registerForPermissionsResult
@ -59,6 +60,8 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
@Inject @Inject
lateinit var activeSessionHolder: ActiveSessionHolder lateinit var activeSessionHolder: ActiveSessionHolder
@Inject
lateinit var clock: Clock
private lateinit var buffer: ByteArray private lateinit var buffer: ByteArray
@ -165,7 +168,7 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
} }
val builder = NotificationCompat.Builder(this, "CHAN") val builder = NotificationCompat.Builder(this, "CHAN")
.setWhen(System.currentTimeMillis()) .setWhen(clock.epochMillis())
.setContentTitle("Title") .setContentTitle("Title")
.setContentText("Content") .setContentText("Content")
// No effect because it's a group summary notif // No effect because it's a group summary notif
@ -180,16 +183,16 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
.setName("User name") .setName("User name")
.build() .build()
) )
.addMessage("Message 1 - 1", System.currentTimeMillis(), Person.Builder().setName("user 1-1").build()) .addMessage("Message 1 - 1", clock.epochMillis(), Person.Builder().setName("user 1-1").build())
.addMessage("Message 1 - 2", System.currentTimeMillis(), Person.Builder().setName("user 1-2").build()) .addMessage("Message 1 - 2", clock.epochMillis(), Person.Builder().setName("user 1-2").build())
val messagingStyle2 = NotificationCompat.MessagingStyle( val messagingStyle2 = NotificationCompat.MessagingStyle(
Person.Builder() Person.Builder()
.setName("User name 2") .setName("User name 2")
.build() .build()
) )
.addMessage("Message 2 - 1", System.currentTimeMillis(), Person.Builder().setName("user 1-1").build()) .addMessage("Message 2 - 1", clock.epochMillis(), Person.Builder().setName("user 1-1").build())
.addMessage("Message 2 - 2", System.currentTimeMillis(), Person.Builder().setName("user 1-2").build()) .addMessage("Message 2 - 2", clock.epochMillis(), Person.Builder().setName("user 1-2").build())
notificationManager.notify(10, builder.build()) notificationManager.notify(10, builder.build())
@ -197,7 +200,7 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
11, 11,
NotificationCompat.Builder(this, "CHAN") NotificationCompat.Builder(this, "CHAN")
.setChannelId("CHAN") .setChannelId("CHAN")
.setWhen(System.currentTimeMillis()) .setWhen(clock.epochMillis())
.setContentTitle("Title 1") .setContentTitle("Title 1")
.setContentText("Content 1") .setContentText("Content 1")
// For shortcut on long press on launcher icon // For shortcut on long press on launcher icon
@ -211,7 +214,7 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
notificationManager.notify( notificationManager.notify(
12, 12,
NotificationCompat.Builder(this, "CHAN2") NotificationCompat.Builder(this, "CHAN2")
.setWhen(System.currentTimeMillis()) .setWhen(clock.epochMillis())
.setContentTitle("Title 2") .setContentTitle("Title 2")
.setContentText("Content 2") .setContentText("Content 2")
.setStyle(messagingStyle2) .setStyle(messagingStyle2)

View file

@ -74,7 +74,7 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
intent, intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
) )
val firstMillis = System.currentTimeMillis() + delayInSeconds * 1000L val firstMillis = clock.epochMillis() + delayInSeconds * 1000L
val alarmMgr = context.getSystemService<AlarmManager>()!! val alarmMgr = context.getSystemService<AlarmManager>()!!
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, firstMillis, pIntent) alarmMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, firstMillis, pIntent)

View file

@ -22,15 +22,19 @@ import android.text.format.DateUtils
import im.vector.app.core.resources.DateProvider import im.vector.app.core.resources.DateProvider
import im.vector.app.core.resources.LocaleProvider import im.vector.app.core.resources.LocaleProvider
import im.vector.app.core.resources.toTimestamp import im.vector.app.core.resources.toTimestamp
import im.vector.app.core.time.Clock
import org.threeten.bp.LocalDateTime import org.threeten.bp.LocalDateTime
import org.threeten.bp.Period import org.threeten.bp.Period
import org.threeten.bp.format.DateTimeFormatter import org.threeten.bp.format.DateTimeFormatter
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
class VectorDateFormatter @Inject constructor(private val context: Context, class VectorDateFormatter @Inject constructor(
private val localeProvider: LocaleProvider, private val context: Context,
private val dateFormatterProviders: DateFormatterProviders) { private val localeProvider: LocaleProvider,
private val dateFormatterProviders: DateFormatterProviders,
private val clock: Clock,
) {
private val hourFormatter by lazy { private val hourFormatter by lazy {
if (DateFormat.is24HourFormat(context)) { if (DateFormat.is24HourFormat(context)) {
@ -158,8 +162,9 @@ class VectorDateFormatter @Inject constructor(private val context: Context,
private fun getRelativeDay(ts: Long): String { private fun getRelativeDay(ts: Long): String {
return DateUtils.getRelativeTimeSpanString( return DateUtils.getRelativeTimeSpanString(
ts, ts,
System.currentTimeMillis(), clock.epochMillis(),
DateUtils.DAY_IN_MILLIS, DateUtils.DAY_IN_MILLIS,
DateUtils.FORMAT_SHOW_WEEKDAY).toString() DateUtils.FORMAT_SHOW_WEEKDAY
).toString()
} }
} }

View file

@ -27,6 +27,7 @@ import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper.Listener
import im.vector.app.core.extensions.insertBeforeLast import im.vector.app.core.extensions.insertBeforeLast
import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.time.Clock
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
import im.vector.app.core.utils.checkPermissions import im.vector.app.core.utils.checkPermissions
import im.vector.app.core.utils.onPermissionDeniedDialog import im.vector.app.core.utils.onPermissionDeniedDialog
@ -45,7 +46,8 @@ import java.io.File
class GalleryOrCameraDialogHelper( class GalleryOrCameraDialogHelper(
// must implement GalleryOrCameraDialogHelper.Listener // must implement GalleryOrCameraDialogHelper.Listener
private val fragment: Fragment, private val fragment: Fragment,
private val colorProvider: ColorProvider private val colorProvider: ColorProvider,
private val clock: Clock,
) { ) {
interface Listener { interface Listener {
fun onImageReady(uri: Uri?) fun onImageReady(uri: Uri?)
@ -91,7 +93,7 @@ class GalleryOrCameraDialogHelper(
} }
private fun startUCrop(image: MultiPickerImageType) { private fun startUCrop(image: MultiPickerImageType) {
val destinationFile = File(activity.cacheDir, image.displayName.insertBeforeLast("_e_${System.currentTimeMillis()}")) val destinationFile = File(activity.cacheDir, image.displayName.insertBeforeLast("_e_${clock.epochMillis()}"))
val uri = image.contentUri val uri = image.contentUri
createUCropWithDefaultSettings(colorProvider, uri, destinationFile.toUri(), fragment.getString(R.string.rotate_and_crop_screen_title)) createUCropWithDefaultSettings(colorProvider, uri, destinationFile.toUri(), fragment.getString(R.string.rotate_and_crop_screen_title))
.withAspectRatio(1f, 1f) .withAspectRatio(1f, 1f)

View file

@ -33,6 +33,8 @@ import androidx.work.WorkerParameters
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.platform.PendingIntentCompat import im.vector.app.core.platform.PendingIntentCompat
import im.vector.app.core.time.Clock
import im.vector.app.core.time.DefaultClock
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 org.matrix.android.sdk.api.Matrix import org.matrix.android.sdk.api.Matrix
@ -77,6 +79,7 @@ class VectorSyncService : SyncService() {
@Inject lateinit var notificationUtils: NotificationUtils @Inject lateinit var notificationUtils: NotificationUtils
@Inject lateinit var matrix: Matrix @Inject lateinit var matrix: Matrix
@Inject lateinit var clock: Clock
override fun provideMatrix() = matrix override fun provideMatrix() = matrix
@ -102,7 +105,8 @@ class VectorSyncService : SyncService() {
syncTimeoutSeconds = syncTimeoutSeconds, syncTimeoutSeconds = syncTimeoutSeconds,
syncDelaySeconds = syncDelaySeconds, syncDelaySeconds = syncDelaySeconds,
isPeriodic = true, isPeriodic = true,
isNetworkBack = false isNetworkBack = false,
now = clock.epochMillis()
) )
} }
@ -114,9 +118,10 @@ class VectorSyncService : SyncService() {
val rescheduleSyncWorkRequest: WorkRequest = val rescheduleSyncWorkRequest: WorkRequest =
OneTimeWorkRequestBuilder<RestartWhenNetworkOn>() OneTimeWorkRequestBuilder<RestartWhenNetworkOn>()
.setInputData(RestartWhenNetworkOn.createInputData(sessionId, syncTimeoutSeconds, syncDelaySeconds, isPeriodic)) .setInputData(RestartWhenNetworkOn.createInputData(sessionId, syncTimeoutSeconds, syncDelaySeconds, isPeriodic))
.setConstraints(Constraints.Builder() .setConstraints(
.setRequiredNetworkType(NetworkType.CONNECTED) Constraints.Builder()
.build() .setRequiredNetworkType(NetworkType.CONNECTED)
.build()
) )
.build() .build()
@ -137,20 +142,27 @@ class VectorSyncService : SyncService() {
} }
// I do not move or rename this class, since I'm not sure about the side effect regarding the WorkManager // I do not move or rename this class, since I'm not sure about the side effect regarding the WorkManager
class RestartWhenNetworkOn(appContext: Context, workerParams: WorkerParameters) : class RestartWhenNetworkOn(
Worker(appContext, workerParams) { appContext: Context,
workerParams: WorkerParameters
) : Worker(appContext, workerParams) {
override fun doWork(): Result { override fun doWork(): Result {
Timber.d("## Sync: RestartWhenNetworkOn.doWork()") Timber.d("## Sync: RestartWhenNetworkOn.doWork()")
val sessionId = inputData.getString(KEY_SESSION_ID) ?: return Result.failure() val sessionId = inputData.getString(KEY_SESSION_ID) ?: return Result.failure()
val syncTimeoutSeconds = inputData.getInt(KEY_SYNC_TIMEOUT_SECONDS, BackgroundSyncMode.DEFAULT_SYNC_TIMEOUT_SECONDS) val syncTimeoutSeconds = inputData.getInt(KEY_SYNC_TIMEOUT_SECONDS, BackgroundSyncMode.DEFAULT_SYNC_TIMEOUT_SECONDS)
val syncDelaySeconds = inputData.getInt(KEY_SYNC_DELAY_SECONDS, BackgroundSyncMode.DEFAULT_SYNC_DELAY_SECONDS) val syncDelaySeconds = inputData.getInt(KEY_SYNC_DELAY_SECONDS, BackgroundSyncMode.DEFAULT_SYNC_DELAY_SECONDS)
val isPeriodic = inputData.getBoolean(KEY_IS_PERIODIC, false) val isPeriodic = inputData.getBoolean(KEY_IS_PERIODIC, false)
// Not sure how to inject a Clock here
val clock = DefaultClock()
applicationContext.rescheduleSyncService( applicationContext.rescheduleSyncService(
sessionId = sessionId, sessionId = sessionId,
syncTimeoutSeconds = syncTimeoutSeconds, syncTimeoutSeconds = syncTimeoutSeconds,
syncDelaySeconds = syncDelaySeconds, syncDelaySeconds = syncDelaySeconds,
isPeriodic = isPeriodic, isPeriodic = isPeriodic,
isNetworkBack = true isNetworkBack = true,
now = clock.epochMillis()
) )
// Indicate whether the work finished successfully with the Result // Indicate whether the work finished successfully with the Result
return Result.success() return Result.success()
@ -182,7 +194,8 @@ private fun Context.rescheduleSyncService(sessionId: String,
syncTimeoutSeconds: Int, syncTimeoutSeconds: Int,
syncDelaySeconds: Int, syncDelaySeconds: Int,
isPeriodic: Boolean, isPeriodic: Boolean,
isNetworkBack: Boolean) { isNetworkBack: Boolean,
now: Long) {
Timber.d("## Sync: rescheduleSyncService") Timber.d("## Sync: rescheduleSyncService")
val intent = if (isPeriodic) { val intent = if (isPeriodic) {
VectorSyncService.newPeriodicIntent( VectorSyncService.newPeriodicIntent(
@ -208,7 +221,7 @@ private fun Context.rescheduleSyncService(sessionId: String,
} else { } else {
PendingIntent.getService(this, 0, intent, PendingIntentCompat.FLAG_IMMUTABLE) PendingIntent.getService(this, 0, intent, PendingIntentCompat.FLAG_IMMUTABLE)
} }
val firstMillis = System.currentTimeMillis() + syncDelaySeconds * 1000L val firstMillis = now + syncDelaySeconds * 1000L
val alarmMgr = getSystemService<AlarmManager>()!! val alarmMgr = getSystemService<AlarmManager>()!!
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, firstMillis, pendingIntent) alarmMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, firstMillis, pendingIntent)

View file

@ -147,8 +147,11 @@ fun openFileSelection(activity: Activity,
* Send an email to address with optional subject and message * Send an email to address with optional subject and message
*/ */
fun sendMailTo(address: String, subject: String? = null, message: String? = null, activity: Activity) { fun sendMailTo(address: String, subject: String? = null, message: String? = null, activity: Activity) {
val intent = Intent(Intent.ACTION_SENDTO, Uri.fromParts( val intent = Intent(
"mailto", address, null)) Intent.ACTION_SENDTO, Uri.fromParts(
"mailto", address, null
)
)
intent.putExtra(Intent.EXTRA_SUBJECT, subject) intent.putExtra(Intent.EXTRA_SUBJECT, subject)
intent.putExtra(Intent.EXTRA_TEXT, message) intent.putExtra(Intent.EXTRA_TEXT, message)
@ -248,7 +251,12 @@ private fun appendTimeToFilename(name: String): String {
return """${filename}_$dateExtension.$fileExtension""" return """${filename}_$dateExtension.$fileExtension"""
} }
suspend fun saveMedia(context: Context, file: File, title: String, mediaMimeType: String?, notificationUtils: NotificationUtils) { suspend fun saveMedia(context: Context,
file: File,
title: String,
mediaMimeType: String?,
notificationUtils: NotificationUtils,
now: Long) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val filename = appendTimeToFilename(title) val filename = appendTimeToFilename(title)
@ -257,8 +265,8 @@ suspend fun saveMedia(context: Context, file: File, title: String, mediaMimeType
put(MediaStore.Images.Media.TITLE, filename) put(MediaStore.Images.Media.TITLE, filename)
put(MediaStore.Images.Media.DISPLAY_NAME, filename) put(MediaStore.Images.Media.DISPLAY_NAME, filename)
put(MediaStore.Images.Media.MIME_TYPE, mediaMimeType) put(MediaStore.Images.Media.MIME_TYPE, mediaMimeType)
put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis()) put(MediaStore.Images.Media.DATE_ADDED, now)
put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis()) put(MediaStore.Images.Media.DATE_TAKEN, now)
} }
val externalContentUri = when { val externalContentUri = when {
mediaMimeType?.isMimeTypeImage() == true -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI mediaMimeType?.isMimeTypeImage() == true -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
@ -289,7 +297,7 @@ suspend fun saveMedia(context: Context, file: File, title: String, mediaMimeType
} }
} }
} else { } else {
saveMediaLegacy(context, mediaMimeType, title, file) saveMediaLegacy(context, mediaMimeType, title, file, now)
} }
} }
} }
@ -298,7 +306,8 @@ suspend fun saveMedia(context: Context, file: File, title: String, mediaMimeType
private fun saveMediaLegacy(context: Context, private fun saveMediaLegacy(context: Context,
mediaMimeType: String?, mediaMimeType: String?,
title: String, title: String,
file: File) { file: File,
now: Long) {
val state = Environment.getExternalStorageState() val state = Environment.getExternalStorageState()
if (Environment.MEDIA_MOUNTED != state) { if (Environment.MEDIA_MOUNTED != state) {
context.toast(context.getString(R.string.error_saving_media_file)) context.toast(context.getString(R.string.error_saving_media_file))
@ -319,7 +328,7 @@ private fun saveMediaLegacy(context: Context,
} else { } else {
title title
} }
val savedFile = saveFileIntoLegacy(file, downloadDir, outputFilename) val savedFile = saveFileIntoLegacy(file, downloadDir, outputFilename, now)
if (savedFile != null) { if (savedFile != null) {
val downloadManager = context.getSystemService<DownloadManager>() val downloadManager = context.getSystemService<DownloadManager>()
downloadManager?.addCompletedDownload( downloadManager?.addCompletedDownload(
@ -329,7 +338,8 @@ private fun saveMediaLegacy(context: Context,
mediaMimeType ?: MimeTypes.OctetStream, mediaMimeType ?: MimeTypes.OctetStream,
savedFile.absolutePath, savedFile.absolutePath,
savedFile.length(), savedFile.length(),
true) true
)
addToGallery(savedFile, mediaMimeType, context) addToGallery(savedFile, mediaMimeType, context)
} }
} catch (error: Throwable) { } catch (error: Throwable) {
@ -411,7 +421,7 @@ fun selectTxtFileToWrite(
* @return the created file * @return the created file
*/ */
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
fun saveFileIntoLegacy(sourceFile: File, dstDirPath: File, outputFilename: String?): File? { fun saveFileIntoLegacy(sourceFile: File, dstDirPath: File, outputFilename: String?, now: Long): File? {
// defines another name for the external media // defines another name for the external media
var dstFileName: String var dstFileName: String
@ -423,7 +433,7 @@ fun saveFileIntoLegacy(sourceFile: File, dstDirPath: File, outputFilename: Strin
if (dotPos > 0) { if (dotPos > 0) {
fileExt = sourceFile.name.substring(dotPos) fileExt = sourceFile.name.substring(dotPos)
} }
dstFileName = "vector_" + System.currentTimeMillis() + fileExt dstFileName = "vector_$now$fileExt"
} else { } else {
dstFileName = outputFilename dstFileName = outputFilename
} }

View file

@ -45,6 +45,7 @@ import im.vector.app.core.extensions.insertBeforeLast
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.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.time.Clock
import im.vector.app.core.utils.OnSnapPositionChangeListener import im.vector.app.core.utils.OnSnapPositionChangeListener
import im.vector.app.core.utils.SnapOnScrollListener import im.vector.app.core.utils.SnapOnScrollListener
import im.vector.app.core.utils.attachSnapHelperWithListener import im.vector.app.core.utils.attachSnapHelperWithListener
@ -64,7 +65,8 @@ data class AttachmentsPreviewArgs(
class AttachmentsPreviewFragment @Inject constructor( class AttachmentsPreviewFragment @Inject constructor(
private val attachmentMiniaturePreviewController: AttachmentMiniaturePreviewController, private val attachmentMiniaturePreviewController: AttachmentMiniaturePreviewController,
private val attachmentBigPreviewController: AttachmentBigPreviewController, private val attachmentBigPreviewController: AttachmentBigPreviewController,
private val colorProvider: ColorProvider private val colorProvider: ColorProvider,
private val clock: Clock,
) : VectorBaseFragment<FragmentAttachmentsPreviewBinding>(), AttachmentMiniaturePreviewController.Callback { ) : VectorBaseFragment<FragmentAttachmentsPreviewBinding>(), AttachmentMiniaturePreviewController.Callback {
private val fragmentArgs: AttachmentsPreviewArgs by args() private val fragmentArgs: AttachmentsPreviewArgs by args()
@ -192,7 +194,7 @@ class AttachmentsPreviewFragment @Inject constructor(
private fun handleEditAction() = withState(viewModel) { private fun handleEditAction() = withState(viewModel) {
val currentAttachment = it.attachments.getOrNull(it.currentAttachmentIndex) ?: return@withState val currentAttachment = it.attachments.getOrNull(it.currentAttachmentIndex) ?: return@withState
val destinationFile = File(requireContext().cacheDir, currentAttachment.name.insertBeforeLast("_edited_image_${System.currentTimeMillis()}")) val destinationFile = File(requireContext().cacheDir, currentAttachment.name.insertBeforeLast("_edited_image_${clock.epochMillis()}"))
val uri = currentAttachment.queryUri val uri = currentAttachment.queryUri
createUCropWithDefaultSettings(colorProvider, uri, destinationFile.toUri(), currentAttachment.name) createUCropWithDefaultSettings(colorProvider, uri, destinationFile.toUri(), currentAttachment.name)
.getIntent(requireContext()) .getIntent(requireContext())

View file

@ -19,6 +19,7 @@ package im.vector.app.features.call.conference
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.network.await import im.vector.app.core.network.await
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.time.Clock
import im.vector.app.core.utils.ensureProtocol import im.vector.app.core.utils.ensureProtocol
import im.vector.app.core.utils.toBase32String import im.vector.app.core.utils.toBase32String
import im.vector.app.features.call.conference.jwt.JitsiJWTFactory import im.vector.app.features.call.conference.jwt.JitsiJWTFactory
@ -46,7 +47,9 @@ class JitsiService @Inject constructor(
private val rawService: RawService, private val rawService: RawService,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val themeProvider: ThemeProvider, private val themeProvider: ThemeProvider,
private val jitsiJWTFactory: JitsiJWTFactory) { private val jitsiJWTFactory: JitsiJWTFactory,
private val clock: Clock,
) {
companion object { companion object {
const val JITSI_OPEN_ID_TOKEN_JWT_AUTH = "openidtoken-jwt" const val JITSI_OPEN_ID_TOKEN_JWT_AUTH = "openidtoken-jwt"
@ -60,7 +63,7 @@ class JitsiService @Inject constructor(
suspend fun createJitsiWidget(roomId: String, withVideo: Boolean): Widget { suspend fun createJitsiWidget(roomId: String, withVideo: Boolean): Widget {
// Build data for a jitsi widget // Build data for a jitsi widget
val widgetId: String = WidgetType.Jitsi.preferred + "_" + session.myUserId + "_" + System.currentTimeMillis() val widgetId: String = WidgetType.Jitsi.preferred + "_" + session.myUserId + "_" + clock.epochMillis()
val preferredJitsiDomain = tryOrNull { val preferredJitsiDomain = tryOrNull {
rawService.getElementWellknown(session.sessionParams) rawService.getElementWellknown(session.sessionParams)
?.jitsiServer ?.jitsiServer

View file

@ -21,6 +21,7 @@ import android.os.Binder
import android.os.IBinder import android.os.IBinder
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.services.VectorService import im.vector.app.core.services.VectorService
import im.vector.app.core.time.Clock
import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.notifications.NotificationUtils
import javax.inject.Inject import javax.inject.Inject
@ -28,6 +29,7 @@ import javax.inject.Inject
class ScreenCaptureService : VectorService() { class ScreenCaptureService : VectorService() {
@Inject lateinit var notificationUtils: NotificationUtils @Inject lateinit var notificationUtils: NotificationUtils
@Inject lateinit var clock: Clock
private val binder = LocalBinder() private val binder = LocalBinder()
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
@ -37,7 +39,7 @@ class ScreenCaptureService : VectorService() {
} }
private fun showStickyNotification() { private fun showStickyNotification() {
val notificationId = System.currentTimeMillis().toInt() val notificationId = clock.epochMillis().toInt()
val notification = notificationUtils.buildScreenSharingNotification() val notification = notificationUtils.buildScreenSharingNotification()
startForeground(notificationId, notification) startForeground(notificationId, notification)
} }

View file

@ -22,6 +22,7 @@ import androidx.lifecycle.ViewModel
import com.nulabinc.zxcvbn.Strength import com.nulabinc.zxcvbn.Strength
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.platform.WaitingViewData import im.vector.app.core.platform.WaitingViewData
import im.vector.app.core.time.Clock
import im.vector.app.core.utils.LiveEvent import im.vector.app.core.utils.LiveEvent
import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.listeners.ProgressListener import org.matrix.android.sdk.api.listeners.ProgressListener
@ -37,7 +38,9 @@ import javax.inject.Inject
/** /**
* The shared view model between all fragments. * The shared view model between all fragments.
*/ */
class KeysBackupSetupSharedViewModel @Inject constructor() : ViewModel() { class KeysBackupSetupSharedViewModel @Inject constructor(
private val clock: Clock,
) : ViewModel() {
companion object { companion object {
const val NAVIGATE_TO_STEP_2 = "NAVIGATE_TO_STEP_2" const val NAVIGATE_TO_STEP_2 = "NAVIGATE_TO_STEP_2"
@ -85,7 +88,7 @@ class KeysBackupSetupSharedViewModel @Inject constructor() : ViewModel() {
fun prepareRecoveryKey(context: Context, withPassphrase: String?) { fun prepareRecoveryKey(context: Context, withPassphrase: String?) {
// Update requestId // Update requestId
currentRequestId.value = System.currentTimeMillis() currentRequestId.value = clock.epochMillis()
isCreatingBackupVersion.value = true isCreatingBackupVersion.value = true
recoveryKey.value = null recoveryKey.value = null
@ -101,9 +104,11 @@ class KeysBackupSetupSharedViewModel @Inject constructor() : ViewModel() {
return return
} }
loadingStatus.value = WaitingViewData(context.getString(R.string.keys_backup_setup_step3_generating_key_status), loadingStatus.value = WaitingViewData(
context.getString(R.string.keys_backup_setup_step3_generating_key_status),
progress, progress,
total) total
)
} }
}, },
object : MatrixCallback<MegolmBackupCreationInfo> { object : MatrixCallback<MegolmBackupCreationInfo> {

View file

@ -18,6 +18,7 @@ package im.vector.app.features.crypto.verification
import android.content.Context import android.content.Context
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.core.time.Clock
import im.vector.app.features.displayname.getBestName 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
@ -42,7 +43,9 @@ import javax.inject.Singleton
class IncomingVerificationRequestHandler @Inject constructor( class IncomingVerificationRequestHandler @Inject constructor(
private val context: Context, private val context: Context,
private var avatarRenderer: Provider<AvatarRenderer>, private var avatarRenderer: Provider<AvatarRenderer>,
private val popupAlertManager: PopupAlertManager) : VerificationService.Listener { private val popupAlertManager: PopupAlertManager,
private val clock: Clock,
) : VerificationService.Listener {
private var session: Session? = null private var session: Session? = null
@ -104,7 +107,7 @@ class IncomingVerificationRequestHandler @Inject constructor(
} }
) )
// 10mn expiration // 10mn expiration
expirationTimestamp = System.currentTimeMillis() + (10 * 60 * 1000L) expirationTimestamp = clock.epochMillis() + (10 * 60 * 1000L)
} }
popupAlertManager.postVectorAlert(alert) popupAlertManager.postVectorAlert(alert)
} }
@ -168,7 +171,7 @@ class IncomingVerificationRequestHandler @Inject constructor(
} }
colorAttribute = R.attr.vctr_notice_secondary colorAttribute = R.attr.vctr_notice_secondary
// 5mn expiration // 5mn expiration
expirationTimestamp = System.currentTimeMillis() + (5 * 60 * 1000L) expirationTimestamp = clock.epochMillis() + (5 * 60 * 1000L)
} }
popupAlertManager.postVectorAlert(alert) popupAlertManager.postVectorAlert(alert)
} }

View file

@ -29,6 +29,7 @@ import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.platform.VectorViewModelAction import im.vector.app.core.platform.VectorViewModelAction
import im.vector.app.core.time.Clock
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
@ -56,10 +57,12 @@ data class DeviceDetectionInfo(
val currentSessionTrust: Boolean val currentSessionTrust: Boolean
) )
class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted initialState: UnknownDevicesState, class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(
session: Session, @Assisted initialState: UnknownDevicesState,
private val vectorPreferences: VectorPreferences) : session: Session,
VectorViewModel<UnknownDevicesState, UnknownDeviceDetectorSharedViewModel.Action, EmptyViewEvents>(initialState) { private val vectorPreferences: VectorPreferences,
clock: Clock,
) : VectorViewModel<UnknownDevicesState, UnknownDeviceDetectorSharedViewModel.Action, EmptyViewEvents>(initialState) {
sealed class Action : VectorViewModelAction { sealed class Action : VectorViewModelAction {
data class IgnoreDevice(val deviceIds: List<String>) : Action() data class IgnoreDevice(val deviceIds: List<String>) : Action()
@ -75,11 +78,10 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted
private val ignoredDeviceList = ArrayList<String>() private val ignoredDeviceList = ArrayList<String>()
init { init {
val currentSessionTs = session.cryptoService().getCryptoDeviceInfo(session.myUserId) val currentSessionTs = session.cryptoService().getCryptoDeviceInfo(session.myUserId)
.firstOrNull { it.deviceId == session.sessionParams.deviceId } .firstOrNull { it.deviceId == session.sessionParams.deviceId }
?.firstTimeSeenLocalTs ?.firstTimeSeenLocalTs
?: System.currentTimeMillis() ?: clock.epochMillis()
Timber.v("## Detector - Current Session first time seen $currentSessionTs") Timber.v("## Detector - Current Session first time seen $currentSessionTs")
ignoredDeviceList.addAll( ignoredDeviceList.addAll(

View file

@ -16,6 +16,7 @@
package im.vector.app.features.home.room.detail package im.vector.app.features.home.room.detail
import im.vector.app.core.time.Clock
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.model.message.MessageType
@ -45,7 +46,9 @@ fun ChatEffect.toMessageType(): String {
* precisely an event decrypted with a few delay won't trigger an effect; it's acceptable) * precisely an event decrypted with a few delay won't trigger an effect; it's acceptable)
* Events that are more that 10s old won't trigger effects * Events that are more that 10s old won't trigger effects
*/ */
class ChatEffectManager @Inject constructor() { class ChatEffectManager @Inject constructor(
private val clock: Clock,
) {
interface Delegate { interface Delegate {
fun stopEffects() fun stopEffects()
@ -61,7 +64,7 @@ class ChatEffectManager @Inject constructor() {
fun checkForEffect(event: TimelineEvent) { fun checkForEffect(event: TimelineEvent) {
val age = event.root.ageLocalTs ?: 0 val age = event.root.ageLocalTs ?: 0
val now = System.currentTimeMillis() val now = clock.epochMillis()
// messages older than 10s should not trigger any effect // messages older than 10s should not trigger any effect
if ((now - age) >= 10_000) return if ((now - age) >= 10_000) return
val content = event.root.getClearContent()?.toModel<MessageContent>() ?: return val content = event.root.getClearContent()?.toModel<MessageContent>() ?: return

View file

@ -33,14 +33,18 @@ import com.airbnb.epoxy.EpoxyModel
import com.airbnb.epoxy.EpoxyTouchHelperCallback import com.airbnb.epoxy.EpoxyTouchHelperCallback
import com.airbnb.epoxy.EpoxyViewHolder import com.airbnb.epoxy.EpoxyViewHolder
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.time.Clock
import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.themes.ThemeUtils
import timber.log.Timber import timber.log.Timber
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.min import kotlin.math.min
class RoomMessageTouchHelperCallback(private val context: Context, class RoomMessageTouchHelperCallback(
@DrawableRes actionIcon: Int, private val context: Context,
private val handler: QuickReplayHandler) : EpoxyTouchHelperCallback() { @DrawableRes actionIcon: Int,
private val handler: QuickReplayHandler,
private val clock: Clock,
) : EpoxyTouchHelperCallback() {
interface QuickReplayHandler { interface QuickReplayHandler {
fun performQuickReplyOnHolder(model: EpoxyModel<*>) fun performQuickReplyOnHolder(model: EpoxyModel<*>)
@ -141,7 +145,7 @@ class RoomMessageTouchHelperCallback(private val context: Context,
private fun drawReplyButton(canvas: Canvas, itemView: View) { private fun drawReplyButton(canvas: Canvas, itemView: View) {
// Timber.v("drawReplyButton") // Timber.v("drawReplyButton")
val translationX = abs(itemView.translationX) val translationX = abs(itemView.translationX)
val newTime = System.currentTimeMillis() val newTime = clock.epochMillis()
val dt = min(17, newTime - lastReplyButtonAnimationTime) val dt = min(17, newTime - lastReplyButtonAnimationTime)
lastReplyButtonAnimationTime = newTime lastReplyButtonAnimationTime = newTime
val showing = translationX >= minShowDistance val showing = translationX >= minShowDistance

View file

@ -296,7 +296,7 @@ class TimelineFragment @Inject constructor(
private const val ircPattern = " (IRC)" private const val ircPattern = " (IRC)"
} }
private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider, clock)
private val timelineArgs: TimelineArgs by args() private val timelineArgs: TimelineArgs by args()
private val glideRequests by lazy { private val glideRequests by lazy {
@ -1443,7 +1443,7 @@ class TimelineFragment @Inject constructor(
} }
} }
} }
val swipeCallback = RoomMessageTouchHelperCallback(requireContext(), R.drawable.ic_reply, quickReplyHandler) val swipeCallback = RoomMessageTouchHelperCallback(requireContext(), R.drawable.ic_reply, quickReplyHandler, clock)
val touchHelper = ItemTouchHelper(swipeCallback) val touchHelper = ItemTouchHelper(swipeCallback)
touchHelper.attachToRecyclerView(views.timelineRecyclerView) touchHelper.attachToRecyclerView(views.timelineRecyclerView)
} }
@ -2186,7 +2186,8 @@ class TimelineFragment @Inject constructor(
file = it, file = it,
title = action.messageContent.body, title = action.messageContent.body,
mediaMimeType = action.messageContent.mimeType ?: getMimeTypeFromUri(requireContext(), it.toUri()), mediaMimeType = action.messageContent.mimeType ?: getMimeTypeFromUri(requireContext(), it.toUri()),
notificationUtils = notificationUtils notificationUtils = notificationUtils,
now = clock.epochMillis()
) )
} }
.onFailure { .onFailure {

View file

@ -21,6 +21,7 @@ import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import kotlin.random.Random
/** /**
* Describes the current send mode: * Describes the current send mode:
@ -35,7 +36,7 @@ sealed interface SendMode {
val text: String, val text: String,
val fromSharing: Boolean, val fromSharing: Boolean,
// This is necessary for forcing refresh on selectSubscribe // This is necessary for forcing refresh on selectSubscribe
private val ts: Long = System.currentTimeMillis() private val ts: Int = Random.nextInt()
) : SendMode ) : SendMode
data class Quote(val timelineEvent: TimelineEvent, val text: String) : SendMode data class Quote(val timelineEvent: TimelineEvent, val text: String) : SendMode
@ -83,7 +84,8 @@ data class MessageComposerViewState(
constructor(args: TimelineArgs) : this( constructor(args: TimelineArgs) : this(
roomId = args.roomId, roomId = args.roomId,
startsThread = args.threadTimelineArgs?.startsThread.orFalse(), startsThread = args.threadTimelineArgs?.startsThread.orFalse(),
rootThreadEventId = args.threadTimelineArgs?.rootThreadEventId) rootThreadEventId = args.threadTimelineArgs?.rootThreadEventId
)
fun isInThreadTimeline(): Boolean = rootThreadEventId != null fun isInThreadTimeline(): Boolean = rootThreadEventId != null
} }

View file

@ -30,6 +30,7 @@ import im.vector.app.core.epoxy.loadingItem
import im.vector.app.core.epoxy.noResultItem import im.vector.app.core.epoxy.noResultItem
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.resources.UserPreferencesProvider import im.vector.app.core.resources.UserPreferencesProvider
import im.vector.app.core.time.Clock
import im.vector.app.core.ui.list.GenericHeaderItem_ import im.vector.app.core.ui.list.GenericHeaderItem_
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.timeline.format.DisplayableEventFormatter import im.vector.app.features.home.room.detail.timeline.format.DisplayableEventFormatter
@ -47,7 +48,8 @@ class SearchResultController @Inject constructor(
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val dateFormatter: VectorDateFormatter, private val dateFormatter: VectorDateFormatter,
private val displayableEventFormatter: DisplayableEventFormatter, private val displayableEventFormatter: DisplayableEventFormatter,
private val userPreferencesProvider: UserPreferencesProvider private val userPreferencesProvider: UserPreferencesProvider,
private val clock: Clock,
) : TypedEpoxyController<SearchViewState>() { ) : TypedEpoxyController<SearchViewState>() {
var listener: Listener? = null var listener: Listener? = null
@ -109,7 +111,7 @@ class SearchResultController @Inject constructor(
val spannable = setHighLightedText(text, data.highlights) ?: return@forEach val spannable = setHighLightedText(text, data.highlights) ?: return@forEach
val eventDate = Calendar.getInstance().apply { val eventDate = Calendar.getInstance().apply {
timeInMillis = eventAndSender.event.originServerTs ?: System.currentTimeMillis() timeInMillis = eventAndSender.event.originServerTs ?: clock.epochMillis()
} }
if (lastDate?.get(Calendar.DAY_OF_YEAR) != eventDate.get(Calendar.DAY_OF_YEAR)) { if (lastDate?.get(Calendar.DAY_OF_YEAR) != eventDate.get(Calendar.DAY_OF_YEAR)) {
GenericHeaderItem_() GenericHeaderItem_()
@ -125,7 +127,8 @@ class SearchResultController @Inject constructor(
.formattedDate(dateFormatter.format(event.originServerTs, DateFormatKind.MESSAGE_SIMPLE)) .formattedDate(dateFormatter.format(event.originServerTs, DateFormatKind.MESSAGE_SIMPLE))
.spannable(spannable.toEpoxyCharSequence()) .spannable(spannable.toEpoxyCharSequence())
.sender(eventAndSender.sender .sender(eventAndSender.sender
?: eventAndSender.event.senderId?.let { session.roomService().getRoomMember(it, data.roomId) }?.toMatrixItem()) ?: eventAndSender.event.senderId?.let { session.roomService().getRoomMember(it, data.roomId) }?.toMatrixItem()
)
.threadDetails(event.threadDetails) .threadDetails(event.threadDetails)
.threadSummaryFormatted(displayableEventFormatter.formatThreadSummary(event.threadDetails?.threadSummaryLatestEvent).toString()) .threadSummaryFormatted(displayableEventFormatter.formatThreadSummary(event.threadDetails?.threadSummaryLatestEvent).toString())
.areThreadMessagesEnabled(userPreferencesProvider.areThreadMessagesEnabled()) .areThreadMessagesEnabled(userPreferencesProvider.areThreadMessagesEnabled())

View file

@ -31,6 +31,7 @@ import im.vector.app.core.epoxy.LoadingItem_
import im.vector.app.core.extensions.localDateTime import im.vector.app.core.extensions.localDateTime
import im.vector.app.core.extensions.nextOrNull import im.vector.app.core.extensions.nextOrNull
import im.vector.app.core.extensions.prevOrNull import im.vector.app.core.extensions.prevOrNull
import im.vector.app.core.time.Clock
import im.vector.app.features.home.room.detail.JitsiState import im.vector.app.features.home.room.detail.JitsiState
import im.vector.app.features.home.room.detail.RoomDetailAction import im.vector.app.features.home.room.detail.RoomDetailAction
import im.vector.app.features.home.room.detail.RoomDetailViewState import im.vector.app.features.home.room.detail.RoomDetailViewState
@ -78,19 +79,21 @@ import javax.inject.Inject
import kotlin.math.min import kotlin.math.min
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
class TimelineEventController @Inject constructor(private val dateFormatter: VectorDateFormatter, class TimelineEventController @Inject constructor(
private val vectorPreferences: VectorPreferences, private val dateFormatter: VectorDateFormatter,
private val contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder, private val vectorPreferences: VectorPreferences,
private val contentDownloadStateTrackerBinder: ContentDownloadStateTrackerBinder, private val contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder,
private val timelineItemFactory: TimelineItemFactory, private val contentDownloadStateTrackerBinder: ContentDownloadStateTrackerBinder,
private val timelineMediaSizeProvider: TimelineMediaSizeProvider, private val timelineItemFactory: TimelineItemFactory,
private val mergedHeaderItemFactory: MergedHeaderItemFactory, private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
private val session: Session, private val mergedHeaderItemFactory: MergedHeaderItemFactory,
@TimelineEventControllerHandler private val session: Session,
private val backgroundHandler: Handler, @TimelineEventControllerHandler
private val timelineEventVisibilityHelper: TimelineEventVisibilityHelper, private val backgroundHandler: Handler,
private val readReceiptsItemFactory: ReadReceiptsItemFactory, private val timelineEventVisibilityHelper: TimelineEventVisibilityHelper,
private val reactionListFactory: ReactionsSummaryFactory private val readReceiptsItemFactory: ReadReceiptsItemFactory,
private val reactionListFactory: ReactionsSummaryFactory,
private val clock: Clock,
) : EpoxyController(backgroundHandler, backgroundHandler), Timeline.Listener, EpoxyController.Interceptor { ) : EpoxyController(backgroundHandler, backgroundHandler), Timeline.Listener, EpoxyController.Interceptor {
/** /**
@ -209,8 +212,10 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
override fun onChanged(position: Int, count: Int, payload: Any?) { override fun onChanged(position: Int, count: Int, payload: Any?) {
synchronized(modelCache) { synchronized(modelCache) {
assertUpdateCallbacksAllowed() assertUpdateCallbacksAllowed()
Timber.v("listUpdateCallback.onChanged(position: $position, count: $count). " + Timber.v(
"\ncurrentSnapshot has size of ${currentSnapshot.size} items") "listUpdateCallback.onChanged(position: $position, count: $count). " +
"\ncurrentSnapshot has size of ${currentSnapshot.size} items"
)
(position until position + count).forEach { (position until position + count).forEach {
// Invalidate cache // Invalidate cache
modelCache[it] = null modelCache[it] = null
@ -238,8 +243,10 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
override fun onMoved(fromPosition: Int, toPosition: Int) { override fun onMoved(fromPosition: Int, toPosition: Int) {
synchronized(modelCache) { synchronized(modelCache) {
assertUpdateCallbacksAllowed() assertUpdateCallbacksAllowed()
Timber.v("listUpdateCallback.onMoved(fromPosition: $fromPosition, toPosition: $toPosition). " + Timber.v(
"\ncurrentSnapshot has size of ${currentSnapshot.size} items") "listUpdateCallback.onMoved(fromPosition: $fromPosition, toPosition: $toPosition). " +
"\ncurrentSnapshot has size of ${currentSnapshot.size} items"
)
val model = modelCache.removeAt(fromPosition) val model = modelCache.removeAt(fromPosition)
modelCache.add(toPosition, model) modelCache.add(toPosition, model)
requestModelBuild() requestModelBuild()
@ -249,8 +256,10 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
override fun onInserted(position: Int, count: Int) { override fun onInserted(position: Int, count: Int) {
synchronized(modelCache) { synchronized(modelCache) {
assertUpdateCallbacksAllowed() assertUpdateCallbacksAllowed()
Timber.v("listUpdateCallback.onInserted(position: $position, count: $count). " + Timber.v(
"\ncurrentSnapshot has size of ${currentSnapshot.size} items") "listUpdateCallback.onInserted(position: $position, count: $count). " +
"\ncurrentSnapshot has size of ${currentSnapshot.size} items"
)
repeat(count) { repeat(count) {
modelCache.add(position, null) modelCache.add(position, null)
} }
@ -261,8 +270,10 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
override fun onRemoved(position: Int, count: Int) { override fun onRemoved(position: Int, count: Int) {
synchronized(modelCache) { synchronized(modelCache) {
assertUpdateCallbacksAllowed() assertUpdateCallbacksAllowed()
Timber.v("listUpdateCallback.onRemoved(position: $position, count: $count). " + Timber.v(
"\ncurrentSnapshot has size of ${currentSnapshot.size} items") "listUpdateCallback.onRemoved(position: $position, count: $count). " +
"\ncurrentSnapshot has size of ${currentSnapshot.size} items"
)
repeat(count) { repeat(count) {
modelCache.removeAt(position) modelCache.removeAt(position)
} }
@ -314,7 +325,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
if (partialState.roomSummary?.membership != Membership.JOIN) { if (partialState.roomSummary?.membership != Membership.JOIN) {
return return
} }
val timestamp = System.currentTimeMillis() val timestamp = clock.epochMillis()
val showingForwardLoader = LoadingItem_() val showingForwardLoader = LoadingItem_()
.id("forward_loading_item_$timestamp") .id("forward_loading_item_$timestamp")
@ -406,14 +417,16 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
timelineEvent = it, timelineEvent = it,
highlightedEventId = partialState.highlightedEventId, highlightedEventId = partialState.highlightedEventId,
isFromThreadTimeline = partialState.isFromThreadTimeline(), isFromThreadTimeline = partialState.isFromThreadTimeline(),
rootThreadEventId = partialState.rootThreadEventId) rootThreadEventId = partialState.rootThreadEventId
)
} }
val nextDisplayableEvent = currentSnapshot.subList(position + 1, currentSnapshot.size).firstOrNull { val nextDisplayableEvent = currentSnapshot.subList(position + 1, currentSnapshot.size).firstOrNull {
timelineEventVisibilityHelper.shouldShowEvent( timelineEventVisibilityHelper.shouldShowEvent(
timelineEvent = it, timelineEvent = it,
highlightedEventId = partialState.highlightedEventId, highlightedEventId = partialState.highlightedEventId,
isFromThreadTimeline = partialState.isFromThreadTimeline(), isFromThreadTimeline = partialState.isFromThreadTimeline(),
rootThreadEventId = partialState.rootThreadEventId) rootThreadEventId = partialState.rootThreadEventId
)
} }
val timelineEventsGroup = timelineEventsGroups.getOrNull(event) val timelineEventsGroup = timelineEventsGroups.getOrNull(event)
val params = TimelineItemFactoryParams( val params = TimelineItemFactoryParams(
@ -466,7 +479,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
position: Int, position: Int,
receiptsByEvents: Map<String, List<ReadReceipt>>): CacheItemData { receiptsByEvents: Map<String, List<ReadReceipt>>): CacheItemData {
val wantsDateSeparator = wantsDateSeparator(event, nextEvent) val wantsDateSeparator = wantsDateSeparator(event, nextEvent)
val mergedHeaderModel = mergedHeaderItemFactory.create(event, val mergedHeaderModel = mergedHeaderItemFactory.create(
event,
nextEvent = nextEvent, nextEvent = nextEvent,
partialState = partialState, partialState = partialState,
items = this@TimelineEventController.currentSnapshot, items = this@TimelineEventController.currentSnapshot,
@ -537,7 +551,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
timelineEvent = event, timelineEvent = event,
highlightedEventId = partialState.highlightedEventId, highlightedEventId = partialState.highlightedEventId,
isFromThreadTimeline = partialState.isFromThreadTimeline(), isFromThreadTimeline = partialState.isFromThreadTimeline(),
rootThreadEventId = partialState.rootThreadEventId)) { rootThreadEventId = partialState.rootThreadEventId
)) {
lastShownEventId = event.eventId lastShownEventId = event.eventId
} }
if (lastShownEventId == null) { if (lastShownEventId == null) {

View file

@ -26,6 +26,7 @@ import im.vector.app.core.date.DateFormatKind
import im.vector.app.core.date.VectorDateFormatter import im.vector.app.core.date.VectorDateFormatter
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.time.Clock
import im.vector.app.core.ui.list.genericFooterItem import im.vector.app.core.ui.list.genericFooterItem
import im.vector.app.core.ui.list.genericHeaderItem import im.vector.app.core.ui.list.genericHeaderItem
import im.vector.app.core.ui.list.genericItem import im.vector.app.core.ui.list.genericItem
@ -49,7 +50,8 @@ class ViewEditHistoryEpoxyController @Inject constructor(
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val colorProvider: ColorProvider, private val colorProvider: ColorProvider,
private val eventHtmlRenderer: EventHtmlRenderer, private val eventHtmlRenderer: EventHtmlRenderer,
private val dateFormatter: VectorDateFormatter private val dateFormatter: VectorDateFormatter,
private val clock: Clock,
) : TypedEpoxyController<ViewEditHistoryViewState>() { ) : TypedEpoxyController<ViewEditHistoryViewState>() {
override fun buildModels(state: ViewEditHistoryViewState) { override fun buildModels(state: ViewEditHistoryViewState) {
@ -86,7 +88,7 @@ class ViewEditHistoryEpoxyController @Inject constructor(
val evDate = Calendar.getInstance().apply { val evDate = Calendar.getInstance().apply {
timeInMillis = timelineEvent.originServerTs timeInMillis = timelineEvent.originServerTs
?: System.currentTimeMillis() ?: clock.epochMillis()
} }
if (lastDate?.get(Calendar.DAY_OF_YEAR) != evDate.get(Calendar.DAY_OF_YEAR)) { if (lastDate?.get(Calendar.DAY_OF_YEAR) != evDate.get(Calendar.DAY_OF_YEAR)) {
// need to display header with day // need to display header with day

View file

@ -25,6 +25,7 @@ import im.vector.app.features.home.room.detail.timeline.item.DaySeparatorItem
import im.vector.app.features.home.room.detail.timeline.item.ItemWithEvents import im.vector.app.features.home.room.detail.timeline.item.ItemWithEvents
import im.vector.app.features.home.room.detail.timeline.item.TimelineReadMarkerItem_ import im.vector.app.features.home.room.detail.timeline.item.TimelineReadMarkerItem_
import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.Timeline
import kotlin.random.Random
import kotlin.reflect.KMutableProperty0 import kotlin.reflect.KMutableProperty0
private const val DEFAULT_PREFETCH_THRESHOLD = 30 private const val DEFAULT_PREFETCH_THRESHOLD = 30
@ -104,7 +105,7 @@ class TimelineControllerInterceptorHelper(private val positionOfReadMarker: KMut
.coerceAtLeast(0) .coerceAtLeast(0)
val loadingItem = LoadingItem_() val loadingItem = LoadingItem_()
.id("prefetch_backward_loading${System.currentTimeMillis()}") .id("prefetch_backward_loading${Random.nextLong()}")
.showLoader(false) .showLoader(false)
.setVisibilityStateChangedListener(Timeline.Direction.BACKWARDS, callback) .setVisibilityStateChangedListener(Timeline.Direction.BACKWARDS, callback)
@ -120,7 +121,7 @@ class TimelineControllerInterceptorHelper(private val positionOfReadMarker: KMut
.coerceAtLeast(0) .coerceAtLeast(0)
val loadingItem = LoadingItem_() val loadingItem = LoadingItem_()
.id("prefetch_forward_loading${System.currentTimeMillis()}") .id("prefetch_forward_loading${Random.nextLong()}")
.showLoader(false) .showLoader(false)
.setVisibilityStateChangedListener(Timeline.Direction.FORWARDS, callback) .setVisibilityStateChangedListener(Timeline.Direction.FORWARDS, callback)
add(indexOfPrefetchForward, loadingItem) add(indexOfPrefetchForward, loadingItem)

View file

@ -31,6 +31,7 @@ import im.vector.app.core.date.VectorDateFormatter
import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper
import im.vector.app.core.intent.getFilenameFromUri import im.vector.app.core.intent.getFilenameFromUri
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.time.Clock
import im.vector.app.databinding.DialogBaseEditTextBinding import im.vector.app.databinding.DialogBaseEditTextBinding
import im.vector.app.databinding.FragmentLoginAccountCreatedBinding import im.vector.app.databinding.FragmentLoginAccountCreatedBinding
import im.vector.app.features.displayname.getBestName import im.vector.app.features.displayname.getBestName
@ -52,13 +53,14 @@ class AccountCreatedFragment @Inject constructor(
private val avatarRenderer: AvatarRenderer, private val avatarRenderer: AvatarRenderer,
private val dateFormatter: VectorDateFormatter, private val dateFormatter: VectorDateFormatter,
private val matrixItemColorProvider: MatrixItemColorProvider, private val matrixItemColorProvider: MatrixItemColorProvider,
private val clock: Clock,
colorProvider: ColorProvider colorProvider: ColorProvider
) : AbstractLoginFragment2<FragmentLoginAccountCreatedBinding>(), ) : AbstractLoginFragment2<FragmentLoginAccountCreatedBinding>(),
GalleryOrCameraDialogHelper.Listener { GalleryOrCameraDialogHelper.Listener {
private val viewModel: AccountCreatedViewModel by fragmentViewModel() private val viewModel: AccountCreatedViewModel by fragmentViewModel()
private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider, clock)
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoginAccountCreatedBinding { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoginAccountCreatedBinding {
return FragmentLoginAccountCreatedBinding.inflate(inflater, container, false) return FragmentLoginAccountCreatedBinding.inflate(inflater, container, false)
@ -73,7 +75,7 @@ class AccountCreatedFragment @Inject constructor(
viewModel.onEach { invalidateState(it) } viewModel.onEach { invalidateState(it) }
views.loginAccountCreatedTime.text = dateFormatter.format(System.currentTimeMillis(), DateFormatKind.MESSAGE_SIMPLE) views.loginAccountCreatedTime.text = dateFormatter.format(clock.epochMillis(), DateFormatKind.MESSAGE_SIMPLE)
} }
private fun observeViewEvents() { private fun observeViewEvents() {

View file

@ -20,6 +20,7 @@ import android.content.Context
import androidx.core.net.toUri import androidx.core.net.toUri
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import im.vector.app.core.intent.getMimeTypeFromUri import im.vector.app.core.intent.getMimeTypeFromUri
import im.vector.app.core.time.Clock
import im.vector.app.core.utils.saveMedia import im.vector.app.core.utils.saveMedia
import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.notifications.NotificationUtils
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -30,7 +31,8 @@ import javax.inject.Inject
class DownloadMediaUseCase @Inject constructor( class DownloadMediaUseCase @Inject constructor(
@ApplicationContext private val appContext: Context, @ApplicationContext private val appContext: Context,
private val session: Session, private val session: Session,
private val notificationUtils: NotificationUtils private val notificationUtils: NotificationUtils,
private val clock: Clock,
) { ) {
suspend fun execute(input: File): Result<Unit> = withContext(session.coroutineDispatchers.io) { suspend fun execute(input: File): Result<Unit> = withContext(session.coroutineDispatchers.io) {
@ -40,7 +42,8 @@ class DownloadMediaUseCase @Inject constructor(
file = input, file = input,
title = input.name, title = input.name,
mediaMimeType = getMimeTypeFromUri(appContext, input.toUri()), mediaMimeType = getMimeTypeFromUri(appContext, input.toUri()),
notificationUtils = notificationUtils notificationUtils = notificationUtils,
now = clock.epochMillis()
) )
} }
} }

View file

@ -20,6 +20,7 @@ import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.takeAs import im.vector.app.core.extensions.takeAs
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.time.Clock
import im.vector.app.features.displayname.getBestName import im.vector.app.features.displayname.getBestName
import im.vector.app.features.home.room.detail.timeline.format.DisplayableEventFormatter import im.vector.app.features.home.room.detail.timeline.format.DisplayableEventFormatter
import im.vector.app.features.home.room.detail.timeline.format.NoticeEventFormatter import im.vector.app.features.home.room.detail.timeline.format.NoticeEventFormatter
@ -58,7 +59,8 @@ import javax.inject.Inject
class NotifiableEventResolver @Inject constructor( class NotifiableEventResolver @Inject constructor(
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val noticeEventFormatter: NoticeEventFormatter, private val noticeEventFormatter: NoticeEventFormatter,
private val displayableEventFormatter: DisplayableEventFormatter private val displayableEventFormatter: DisplayableEventFormatter,
private val clock: Clock,
) { ) {
// private val eventDisplay = RiotEventDisplay(context) // private val eventDisplay = RiotEventDisplay(context)
@ -86,7 +88,7 @@ class NotifiableEventResolver @Inject constructor(
eventId = event.eventId!!, eventId = event.eventId!!,
editedEventId = timelineEvent.getEditedEventId(), editedEventId = timelineEvent.getEditedEventId(),
noisy = false, // will be updated noisy = false, // will be updated
timestamp = event.originServerTs ?: System.currentTimeMillis(), timestamp = event.originServerTs ?: clock.epochMillis(),
description = bodyPreview, description = bodyPreview,
title = stringProvider.getString(R.string.notification_unknown_new_event), title = stringProvider.getString(R.string.notification_unknown_new_event),
soundName = null, soundName = null,
@ -178,15 +180,19 @@ class NotifiableEventResolver @Inject constructor(
roomName = roomName, roomName = roomName,
roomIsDirect = room.roomSummary()?.isDirect ?: false, roomIsDirect = room.roomSummary()?.isDirect ?: false,
roomAvatarPath = session.contentUrlResolver() roomAvatarPath = session.contentUrlResolver()
.resolveThumbnail(room.roomSummary()?.avatarUrl, .resolveThumbnail(
room.roomSummary()?.avatarUrl,
250, 250,
250, 250,
ContentUrlResolver.ThumbnailMethod.SCALE), ContentUrlResolver.ThumbnailMethod.SCALE
),
senderAvatarPath = session.contentUrlResolver() senderAvatarPath = session.contentUrlResolver()
.resolveThumbnail(event.senderInfo.avatarUrl, .resolveThumbnail(
event.senderInfo.avatarUrl,
250, 250,
250, 250,
ContentUrlResolver.ThumbnailMethod.SCALE), ContentUrlResolver.ThumbnailMethod.SCALE
),
matrixID = session.myUserId, matrixID = session.myUserId,
soundName = null soundName = null
) )

View file

@ -23,6 +23,7 @@ import androidx.core.app.RemoteInput
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
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.time.Clock
import im.vector.app.features.analytics.AnalyticsTracker import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.extensions.toAnalyticsJoinedRoom import im.vector.app.features.analytics.extensions.toAnalyticsJoinedRoom
import im.vector.app.features.session.coroutineScope import im.vector.app.features.session.coroutineScope
@ -45,6 +46,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager @Inject lateinit var notificationDrawerManager: NotificationDrawerManager
@Inject lateinit var activeSessionHolder: ActiveSessionHolder @Inject lateinit var activeSessionHolder: ActiveSessionHolder
@Inject lateinit var analyticsTracker: AnalyticsTracker @Inject lateinit var analyticsTracker: AnalyticsTracker
@Inject lateinit var clock: Clock
override fun onReceive(context: Context?, intent: Intent?) { override fun onReceive(context: Context?, intent: Intent?) {
if (intent == null || context == null) return if (intent == null || context == null) return
@ -137,7 +139,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
eventId = UUID.randomUUID().toString(), eventId = UUID.randomUUID().toString(),
editedEventId = null, editedEventId = null,
noisy = false, noisy = false,
timestamp = System.currentTimeMillis(), timestamp = clock.epochMillis(),
senderName = session.roomService().getRoomMember(session.myUserId, room.roomId)?.displayName senderName = session.roomService().getRoomMember(session.myUserId, room.roomId)?.displayName
?: context?.getString(R.string.notification_sender_me), ?: context?.getString(R.string.notification_sender_me),
senderId = session.myUserId, senderId = session.myUserId,
@ -188,7 +190,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
val notifiableMessageEvent = NotifiableMessageEvent( val notifiableMessageEvent = NotifiableMessageEvent(
event.eventId, event.eventId,
false, false,
System.currentTimeMillis(), clock.epochMillis(),
session.myUser?.displayname session.myUser?.displayname
?: context?.getString(R.string.notification_sender_me), ?: context?.getString(R.string.notification_sender_me),
session.myUserId, session.myUserId,

View file

@ -51,6 +51,7 @@ import im.vector.app.core.extensions.createIgnoredUri
import im.vector.app.core.platform.PendingIntentCompat import im.vector.app.core.platform.PendingIntentCompat
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.services.CallService import im.vector.app.core.services.CallService
import im.vector.app.core.time.Clock
import im.vector.app.core.utils.startNotificationChannelSettingsIntent import im.vector.app.core.utils.startNotificationChannelSettingsIntent
import im.vector.app.features.call.VectorCallActivity import im.vector.app.features.call.VectorCallActivity
import im.vector.app.features.call.service.CallHeadsUpActionReceiver import im.vector.app.features.call.service.CallHeadsUpActionReceiver
@ -72,9 +73,12 @@ import kotlin.random.Random
* Note: Cannot inject ColorProvider in the constructor, because it requires an Activity * Note: Cannot inject ColorProvider in the constructor, because it requires an Activity
*/ */
@Singleton @Singleton
class NotificationUtils @Inject constructor(private val context: Context, class NotificationUtils @Inject constructor(
private val stringProvider: StringProvider, private val context: Context,
private val vectorPreferences: VectorPreferences) { private val stringProvider: StringProvider,
private val vectorPreferences: VectorPreferences,
private val clock: Clock,
) {
companion object { companion object {
/* ========================================================================================== /* ==========================================================================================
@ -323,7 +327,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
} }
val contentPendingIntent = PendingIntent.getActivity( val contentPendingIntent = PendingIntent.getActivity(
context, context,
System.currentTimeMillis().toInt(), clock.epochMillis().toInt(),
contentIntent, contentIntent,
PendingIntentCompat.FLAG_IMMUTABLE PendingIntentCompat.FLAG_IMMUTABLE
) )
@ -337,7 +341,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
mode = VectorCallActivity.INCOMING_ACCEPT mode = VectorCallActivity.INCOMING_ACCEPT
) )
) )
.getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE) .getPendingIntent(clock.epochMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE)
val rejectCallPendingIntent = buildRejectCallPendingIntent(call.callId) val rejectCallPendingIntent = buildRejectCallPendingIntent(call.callId)
@ -392,7 +396,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
} }
val contentPendingIntent = PendingIntent.getActivity( val contentPendingIntent = PendingIntent.getActivity(
context, context,
System.currentTimeMillis().toInt(), clock.epochMillis().toInt(),
contentIntent, contentIntent,
PendingIntentCompat.FLAG_IMMUTABLE PendingIntentCompat.FLAG_IMMUTABLE
) )
@ -453,7 +457,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
val contentPendingIntent = TaskStackBuilder.create(context) val contentPendingIntent = TaskStackBuilder.create(context)
.addNextIntentWithParentStack(HomeActivity.newIntent(context)) .addNextIntentWithParentStack(HomeActivity.newIntent(context))
.addNextIntent(VectorCallActivity.newIntent(context, call, null)) .addNextIntent(VectorCallActivity.newIntent(context, call, null))
.getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE) .getPendingIntent(clock.epochMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE)
builder.setContentIntent(contentPendingIntent) builder.setContentIntent(contentPendingIntent)
@ -467,7 +471,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
} }
return PendingIntent.getBroadcast( return PendingIntent.getBroadcast(
context, context,
System.currentTimeMillis().toInt(), clock.epochMillis().toInt(),
rejectCallActionReceiver, rejectCallActionReceiver,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
) )
@ -515,7 +519,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
val contentPendingIntent = TaskStackBuilder.create(context) val contentPendingIntent = TaskStackBuilder.create(context)
.addNextIntentWithParentStack(HomeActivity.newIntent(context)) .addNextIntentWithParentStack(HomeActivity.newIntent(context))
.addNextIntent(RoomDetailActivity.newIntent(context, TimelineArgs(callInformation.nativeRoomId))) .addNextIntent(RoomDetailActivity.newIntent(context, TimelineArgs(callInformation.nativeRoomId)))
.getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE) .getPendingIntent(clock.epochMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE)
builder.setContentIntent(contentPendingIntent) builder.setContentIntent(contentPendingIntent)
return builder.build() return builder.build()
@ -562,7 +566,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
} }
PendingIntent.getActivity( PendingIntent.getActivity(
context, context,
System.currentTimeMillis().toInt(), clock.epochMillis().toInt(),
intent, intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
).let { ).let {
@ -636,7 +640,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
markRoomReadIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomInfo.roomId) markRoomReadIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomInfo.roomId)
val markRoomReadPendingIntent = PendingIntent.getBroadcast( val markRoomReadPendingIntent = PendingIntent.getBroadcast(
context, context,
System.currentTimeMillis().toInt(), clock.epochMillis().toInt(),
markRoomReadIntent, markRoomReadIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
) )
@ -677,7 +681,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
intent.action = DISMISS_ROOM_NOTIF_ACTION intent.action = DISMISS_ROOM_NOTIF_ACTION
val pendingIntent = PendingIntent.getBroadcast( val pendingIntent = PendingIntent.getBroadcast(
context.applicationContext, context.applicationContext,
System.currentTimeMillis().toInt(), clock.epochMillis().toInt(),
intent, intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
) )
@ -712,7 +716,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
rejectIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId) rejectIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId)
val rejectIntentPendingIntent = PendingIntent.getBroadcast( val rejectIntentPendingIntent = PendingIntent.getBroadcast(
context, context,
System.currentTimeMillis().toInt(), clock.epochMillis().toInt(),
rejectIntent, rejectIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
) )
@ -730,7 +734,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
joinIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId) joinIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId)
val joinIntentPendingIntent = PendingIntent.getBroadcast( val joinIntentPendingIntent = PendingIntent.getBroadcast(
context, context,
System.currentTimeMillis().toInt(), clock.epochMillis().toInt(),
joinIntent, joinIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
) )
@ -811,7 +815,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
.addNextIntentWithParentStack(HomeActivity.newIntent(context)) .addNextIntentWithParentStack(HomeActivity.newIntent(context))
.addNextIntent(roomIntentTap) .addNextIntent(roomIntentTap)
.getPendingIntent( .getPendingIntent(
System.currentTimeMillis().toInt(), clock.epochMillis().toInt(),
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
) )
} }
@ -844,7 +848,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
intent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId) intent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId)
return PendingIntent.getBroadcast( return PendingIntent.getBroadcast(
context, context,
System.currentTimeMillis().toInt(), clock.epochMillis().toInt(),
intent, intent,
// PendingIntents attached to actions with remote inputs must be mutable // PendingIntents attached to actions with remote inputs must be mutable
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_MUTABLE PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_MUTABLE

View file

@ -29,6 +29,7 @@ import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper
import im.vector.app.core.extensions.singletonEntryPoint import im.vector.app.core.extensions.singletonEntryPoint
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.time.Clock
import im.vector.app.databinding.FragmentFtueProfilePictureBinding import im.vector.app.databinding.FragmentFtueProfilePictureBinding
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.onboarding.OnboardingAction import im.vector.app.features.onboarding.OnboardingAction
@ -39,10 +40,11 @@ import javax.inject.Inject
class FtueAuthChooseProfilePictureFragment @Inject constructor( class FtueAuthChooseProfilePictureFragment @Inject constructor(
private val activeSessionHolder: ActiveSessionHolder, private val activeSessionHolder: ActiveSessionHolder,
colorProvider: ColorProvider colorProvider: ColorProvider,
clock: Clock,
) : AbstractFtueAuthFragment<FragmentFtueProfilePictureBinding>(), GalleryOrCameraDialogHelper.Listener { ) : AbstractFtueAuthFragment<FragmentFtueProfilePictureBinding>(), GalleryOrCameraDialogHelper.Listener {
private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider, clock)
private val avatarRenderer: AvatarRenderer by lazy { requireContext().singletonEntryPoint().avatarRenderer() } private val avatarRenderer: AvatarRenderer by lazy { requireContext().singletonEntryPoint().avatarRenderer() }
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueProfilePictureBinding { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueProfilePictureBinding {

View file

@ -24,6 +24,7 @@ import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
import com.tapadoo.alerter.Alerter import com.tapadoo.alerter.Alerter
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.core.time.Clock
import im.vector.app.core.utils.isAnimationDisabled import im.vector.app.core.utils.isAnimationDisabled
import im.vector.app.features.analytics.ui.consent.AnalyticsOptInActivity import im.vector.app.features.analytics.ui.consent.AnalyticsOptInActivity
import im.vector.app.features.pin.PinActivity import im.vector.app.features.pin.PinActivity
@ -41,7 +42,9 @@ import javax.inject.Singleton
* will be back in the queue in first position. * will be back in the queue in first position.
*/ */
@Singleton @Singleton
class PopupAlertManager @Inject constructor() { class PopupAlertManager @Inject constructor(
private val clock: Clock,
) {
companion object { companion object {
const val INCOMING_CALL_PRIORITY = Int.MAX_VALUE const val INCOMING_CALL_PRIORITY = Int.MAX_VALUE
@ -116,7 +119,7 @@ class PopupAlertManager @Inject constructor() {
return return
} }
if (currentAlerter != null) { if (currentAlerter != null) {
if (currentAlerter!!.expirationTimestamp != null && System.currentTimeMillis() > currentAlerter!!.expirationTimestamp!!) { if (currentAlerter!!.expirationTimestamp != null && clock.epochMillis() > currentAlerter!!.expirationTimestamp!!) {
// this alert has expired, remove it // this alert has expired, remove it
// perform dismiss // perform dismiss
try { try {
@ -162,7 +165,7 @@ class PopupAlertManager @Inject constructor() {
currentAlerter = next currentAlerter = next
next?.let { next?.let {
if (!shouldBeDisplayedIn(next, currentActivity)) return if (!shouldBeDisplayedIn(next, currentActivity)) return
val currentTime = System.currentTimeMillis() val currentTime = clock.epochMillis()
if (next.expirationTimestamp != null && currentTime > next.expirationTimestamp!!) { if (next.expirationTimestamp != null && currentTime > next.expirationTimestamp!!) {
// skip // skip
try { try {

View file

@ -37,6 +37,7 @@ import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.OnBackPressed import im.vector.app.core.platform.OnBackPressed
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.time.Clock
import im.vector.app.databinding.FragmentCreateRoomBinding import im.vector.app.databinding.FragmentCreateRoomBinding
import im.vector.app.features.navigation.Navigator import im.vector.app.features.navigation.Navigator
import im.vector.app.features.roomdirectory.RoomDirectorySharedAction import im.vector.app.features.roomdirectory.RoomDirectorySharedAction
@ -62,7 +63,8 @@ data class CreateRoomArgs(
class CreateRoomFragment @Inject constructor( class CreateRoomFragment @Inject constructor(
private val createRoomController: CreateRoomController, private val createRoomController: CreateRoomController,
private val createSpaceController: CreateSubSpaceController, private val createSpaceController: CreateSubSpaceController,
colorProvider: ColorProvider colorProvider: ColorProvider,
clock: Clock,
) : VectorBaseFragment<FragmentCreateRoomBinding>(), ) : VectorBaseFragment<FragmentCreateRoomBinding>(),
CreateRoomController.Listener, CreateRoomController.Listener,
GalleryOrCameraDialogHelper.Listener, GalleryOrCameraDialogHelper.Listener,
@ -74,7 +76,7 @@ class CreateRoomFragment @Inject constructor(
private lateinit var roomJoinRuleSharedActionViewModel: RoomJoinRuleSharedActionViewModel private lateinit var roomJoinRuleSharedActionViewModel: RoomJoinRuleSharedActionViewModel
private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider, clock)
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentCreateRoomBinding { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentCreateRoomBinding {
return FragmentCreateRoomBinding.inflate(inflater, container, false) return FragmentCreateRoomBinding.inflate(inflater, container, false)
@ -166,7 +168,8 @@ class CreateRoomFragment @Inject constructor(
} else { } else {
listOf(RoomJoinRules.INVITE, RoomJoinRules.PUBLIC) listOf(RoomJoinRules.INVITE, RoomJoinRules.PUBLIC)
} }
RoomJoinRuleBottomSheet.newInstance(state.roomJoinRules, RoomJoinRuleBottomSheet.newInstance(
state.roomJoinRules,
allowed.map { it.toOption(false) }, allowed.map { it.toOption(false) },
state.isSubSpace, state.isSubSpace,
state.parentSpaceSummary?.displayName state.parentSpaceSummary?.displayName

View file

@ -37,6 +37,7 @@ import im.vector.app.core.intent.getFilenameFromUri
import im.vector.app.core.platform.OnBackPressed import im.vector.app.core.platform.OnBackPressed
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.time.Clock
import im.vector.app.core.utils.toast import im.vector.app.core.utils.toast
import im.vector.app.databinding.FragmentRoomSettingGenericBinding import im.vector.app.databinding.FragmentRoomSettingGenericBinding
import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.analytics.plan.MobileScreen
@ -57,7 +58,8 @@ import javax.inject.Inject
class RoomSettingsFragment @Inject constructor( class RoomSettingsFragment @Inject constructor(
private val controller: RoomSettingsController, private val controller: RoomSettingsController,
colorProvider: ColorProvider, colorProvider: ColorProvider,
private val avatarRenderer: AvatarRenderer private val avatarRenderer: AvatarRenderer,
clock: Clock,
) : ) :
VectorBaseFragment<FragmentRoomSettingGenericBinding>(), VectorBaseFragment<FragmentRoomSettingGenericBinding>(),
RoomSettingsController.Callback, RoomSettingsController.Callback,
@ -70,7 +72,7 @@ class RoomSettingsFragment @Inject constructor(
private lateinit var roomJoinRuleSharedActionViewModel: RoomJoinRuleSharedActionViewModel private lateinit var roomJoinRuleSharedActionViewModel: RoomJoinRuleSharedActionViewModel
private val roomProfileArgs: RoomProfileArgs by args() private val roomProfileArgs: RoomProfileArgs by args()
private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider, clock)
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomSettingGenericBinding { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomSettingGenericBinding {
return FragmentRoomSettingGenericBinding.inflate(inflater, container, false) return FragmentRoomSettingGenericBinding.inflate(inflater, container, false)
@ -198,7 +200,8 @@ class RoomSettingsFragment @Inject constructor(
RoomSettingsAction.SetAvatarAction( RoomSettingsAction.SetAvatarAction(
RoomSettingsViewState.AvatarAction.UpdateAvatar( RoomSettingsViewState.AvatarAction.UpdateAvatar(
newAvatarUri = uri, newAvatarUri = uri,
newAvatarFileName = getFilenameFromUri(requireContext(), uri) ?: UUID.randomUUID().toString()) newAvatarFileName = getFilenameFromUri(requireContext(), uri) ?: UUID.randomUUID().toString()
)
) )
) )
} }

View file

@ -30,6 +30,7 @@ import com.google.android.material.tabs.TabLayoutMediator
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.intent.getMimeTypeFromUri import im.vector.app.core.intent.getMimeTypeFromUri
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.time.Clock
import im.vector.app.core.utils.saveMedia import im.vector.app.core.utils.saveMedia
import im.vector.app.core.utils.shareMedia import im.vector.app.core.utils.shareMedia
import im.vector.app.databinding.FragmentRoomUploadsBinding import im.vector.app.databinding.FragmentRoomUploadsBinding
@ -43,7 +44,8 @@ import javax.inject.Inject
class RoomUploadsFragment @Inject constructor( class RoomUploadsFragment @Inject constructor(
private val avatarRenderer: AvatarRenderer, private val avatarRenderer: AvatarRenderer,
private val notificationUtils: NotificationUtils private val notificationUtils: NotificationUtils,
private val clock: Clock,
) : VectorBaseFragment<FragmentRoomUploadsBinding>() { ) : VectorBaseFragment<FragmentRoomUploadsBinding>() {
private val roomProfileArgs: RoomProfileArgs by args() private val roomProfileArgs: RoomProfileArgs by args()
@ -88,7 +90,8 @@ class RoomUploadsFragment @Inject constructor(
file = it.file, file = it.file,
title = it.title, title = it.title,
mediaMimeType = getMimeTypeFromUri(requireContext(), it.file.toUri()), mediaMimeType = getMimeTypeFromUri(requireContext(), it.file.toUri()),
notificationUtils = notificationUtils notificationUtils = notificationUtils,
now = clock.epochMillis()
) )
}.onFailure { failure -> }.onFailure { failure ->
if (!isAdded) return@onFailure if (!isAdded) return@onFailure

View file

@ -26,6 +26,7 @@ import com.squareup.seismic.ShakeDetector
import im.vector.app.BuildConfig import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.DefaultSharedPreferences import im.vector.app.core.di.DefaultSharedPreferences
import im.vector.app.core.time.Clock
import im.vector.app.features.disclaimer.SHARED_PREF_KEY import im.vector.app.features.disclaimer.SHARED_PREF_KEY
import im.vector.app.features.homeserver.ServerUrlsRepository import im.vector.app.features.homeserver.ServerUrlsRepository
import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.themes.ThemeUtils
@ -33,7 +34,10 @@ import org.matrix.android.sdk.api.extensions.tryOrNull
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class VectorPreferences @Inject constructor(private val context: Context) { class VectorPreferences @Inject constructor(
private val context: Context,
private val clock: Clock,
) {
companion object { companion object {
const val SETTINGS_HELP_PREFERENCE_KEY = "SETTINGS_HELP_PREFERENCE_KEY" const val SETTINGS_HELP_PREFERENCE_KEY = "SETTINGS_HELP_PREFERENCE_KEY"
@ -664,9 +668,9 @@ class VectorPreferences @Inject constructor(private val context: Context) {
*/ */
fun getMinMediasLastAccessTime(): Long { fun getMinMediasLastAccessTime(): Long {
return when (getSelectedMediasSavingPeriod()) { return when (getSelectedMediasSavingPeriod()) {
MEDIA_SAVING_3_DAYS -> System.currentTimeMillis() / 1000 - 3 * 24 * 60 * 60 MEDIA_SAVING_3_DAYS -> clock.epochMillis() / 1000 - 3 * 24 * 60 * 60
MEDIA_SAVING_1_WEEK -> System.currentTimeMillis() / 1000 - 7 * 24 * 60 * 60 MEDIA_SAVING_1_WEEK -> clock.epochMillis() / 1000 - 7 * 24 * 60 * 60
MEDIA_SAVING_1_MONTH -> System.currentTimeMillis() / 1000 - 30 * 24 * 60 * 60 MEDIA_SAVING_1_MONTH -> clock.epochMillis() / 1000 - 30 * 24 * 60 * 60
MEDIA_SAVING_FOREVER -> 0 MEDIA_SAVING_FOREVER -> 0
else -> 0 else -> 0
} }
@ -872,8 +876,10 @@ class VectorPreferences @Inject constructor(private val context: Context) {
* @return true if user should always appear offline * @return true if user should always appear offline
*/ */
fun userAlwaysAppearsOffline(): Boolean { fun userAlwaysAppearsOffline(): Boolean {
return defaultPrefs.getBoolean(SETTINGS_PRESENCE_USER_ALWAYS_APPEARS_OFFLINE, return defaultPrefs.getBoolean(
getDefault(R.bool.settings_presence_user_always_appears_offline_default)) SETTINGS_PRESENCE_USER_ALWAYS_APPEARS_OFFLINE,
getDefault(R.bool.settings_presence_user_always_appears_offline_default)
)
} }
/** /**
@ -1005,9 +1011,11 @@ class VectorPreferences @Inject constructor(private val context: Context) {
} }
fun prefSpacesShowAllRoomInHome(): Boolean { fun prefSpacesShowAllRoomInHome(): Boolean {
return defaultPrefs.getBoolean(SETTINGS_PREF_SPACE_SHOW_ALL_ROOM_IN_HOME, return defaultPrefs.getBoolean(
SETTINGS_PREF_SPACE_SHOW_ALL_ROOM_IN_HOME,
// migration of old property // migration of old property
!labsSpacesOnlyOrphansInHome()) !labsSpacesOnlyOrphansInHome()
)
} }
/* /*

View file

@ -45,6 +45,7 @@ import im.vector.app.core.preference.UserAvatarPreference
import im.vector.app.core.preference.VectorPreference import im.vector.app.core.preference.VectorPreference
import im.vector.app.core.preference.VectorSwitchPreference import im.vector.app.core.preference.VectorSwitchPreference
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.time.Clock
import im.vector.app.core.utils.TextUtils import im.vector.app.core.utils.TextUtils
import im.vector.app.core.utils.getSizeOfFiles import im.vector.app.core.utils.getSizeOfFiles
import im.vector.app.core.utils.toast import im.vector.app.core.utils.toast
@ -74,7 +75,8 @@ import java.util.UUID
import javax.inject.Inject import javax.inject.Inject
class VectorSettingsGeneralFragment @Inject constructor( class VectorSettingsGeneralFragment @Inject constructor(
colorProvider: ColorProvider colorProvider: ColorProvider,
clock: Clock,
) : ) :
VectorSettingsBaseFragment(), VectorSettingsBaseFragment(),
GalleryOrCameraDialogHelper.Listener { GalleryOrCameraDialogHelper.Listener {
@ -82,7 +84,7 @@ class VectorSettingsGeneralFragment @Inject constructor(
override var titleRes = R.string.settings_general_title override var titleRes = R.string.settings_general_title
override val preferenceXmlRes = R.xml.vector_settings_general override val preferenceXmlRes = R.xml.vector_settings_general
private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider, clock)
private val mUserSettingsCategory by lazy { private val mUserSettingsCategory by lazy {
findPreference<PreferenceCategory>(VectorPreferences.SETTINGS_USER_SETTINGS_PREFERENCE_KEY)!! findPreference<PreferenceCategory>(VectorPreferences.SETTINGS_USER_SETTINGS_PREFERENCE_KEY)!!

View file

@ -36,12 +36,15 @@ import im.vector.app.R
import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.extensions.safeOpenOutputStream import im.vector.app.core.extensions.safeOpenOutputStream
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.time.Clock
import im.vector.app.core.utils.selectTxtFileToWrite import im.vector.app.core.utils.selectTxtFileToWrite
import im.vector.app.databinding.FragmentDevtoolKeyrequestsBinding import im.vector.app.databinding.FragmentDevtoolKeyrequestsBinding
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
import javax.inject.Inject import javax.inject.Inject
class KeyRequestsFragment @Inject constructor() : VectorBaseFragment<FragmentDevtoolKeyrequestsBinding>() { class KeyRequestsFragment @Inject constructor(
private val clock: Clock,
) : VectorBaseFragment<FragmentDevtoolKeyrequestsBinding>() {
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentDevtoolKeyrequestsBinding { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentDevtoolKeyrequestsBinding {
return FragmentDevtoolKeyrequestsBinding.inflate(inflater, container, false) return FragmentDevtoolKeyrequestsBinding.inflate(inflater, container, false)
@ -126,7 +129,7 @@ class KeyRequestsFragment @Inject constructor() : VectorBaseFragment<FragmentDev
selectTxtFileToWrite( selectTxtFileToWrite(
activity = requireActivity(), activity = requireActivity(),
activityResultLauncher = epxortAuditForActivityResult, activityResultLauncher = epxortAuditForActivityResult,
defaultFileName = "audit-export_${System.currentTimeMillis()}.txt", defaultFileName = "audit-export_${clock.epochMillis()}.txt",
chooserHint = "Export Audit" chooserHint = "Export Audit"
) )
return true return true

View file

@ -28,12 +28,14 @@ import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.platform.OnBackPressed import im.vector.app.core.platform.OnBackPressed
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.time.Clock
import im.vector.app.databinding.FragmentSpaceCreateGenericEpoxyFormBinding import im.vector.app.databinding.FragmentSpaceCreateGenericEpoxyFormBinding
import javax.inject.Inject import javax.inject.Inject
class CreateSpaceDetailsFragment @Inject constructor( class CreateSpaceDetailsFragment @Inject constructor(
private val epoxyController: SpaceDetailEpoxyController, private val epoxyController: SpaceDetailEpoxyController,
colorProvider: ColorProvider colorProvider: ColorProvider,
clock: Clock,
) : VectorBaseFragment<FragmentSpaceCreateGenericEpoxyFormBinding>(), SpaceDetailEpoxyController.Listener, ) : VectorBaseFragment<FragmentSpaceCreateGenericEpoxyFormBinding>(), SpaceDetailEpoxyController.Listener,
GalleryOrCameraDialogHelper.Listener, OnBackPressed { GalleryOrCameraDialogHelper.Listener, OnBackPressed {
@ -42,7 +44,7 @@ class CreateSpaceDetailsFragment @Inject constructor(
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) = override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) =
FragmentSpaceCreateGenericEpoxyFormBinding.inflate(layoutInflater, container, false) FragmentSpaceCreateGenericEpoxyFormBinding.inflate(layoutInflater, container, false)
private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider, clock)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)

View file

@ -38,7 +38,7 @@ import im.vector.app.core.intent.getFilenameFromUri
import im.vector.app.core.platform.OnBackPressed import im.vector.app.core.platform.OnBackPressed
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.DrawableProvider import im.vector.app.core.time.Clock
import im.vector.app.core.utils.toast import im.vector.app.core.utils.toast
import im.vector.app.databinding.FragmentRoomSettingGenericBinding import im.vector.app.databinding.FragmentRoomSettingGenericBinding
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
@ -60,9 +60,9 @@ import javax.inject.Inject
class SpaceSettingsFragment @Inject constructor( class SpaceSettingsFragment @Inject constructor(
private val epoxyController: SpaceSettingsController, private val epoxyController: SpaceSettingsController,
private val colorProvider: ColorProvider, colorProvider: ColorProvider,
clock: Clock,
private val avatarRenderer: AvatarRenderer, private val avatarRenderer: AvatarRenderer,
private val drawableProvider: DrawableProvider
) : VectorBaseFragment<FragmentRoomSettingGenericBinding>(), ) : VectorBaseFragment<FragmentRoomSettingGenericBinding>(),
SpaceSettingsController.Callback, SpaceSettingsController.Callback,
GalleryOrCameraDialogHelper.Listener, GalleryOrCameraDialogHelper.Listener,
@ -73,7 +73,7 @@ class SpaceSettingsFragment @Inject constructor(
private lateinit var roomJoinRuleSharedActionViewModel: RoomJoinRuleSharedActionViewModel private lateinit var roomJoinRuleSharedActionViewModel: RoomJoinRuleSharedActionViewModel
private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider, clock)
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) = FragmentRoomSettingGenericBinding.inflate(inflater) override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) = FragmentRoomSettingGenericBinding.inflate(inflater)
@ -236,7 +236,8 @@ class SpaceSettingsFragment @Inject constructor(
RoomSettingsAction.SetAvatarAction( RoomSettingsAction.SetAvatarAction(
RoomSettingsViewState.AvatarAction.UpdateAvatar( RoomSettingsViewState.AvatarAction.UpdateAvatar(
newAvatarUri = uri, newAvatarUri = uri,
newAvatarFileName = getFilenameFromUri(requireContext(), uri) ?: UUID.randomUUID().toString()) newAvatarFileName = getFilenameFromUri(requireContext(), uri) ?: UUID.randomUUID().toString()
)
) )
) )
} }

View file

@ -47,6 +47,7 @@ import org.matrix.android.sdk.api.session.identity.IdentityServiceListener
import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.identity.ThreePid
import org.matrix.android.sdk.api.session.user.model.User import org.matrix.android.sdk.api.session.user.model.User
import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.api.util.toMatrixItem
import kotlin.random.Random
data class ThreePidUser( data class ThreePidUser(
val email: String, val email: String,
@ -142,7 +143,7 @@ class UserListViewModel @AssistedInject constructor(
} }
private fun retryUserSearch(state: UserListViewState) { private fun retryUserSearch(state: UserListViewState) {
identityServerUsersSearch.tryEmit(UserSearch(state.searchTerm, cacheBuster = System.currentTimeMillis())) identityServerUsersSearch.tryEmit(UserSearch(state.searchTerm, cacheBuster = Random.nextLong()))
} }
private fun handleSearchUsers(searchTerm: String) { private fun handleSearchUsers(searchTerm: String) {

View file

@ -20,11 +20,13 @@ import android.content.Context
import android.os.Build import android.os.Build
import com.arthenica.ffmpegkit.FFmpegKit import com.arthenica.ffmpegkit.FFmpegKit
import com.arthenica.ffmpegkit.ReturnCode import com.arthenica.ffmpegkit.ReturnCode
import im.vector.app.core.time.Clock
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
import javax.inject.Inject import javax.inject.Inject
class VoicePlayerHelper @Inject constructor( class VoicePlayerHelper @Inject constructor(
private val clock: Clock,
context: Context context: Context
) { ) {
private val outputDirectory: File by lazy { private val outputDirectory: File by lazy {
@ -46,9 +48,9 @@ class VoicePlayerHelper @Inject constructor(
if (targetFile.exists()) { if (targetFile.exists()) {
targetFile.delete() targetFile.delete()
} }
val start = System.currentTimeMillis() val start = clock.epochMillis()
val session = FFmpegKit.execute("-i \"${file.path}\" -c:a aac \"${targetFile.path}\"") val session = FFmpegKit.execute("-i \"${file.path}\" -c:a aac \"${targetFile.path}\"")
val duration = System.currentTimeMillis() - start val duration = clock.epochMillis() - start
Timber.d("Convert to mp4 in $duration ms. Size in bytes from ${file.length()} to ${targetFile.length()}") Timber.d("Convert to mp4 in $duration ms. Size in bytes from ${file.length()} to ${targetFile.length()}")
return when { return when {
ReturnCode.isSuccess(session.returnCode) -> { ReturnCode.isSuccess(session.returnCode) -> {

View file

@ -23,10 +23,14 @@ import com.arthenica.ffmpegkit.FFmpegKitConfig
import com.arthenica.ffmpegkit.Level import com.arthenica.ffmpegkit.Level
import com.arthenica.ffmpegkit.ReturnCode import com.arthenica.ffmpegkit.ReturnCode
import im.vector.app.BuildConfig import im.vector.app.BuildConfig
import im.vector.app.core.time.Clock
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
class VoiceRecorderL(context: Context) : AbstractVoiceRecorder(context, "mp4") { class VoiceRecorderL(
context: Context,
private val clock: Clock,
) : AbstractVoiceRecorder(context, "mp4") {
override fun setOutputFormat(mediaRecorder: MediaRecorder) { override fun setOutputFormat(mediaRecorder: MediaRecorder) {
// Use AAC/MP4 format here // Use AAC/MP4 format here
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
@ -43,9 +47,9 @@ class VoiceRecorderL(context: Context) : AbstractVoiceRecorder(context, "mp4") {
if (targetFile.exists()) { if (targetFile.exists()) {
targetFile.delete() targetFile.delete()
} }
val start = System.currentTimeMillis() val start = clock.epochMillis()
val session = FFmpegKit.execute("-i \"${recordedFile.path}\" -c:a libvorbis \"${targetFile.path}\"") val session = FFmpegKit.execute("-i \"${recordedFile.path}\" -c:a libvorbis \"${targetFile.path}\"")
val duration = System.currentTimeMillis() - start val duration = clock.epochMillis() - start
Timber.d("Convert to ogg in $duration ms. Size in bytes from ${recordedFile.length()} to ${targetFile.length()}") Timber.d("Convert to ogg in $duration ms. Size in bytes from ${recordedFile.length()} to ${targetFile.length()}")
return when { return when {
ReturnCode.isSuccess(session.returnCode) -> { ReturnCode.isSuccess(session.returnCode) -> {

View file

@ -18,16 +18,18 @@ package im.vector.app.features.voice
import android.content.Context import android.content.Context
import android.os.Build import android.os.Build
import im.vector.app.core.time.Clock
import javax.inject.Inject import javax.inject.Inject
class VoiceRecorderProvider @Inject constructor( class VoiceRecorderProvider @Inject constructor(
private val context: Context private val context: Context,
private val clock: Clock,
) { ) {
fun provideVoiceRecorder(): VoiceRecorder { fun provideVoiceRecorder(): VoiceRecorder {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
VoiceRecorderQ(context) VoiceRecorderQ(context)
} else { } else {
VoiceRecorderL(context) VoiceRecorderL(context, clock)
} }
} }
} }