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.BottomSheetDialogFragment
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.core.time.DefaultClock
import im.vector.app.espresso.tools.waitUntilViewVisible
import org.hamcrest.Matcher
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.util.Optional
import java.util.concurrent.TimeoutException
import kotlin.random.Random
object EspressoHelper {
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 {
return object : ViewAction {
private val clock = DefaultClock()
override fun getConstraints(): Matcher<View> {
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) {
println("*** waitForView 1 $view")
uiController.loopMainThreadUntilIdle()
val startTime = System.currentTimeMillis()
val startTime = clock.epochMillis()
val endTime = startTime + timeout
val visibleMatcher = isDisplayed()
uiController.loopMainThreadForAtLeast(100)
do {
println("*** waitForView loop $view end:$endTime current:${System.currentTimeMillis()}")
println("*** waitForView loop $view end:$endTime current:${clock.epochMillis()}")
val viewVisible = TreeIterables.breadthFirstViewTraversal(view)
.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...")
uiController.loopMainThreadForAtLeast(50)
println("*** waitForView loop ...loopMainThreadForAtLeast")
} while (System.currentTimeMillis() < endTime)
} while (clock.epochMillis() < endTime)
println("*** waitForView timeout $view")
// Timeout happens.
@ -168,7 +172,7 @@ fun activityIdlingResource(activityClass: Class<*>): IdlingResource {
val res = object : IdlingResource, ActivityLifecycleCallback {
private var callback: IdlingResource.ResourceCallback? = null
private var resumedActivity: Activity? = null
private val uniqTS = System.currentTimeMillis()
private val uniqTS = Random.nextLong()
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.Test
import org.junit.runner.RunWith
import kotlin.random.Random
@RunWith(AndroidJUnit4::class)
@LargeTest
@ -44,7 +45,7 @@ class RegistrationTest {
@Test
fun simpleRegister() {
val userId: String = "UiAutoTest_${System.currentTimeMillis()}"
val userId: String = "UiAutoTest_${Random.nextLong()}"
val password: String = "password"
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.runner.RunWith
import org.matrix.android.sdk.api.session.Session
import kotlin.random.Random
@RunWith(AndroidJUnit4::class)
@LargeTest
@ -61,7 +62,7 @@ class SecurityBootstrapTest : VerificationTestBase() {
@Before
fun createSessionWithCrossSigning() {
val matrix = getMatrixInstance()
val userName = "foobar_${System.currentTimeMillis()}"
val userName = "foobar_${Random.nextLong()}"
existingSession = createAccountAndSync(matrix, userName, password, true)
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 kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.random.Random
@RunWith(AndroidJUnit4::class)
@LargeTest
@ -66,7 +67,7 @@ class VerifySessionInteractiveTest : VerificationTestBase() {
@Before
fun createSessionWithCrossSigning() {
val matrix = getMatrixInstance()
val userName = "foobar_${System.currentTimeMillis()}"
val userName = "foobar_${Random.nextLong()}"
existingSession = createAccountAndSync(matrix, userName, password, true)
doSync<Unit> {
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 kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.random.Random
@RunWith(AndroidJUnit4::class)
@LargeTest
@ -68,7 +69,7 @@ class VerifySessionPassphraseTest : VerificationTestBase() {
fun createSessionWithCrossSigningAnd4S() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val matrix = getMatrixInstance()
val userName = "foobar_${System.currentTimeMillis()}"
val userName = "foobar_${Random.nextLong()}"
existingSession = createAccountAndSync(matrix, userName, password, true)
doSync<Unit> {
existingSession!!.cryptoService().crossSigningService()

View file

@ -24,6 +24,7 @@ import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import im.vector.app.core.time.DefaultClock
import org.junit.rules.TestWatcher
import org.junit.runner.Description
import timber.log.Timber
@ -54,7 +55,7 @@ private fun storeFailureScreenshot(bitmap: Bitmap, screenshotName: String) {
val contentValues = ContentValues().apply {
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) {
useMediaStoreScreenshotStorage(

View file

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

View file

@ -74,7 +74,7 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
intent,
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>()!!
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
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.LocaleProvider
import im.vector.app.core.resources.toTimestamp
import im.vector.app.core.time.Clock
import org.threeten.bp.LocalDateTime
import org.threeten.bp.Period
import org.threeten.bp.format.DateTimeFormatter
import javax.inject.Inject
import kotlin.math.absoluteValue
class VectorDateFormatter @Inject constructor(private val context: Context,
private val localeProvider: LocaleProvider,
private val dateFormatterProviders: DateFormatterProviders) {
class VectorDateFormatter @Inject constructor(
private val context: Context,
private val localeProvider: LocaleProvider,
private val dateFormatterProviders: DateFormatterProviders,
private val clock: Clock,
) {
private val hourFormatter by lazy {
if (DateFormat.is24HourFormat(context)) {
@ -158,8 +162,9 @@ class VectorDateFormatter @Inject constructor(private val context: Context,
private fun getRelativeDay(ts: Long): String {
return DateUtils.getRelativeTimeSpanString(
ts,
System.currentTimeMillis(),
clock.epochMillis(),
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.registerStartForActivityResult
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.checkPermissions
import im.vector.app.core.utils.onPermissionDeniedDialog
@ -45,7 +46,8 @@ import java.io.File
class GalleryOrCameraDialogHelper(
// must implement GalleryOrCameraDialogHelper.Listener
private val fragment: Fragment,
private val colorProvider: ColorProvider
private val colorProvider: ColorProvider,
private val clock: Clock,
) {
interface Listener {
fun onImageReady(uri: Uri?)
@ -91,7 +93,7 @@ class GalleryOrCameraDialogHelper(
}
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
createUCropWithDefaultSettings(colorProvider, uri, destinationFile.toUri(), fragment.getString(R.string.rotate_and_crop_screen_title))
.withAspectRatio(1f, 1f)

View file

@ -33,6 +33,8 @@ import androidx.work.WorkerParameters
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R
import im.vector.app.core.platform.PendingIntentCompat
import im.vector.app.core.time.Clock
import im.vector.app.core.time.DefaultClock
import im.vector.app.features.notifications.NotificationUtils
import im.vector.app.features.settings.BackgroundSyncMode
import org.matrix.android.sdk.api.Matrix
@ -77,6 +79,7 @@ class VectorSyncService : SyncService() {
@Inject lateinit var notificationUtils: NotificationUtils
@Inject lateinit var matrix: Matrix
@Inject lateinit var clock: Clock
override fun provideMatrix() = matrix
@ -102,7 +105,8 @@ class VectorSyncService : SyncService() {
syncTimeoutSeconds = syncTimeoutSeconds,
syncDelaySeconds = syncDelaySeconds,
isPeriodic = true,
isNetworkBack = false
isNetworkBack = false,
now = clock.epochMillis()
)
}
@ -114,9 +118,10 @@ class VectorSyncService : SyncService() {
val rescheduleSyncWorkRequest: WorkRequest =
OneTimeWorkRequestBuilder<RestartWhenNetworkOn>()
.setInputData(RestartWhenNetworkOn.createInputData(sessionId, syncTimeoutSeconds, syncDelaySeconds, isPeriodic))
.setConstraints(Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.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
class RestartWhenNetworkOn(appContext: Context, workerParams: WorkerParameters) :
Worker(appContext, workerParams) {
class RestartWhenNetworkOn(
appContext: Context,
workerParams: WorkerParameters
) : Worker(appContext, workerParams) {
override fun doWork(): Result {
Timber.d("## Sync: RestartWhenNetworkOn.doWork()")
val sessionId = inputData.getString(KEY_SESSION_ID) ?: return Result.failure()
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 isPeriodic = inputData.getBoolean(KEY_IS_PERIODIC, false)
// Not sure how to inject a Clock here
val clock = DefaultClock()
applicationContext.rescheduleSyncService(
sessionId = sessionId,
syncTimeoutSeconds = syncTimeoutSeconds,
syncDelaySeconds = syncDelaySeconds,
isPeriodic = isPeriodic,
isNetworkBack = true
isNetworkBack = true,
now = clock.epochMillis()
)
// Indicate whether the work finished successfully with the Result
return Result.success()
@ -182,7 +194,8 @@ private fun Context.rescheduleSyncService(sessionId: String,
syncTimeoutSeconds: Int,
syncDelaySeconds: Int,
isPeriodic: Boolean,
isNetworkBack: Boolean) {
isNetworkBack: Boolean,
now: Long) {
Timber.d("## Sync: rescheduleSyncService")
val intent = if (isPeriodic) {
VectorSyncService.newPeriodicIntent(
@ -208,7 +221,7 @@ private fun Context.rescheduleSyncService(sessionId: String,
} else {
PendingIntent.getService(this, 0, intent, PendingIntentCompat.FLAG_IMMUTABLE)
}
val firstMillis = System.currentTimeMillis() + syncDelaySeconds * 1000L
val firstMillis = now + syncDelaySeconds * 1000L
val alarmMgr = getSystemService<AlarmManager>()!!
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
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
*/
fun sendMailTo(address: String, subject: String? = null, message: String? = null, activity: Activity) {
val intent = Intent(Intent.ACTION_SENDTO, Uri.fromParts(
"mailto", address, null))
val intent = Intent(
Intent.ACTION_SENDTO, Uri.fromParts(
"mailto", address, null
)
)
intent.putExtra(Intent.EXTRA_SUBJECT, subject)
intent.putExtra(Intent.EXTRA_TEXT, message)
@ -248,7 +251,12 @@ private fun appendTimeToFilename(name: String): String {
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) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
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.DISPLAY_NAME, filename)
put(MediaStore.Images.Media.MIME_TYPE, mediaMimeType)
put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis())
put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis())
put(MediaStore.Images.Media.DATE_ADDED, now)
put(MediaStore.Images.Media.DATE_TAKEN, now)
}
val externalContentUri = when {
mediaMimeType?.isMimeTypeImage() == true -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
@ -289,7 +297,7 @@ suspend fun saveMedia(context: Context, file: File, title: String, mediaMimeType
}
}
} 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,
mediaMimeType: String?,
title: String,
file: File) {
file: File,
now: Long) {
val state = Environment.getExternalStorageState()
if (Environment.MEDIA_MOUNTED != state) {
context.toast(context.getString(R.string.error_saving_media_file))
@ -319,7 +328,7 @@ private fun saveMediaLegacy(context: Context,
} else {
title
}
val savedFile = saveFileIntoLegacy(file, downloadDir, outputFilename)
val savedFile = saveFileIntoLegacy(file, downloadDir, outputFilename, now)
if (savedFile != null) {
val downloadManager = context.getSystemService<DownloadManager>()
downloadManager?.addCompletedDownload(
@ -329,7 +338,8 @@ private fun saveMediaLegacy(context: Context,
mediaMimeType ?: MimeTypes.OctetStream,
savedFile.absolutePath,
savedFile.length(),
true)
true
)
addToGallery(savedFile, mediaMimeType, context)
}
} catch (error: Throwable) {
@ -411,7 +421,7 @@ fun selectTxtFileToWrite(
* @return the created file
*/
@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
var dstFileName: String
@ -423,7 +433,7 @@ fun saveFileIntoLegacy(sourceFile: File, dstDirPath: File, outputFilename: Strin
if (dotPos > 0) {
fileExt = sourceFile.name.substring(dotPos)
}
dstFileName = "vector_" + System.currentTimeMillis() + fileExt
dstFileName = "vector_$now$fileExt"
} else {
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.platform.VectorBaseFragment
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.SnapOnScrollListener
import im.vector.app.core.utils.attachSnapHelperWithListener
@ -64,7 +65,8 @@ data class AttachmentsPreviewArgs(
class AttachmentsPreviewFragment @Inject constructor(
private val attachmentMiniaturePreviewController: AttachmentMiniaturePreviewController,
private val attachmentBigPreviewController: AttachmentBigPreviewController,
private val colorProvider: ColorProvider
private val colorProvider: ColorProvider,
private val clock: Clock,
) : VectorBaseFragment<FragmentAttachmentsPreviewBinding>(), AttachmentMiniaturePreviewController.Callback {
private val fragmentArgs: AttachmentsPreviewArgs by args()
@ -192,7 +194,7 @@ class AttachmentsPreviewFragment @Inject constructor(
private fun handleEditAction() = withState(viewModel) {
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
createUCropWithDefaultSettings(colorProvider, uri, destinationFile.toUri(), currentAttachment.name)
.getIntent(requireContext())

View file

@ -19,6 +19,7 @@ package im.vector.app.features.call.conference
import im.vector.app.R
import im.vector.app.core.network.await
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.toBase32String
import im.vector.app.features.call.conference.jwt.JitsiJWTFactory
@ -46,7 +47,9 @@ class JitsiService @Inject constructor(
private val rawService: RawService,
private val stringProvider: StringProvider,
private val themeProvider: ThemeProvider,
private val jitsiJWTFactory: JitsiJWTFactory) {
private val jitsiJWTFactory: JitsiJWTFactory,
private val clock: Clock,
) {
companion object {
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 {
// 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 {
rawService.getElementWellknown(session.sessionParams)
?.jitsiServer

View file

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

View file

@ -22,6 +22,7 @@ import androidx.lifecycle.ViewModel
import com.nulabinc.zxcvbn.Strength
import im.vector.app.R
import im.vector.app.core.platform.WaitingViewData
import im.vector.app.core.time.Clock
import im.vector.app.core.utils.LiveEvent
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.listeners.ProgressListener
@ -37,7 +38,9 @@ import javax.inject.Inject
/**
* The shared view model between all fragments.
*/
class KeysBackupSetupSharedViewModel @Inject constructor() : ViewModel() {
class KeysBackupSetupSharedViewModel @Inject constructor(
private val clock: Clock,
) : ViewModel() {
companion object {
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?) {
// Update requestId
currentRequestId.value = System.currentTimeMillis()
currentRequestId.value = clock.epochMillis()
isCreatingBackupVersion.value = true
recoveryKey.value = null
@ -101,9 +104,11 @@ class KeysBackupSetupSharedViewModel @Inject constructor() : ViewModel() {
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,
total)
total
)
}
},
object : MatrixCallback<MegolmBackupCreationInfo> {

View file

@ -18,6 +18,7 @@ package im.vector.app.features.crypto.verification
import android.content.Context
import im.vector.app.R
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.home.AvatarRenderer
import im.vector.app.features.home.room.detail.RoomDetailActivity
@ -42,7 +43,9 @@ import javax.inject.Singleton
class IncomingVerificationRequestHandler @Inject constructor(
private val context: Context,
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
@ -104,7 +107,7 @@ class IncomingVerificationRequestHandler @Inject constructor(
}
)
// 10mn expiration
expirationTimestamp = System.currentTimeMillis() + (10 * 60 * 1000L)
expirationTimestamp = clock.epochMillis() + (10 * 60 * 1000L)
}
popupAlertManager.postVectorAlert(alert)
}
@ -168,7 +171,7 @@ class IncomingVerificationRequestHandler @Inject constructor(
}
colorAttribute = R.attr.vctr_notice_secondary
// 5mn expiration
expirationTimestamp = System.currentTimeMillis() + (5 * 60 * 1000L)
expirationTimestamp = clock.epochMillis() + (5 * 60 * 1000L)
}
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.VectorViewModel
import im.vector.app.core.platform.VectorViewModelAction
import im.vector.app.core.time.Clock
import im.vector.app.features.settings.VectorPreferences
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
@ -56,10 +57,12 @@ data class DeviceDetectionInfo(
val currentSessionTrust: Boolean
)
class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted initialState: UnknownDevicesState,
session: Session,
private val vectorPreferences: VectorPreferences) :
VectorViewModel<UnknownDevicesState, UnknownDeviceDetectorSharedViewModel.Action, EmptyViewEvents>(initialState) {
class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(
@Assisted initialState: UnknownDevicesState,
session: Session,
private val vectorPreferences: VectorPreferences,
clock: Clock,
) : VectorViewModel<UnknownDevicesState, UnknownDeviceDetectorSharedViewModel.Action, EmptyViewEvents>(initialState) {
sealed class Action : VectorViewModelAction {
data class IgnoreDevice(val deviceIds: List<String>) : Action()
@ -75,11 +78,10 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted
private val ignoredDeviceList = ArrayList<String>()
init {
val currentSessionTs = session.cryptoService().getCryptoDeviceInfo(session.myUserId)
.firstOrNull { it.deviceId == session.sessionParams.deviceId }
?.firstTimeSeenLocalTs
?: System.currentTimeMillis()
?: clock.epochMillis()
Timber.v("## Detector - Current Session first time seen $currentSessionTs")
ignoredDeviceList.addAll(

View file

@ -16,6 +16,7 @@
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.room.model.message.MessageContent
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)
* 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 {
fun stopEffects()
@ -61,7 +64,7 @@ class ChatEffectManager @Inject constructor() {
fun checkForEffect(event: TimelineEvent) {
val age = event.root.ageLocalTs ?: 0
val now = System.currentTimeMillis()
val now = clock.epochMillis()
// messages older than 10s should not trigger any effect
if ((now - age) >= 10_000) 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.EpoxyViewHolder
import im.vector.app.R
import im.vector.app.core.time.Clock
import im.vector.app.features.themes.ThemeUtils
import timber.log.Timber
import kotlin.math.abs
import kotlin.math.min
class RoomMessageTouchHelperCallback(private val context: Context,
@DrawableRes actionIcon: Int,
private val handler: QuickReplayHandler) : EpoxyTouchHelperCallback() {
class RoomMessageTouchHelperCallback(
private val context: Context,
@DrawableRes actionIcon: Int,
private val handler: QuickReplayHandler,
private val clock: Clock,
) : EpoxyTouchHelperCallback() {
interface QuickReplayHandler {
fun performQuickReplyOnHolder(model: EpoxyModel<*>)
@ -141,7 +145,7 @@ class RoomMessageTouchHelperCallback(private val context: Context,
private fun drawReplyButton(canvas: Canvas, itemView: View) {
// Timber.v("drawReplyButton")
val translationX = abs(itemView.translationX)
val newTime = System.currentTimeMillis()
val newTime = clock.epochMillis()
val dt = min(17, newTime - lastReplyButtonAnimationTime)
lastReplyButtonAnimationTime = newTime
val showing = translationX >= minShowDistance

View file

@ -296,7 +296,7 @@ class TimelineFragment @Inject constructor(
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 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)
touchHelper.attachToRecyclerView(views.timelineRecyclerView)
}
@ -2186,7 +2186,8 @@ class TimelineFragment @Inject constructor(
file = it,
title = action.messageContent.body,
mediaMimeType = action.messageContent.mimeType ?: getMimeTypeFromUri(requireContext(), it.toUri()),
notificationUtils = notificationUtils
notificationUtils = notificationUtils,
now = clock.epochMillis()
)
}
.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 org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import kotlin.random.Random
/**
* Describes the current send mode:
@ -35,7 +36,7 @@ sealed interface SendMode {
val text: String,
val fromSharing: Boolean,
// This is necessary for forcing refresh on selectSubscribe
private val ts: Long = System.currentTimeMillis()
private val ts: Int = Random.nextInt()
) : SendMode
data class Quote(val timelineEvent: TimelineEvent, val text: String) : SendMode
@ -83,7 +84,8 @@ data class MessageComposerViewState(
constructor(args: TimelineArgs) : this(
roomId = args.roomId,
startsThread = args.threadTimelineArgs?.startsThread.orFalse(),
rootThreadEventId = args.threadTimelineArgs?.rootThreadEventId)
rootThreadEventId = args.threadTimelineArgs?.rootThreadEventId
)
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.resources.StringProvider
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.features.home.AvatarRenderer
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 dateFormatter: VectorDateFormatter,
private val displayableEventFormatter: DisplayableEventFormatter,
private val userPreferencesProvider: UserPreferencesProvider
private val userPreferencesProvider: UserPreferencesProvider,
private val clock: Clock,
) : TypedEpoxyController<SearchViewState>() {
var listener: Listener? = null
@ -109,7 +111,7 @@ class SearchResultController @Inject constructor(
val spannable = setHighLightedText(text, data.highlights) ?: return@forEach
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)) {
GenericHeaderItem_()
@ -125,7 +127,8 @@ class SearchResultController @Inject constructor(
.formattedDate(dateFormatter.format(event.originServerTs, DateFormatKind.MESSAGE_SIMPLE))
.spannable(spannable.toEpoxyCharSequence())
.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)
.threadSummaryFormatted(displayableEventFormatter.formatThreadSummary(event.threadDetails?.threadSummaryLatestEvent).toString())
.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.nextOrNull
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.RoomDetailAction
import im.vector.app.features.home.room.detail.RoomDetailViewState
@ -78,19 +79,21 @@ import javax.inject.Inject
import kotlin.math.min
import kotlin.system.measureTimeMillis
class TimelineEventController @Inject constructor(private val dateFormatter: VectorDateFormatter,
private val vectorPreferences: VectorPreferences,
private val contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder,
private val contentDownloadStateTrackerBinder: ContentDownloadStateTrackerBinder,
private val timelineItemFactory: TimelineItemFactory,
private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
private val mergedHeaderItemFactory: MergedHeaderItemFactory,
private val session: Session,
@TimelineEventControllerHandler
private val backgroundHandler: Handler,
private val timelineEventVisibilityHelper: TimelineEventVisibilityHelper,
private val readReceiptsItemFactory: ReadReceiptsItemFactory,
private val reactionListFactory: ReactionsSummaryFactory
class TimelineEventController @Inject constructor(
private val dateFormatter: VectorDateFormatter,
private val vectorPreferences: VectorPreferences,
private val contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder,
private val contentDownloadStateTrackerBinder: ContentDownloadStateTrackerBinder,
private val timelineItemFactory: TimelineItemFactory,
private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
private val mergedHeaderItemFactory: MergedHeaderItemFactory,
private val session: Session,
@TimelineEventControllerHandler
private val backgroundHandler: Handler,
private val timelineEventVisibilityHelper: TimelineEventVisibilityHelper,
private val readReceiptsItemFactory: ReadReceiptsItemFactory,
private val reactionListFactory: ReactionsSummaryFactory,
private val clock: Clock,
) : 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?) {
synchronized(modelCache) {
assertUpdateCallbacksAllowed()
Timber.v("listUpdateCallback.onChanged(position: $position, count: $count). " +
"\ncurrentSnapshot has size of ${currentSnapshot.size} items")
Timber.v(
"listUpdateCallback.onChanged(position: $position, count: $count). " +
"\ncurrentSnapshot has size of ${currentSnapshot.size} items"
)
(position until position + count).forEach {
// Invalidate cache
modelCache[it] = null
@ -238,8 +243,10 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
override fun onMoved(fromPosition: Int, toPosition: Int) {
synchronized(modelCache) {
assertUpdateCallbacksAllowed()
Timber.v("listUpdateCallback.onMoved(fromPosition: $fromPosition, toPosition: $toPosition). " +
"\ncurrentSnapshot has size of ${currentSnapshot.size} items")
Timber.v(
"listUpdateCallback.onMoved(fromPosition: $fromPosition, toPosition: $toPosition). " +
"\ncurrentSnapshot has size of ${currentSnapshot.size} items"
)
val model = modelCache.removeAt(fromPosition)
modelCache.add(toPosition, model)
requestModelBuild()
@ -249,8 +256,10 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
override fun onInserted(position: Int, count: Int) {
synchronized(modelCache) {
assertUpdateCallbacksAllowed()
Timber.v("listUpdateCallback.onInserted(position: $position, count: $count). " +
"\ncurrentSnapshot has size of ${currentSnapshot.size} items")
Timber.v(
"listUpdateCallback.onInserted(position: $position, count: $count). " +
"\ncurrentSnapshot has size of ${currentSnapshot.size} items"
)
repeat(count) {
modelCache.add(position, null)
}
@ -261,8 +270,10 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
override fun onRemoved(position: Int, count: Int) {
synchronized(modelCache) {
assertUpdateCallbacksAllowed()
Timber.v("listUpdateCallback.onRemoved(position: $position, count: $count). " +
"\ncurrentSnapshot has size of ${currentSnapshot.size} items")
Timber.v(
"listUpdateCallback.onRemoved(position: $position, count: $count). " +
"\ncurrentSnapshot has size of ${currentSnapshot.size} items"
)
repeat(count) {
modelCache.removeAt(position)
}
@ -314,7 +325,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
if (partialState.roomSummary?.membership != Membership.JOIN) {
return
}
val timestamp = System.currentTimeMillis()
val timestamp = clock.epochMillis()
val showingForwardLoader = LoadingItem_()
.id("forward_loading_item_$timestamp")
@ -406,14 +417,16 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
timelineEvent = it,
highlightedEventId = partialState.highlightedEventId,
isFromThreadTimeline = partialState.isFromThreadTimeline(),
rootThreadEventId = partialState.rootThreadEventId)
rootThreadEventId = partialState.rootThreadEventId
)
}
val nextDisplayableEvent = currentSnapshot.subList(position + 1, currentSnapshot.size).firstOrNull {
timelineEventVisibilityHelper.shouldShowEvent(
timelineEvent = it,
highlightedEventId = partialState.highlightedEventId,
isFromThreadTimeline = partialState.isFromThreadTimeline(),
rootThreadEventId = partialState.rootThreadEventId)
rootThreadEventId = partialState.rootThreadEventId
)
}
val timelineEventsGroup = timelineEventsGroups.getOrNull(event)
val params = TimelineItemFactoryParams(
@ -466,7 +479,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
position: Int,
receiptsByEvents: Map<String, List<ReadReceipt>>): CacheItemData {
val wantsDateSeparator = wantsDateSeparator(event, nextEvent)
val mergedHeaderModel = mergedHeaderItemFactory.create(event,
val mergedHeaderModel = mergedHeaderItemFactory.create(
event,
nextEvent = nextEvent,
partialState = partialState,
items = this@TimelineEventController.currentSnapshot,
@ -537,7 +551,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
timelineEvent = event,
highlightedEventId = partialState.highlightedEventId,
isFromThreadTimeline = partialState.isFromThreadTimeline(),
rootThreadEventId = partialState.rootThreadEventId)) {
rootThreadEventId = partialState.rootThreadEventId
)) {
lastShownEventId = event.eventId
}
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.resources.ColorProvider
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.genericHeaderItem
import im.vector.app.core.ui.list.genericItem
@ -49,7 +50,8 @@ class ViewEditHistoryEpoxyController @Inject constructor(
private val stringProvider: StringProvider,
private val colorProvider: ColorProvider,
private val eventHtmlRenderer: EventHtmlRenderer,
private val dateFormatter: VectorDateFormatter
private val dateFormatter: VectorDateFormatter,
private val clock: Clock,
) : TypedEpoxyController<ViewEditHistoryViewState>() {
override fun buildModels(state: ViewEditHistoryViewState) {
@ -86,7 +88,7 @@ class ViewEditHistoryEpoxyController @Inject constructor(
val evDate = Calendar.getInstance().apply {
timeInMillis = timelineEvent.originServerTs
?: System.currentTimeMillis()
?: clock.epochMillis()
}
if (lastDate?.get(Calendar.DAY_OF_YEAR) != evDate.get(Calendar.DAY_OF_YEAR)) {
// 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.TimelineReadMarkerItem_
import org.matrix.android.sdk.api.session.room.timeline.Timeline
import kotlin.random.Random
import kotlin.reflect.KMutableProperty0
private const val DEFAULT_PREFETCH_THRESHOLD = 30
@ -104,7 +105,7 @@ class TimelineControllerInterceptorHelper(private val positionOfReadMarker: KMut
.coerceAtLeast(0)
val loadingItem = LoadingItem_()
.id("prefetch_backward_loading${System.currentTimeMillis()}")
.id("prefetch_backward_loading${Random.nextLong()}")
.showLoader(false)
.setVisibilityStateChangedListener(Timeline.Direction.BACKWARDS, callback)
@ -120,7 +121,7 @@ class TimelineControllerInterceptorHelper(private val positionOfReadMarker: KMut
.coerceAtLeast(0)
val loadingItem = LoadingItem_()
.id("prefetch_forward_loading${System.currentTimeMillis()}")
.id("prefetch_forward_loading${Random.nextLong()}")
.showLoader(false)
.setVisibilityStateChangedListener(Timeline.Direction.FORWARDS, callback)
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.intent.getFilenameFromUri
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.FragmentLoginAccountCreatedBinding
import im.vector.app.features.displayname.getBestName
@ -52,13 +53,14 @@ class AccountCreatedFragment @Inject constructor(
private val avatarRenderer: AvatarRenderer,
private val dateFormatter: VectorDateFormatter,
private val matrixItemColorProvider: MatrixItemColorProvider,
private val clock: Clock,
colorProvider: ColorProvider
) : AbstractLoginFragment2<FragmentLoginAccountCreatedBinding>(),
GalleryOrCameraDialogHelper.Listener {
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 {
return FragmentLoginAccountCreatedBinding.inflate(inflater, container, false)
@ -73,7 +75,7 @@ class AccountCreatedFragment @Inject constructor(
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() {

View file

@ -20,6 +20,7 @@ import android.content.Context
import androidx.core.net.toUri
import dagger.hilt.android.qualifiers.ApplicationContext
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.features.notifications.NotificationUtils
import kotlinx.coroutines.withContext
@ -30,7 +31,8 @@ import javax.inject.Inject
class DownloadMediaUseCase @Inject constructor(
@ApplicationContext private val appContext: Context,
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) {
@ -40,7 +42,8 @@ class DownloadMediaUseCase @Inject constructor(
file = input,
title = input.name,
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.core.extensions.takeAs
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.home.room.detail.timeline.format.DisplayableEventFormatter
import im.vector.app.features.home.room.detail.timeline.format.NoticeEventFormatter
@ -58,7 +59,8 @@ import javax.inject.Inject
class NotifiableEventResolver @Inject constructor(
private val stringProvider: StringProvider,
private val noticeEventFormatter: NoticeEventFormatter,
private val displayableEventFormatter: DisplayableEventFormatter
private val displayableEventFormatter: DisplayableEventFormatter,
private val clock: Clock,
) {
// private val eventDisplay = RiotEventDisplay(context)
@ -86,7 +88,7 @@ class NotifiableEventResolver @Inject constructor(
eventId = event.eventId!!,
editedEventId = timelineEvent.getEditedEventId(),
noisy = false, // will be updated
timestamp = event.originServerTs ?: System.currentTimeMillis(),
timestamp = event.originServerTs ?: clock.epochMillis(),
description = bodyPreview,
title = stringProvider.getString(R.string.notification_unknown_new_event),
soundName = null,
@ -178,15 +180,19 @@ class NotifiableEventResolver @Inject constructor(
roomName = roomName,
roomIsDirect = room.roomSummary()?.isDirect ?: false,
roomAvatarPath = session.contentUrlResolver()
.resolveThumbnail(room.roomSummary()?.avatarUrl,
.resolveThumbnail(
room.roomSummary()?.avatarUrl,
250,
250,
ContentUrlResolver.ThumbnailMethod.SCALE),
ContentUrlResolver.ThumbnailMethod.SCALE
),
senderAvatarPath = session.contentUrlResolver()
.resolveThumbnail(event.senderInfo.avatarUrl,
.resolveThumbnail(
event.senderInfo.avatarUrl,
250,
250,
ContentUrlResolver.ThumbnailMethod.SCALE),
ContentUrlResolver.ThumbnailMethod.SCALE
),
matrixID = session.myUserId,
soundName = null
)

View file

@ -23,6 +23,7 @@ import androidx.core.app.RemoteInput
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R
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.extensions.toAnalyticsJoinedRoom
import im.vector.app.features.session.coroutineScope
@ -45,6 +46,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
@Inject lateinit var analyticsTracker: AnalyticsTracker
@Inject lateinit var clock: Clock
override fun onReceive(context: Context?, intent: Intent?) {
if (intent == null || context == null) return
@ -137,7 +139,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
eventId = UUID.randomUUID().toString(),
editedEventId = null,
noisy = false,
timestamp = System.currentTimeMillis(),
timestamp = clock.epochMillis(),
senderName = session.roomService().getRoomMember(session.myUserId, room.roomId)?.displayName
?: context?.getString(R.string.notification_sender_me),
senderId = session.myUserId,
@ -188,7 +190,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
val notifiableMessageEvent = NotifiableMessageEvent(
event.eventId,
false,
System.currentTimeMillis(),
clock.epochMillis(),
session.myUser?.displayname
?: context?.getString(R.string.notification_sender_me),
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.resources.StringProvider
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.features.call.VectorCallActivity
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
*/
@Singleton
class NotificationUtils @Inject constructor(private val context: Context,
private val stringProvider: StringProvider,
private val vectorPreferences: VectorPreferences) {
class NotificationUtils @Inject constructor(
private val context: Context,
private val stringProvider: StringProvider,
private val vectorPreferences: VectorPreferences,
private val clock: Clock,
) {
companion object {
/* ==========================================================================================
@ -323,7 +327,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
}
val contentPendingIntent = PendingIntent.getActivity(
context,
System.currentTimeMillis().toInt(),
clock.epochMillis().toInt(),
contentIntent,
PendingIntentCompat.FLAG_IMMUTABLE
)
@ -337,7 +341,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
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)
@ -392,7 +396,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
}
val contentPendingIntent = PendingIntent.getActivity(
context,
System.currentTimeMillis().toInt(),
clock.epochMillis().toInt(),
contentIntent,
PendingIntentCompat.FLAG_IMMUTABLE
)
@ -453,7 +457,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
val contentPendingIntent = TaskStackBuilder.create(context)
.addNextIntentWithParentStack(HomeActivity.newIntent(context))
.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)
@ -467,7 +471,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
}
return PendingIntent.getBroadcast(
context,
System.currentTimeMillis().toInt(),
clock.epochMillis().toInt(),
rejectCallActionReceiver,
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)
.addNextIntentWithParentStack(HomeActivity.newIntent(context))
.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)
return builder.build()
@ -562,7 +566,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
}
PendingIntent.getActivity(
context,
System.currentTimeMillis().toInt(),
clock.epochMillis().toInt(),
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
).let {
@ -636,7 +640,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
markRoomReadIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomInfo.roomId)
val markRoomReadPendingIntent = PendingIntent.getBroadcast(
context,
System.currentTimeMillis().toInt(),
clock.epochMillis().toInt(),
markRoomReadIntent,
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
val pendingIntent = PendingIntent.getBroadcast(
context.applicationContext,
System.currentTimeMillis().toInt(),
clock.epochMillis().toInt(),
intent,
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)
val rejectIntentPendingIntent = PendingIntent.getBroadcast(
context,
System.currentTimeMillis().toInt(),
clock.epochMillis().toInt(),
rejectIntent,
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)
val joinIntentPendingIntent = PendingIntent.getBroadcast(
context,
System.currentTimeMillis().toInt(),
clock.epochMillis().toInt(),
joinIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
)
@ -811,7 +815,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
.addNextIntentWithParentStack(HomeActivity.newIntent(context))
.addNextIntent(roomIntentTap)
.getPendingIntent(
System.currentTimeMillis().toInt(),
clock.epochMillis().toInt(),
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)
return PendingIntent.getBroadcast(
context,
System.currentTimeMillis().toInt(),
clock.epochMillis().toInt(),
intent,
// PendingIntents attached to actions with remote inputs must be 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.extensions.singletonEntryPoint
import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.time.Clock
import im.vector.app.databinding.FragmentFtueProfilePictureBinding
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.onboarding.OnboardingAction
@ -39,10 +40,11 @@ import javax.inject.Inject
class FtueAuthChooseProfilePictureFragment @Inject constructor(
private val activeSessionHolder: ActiveSessionHolder,
colorProvider: ColorProvider
colorProvider: ColorProvider,
clock: Clock,
) : 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() }
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 im.vector.app.R
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.features.analytics.ui.consent.AnalyticsOptInActivity
import im.vector.app.features.pin.PinActivity
@ -41,7 +42,9 @@ import javax.inject.Singleton
* will be back in the queue in first position.
*/
@Singleton
class PopupAlertManager @Inject constructor() {
class PopupAlertManager @Inject constructor(
private val clock: Clock,
) {
companion object {
const val INCOMING_CALL_PRIORITY = Int.MAX_VALUE
@ -116,7 +119,7 @@ class PopupAlertManager @Inject constructor() {
return
}
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
// perform dismiss
try {
@ -162,7 +165,7 @@ class PopupAlertManager @Inject constructor() {
currentAlerter = next
next?.let {
if (!shouldBeDisplayedIn(next, currentActivity)) return
val currentTime = System.currentTimeMillis()
val currentTime = clock.epochMillis()
if (next.expirationTimestamp != null && currentTime > next.expirationTimestamp!!) {
// skip
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.VectorBaseFragment
import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.time.Clock
import im.vector.app.databinding.FragmentCreateRoomBinding
import im.vector.app.features.navigation.Navigator
import im.vector.app.features.roomdirectory.RoomDirectorySharedAction
@ -62,7 +63,8 @@ data class CreateRoomArgs(
class CreateRoomFragment @Inject constructor(
private val createRoomController: CreateRoomController,
private val createSpaceController: CreateSubSpaceController,
colorProvider: ColorProvider
colorProvider: ColorProvider,
clock: Clock,
) : VectorBaseFragment<FragmentCreateRoomBinding>(),
CreateRoomController.Listener,
GalleryOrCameraDialogHelper.Listener,
@ -74,7 +76,7 @@ class CreateRoomFragment @Inject constructor(
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 {
return FragmentCreateRoomBinding.inflate(inflater, container, false)
@ -166,7 +168,8 @@ class CreateRoomFragment @Inject constructor(
} else {
listOf(RoomJoinRules.INVITE, RoomJoinRules.PUBLIC)
}
RoomJoinRuleBottomSheet.newInstance(state.roomJoinRules,
RoomJoinRuleBottomSheet.newInstance(
state.roomJoinRules,
allowed.map { it.toOption(false) },
state.isSubSpace,
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.VectorBaseFragment
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.databinding.FragmentRoomSettingGenericBinding
import im.vector.app.features.analytics.plan.MobileScreen
@ -57,7 +58,8 @@ import javax.inject.Inject
class RoomSettingsFragment @Inject constructor(
private val controller: RoomSettingsController,
colorProvider: ColorProvider,
private val avatarRenderer: AvatarRenderer
private val avatarRenderer: AvatarRenderer,
clock: Clock,
) :
VectorBaseFragment<FragmentRoomSettingGenericBinding>(),
RoomSettingsController.Callback,
@ -70,7 +72,7 @@ class RoomSettingsFragment @Inject constructor(
private lateinit var roomJoinRuleSharedActionViewModel: RoomJoinRuleSharedActionViewModel
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 {
return FragmentRoomSettingGenericBinding.inflate(inflater, container, false)
@ -198,7 +200,8 @@ class RoomSettingsFragment @Inject constructor(
RoomSettingsAction.SetAvatarAction(
RoomSettingsViewState.AvatarAction.UpdateAvatar(
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.core.intent.getMimeTypeFromUri
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.shareMedia
import im.vector.app.databinding.FragmentRoomUploadsBinding
@ -43,7 +44,8 @@ import javax.inject.Inject
class RoomUploadsFragment @Inject constructor(
private val avatarRenderer: AvatarRenderer,
private val notificationUtils: NotificationUtils
private val notificationUtils: NotificationUtils,
private val clock: Clock,
) : VectorBaseFragment<FragmentRoomUploadsBinding>() {
private val roomProfileArgs: RoomProfileArgs by args()
@ -88,7 +90,8 @@ class RoomUploadsFragment @Inject constructor(
file = it.file,
title = it.title,
mediaMimeType = getMimeTypeFromUri(requireContext(), it.file.toUri()),
notificationUtils = notificationUtils
notificationUtils = notificationUtils,
now = clock.epochMillis()
)
}.onFailure { failure ->
if (!isAdded) return@onFailure

View file

@ -26,6 +26,7 @@ import com.squareup.seismic.ShakeDetector
import im.vector.app.BuildConfig
import im.vector.app.R
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.homeserver.ServerUrlsRepository
import im.vector.app.features.themes.ThemeUtils
@ -33,7 +34,10 @@ import org.matrix.android.sdk.api.extensions.tryOrNull
import timber.log.Timber
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 {
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 {
return when (getSelectedMediasSavingPeriod()) {
MEDIA_SAVING_3_DAYS -> System.currentTimeMillis() / 1000 - 3 * 24 * 60 * 60
MEDIA_SAVING_1_WEEK -> System.currentTimeMillis() / 1000 - 7 * 24 * 60 * 60
MEDIA_SAVING_1_MONTH -> System.currentTimeMillis() / 1000 - 30 * 24 * 60 * 60
MEDIA_SAVING_3_DAYS -> clock.epochMillis() / 1000 - 3 * 24 * 60 * 60
MEDIA_SAVING_1_WEEK -> clock.epochMillis() / 1000 - 7 * 24 * 60 * 60
MEDIA_SAVING_1_MONTH -> clock.epochMillis() / 1000 - 30 * 24 * 60 * 60
MEDIA_SAVING_FOREVER -> 0
else -> 0
}
@ -872,8 +876,10 @@ class VectorPreferences @Inject constructor(private val context: Context) {
* @return true if user should always appear offline
*/
fun userAlwaysAppearsOffline(): Boolean {
return defaultPrefs.getBoolean(SETTINGS_PRESENCE_USER_ALWAYS_APPEARS_OFFLINE,
getDefault(R.bool.settings_presence_user_always_appears_offline_default))
return defaultPrefs.getBoolean(
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 {
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
!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.VectorSwitchPreference
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.getSizeOfFiles
import im.vector.app.core.utils.toast
@ -74,7 +75,8 @@ import java.util.UUID
import javax.inject.Inject
class VectorSettingsGeneralFragment @Inject constructor(
colorProvider: ColorProvider
colorProvider: ColorProvider,
clock: Clock,
) :
VectorSettingsBaseFragment(),
GalleryOrCameraDialogHelper.Listener {
@ -82,7 +84,7 @@ class VectorSettingsGeneralFragment @Inject constructor(
override var titleRes = R.string.settings_general_title
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 {
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.safeOpenOutputStream
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.databinding.FragmentDevtoolKeyrequestsBinding
import org.matrix.android.sdk.api.extensions.tryOrNull
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 {
return FragmentDevtoolKeyrequestsBinding.inflate(inflater, container, false)
@ -126,7 +129,7 @@ class KeyRequestsFragment @Inject constructor() : VectorBaseFragment<FragmentDev
selectTxtFileToWrite(
activity = requireActivity(),
activityResultLauncher = epxortAuditForActivityResult,
defaultFileName = "audit-export_${System.currentTimeMillis()}.txt",
defaultFileName = "audit-export_${clock.epochMillis()}.txt",
chooserHint = "Export Audit"
)
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.VectorBaseFragment
import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.time.Clock
import im.vector.app.databinding.FragmentSpaceCreateGenericEpoxyFormBinding
import javax.inject.Inject
class CreateSpaceDetailsFragment @Inject constructor(
private val epoxyController: SpaceDetailEpoxyController,
colorProvider: ColorProvider
colorProvider: ColorProvider,
clock: Clock,
) : VectorBaseFragment<FragmentSpaceCreateGenericEpoxyFormBinding>(), SpaceDetailEpoxyController.Listener,
GalleryOrCameraDialogHelper.Listener, OnBackPressed {
@ -42,7 +44,7 @@ class CreateSpaceDetailsFragment @Inject constructor(
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) =
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?) {
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.VectorBaseFragment
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.databinding.FragmentRoomSettingGenericBinding
import im.vector.app.features.home.AvatarRenderer
@ -60,9 +60,9 @@ import javax.inject.Inject
class SpaceSettingsFragment @Inject constructor(
private val epoxyController: SpaceSettingsController,
private val colorProvider: ColorProvider,
colorProvider: ColorProvider,
clock: Clock,
private val avatarRenderer: AvatarRenderer,
private val drawableProvider: DrawableProvider
) : VectorBaseFragment<FragmentRoomSettingGenericBinding>(),
SpaceSettingsController.Callback,
GalleryOrCameraDialogHelper.Listener,
@ -73,7 +73,7 @@ class SpaceSettingsFragment @Inject constructor(
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)
@ -236,7 +236,8 @@ class SpaceSettingsFragment @Inject constructor(
RoomSettingsAction.SetAvatarAction(
RoomSettingsViewState.AvatarAction.UpdateAvatar(
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.user.model.User
import org.matrix.android.sdk.api.util.toMatrixItem
import kotlin.random.Random
data class ThreePidUser(
val email: String,
@ -142,7 +143,7 @@ class UserListViewModel @AssistedInject constructor(
}
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) {

View file

@ -20,11 +20,13 @@ import android.content.Context
import android.os.Build
import com.arthenica.ffmpegkit.FFmpegKit
import com.arthenica.ffmpegkit.ReturnCode
import im.vector.app.core.time.Clock
import timber.log.Timber
import java.io.File
import javax.inject.Inject
class VoicePlayerHelper @Inject constructor(
private val clock: Clock,
context: Context
) {
private val outputDirectory: File by lazy {
@ -46,9 +48,9 @@ class VoicePlayerHelper @Inject constructor(
if (targetFile.exists()) {
targetFile.delete()
}
val start = System.currentTimeMillis()
val start = clock.epochMillis()
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()}")
return when {
ReturnCode.isSuccess(session.returnCode) -> {

View file

@ -23,10 +23,14 @@ import com.arthenica.ffmpegkit.FFmpegKitConfig
import com.arthenica.ffmpegkit.Level
import com.arthenica.ffmpegkit.ReturnCode
import im.vector.app.BuildConfig
import im.vector.app.core.time.Clock
import timber.log.Timber
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) {
// Use AAC/MP4 format here
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
@ -43,9 +47,9 @@ class VoiceRecorderL(context: Context) : AbstractVoiceRecorder(context, "mp4") {
if (targetFile.exists()) {
targetFile.delete()
}
val start = System.currentTimeMillis()
val start = clock.epochMillis()
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()}")
return when {
ReturnCode.isSuccess(session.returnCode) -> {

View file

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