diff --git a/dependencies.gradle b/dependencies.gradle index 39eb6e60a9..9d3fd85cd3 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -55,6 +55,8 @@ ext.libs = [ 'lifecycleExtensions' : "androidx.lifecycle:lifecycle-extensions:$lifecycle", 'lifecycleJava8' : "androidx.lifecycle:lifecycle-common-java8:$lifecycle", 'lifecycleLivedata' : "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1", + 'datastore' : "androidx.datastore:datastore:1.0.0", + 'datastorepreferences' : "androidx.datastore:datastore-preferences:1.0.0", 'pagingRuntimeKtx' : "androidx.paging:paging-runtime-ktx:2.1.2", 'coreTesting' : "androidx.arch.core:core-testing:2.1.0", 'testCore' : "androidx.test:core:$androidxTest", diff --git a/vector/build.gradle b/vector/build.gradle index a9b8adf883..30d60e560d 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -345,6 +345,9 @@ dependencies { implementation libs.androidx.lifecycleExtensions implementation libs.androidx.lifecycleLivedata + implementation libs.androidx.datastore + implementation libs.androidx.datastorepreferences + // Log implementation libs.jakewharton.timber @@ -406,7 +409,7 @@ dependencies { // To convert voice message on old platforms implementation 'com.arthenica:ffmpeg-kit-audio:4.4.LTS' - //Alerter + // Alerter implementation 'com.tapadoo.android:alerter:7.0.1' implementation 'com.otaliastudios:autocomplete:1.1.0' diff --git a/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt b/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt index 444fed0d57..aaa81d7bc4 100755 --- a/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt +++ b/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt @@ -39,11 +39,13 @@ import im.vector.app.features.notifications.NotifiableMessageEvent import im.vector.app.features.notifications.NotificationDrawerManager import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.notifications.SimpleNotifiableEvent +import im.vector.app.features.settings.VectorDataStore import im.vector.app.features.settings.VectorPreferences import im.vector.app.push.fcm.FcmHelper import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.pushrules.Action import org.matrix.android.sdk.api.session.Session @@ -60,6 +62,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { private lateinit var pusherManager: PushersManager private lateinit var activeSessionHolder: ActiveSessionHolder private lateinit var vectorPreferences: VectorPreferences + private lateinit var vectorDataStore: VectorDataStore private lateinit var wifiDetector: WifiDetector private val coroutineScope = CoroutineScope(SupervisorJob()) @@ -77,6 +80,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { pusherManager = pusherManager() activeSessionHolder = activeSessionHolder() vectorPreferences = vectorPreferences() + vectorDataStore = vectorDataStore() wifiDetector = wifiDetector() } } @@ -92,6 +96,10 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { } Timber.d("## onMessageReceived() from FCM with priority %s", message.priority) + runBlocking { + vectorDataStore.incrementPushCounter() + } + // Diagnostic Push if (message.data["event_id"] == PushersManager.TEST_EVENT_ID) { val intent = Intent(NotificationUtils.PUSH_ACTION) diff --git a/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt b/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt index 68b212c830..384f2d3106 100644 --- a/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt +++ b/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt @@ -58,6 +58,7 @@ import im.vector.app.features.rageshake.VectorFileLogger import im.vector.app.features.rageshake.VectorUncaughtExceptionHandler import im.vector.app.features.reactions.data.EmojiDataSource import im.vector.app.features.session.SessionListener +import im.vector.app.features.settings.VectorDataStore import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.ui.UiStateRepository import org.matrix.android.sdk.api.Matrix @@ -145,6 +146,8 @@ interface VectorComponent { fun vectorPreferences(): VectorPreferences + fun vectorDataStore(): VectorDataStore + fun wifiDetector(): WifiDetector fun vectorFileLogger(): VectorFileLogger diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt index 381873b4dc..627f4b4581 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt @@ -440,7 +440,11 @@ class HomeDetailFragment @Inject constructor( views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_people).render(it.notificationCountPeople, it.notificationHighlightPeople) views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_rooms).render(it.notificationCountRooms, it.notificationHighlightRooms) views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_notification).render(it.notificationCountCatchup, it.notificationHighlightCatchup) - views.syncStateView.render(it.syncState, it.incrementalSyncStatus, vectorPreferences.developerShowDebugInfo()) + views.syncStateView.render( + it.syncState, + it.incrementalSyncStatus, + it.pushCounter, + vectorPreferences.developerShowDebugInfo()) hasUnreadRooms = it.hasUnreadMessages } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt index fbc006bbcd..934c3c5746 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt @@ -33,9 +33,11 @@ import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.createdirect.DirectRoomHelper import im.vector.app.features.invite.AutoAcceptInvites import im.vector.app.features.invite.showInvites +import im.vector.app.features.settings.VectorDataStore import im.vector.app.features.ui.UiStateRepository import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import org.matrix.android.sdk.api.query.ActiveSpaceFilter import org.matrix.android.sdk.api.query.RoomCategoryFilter @@ -57,6 +59,7 @@ import java.util.concurrent.TimeUnit class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: HomeDetailViewState, private val session: Session, private val uiStateRepository: UiStateRepository, + private val vectorDataStore: VectorDataStore, private val callManager: WebRtcCallManager, private val directRoomHelper: DirectRoomHelper, private val appStateHandler: AppStateHandler, @@ -90,6 +93,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho observeRoomGroupingMethod() observeRoomSummaries() updateShowDialPadTab() + observeDataStore() callManager.addProtocolsCheckerListener(this) session.rx().liveUser(session.myUserId).execute { copy( @@ -98,6 +102,18 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho } } + private fun observeDataStore() { + viewModelScope.launch { + vectorDataStore.pushCounterFlow.collect { nbOfPush -> + setState { + copy( + pushCounter = nbOfPush + ) + } + } + } + } + override fun handle(action: HomeDetailAction) { when (action) { is HomeDetailAction.SwitchTab -> handleSwitchTab(action) diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt index 9932758dc0..897092613c 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt @@ -41,6 +41,7 @@ data class HomeDetailViewState( val hasUnreadMessages: Boolean = false, val syncState: SyncState = SyncState.Idle, val incrementalSyncStatus: InitialSyncProgressService.Status.IncrementalSyncStatus = InitialSyncProgressService.Status.IncrementalSyncIdle, + val pushCounter: Int = 0, val showDialPadTab: Boolean = false ) : MvRxState diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index ebe6ff7e0a..765e69d669 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -389,9 +389,15 @@ class RoomDetailFragment @Inject constructor( roomDetailViewModel.selectSubscribe( RoomDetailViewState::syncState, - RoomDetailViewState::incrementalSyncStatus - ) { syncState, incrementalSyncStatus -> - views.syncStateView.render(syncState, incrementalSyncStatus, vectorPreferences.developerShowDebugInfo()) + RoomDetailViewState::incrementalSyncStatus, + RoomDetailViewState::pushCounter + ) { syncState, incrementalSyncStatus, pushCounter -> + views.syncStateView.render( + syncState, + incrementalSyncStatus, + pushCounter, + vectorPreferences.developerShowDebugInfo() + ) } roomDetailViewModel.observeViewEvents { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index 6d396adafa..321a64c4a7 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -57,12 +57,14 @@ import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever import im.vector.app.features.home.room.typing.TypingHelper import im.vector.app.features.powerlevel.PowerLevelsObservableFactory import im.vector.app.features.session.coroutineScope +import im.vector.app.features.settings.VectorDataStore import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.voice.VoicePlayerHelper import io.reactivex.Observable import io.reactivex.rxkotlin.subscribeBy import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.commonmark.parser.Parser @@ -113,6 +115,7 @@ import java.util.concurrent.atomic.AtomicBoolean class RoomDetailViewModel @AssistedInject constructor( @Assisted private val initialState: RoomDetailViewState, private val vectorPreferences: VectorPreferences, + private val vectorDataStore: VectorDataStore, private val stringProvider: StringProvider, private val rainbowGenerator: RainbowGenerator, private val session: Session, @@ -176,6 +179,7 @@ class RoomDetailViewModel @AssistedInject constructor( observeSummaryState() getUnreadState() observeSyncState() + observeDataStore() observeEventDisplayedActions() loadDraftIfAny() observeUnreadState() @@ -200,6 +204,18 @@ class RoomDetailViewModel @AssistedInject constructor( } } + private fun observeDataStore() { + viewModelScope.launch { + vectorDataStore.pushCounterFlow.collect { nbOfPush -> + setState { + copy( + pushCounter = nbOfPush + ) + } + } + } + } + private fun prepareForEncryption() { // check if there is not already a call made, or if there has been an error if (prepareToEncrypt.shouldLoad) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt index 421ef5165f..16df81347b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt @@ -79,6 +79,7 @@ data class RoomDetailViewState( val joinUpgradedRoomAsync: Async = Uninitialized, val syncState: SyncState = SyncState.Idle, val incrementalSyncStatus: InitialSyncProgressService.Status.IncrementalSyncStatus = InitialSyncProgressService.Status.IncrementalSyncIdle, + val pushCounter: Int = 0, val highlightedEventId: String? = null, val unreadState: UnreadState = UnreadState.Unknown, val canShowJumpToReadMarker: Boolean = true, diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorDataStore.kt b/vector/src/main/java/im/vector/app/features/settings/VectorDataStore.kt new file mode 100644 index 0000000000..74b3794b2c --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/VectorDataStore.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.intPreferencesKey +import androidx.datastore.preferences.preferencesDataStore +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +private val Context.dataStore: DataStore by preferencesDataStore(name = "vector_settings") + +class VectorDataStore @Inject constructor( + private val context: Context +) { + + private val pushCounter = intPreferencesKey("push_counter") + + val pushCounterFlow: Flow = context.dataStore.data.map { preferences -> + preferences[pushCounter] ?: 0 + } + + suspend fun incrementPushCounter() { + context.dataStore.edit { settings -> + val currentCounterValue = settings[pushCounter] ?: 0 + settings[pushCounter] = currentCounterValue + 1 + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/sync/widget/SyncStateView.kt b/vector/src/main/java/im/vector/app/features/sync/widget/SyncStateView.kt index 03fe8cc605..e4ee67f0c8 100755 --- a/vector/src/main/java/im/vector/app/features/sync/widget/SyncStateView.kt +++ b/vector/src/main/java/im/vector/app/features/sync/widget/SyncStateView.kt @@ -41,11 +41,15 @@ class SyncStateView @JvmOverloads constructor(context: Context, attrs: Attribute @SuppressLint("SetTextI18n") fun render(newState: SyncState, incrementalSyncStatus: InitialSyncProgressService.Status.IncrementalSyncStatus, - showDebugInfo: Boolean) { + pushCounter: Int, + showDebugInfo: Boolean + ) { views.syncStateDebugInfo.isVisible = showDebugInfo if (showDebugInfo) { - views.syncStateDebugInfo.text = + views.syncStateDebugInfoText.text = "Sync thread : ${newState.toHumanReadable()}\nSync request: ${incrementalSyncStatus.toHumanReadable()}" + views.syncStateDebugInfoPushCounter.text = + "Push: $pushCounter" } views.syncStateProgressBar.isVisible = newState is SyncState.Running && newState.afterPause diff --git a/vector/src/main/res/layout/view_sync_state.xml b/vector/src/main/res/layout/view_sync_state.xml index 7924d689ee..38d2a5bfc3 100644 --- a/vector/src/main/res/layout/view_sync_state.xml +++ b/vector/src/main/res/layout/view_sync_state.xml @@ -7,16 +7,33 @@ tools:orientation="vertical" tools:parentTag="android.widget.LinearLayout"> - + tools:visibility="visible"> + + + + + +