Dagger: merge develop compiling now.

This commit is contained in:
ganfra 2019-06-27 15:25:01 +02:00
parent b2d2582e0f
commit 1fa7b7367a
25 changed files with 192 additions and 732 deletions

View File

@ -18,6 +18,7 @@ package im.vector.matrix.rx


import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.matrix.android.api.session.pushers.Pusher
import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.sync.SyncState import im.vector.matrix.android.api.session.sync.SyncState
import io.reactivex.Observable import io.reactivex.Observable
@ -36,6 +37,10 @@ class RxSession(private val session: Session) {
return session.syncState().asObservable() return session.syncState().asObservable()
} }


fun livePushers(): Observable<List<Pusher>> {
return session.livePushers().asObservable()
}

} }


fun Session.rx(): RxSession { fun Session.rx(): RxSession {

View File

@ -54,6 +54,10 @@ interface Session :
*/ */
val sessionParams: SessionParams val sessionParams: SessionParams


val myUserId : String
get() = sessionParams.credentials.userId


/** /**
* This method allow to open a session. It does start some service on the background. * This method allow to open a session. It does start some service on the background.
*/ */

View File

@ -22,6 +22,7 @@ import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.internal.crypto.CryptoModule import im.vector.matrix.android.internal.crypto.CryptoModule
import im.vector.matrix.android.internal.di.MatrixComponent import im.vector.matrix.android.internal.di.MatrixComponent
import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
import im.vector.matrix.android.internal.session.cache.CacheModule import im.vector.matrix.android.internal.session.cache.CacheModule
import im.vector.matrix.android.internal.session.content.ContentModule import im.vector.matrix.android.internal.session.content.ContentModule
import im.vector.matrix.android.internal.session.content.UploadContentWorker import im.vector.matrix.android.internal.session.content.UploadContentWorker
@ -37,8 +38,11 @@ import im.vector.matrix.android.internal.session.room.send.RedactEventWorker
import im.vector.matrix.android.internal.session.room.send.SendEventWorker import im.vector.matrix.android.internal.session.room.send.SendEventWorker
import im.vector.matrix.android.internal.session.signout.SignOutModule import im.vector.matrix.android.internal.session.signout.SignOutModule
import im.vector.matrix.android.internal.session.sync.SyncModule import im.vector.matrix.android.internal.session.sync.SyncModule
import im.vector.matrix.android.internal.session.sync.SyncTask
import im.vector.matrix.android.internal.session.sync.SyncTokenStore
import im.vector.matrix.android.internal.session.sync.job.SyncWorker import im.vector.matrix.android.internal.session.sync.job.SyncWorker
import im.vector.matrix.android.internal.session.user.UserModule import im.vector.matrix.android.internal.session.user.UserModule
import im.vector.matrix.android.internal.task.TaskExecutor


@Component(dependencies = [MatrixComponent::class], @Component(dependencies = [MatrixComponent::class],
modules = [ modules = [
@ -61,6 +65,14 @@ internal interface SessionComponent {


fun session(): Session fun session(): Session


fun syncTask(): SyncTask

fun syncTokenStore(): SyncTokenStore

fun networkConnectivityChecker(): NetworkConnectivityChecker

fun taskExecutor(): TaskExecutor

fun inject(sendEventWorker: SendEventWorker) fun inject(sendEventWorker: SendEventWorker)


fun inject(sendEventWorker: SendRelationWorker) fun inject(sendEventWorker: SendRelationWorker)
@ -77,7 +89,6 @@ internal interface SessionComponent {


fun inject(addHttpPusherWorker: AddHttpPusherWorker) fun inject(addHttpPusherWorker: AddHttpPusherWorker)



@Component.Factory @Component.Factory
interface Factory { interface Factory {
fun create( fun create(
@ -86,4 +97,6 @@ internal interface SessionComponent {
} }




} }



View File

@ -27,6 +27,7 @@ import im.vector.matrix.android.internal.database.mapper.PushRulesMapper
import im.vector.matrix.android.internal.database.model.PushRulesEntity import im.vector.matrix.android.internal.database.model.PushRulesEntity
import im.vector.matrix.android.internal.database.model.PusherEntityFields import im.vector.matrix.android.internal.database.model.PusherEntityFields
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.session.SessionScope
import im.vector.matrix.android.internal.session.pushers.GetPushRulesTask import im.vector.matrix.android.internal.session.pushers.GetPushRulesTask
import im.vector.matrix.android.internal.session.pushers.UpdatePushRuleEnableStatusTask import im.vector.matrix.android.internal.session.pushers.UpdatePushRuleEnableStatusTask
import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.TaskExecutor
@ -34,7 +35,7 @@ import im.vector.matrix.android.internal.task.configureWith
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject



@SessionScope
internal class DefaultPushRuleService @Inject constructor( internal class DefaultPushRuleService @Inject constructor(
private val sessionParams: SessionParams, private val sessionParams: SessionParams,
private val pushRulesTask: GetPushRulesTask, private val pushRulesTask: GetPushRulesTask,
@ -43,10 +44,8 @@ internal class DefaultPushRuleService @Inject constructor(
private val monarchy: Monarchy private val monarchy: Monarchy
) : PushRuleService { ) : PushRuleService {



private var listeners = ArrayList<PushRuleService.PushRuleListener>() private var listeners = ArrayList<PushRuleService.PushRuleListener>()



override fun fetchPushRules(scope: String) { override fun fetchPushRules(scope: String) {
pushRulesTask pushRulesTask
.configureWith(Unit) .configureWith(Unit)

View File

@ -19,7 +19,9 @@ import android.app.Service
import android.content.Intent import android.content.Intent
import android.os.Binder import android.os.Binder
import android.os.IBinder import android.os.IBinder
import androidx.work.ListenableWorker
import com.squareup.moshi.JsonEncodingException import com.squareup.moshi.JsonEncodingException
import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.failure.MatrixError import im.vector.matrix.android.api.failure.MatrixError
@ -31,6 +33,7 @@ import im.vector.matrix.android.internal.session.sync.model.SyncResponse
import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.TaskThread import im.vector.matrix.android.internal.task.TaskThread
import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.task.configureWith
import im.vector.matrix.android.internal.worker.getSessionComponent
import timber.log.Timber import timber.log.Timber
import java.net.SocketTimeoutException import java.net.SocketTimeoutException
import java.util.* import java.util.*
@ -46,15 +49,15 @@ private const val BACKGROUND_LONG_POOL_TIMEOUT = 0L
* in order to be able to perform a sync even if the app is not running. * in order to be able to perform a sync even if the app is not running.
* The <receiver> and <service> must be declared in the Manifest or the app using the SDK * The <receiver> and <service> must be declared in the Manifest or the app using the SDK
*/ */
internal open class SyncService : Service() { open class SyncService : Service() {


private var mIsSelfDestroyed: Boolean = false private var mIsSelfDestroyed: Boolean = false
private var cancelableTask: Cancelable? = null private var cancelableTask: Cancelable? = null


@Inject lateinit var syncTokenStore: SyncTokenStore private lateinit var syncTokenStore: SyncTokenStore
@Inject lateinit var syncTask: SyncTask private lateinit var syncTask: SyncTask
@Inject lateinit var networkConnectivityChecker: NetworkConnectivityChecker private lateinit var networkConnectivityChecker: NetworkConnectivityChecker
@Inject lateinit var taskExecutor: TaskExecutor private lateinit var taskExecutor: TaskExecutor


private var localBinder = LocalBinder() private var localBinder = LocalBinder()


@ -68,6 +71,12 @@ internal open class SyncService : Service() {
nextBatchDelay = 60_000L nextBatchDelay = 60_000L
timeout = 0 timeout = 0
intent?.let { intent?.let {
val userId = it.getStringExtra(EXTRA_USER_ID)
val sessionComponent = Matrix.getInstance(applicationContext).sessionManager.getSessionComponent(userId) ?: return@let
syncTokenStore = sessionComponent.syncTokenStore()
syncTask = sessionComponent.syncTask()
networkConnectivityChecker = sessionComponent.networkConnectivityChecker()
taskExecutor = sessionComponent.taskExecutor()
if (cancelableTask == null) { if (cancelableTask == null) {
timer.cancel() timer.cancel()
timer = Timer() timer = Timer()
@ -191,7 +200,6 @@ internal open class SyncService : Service() {
} }


internal fun notifySyncFinish() { internal fun notifySyncFinish() {

listeners.forEach { listeners.forEach {
try { try {
it.onSyncFinsh() it.onSyncFinsh()
@ -235,6 +243,8 @@ internal open class SyncService : Service() {


companion object { companion object {


const val EXTRA_USER_ID = "EXTRA_USER_ID"

fun startLongPool(delay: Long) { fun startLongPool(delay: Long) {


} }

View File

@ -20,6 +20,7 @@ package im.vector.riotredesign.receiver
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import im.vector.riotredesign.core.di.HasVectorInjector
import im.vector.riotredesign.core.services.AlarmSyncBroadcastReceiver import im.vector.riotredesign.core.services.AlarmSyncBroadcastReceiver
import timber.log.Timber import timber.log.Timber


@ -27,6 +28,11 @@ class OnApplicationUpgradeOrRebootReceiver : BroadcastReceiver() {


override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
Timber.v("## onReceive() ${intent.action}") Timber.v("## onReceive() ${intent.action}")
AlarmSyncBroadcastReceiver.scheduleAlarm(context, 10) if (context is HasVectorInjector) {
val activeSession = context.injector().activeSessionHolder().getSafeActiveSession()
if (activeSession != null) {
AlarmSyncBroadcastReceiver.scheduleAlarm(context, activeSession.myUserId, 10)
}
}
} }
} }

View File

@ -114,7 +114,7 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME) @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun entersForeground() { fun entersForeground() {
AlarmSyncBroadcastReceiver.cancelAlarm(appContext) AlarmSyncBroadcastReceiver.cancelAlarm(appContext)
activeSessionHolder.getActiveSession().also { activeSessionHolder.getSafeActiveSession()?.also {
it.stopAnyBackgroundSync() it.stopAnyBackgroundSync()
} }
} }
@ -128,8 +128,9 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
} else { } else {
//TODO check if notifications are enabled for this device //TODO check if notifications are enabled for this device
//We need to use alarm in this mode //We need to use alarm in this mode
if (PreferencesManager.areNotificationEnabledForDevice(applicationContext)) { val activeSession = activeSessionHolder.getSafeActiveSession()
AlarmSyncBroadcastReceiver.scheduleAlarm(applicationContext, 4_000L) if (activeSession != null && PreferencesManager.areNotificationEnabledForDevice(applicationContext)) {
AlarmSyncBroadcastReceiver.scheduleAlarm(applicationContext, activeSession.myUserId, 4_000L)
Timber.i("Alarm scheduled to restart service") Timber.i("Alarm scheduled to restart service")
} }
} }

View File

@ -53,6 +53,7 @@ import im.vector.riotredesign.features.roomdirectory.createroom.CreateRoomFragme
import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryPickerFragment import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryPickerFragment
import im.vector.riotredesign.features.roomdirectory.roompreview.RoomPreviewNoPreviewFragment import im.vector.riotredesign.features.roomdirectory.roompreview.RoomPreviewNoPreviewFragment
import im.vector.riotredesign.features.settings.VectorSettingsActivity import im.vector.riotredesign.features.settings.VectorSettingsActivity
import im.vector.riotredesign.features.settings.VectorSettingsNotificationPreferenceFragment
import im.vector.riotredesign.features.settings.VectorSettingsPreferencesFragment import im.vector.riotredesign.features.settings.VectorSettingsPreferencesFragment


@Component(dependencies = [VectorComponent::class], modules = [ViewModelModule::class, HomeModule::class]) @Component(dependencies = [VectorComponent::class], modules = [ViewModelModule::class, HomeModule::class])
@ -133,6 +134,8 @@ interface ScreenComponent {


fun inject(videoMediaViewerActivity: VideoMediaViewerActivity) fun inject(videoMediaViewerActivity: VideoMediaViewerActivity)


fun inject(vectorSettingsNotificationPreferenceFragment: VectorSettingsNotificationPreferenceFragment)

@Component.Factory @Component.Factory
interface Factory { interface Factory {
fun create(vectorComponent: VectorComponent, fun create(vectorComponent: VectorComponent,

View File

@ -33,7 +33,9 @@ import im.vector.riotredesign.features.home.HomeNavigator
import im.vector.riotredesign.features.home.HomeRoomListObservableStore import im.vector.riotredesign.features.home.HomeRoomListObservableStore
import im.vector.riotredesign.features.home.group.SelectedGroupStore import im.vector.riotredesign.features.home.group.SelectedGroupStore
import im.vector.riotredesign.features.navigation.Navigator import im.vector.riotredesign.features.navigation.Navigator
import im.vector.riotredesign.features.notifications.NotificationBroadcastReceiver
import im.vector.riotredesign.features.notifications.NotificationDrawerManager import im.vector.riotredesign.features.notifications.NotificationDrawerManager
import im.vector.riotredesign.features.notifications.PushRuleTriggerListener
import im.vector.riotredesign.features.rageshake.BugReporter import im.vector.riotredesign.features.rageshake.BugReporter
import im.vector.riotredesign.features.rageshake.RageShake import im.vector.riotredesign.features.rageshake.RageShake
import im.vector.riotredesign.features.rageshake.VectorUncaughtExceptionHandler import im.vector.riotredesign.features.rageshake.VectorUncaughtExceptionHandler
@ -43,6 +45,8 @@ import javax.inject.Singleton
@Singleton @Singleton
interface VectorComponent { interface VectorComponent {


fun inject(vectorApplication: NotificationBroadcastReceiver)

fun inject(vectorApplication: VectorApplication) fun inject(vectorApplication: VectorApplication)


fun matrix(): Matrix fun matrix(): Matrix
@ -79,6 +83,8 @@ interface VectorComponent {


fun vectorUncaughtExceptionHandler(): VectorUncaughtExceptionHandler fun vectorUncaughtExceptionHandler(): VectorUncaughtExceptionHandler


fun pushRuleTriggerListener(): PushRuleTriggerListener

@Component.Factory @Component.Factory
interface Factory { interface Factory {
fun create(@BindsInstance context: Context): VectorComponent fun create(@BindsInstance context: Context): VectorComponent

View File

@ -57,6 +57,7 @@ import im.vector.riotredesign.features.workers.signout.SignOutViewModel


@Module @Module
interface ViewModelModule { interface ViewModelModule {


@Binds @Binds
fun bindViewModelFactory(factory: VectorViewModelFactory): ViewModelProvider.Factory fun bindViewModelFactory(factory: VectorViewModelFactory): ViewModelProvider.Factory

View File

@ -6,10 +6,11 @@ import im.vector.riotredesign.R
import im.vector.riotredesign.core.resources.AppNameProvider import im.vector.riotredesign.core.resources.AppNameProvider
import im.vector.riotredesign.core.resources.LocaleProvider import im.vector.riotredesign.core.resources.LocaleProvider
import im.vector.riotredesign.core.resources.StringProvider import im.vector.riotredesign.core.resources.StringProvider
import javax.inject.Inject


private const val DEFAULT_PUSHER_FILE_TAG = "mobile" private const val DEFAULT_PUSHER_FILE_TAG = "mobile"


class PushersManager( class PushersManager @Inject constructor(
private val currentSession: Session, private val currentSession: Session,
private val localeProvider: LocaleProvider, private val localeProvider: LocaleProvider,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,

View File

@ -2,9 +2,10 @@ package im.vector.riotredesign.core.resources


import android.content.Context import android.content.Context
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject




class AppNameProvider(private val context: Context) { class AppNameProvider @Inject constructor(private val context: Context) {


fun getAppName(): String { fun getAppName(): String {
try { try {

View File

@ -8,6 +8,7 @@ import android.content.Intent
import android.os.Build import android.os.Build
import android.os.PowerManager import android.os.PowerManager
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import im.vector.matrix.android.internal.session.sync.job.SyncService
import timber.log.Timber import timber.log.Timber


class AlarmSyncBroadcastReceiver : BroadcastReceiver() { class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
@ -22,11 +23,12 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
} }
} }



val userId = intent.getStringExtra(SyncService.EXTRA_USER_ID)
// This method is called when the BroadcastReceiver is receiving an Intent broadcast. // This method is called when the BroadcastReceiver is receiving an Intent broadcast.
Timber.d("RestartBroadcastReceiver received intent") Timber.d("RestartBroadcastReceiver received intent")
Intent(context, VectorSyncService::class.java).also { Intent(context, VectorSyncService::class.java).also {
it.action = "SLOW" it.action = "SLOW"
it.putExtra(SyncService.EXTRA_USER_ID, userId)
context.startService(it) context.startService(it)
try { try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@ -40,7 +42,7 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
} }
} }


scheduleAlarm(context, 30_000L) scheduleAlarm(context, userId, 30_000L)


Timber.i("Alarm scheduled to restart service") Timber.i("Alarm scheduled to restart service")
} }
@ -48,11 +50,13 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
companion object { companion object {
const val REQUEST_CODE = 0 const val REQUEST_CODE = 0


fun scheduleAlarm(context: Context, delay: Long) { fun scheduleAlarm(context: Context, userId: String, delay: Long) {
//Reschedule //Reschedule
val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java) val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java).apply {
putExtra(SyncService.EXTRA_USER_ID, userId)
}
val pIntent = PendingIntent.getBroadcast(context, AlarmSyncBroadcastReceiver.REQUEST_CODE, val pIntent = PendingIntent.getBroadcast(context, AlarmSyncBroadcastReceiver.REQUEST_CODE,
intent, PendingIntent.FLAG_UPDATE_CURRENT) intent, PendingIntent.FLAG_UPDATE_CURRENT)
val firstMillis = System.currentTimeMillis() + delay val firstMillis = System.currentTimeMillis() + delay
val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
@ -65,7 +69,7 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
fun cancelAlarm(context: Context) { fun cancelAlarm(context: Context) {
val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java) val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java)
val pIntent = PendingIntent.getBroadcast(context, AlarmSyncBroadcastReceiver.REQUEST_CODE, val pIntent = PendingIntent.getBroadcast(context, AlarmSyncBroadcastReceiver.REQUEST_CODE,
intent, PendingIntent.FLAG_UPDATE_CURRENT) intent, PendingIntent.FLAG_UPDATE_CURRENT)
val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmMgr.cancel(pIntent) alarmMgr.cancel(pIntent)
} }

View File

@ -1,587 +0,0 @@
/*
* Copyright 2019 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.riotredesign.core.services

import android.content.Context
import android.content.Intent
import androidx.core.content.ContextCompat
import androidx.work.Constraints
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.riotredesign.R
import im.vector.riotredesign.features.notifications.NotifiableEventResolver
import im.vector.riotredesign.features.notifications.NotificationUtils
import timber.log.Timber
import java.util.concurrent.TimeUnit
import javax.inject.Inject

/**
* A service in charge of controlling whether the event stream is running or not.
*
* It manages messages notifications displayed to the end user.
*/
class EventStreamServiceX : VectorService() {

/**
* Managed session (no multi session for Riot)
*/
@Inject lateinit var session: Session

/**
* Set to true to simulate a push immediately when service is destroyed
*/
private var mSimulatePushImmediate = false

/**
* The current state.
*/
private var serviceState = ServiceState.INIT
set(newServiceState) {
Timber.i("setServiceState from $field to $newServiceState")
field = newServiceState
}

/**
* Push manager
*/
// TODO private var mPushManager: PushManager? = null

private var mNotifiableEventResolver: NotifiableEventResolver? = null

/**
* Live events listener
*/
/* TODO
private val mEventsListener = object : MXEventListener() {
override fun onBingEvent(event: Event, roomState: RoomState, bingRule: BingRule) {
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) {
Timber.i("%%%%%%%% MXEventListener: the event $event")
}

Timber.i("prepareNotification : " + event.eventId + " in " + roomState.roomId)
val session = Matrix.getMXSession(applicationContext, event.matrixId)

// invalid session ?
// should never happen.
// But it could be triggered because of multi accounts management.
// The dedicated account is removing but some pushes are still received.
if (null == session || !session.isAlive) {
Timber.i("prepareNotification : don't bing - no session")
return
}

if (EventType.CALL_INVITE == event.getClearType()) {
handleCallInviteEvent(event)
return
}


val notifiableEvent = mNotifiableEventResolver!!.resolveEvent(event, roomState, bingRule, session)
if (notifiableEvent != null) {
VectorApp.getInstance().notificationDrawerManager.onNotifiableEventReceived(notifiableEvent)
}
}

override fun onLiveEventsChunkProcessed(fromToken: String, toToken: String) {
Timber.i("%%%%%%%% MXEventListener: onLiveEventsChunkProcessed[$fromToken->$toToken]")

VectorApp.getInstance().notificationDrawerManager.refreshNotificationDrawer(OutdatedEventDetector(this@EventStreamServiceX))

// do not suspend the application if there is some active calls
if (ServiceState.CATCHUP == serviceState) {
val hasActiveCalls = session?.mCallsManager?.hasActiveCalls() == true

// if there are some active calls, the catchup should not be stopped.
// because an user could answer to a call from another device.
// there will no push because it is his own message.
// so, the client has no choice to catchup until the ring is shutdown
if (hasActiveCalls) {
Timber.i("onLiveEventsChunkProcessed : Catchup again because there are active calls")
catchup(false)
} else if (ServiceState.CATCHUP == serviceState) {
Timber.i("onLiveEventsChunkProcessed : no Active call")
CallsManager.getSharedInstance().checkDeadCalls()
stop()
}
}
}
} */

/**
* Service internal state
*/
private enum class ServiceState {
// Initial state
INIT,
// Service is started for a Catchup. Once the catchup is finished the service will be stopped
CATCHUP,
// Service is started, and session is monitored
STARTED
}

override fun onCreate() {
//setup injector
super.onCreate()
}

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// Cancel any previous worker
cancelAnySimulatedPushSchedule()

// no intent : restarted by Android
if (null == intent) {
// Cannot happen anymore
Timber.e("onStartCommand : null intent")
myStopSelf()
return START_NOT_STICKY
}

val action = intent.action

Timber.i("onStartCommand with action : $action (current state $serviceState)")

// Manage foreground notification
when (action) {
ACTION_BOOT_COMPLETE,
ACTION_APPLICATION_UPGRADE,
ACTION_SIMULATED_PUSH_RECEIVED -> {
// Display foreground notification
Timber.i("startForeground")
val notification = NotificationUtils.buildForegroundServiceNotification(this, R.string.notification_sync_in_progress)
startForeground(NotificationUtils.NOTIFICATION_ID_FOREGROUND_SERVICE, notification)
}
ACTION_GO_TO_FOREGROUND -> {
// Stop foreground notification display
Timber.i("stopForeground")
stopForeground(true)
}
}

if (null == session) {
Timber.e("onStartCommand : no sessions")
myStopSelf()
return START_NOT_STICKY
}

when (action) {
ACTION_START,
ACTION_GO_TO_FOREGROUND ->
when (serviceState) {
ServiceState.INIT ->
start(false)
ServiceState.CATCHUP ->
// A push has been received before, just change state, to avoid stopping the service when catchup is over
serviceState = ServiceState.STARTED
ServiceState.STARTED -> {
// Nothing to do
}
}
ACTION_STOP,
ACTION_GO_TO_BACKGROUND,
ACTION_LOGOUT ->
stop()
ACTION_PUSH_RECEIVED,
ACTION_SIMULATED_PUSH_RECEIVED ->
when (serviceState) {
ServiceState.INIT ->
start(true)
ServiceState.CATCHUP ->
catchup(true)
ServiceState.STARTED ->
// Nothing to do
Unit
}
ACTION_PUSH_UPDATE -> pushStatusUpdate()
ACTION_BOOT_COMPLETE -> {
// No FCM only
mSimulatePushImmediate = true
stop()
}
ACTION_APPLICATION_UPGRADE -> {
// FDroid only
catchup(true)
}
else -> {
// Should not happen
}
}

// We don't want the service to be restarted automatically by the System
return START_NOT_STICKY
}

override fun onDestroy() {
super.onDestroy()

// Schedule worker?
scheduleSimulatedPushIfNeeded()
}

/**
* Tell the WorkManager to cancel any schedule of push simulation
*/
private fun cancelAnySimulatedPushSchedule() {
WorkManager.getInstance().cancelAllWorkByTag(PUSH_SIMULATOR_REQUEST_TAG)
}

/**
* Configure the WorkManager to schedule a simulated push, if necessary
*/
private fun scheduleSimulatedPushIfNeeded() {
if (shouldISimulatePush()) {
val delay = if (mSimulatePushImmediate) 0 else 60_000 // TODO mPushManager?.backgroundSyncDelay ?: let { 60_000 }
Timber.i("## service is schedule to restart in $delay millis, if network is connected")

val pushSimulatorRequest = OneTimeWorkRequestBuilder<PushSimulatorWorker>()
.setInitialDelay(delay.toLong(), TimeUnit.MILLISECONDS)
.setConstraints(Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build())
.addTag(PUSH_SIMULATOR_REQUEST_TAG)
.build()

WorkManager.getInstance().let {
// Cancel any previous worker
it.cancelAllWorkByTag(PUSH_SIMULATOR_REQUEST_TAG)
it.enqueue(pushSimulatorRequest)
}
}
}

/**
* Start the even stream.
*
* @param session the session
*/
private fun startEventStream(session: Session) {
/* TODO
// resume if it was only suspended
if (null != session.currentSyncToken) {
session.resumeEventStream()
} else {
session.startEventStream(store?.eventStreamToken)
}
*/
}

/**
* Monitor the provided session.
*
* @param session the session
*/
private fun monitorSession(session: Session) {
/* TODO
session.dataHandler.addListener(mEventsListener)
CallsManager.getSharedInstance().addSession(session)

val store = session.dataHandler.store

// the store is ready (no data loading in progress...)
if (store!!.isReady) {
startEventStream(session, store)
} else {
// wait that the store is ready before starting the events stream
store.addMXStoreListener(object : MXStoreListener() {
override fun onStoreReady(accountId: String) {
startEventStream(session, store)

store.removeMXStoreListener(this)
}

override fun onStoreCorrupted(accountId: String, description: String) {
// start a new initial sync
if (null == store.eventStreamToken) {
startEventStream(session, store)
} else {
// the data are out of sync
Matrix.getInstance(applicationContext)!!.reloadSessions(applicationContext)
}

store.removeMXStoreListener(this)
}

override fun onStoreOOM(accountId: String, description: String) {
val uiHandler = Handler(mainLooper)

uiHandler.post {
Toast.makeText(applicationContext, "$accountId : $description", Toast.LENGTH_LONG).show()
Matrix.getInstance(applicationContext)!!.reloadSessions(applicationContext)
}
}
})

store.open()
}
*/
}

/**
* internal start.
*/
private fun start(forPush: Boolean) {
val applicationContext = applicationContext
// TODO mPushManager = Matrix.getInstance(applicationContext)!!.pushManager
mNotifiableEventResolver = NotifiableEventResolver(applicationContext)

monitorSession(session!!)

serviceState = if (forPush) {
ServiceState.CATCHUP
} else {
ServiceState.STARTED
}
}

/**
* internal stop.
*/
private fun stop() {
Timber.i("## stop(): the service is stopped")

/* TODO
if (null != session && session!!.isAlive) {
session!!.stopEventStream()
session!!.dataHandler.removeListener(mEventsListener)
CallsManager.getSharedInstance().removeSession(session)
}
session = null
*/

// Stop the service
myStopSelf()
}

/**
* internal catchup method.
*
* @param checkState true to check if the current state allow to perform a catchup
*/
private fun catchup(checkState: Boolean) {
var canCatchup = true

if (!checkState) {
Timber.i("catchup without checking serviceState ")
} else {
Timber.i("catchup with serviceState " + serviceState + " CurrentActivity ") // TODO + VectorApp.getCurrentActivity())

/* TODO
// the catchup should only be done
// 1- the serviceState is in catchup : the event stream might have gone to sleep between two catchups
// 2- the thread is suspended
// 3- the application has been launched by a push so there is no displayed activity
canCatchup = (serviceState == ServiceState.CATCHUP
//|| (serviceState == ServiceState.PAUSE)
|| ServiceState.STARTED == serviceState && null == VectorApp.getCurrentActivity())
*/
}

if (canCatchup) {
if (session != null) {
// TODO session!!.catchupEventStream()
} else {
Timber.i("catchup no session")
}

serviceState = ServiceState.CATCHUP
} else {
Timber.i("No catchup is triggered because there is already a running event thread")
}
}

/**
* The push status has been updated (i.e disabled or enabled).
* TODO Useless now?
*/
private fun pushStatusUpdate() {
Timber.i("## pushStatusUpdate")
}

/* ==========================================================================================
* Push simulator
* ========================================================================================== */

/**
* @return true if the FCM is disable or not setup, user allowed background sync, user wants notification
*/
private fun shouldISimulatePush(): Boolean {
return false

/* TODO

if (Matrix.getInstance(applicationContext)?.defaultSession == null) {
Timber.i("## shouldISimulatePush: NO: no session")

return false
}

mPushManager?.let { pushManager ->
if (pushManager.useFcm()
&& !TextUtils.isEmpty(pushManager.currentRegistrationToken)
&& pushManager.isServerRegistered) {
// FCM is ok
Timber.i("## shouldISimulatePush: NO: FCM is up")
return false
}

if (!pushManager.isBackgroundSyncAllowed) {
// User has disabled background sync
Timber.i("## shouldISimulatePush: NO: background sync not allowed")
return false
}

if (!pushManager.areDeviceNotificationsAllowed()) {
// User does not want notifications
Timber.i("## shouldISimulatePush: NO: user does not want notification")
return false
}
}

// Lets simulate push
Timber.i("## shouldISimulatePush: YES")
return true
*/
}


//================================================================================
// Call management
//================================================================================

private fun handleCallInviteEvent(event: Event) {
/*
TODO
val session = Matrix.getMXSession(applicationContext, event.matrixId)

// invalid session ?
// should never happen.
// But it could be triggered because of multi accounts management.
// The dedicated account is removing but some pushes are still received.
if (null == session || !session.isAlive) {
Timber.v("prepareCallNotification : don't bing - no session")
return
}

val room: Room? = session.dataHandler.getRoom(event.roomId)

// invalid room ?
if (null == room) {
Timber.i("prepareCallNotification : don't bing - the room does not exist")
return
}

var callId: String? = null
var isVideo = false

try {
callId = event.contentAsJsonObject?.get("call_id")?.asString

// Check if it is a video call
val offer = event.contentAsJsonObject?.get("offer")?.asJsonObject
val sdp = offer?.get("sdp")
val sdpValue = sdp?.asString

isVideo = sdpValue?.contains("m=video") == true
} catch (e: Exception) {
Timber.e(e, "prepareNotification : getContentAsJsonObject")
}

if (!TextUtils.isEmpty(callId)) {
CallService.onIncomingCall(this,
isVideo,
room.getRoomDisplayName(this),
room.roomId,
session.myUserId!!,
callId!!)
}
*/
}

companion object {
private const val PUSH_SIMULATOR_REQUEST_TAG = "PUSH_SIMULATOR_REQUEST_TAG"

private const val ACTION_START = "im.vector.riotredesign.core.services.EventStreamServiceX.START"
private const val ACTION_LOGOUT = "im.vector.riotredesign.core.services.EventStreamServiceX.LOGOUT"
private const val ACTION_GO_TO_FOREGROUND = "im.vector.riotredesign.core.services.EventStreamServiceX.GO_TO_FOREGROUND"
private const val ACTION_GO_TO_BACKGROUND = "im.vector.riotredesign.core.services.EventStreamServiceX.GO_TO_BACKGROUND"
private const val ACTION_PUSH_UPDATE = "im.vector.riotredesign.core.services.EventStreamServiceX.PUSH_UPDATE"
private const val ACTION_PUSH_RECEIVED = "im.vector.riotredesign.core.services.EventStreamServiceX.PUSH_RECEIVED"
private const val ACTION_SIMULATED_PUSH_RECEIVED = "im.vector.riotredesign.core.services.EventStreamServiceX.SIMULATED_PUSH_RECEIVED"
private const val ACTION_STOP = "im.vector.riotredesign.core.services.EventStreamServiceX.STOP"
private const val ACTION_BOOT_COMPLETE = "im.vector.riotredesign.core.services.EventStreamServiceX.BOOT_COMPLETE"
private const val ACTION_APPLICATION_UPGRADE = "im.vector.riotredesign.core.services.EventStreamServiceX.APPLICATION_UPGRADE"

/* ==========================================================================================
* Events sent to the service
* ========================================================================================== */

fun onApplicationStarted(context: Context) {
sendAction(context, ACTION_START)
}

fun onLogout(context: Context) {
sendAction(context, ACTION_LOGOUT)
}

fun onAppGoingToForeground(context: Context) {
sendAction(context, ACTION_GO_TO_FOREGROUND)
}

fun onAppGoingToBackground(context: Context) {
sendAction(context, ACTION_GO_TO_BACKGROUND)
}

fun onPushUpdate(context: Context) {
sendAction(context, ACTION_PUSH_UPDATE)
}

fun onPushReceived(context: Context) {
sendAction(context, ACTION_PUSH_RECEIVED)
}

fun onSimulatedPushReceived(context: Context) {
sendAction(context, ACTION_SIMULATED_PUSH_RECEIVED, true)
}

fun onApplicationStopped(context: Context) {
sendAction(context, ACTION_STOP)
}

fun onBootComplete(context: Context) {
sendAction(context, ACTION_BOOT_COMPLETE, true)
}

fun onApplicationUpgrade(context: Context) {
sendAction(context, ACTION_APPLICATION_UPGRADE, true)
}

private fun sendAction(context: Context, action: String, foreground: Boolean = false) {
Timber.i("sendAction $action")

val intent = Intent(context, EventStreamServiceX::class.java)
intent.action = action

if (foreground) {
ContextCompat.startForegroundService(context, intent)
} else {
context.startService(intent)
}
}
}
}

View File

@ -1,57 +0,0 @@
/*
* Copyright 2019 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.riotredesign.features.home

import android.net.Uri
import im.vector.matrix.android.api.permalinks.PermalinkData
import im.vector.matrix.android.api.permalinks.PermalinkParser
import im.vector.riotredesign.features.navigation.Navigator
import javax.inject.Inject

class HomePermalinkHandler @Inject constructor(private val homeNavigator: HomeNavigator,
private val navigator: Navigator) {

fun launch(deepLink: String?) {
val uri = deepLink?.let { Uri.parse(it) }
launch(uri)
}

fun launch(deepLink: Uri?) {
if (deepLink == null) {
return
}
val permalinkData = PermalinkParser.parse(deepLink)
when (permalinkData) {
is PermalinkData.EventLink -> {
homeNavigator.openRoomDetail(permalinkData.roomIdOrAlias, permalinkData.eventId, navigator)
}
is PermalinkData.RoomLink -> {
homeNavigator.openRoomDetail(permalinkData.roomIdOrAlias, null, navigator)
}
is PermalinkData.GroupLink -> {
homeNavigator.openGroupDetail(permalinkData.groupId)
}
is PermalinkData.UserLink -> {
homeNavigator.openUserDetail(permalinkData.userId)
}
is PermalinkData.FallbackLink -> {

}
}
}

}

View File

@ -22,9 +22,10 @@ import im.vector.matrix.android.api.permalinks.PermalinkData
import im.vector.matrix.android.api.permalinks.PermalinkParser import im.vector.matrix.android.api.permalinks.PermalinkParser
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.riotredesign.features.navigation.Navigator import im.vector.riotredesign.features.navigation.Navigator
import javax.inject.Inject


class PermalinkHandler(private val session: Session, class PermalinkHandler @Inject constructor(private val session: Session,
private val navigator: Navigator) { private val navigator: Navigator) {


fun launch(context: Context, deepLink: String?, navigateToRoomInterceptor: NavigateToRoomInterceptor? = null): Boolean { fun launch(context: Context, deepLink: String?, navigateToRoomInterceptor: NavigateToRoomInterceptor? = null): Boolean {
val uri = deepLink?.let { Uri.parse(it) } val uri = deepLink?.let { Uri.parse(it) }

View File

@ -31,12 +31,13 @@ import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageTex
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_ import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_
import im.vector.riotredesign.features.home.room.detail.timeline.util.MessageInformationDataFactory import im.vector.riotredesign.features.home.room.detail.timeline.util.MessageInformationDataFactory
import me.gujun.android.span.span import me.gujun.android.span.span
import javax.inject.Inject


// This class handles timeline events who haven't been successfully decrypted // This class handles timeline events who haven't been successfully decrypted
class EncryptedItemFactory(private val messageInformationDataFactory: MessageInformationDataFactory, class EncryptedItemFactory @Inject constructor(private val messageInformationDataFactory: MessageInformationDataFactory,
private val colorProvider: ColorProvider, private val colorProvider: ColorProvider,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val avatarRenderer: AvatarRenderer) { private val avatarRenderer: AvatarRenderer) {


fun create(event: TimelineEvent, fun create(event: TimelineEvent,
nextEvent: TimelineEvent?, nextEvent: TimelineEvent?,
@ -79,7 +80,7 @@ class EncryptedItemFactory(private val messageInformationDataFactory: MessageInf
})) }))
.longClickListener { view -> .longClickListener { view ->
return@longClickListener callback?.onEventLongClicked(informationData, null, view) return@longClickListener callback?.onEventLongClicked(informationData, null, view)
?: false ?: false
} }
} }
else -> null else -> null

View File

@ -30,6 +30,7 @@ import im.vector.riotredesign.R
import im.vector.riotredesign.core.resources.StringProvider import im.vector.riotredesign.core.resources.StringProvider
import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject


/** /**
* The notifiable event resolver is able to create a NotifiableEvent (view model for notifications) from an sdk Event. * The notifiable event resolver is able to create a NotifiableEvent (view model for notifications) from an sdk Event.
@ -37,8 +38,8 @@ import timber.log.Timber
* The NotifiableEventResolver is the only aware of session/store, the NotificationDrawerManager has no knowledge of that, * The NotifiableEventResolver is the only aware of session/store, the NotificationDrawerManager has no knowledge of that,
* this pattern allow decoupling between the object responsible of displaying notifications and the matrix sdk. * this pattern allow decoupling between the object responsible of displaying notifications and the matrix sdk.
*/ */
class NotifiableEventResolver(private val stringProvider: StringProvider, class NotifiableEventResolver @Inject constructor(private val stringProvider: StringProvider,
private val noticeEventFormatter: NoticeEventFormatter) { private val noticeEventFormatter: NoticeEventFormatter) {


//private val eventDisplay = RiotEventDisplay(context) //private val eventDisplay = RiotEventDisplay(context)


@ -95,8 +96,8 @@ class NotifiableEventResolver(private val stringProvider: StringProvider,
// Ok room is not known in store, but we can still display something // Ok room is not known in store, but we can still display something
val body = val body =
event.annotations?.editSummary?.aggregatedContent?.toModel<MessageContent>()?.body event.annotations?.editSummary?.aggregatedContent?.toModel<MessageContent>()?.body
?: event.root.getClearContent().toModel<MessageContent>()?.body ?: event.root.getClearContent().toModel<MessageContent>()?.body
?: stringProvider.getString(R.string.notification_unknown_new_event) ?: stringProvider.getString(R.string.notification_unknown_new_event)
val roomName = stringProvider.getString(R.string.notification_unknown_room_name) val roomName = stringProvider.getString(R.string.notification_unknown_room_name)
val senderDisplayName = event.senderName val senderDisplayName = event.senderName


@ -114,8 +115,8 @@ class NotifiableEventResolver(private val stringProvider: StringProvider,
return notifiableEvent return notifiableEvent
} else { } else {
val body = event.annotations?.editSummary?.aggregatedContent?.toModel<MessageContent>()?.body val body = event.annotations?.editSummary?.aggregatedContent?.toModel<MessageContent>()?.body
?: event.root.getClearContent().toModel<MessageContent>()?.body ?: event.root.getClearContent().toModel<MessageContent>()?.body
?: stringProvider.getString(R.string.notification_unknown_new_event) ?: stringProvider.getString(R.string.notification_unknown_new_event)
val roomName = room.roomSummary?.displayName ?: "" val roomName = room.roomSummary?.displayName ?: ""
val senderDisplayName = event.senderName ?: "" val senderDisplayName = event.senderName ?: ""


@ -137,15 +138,15 @@ class NotifiableEventResolver(private val stringProvider: StringProvider,
// TODO They will be not displayed the first time (known limitation) // TODO They will be not displayed the first time (known limitation)
notifiableEvent.roomAvatarPath = session.contentUrlResolver() notifiableEvent.roomAvatarPath = session.contentUrlResolver()
.resolveThumbnail(room.roomSummary?.avatarUrl, .resolveThumbnail(room.roomSummary?.avatarUrl,
250, 250,
250, 250,
ContentUrlResolver.ThumbnailMethod.SCALE) ContentUrlResolver.ThumbnailMethod.SCALE)


notifiableEvent.senderAvatarPath = session.contentUrlResolver() notifiableEvent.senderAvatarPath = session.contentUrlResolver()
.resolveThumbnail(event.senderAvatar, .resolveThumbnail(event.senderAvatar,
250, 250,
250, 250,
ContentUrlResolver.ThumbnailMethod.SCALE) ContentUrlResolver.ThumbnailMethod.SCALE)


return notifiableEvent return notifiableEvent
} }
@ -158,7 +159,7 @@ class NotifiableEventResolver(private val stringProvider: StringProvider,
val dName = event.senderId?.let { session.getUser(it)?.displayName } val dName = event.senderId?.let { session.getUser(it)?.displayName }
if (Membership.INVITE == content.membership) { if (Membership.INVITE == content.membership) {
val body = noticeEventFormatter.format(event, dName) val body = noticeEventFormatter.format(event, dName)
?: stringProvider.getString(R.string.notification_new_invitation) ?: stringProvider.getString(R.string.notification_new_invitation)
return InviteNotifiableEvent( return InviteNotifiableEvent(
session.sessionParams.credentials.userId, session.sessionParams.credentials.userId,
eventId = event.eventId!!, eventId = event.eventId!!,

View File

@ -20,12 +20,12 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import androidx.core.app.RemoteInput import androidx.core.app.RemoteInput
import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.Room
import im.vector.riotredesign.R import im.vector.riotredesign.R
import im.vector.riotredesign.core.di.ActiveSessionHolder import im.vector.riotredesign.core.di.ActiveSessionHolder
import im.vector.riotredesign.core.di.HasVectorInjector
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
@ -38,10 +38,14 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager @Inject lateinit var notificationDrawerManager: NotificationDrawerManager
@Inject lateinit var activeSessionHolder: ActiveSessionHolder @Inject lateinit var activeSessionHolder: ActiveSessionHolder



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
Timber.v("NotificationBroadcastReceiver received : $intent") Timber.v("NotificationBroadcastReceiver received : $intent")

val appContext = context.applicationContext
if (appContext is HasVectorInjector) {
appContext.injector().inject(this)
}
when (intent.action) { when (intent.action) {
NotificationUtils.SMART_REPLY_ACTION -> NotificationUtils.SMART_REPLY_ACTION ->
handleSmartReply(intent, context) handleSmartReply(intent, context)
@ -60,7 +64,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
} }


private fun handleMarkAsRead(context: Context, roomId: String) { private fun handleMarkAsRead(context: Context, roomId: String) {
activeSessionHolder.getActiveSession().let { session -> activeSessionHolder.getActiveSession().let { session ->
session.getRoom(roomId) session.getRoom(roomId)
?.markAllAsRead(object : MatrixCallback<Unit> {}) ?.markAllAsRead(object : MatrixCallback<Unit> {})
} }
@ -95,7 +99,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
false, false,
System.currentTimeMillis(), System.currentTimeMillis(),
session.getUser(session.sessionParams.credentials.userId)?.displayName session.getUser(session.sessionParams.credentials.userId)?.displayName
?: context?.getString(R.string.notification_sender_me), ?: context?.getString(R.string.notification_sender_me),
session.sessionParams.credentials.userId, session.sessionParams.credentials.userId,
message, message,
room.roomId, room.roomId,

View File

@ -15,10 +15,10 @@
*/ */
package im.vector.riotredesign.features.notifications package im.vector.riotredesign.features.notifications


import im.vector.matrix.android.api.session.Session import im.vector.riotredesign.core.di.ActiveSessionHolder
import javax.inject.Inject import javax.inject.Inject


class OutdatedEventDetector @Inject constructor(private val session: Session) { class OutdatedEventDetector @Inject constructor(private val activeSessionHolder: ActiveSessionHolder) {


/** /**
* Returns true if the given event is outdated. * Returns true if the given event is outdated.
@ -29,7 +29,7 @@ class OutdatedEventDetector @Inject constructor(private val session: Session) {
if (notifiableEvent is NotifiableMessageEvent) { if (notifiableEvent is NotifiableMessageEvent) {
val eventID = notifiableEvent.eventId val eventID = notifiableEvent.eventId
val roomID = notifiableEvent.roomId val roomID = notifiableEvent.roomId
val room = session.getRoom(roomID) ?: return false val room = activeSessionHolder.getSafeActiveSession()?.getRoom(roomID) ?: return false
return room.isEventRead(eventID) return room.isEventRead(eventID)
} }
return false return false

View File

@ -16,32 +16,43 @@


package im.vector.riotredesign.features.settings package im.vector.riotredesign.features.settings


import android.content.Context
import android.os.Bundle import android.os.Bundle
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.SwitchPreference import androidx.preference.SwitchPreference
import im.vector.matrix.android.api.Matrix import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.riotredesign.R import im.vector.riotredesign.R
import im.vector.riotredesign.core.di.ActiveSessionHolder
import im.vector.riotredesign.core.di.DaggerScreenComponent
import im.vector.riotredesign.core.platform.VectorPreferenceFragment import im.vector.riotredesign.core.platform.VectorPreferenceFragment
import im.vector.riotredesign.core.pushers.PushersManager import im.vector.riotredesign.core.pushers.PushersManager
import im.vector.riotredesign.push.fcm.FcmHelper import im.vector.riotredesign.push.fcm.FcmHelper
import org.koin.android.ext.android.inject import javax.inject.Inject


// Referenced in vector_settings_preferences_root.xml // Referenced in vector_settings_preferences_root.xml
class VectorSettingsNotificationPreferenceFragment : VectorPreferenceFragment() { class VectorSettingsNotificationPreferenceFragment : VectorPreferenceFragment() {



override var titleRes: Int = R.string.settings_notifications override var titleRes: Int = R.string.settings_notifications


val pushManager: PushersManager by inject() @Inject lateinit var pushManager: PushersManager

@Inject lateinit var activeSessionHolder: ActiveSessionHolder


override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.vector_settings_notifications) addPreferencesFromResource(R.xml.vector_settings_notifications)
} }


override fun onAttach(context: Context) {
val screenComponent = DaggerScreenComponent.factory().create(vectorActivity.getVectorComponent(), vectorActivity)
super.onAttach(context)
screenComponent.inject(this)
}


override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
Matrix.getInstance().currentSession?.refreshPushers() activeSessionHolder.getSafeActiveSession()?.refreshPushers()
} }


override fun onPreferenceTreeClick(preference: Preference?): Boolean { override fun onPreferenceTreeClick(preference: Preference?): Boolean {

View File

@ -35,12 +35,21 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.* import android.widget.Button
import android.widget.CheckedTextView
import android.widget.EditText
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.edit import androidx.core.content.edit
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.preference.* import androidx.preference.EditTextPreference
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceManager
import androidx.preference.SwitchPreference
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
@ -68,7 +77,19 @@ import im.vector.riotredesign.core.preference.BingRule
import im.vector.riotredesign.core.preference.ProgressBarPreference import im.vector.riotredesign.core.preference.ProgressBarPreference
import im.vector.riotredesign.core.preference.UserAvatarPreference import im.vector.riotredesign.core.preference.UserAvatarPreference
import im.vector.riotredesign.core.preference.VectorPreference import im.vector.riotredesign.core.preference.VectorPreference
import im.vector.riotredesign.core.utils.* import im.vector.riotredesign.core.utils.PERMISSIONS_FOR_WRITING_FILES
import im.vector.riotredesign.core.utils.PERMISSION_REQUEST_CODE_EXPORT_KEYS
import im.vector.riotredesign.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA
import im.vector.riotredesign.core.utils.allGranted
import im.vector.riotredesign.core.utils.checkPermissions
import im.vector.riotredesign.core.utils.copyToClipboard
import im.vector.riotredesign.core.utils.displayInWebView
import im.vector.riotredesign.core.utils.getCallRingtoneName
import im.vector.riotredesign.core.utils.getCallRingtoneUri
import im.vector.riotredesign.core.utils.openFileSelection
import im.vector.riotredesign.core.utils.setCallRingtoneUri
import im.vector.riotredesign.core.utils.setUseRiotDefaultRingtone
import im.vector.riotredesign.core.utils.toast
import im.vector.riotredesign.features.MainActivity import im.vector.riotredesign.features.MainActivity
import im.vector.riotredesign.features.configuration.VectorConfiguration import im.vector.riotredesign.features.configuration.VectorConfiguration
import im.vector.riotredesign.features.crypto.keys.KeysExporter import im.vector.riotredesign.features.crypto.keys.KeysExporter
@ -76,7 +97,6 @@ import im.vector.riotredesign.features.crypto.keys.KeysImporter
import im.vector.riotredesign.features.crypto.keysbackup.settings.KeysBackupManageActivity import im.vector.riotredesign.features.crypto.keysbackup.settings.KeysBackupManageActivity
import im.vector.riotredesign.features.themes.ThemeUtils import im.vector.riotredesign.features.themes.ThemeUtils
import im.vector.riotredesign.features.version.getVersion import im.vector.riotredesign.features.version.getVersion
import org.koin.android.ext.android.inject
import timber.log.Timber import timber.log.Timber
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.text.DateFormat import java.text.DateFormat
@ -314,7 +334,7 @@ class VectorSettingsPreferencesFragment : VectorPreferenceFragment(), SharedPref
// It does not work on XML, do it here // It does not work on XML, do it here
it.icon = activity?.let { it.icon = activity?.let {
ThemeUtils.tintDrawable(it, ThemeUtils.tintDrawable(it,
ContextCompat.getDrawable(it, R.drawable.ic_add_black)!!, R.attr.vctr_settings_icon_tint_color) ContextCompat.getDrawable(it, R.drawable.ic_add_black)!!, R.attr.vctr_settings_icon_tint_color)
} }


// Unfortunately, this is not supported in lib v7 // Unfortunately, this is not supported in lib v7
@ -331,7 +351,7 @@ class VectorSettingsPreferencesFragment : VectorPreferenceFragment(), SharedPref
// It does not work on XML, do it here // It does not work on XML, do it here
it.icon = activity?.let { it.icon = activity?.let {
ThemeUtils.tintDrawable(it, ThemeUtils.tintDrawable(it,
ContextCompat.getDrawable(it, R.drawable.ic_add_black)!!, R.attr.vctr_settings_icon_tint_color) ContextCompat.getDrawable(it, R.drawable.ic_add_black)!!, R.attr.vctr_settings_icon_tint_color)
} }


it.setOnPreferenceClickListener { it.setOnPreferenceClickListener {
@ -713,7 +733,7 @@ class VectorSettingsPreferencesFragment : VectorPreferenceFragment(), SharedPref
context?.let { context: Context -> context?.let { context: Context ->
AlertDialog.Builder(context) AlertDialog.Builder(context)
.setSingleChoiceItems(R.array.media_saving_choice, .setSingleChoiceItems(R.array.media_saving_choice,
PreferencesManager.getSelectedMediasSavingPeriod(activity)) { d, n -> PreferencesManager.getSelectedMediasSavingPeriod(activity)) { d, n ->
PreferencesManager.setSelectedMediasSavingPeriod(activity, n) PreferencesManager.setSelectedMediasSavingPeriod(activity, n)
d.cancel() d.cancel()


@ -1773,7 +1793,7 @@ class VectorSettingsPreferencesFragment : VectorPreferenceFragment(), SharedPref
mDisplayedEmails = newEmailsList mDisplayedEmails = newEmailsList


val addEmailBtn = mUserSettingsCategory.findPreference(ADD_EMAIL_PREFERENCE_KEY) val addEmailBtn = mUserSettingsCategory.findPreference(ADD_EMAIL_PREFERENCE_KEY)
?: return ?: return


var order = addEmailBtn.order var order = addEmailBtn.order


@ -2449,7 +2469,9 @@ class VectorSettingsPreferencesFragment : VectorPreferenceFragment(), SharedPref


// disable the deletion for our own device // disable the deletion for our own device
if (!TextUtils.equals(session.getMyDevice()?.deviceId, aDeviceInfo.deviceId)) { if (!TextUtils.equals(session.getMyDevice()?.deviceId, aDeviceInfo.deviceId)) {
builder.setNegativeButton(R.string.delete) { _, _ -> displayDeviceDeletionDialog(aDeviceInfo) } builder.setNegativeButton(R.string.delete) { _, _ ->
//displayDeviceDeletionDialog(aDeviceInfo)
}
} }
builder.setNeutralButton(R.string.cancel, null) builder.setNeutralButton(R.string.cancel, null)
.setOnKeyListener(DialogInterface.OnKeyListener { dialog, keyCode, event -> .setOnKeyListener(DialogInterface.OnKeyListener { dialog, keyCode, event ->
@ -2746,8 +2768,8 @@ class VectorSettingsPreferencesFragment : VectorPreferenceFragment(), SharedPref


AlertDialog.Builder(thisActivity) AlertDialog.Builder(thisActivity)
.setMessage(getString(R.string.encryption_import_room_keys_success, .setMessage(getString(R.string.encryption_import_room_keys_success,
data.successfullyNumberOfImportedKeys, data.successfullyNumberOfImportedKeys,
data.totalNumberOfKeys)) data.totalNumberOfKeys))
.setPositiveButton(R.string.ok) { dialog, _ -> dialog.dismiss() } .setPositiveButton(R.string.ok) { dialog, _ -> dialog.dismiss() }
.show() .show()
} }

View File

@ -29,14 +29,15 @@ import im.vector.riotredesign.core.platform.VectorBaseFragment
import im.vector.riotredesign.core.resources.StringProvider import im.vector.riotredesign.core.resources.StringProvider
import im.vector.riotredesign.core.ui.list.genericFooterItem import im.vector.riotredesign.core.ui.list.genericFooterItem
import kotlinx.android.synthetic.main.fragment_generic_recycler_epoxy.* import kotlinx.android.synthetic.main.fragment_generic_recycler_epoxy.*
import javax.inject.Inject


// Referenced in vector_settings_notifications.xml // Referenced in vector_settings_notifications.xml
class PushGatewaysFragment : VectorBaseFragment() { class PushGatewaysFragment : VectorBaseFragment() {


override fun getLayoutResId(): Int = R.layout.fragment_generic_recycler_epoxy override fun getLayoutResId(): Int = R.layout.fragment_generic_recycler_epoxy


@Inject lateinit var pushGatewaysViewModelFactory: PushGatewaysViewModel.Factory
private val viewModel: PushGatewaysViewModel by fragmentViewModel(PushGatewaysViewModel::class) private val viewModel: PushGatewaysViewModel by fragmentViewModel(PushGatewaysViewModel::class)

private val epoxyController by lazy { PushGateWayController(StringProvider(requireContext().resources)) } private val epoxyController by lazy { PushGateWayController(StringProvider(requireContext().resources)) }


override fun onResume() { override fun onResume() {

View File

@ -16,38 +16,50 @@


package im.vector.riotredesign.features.settings.push package im.vector.riotredesign.features.settings.push


import androidx.lifecycle.Observer import com.airbnb.mvrx.Async
import com.airbnb.mvrx.* import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.pushers.Pusher import im.vector.matrix.android.api.session.pushers.Pusher
import im.vector.matrix.rx.RxSession
import im.vector.riotredesign.core.platform.VectorViewModel import im.vector.riotredesign.core.platform.VectorViewModel
import org.koin.android.ext.android.get




data class PushGatewayViewState( data class PushGatewayViewState(
val pushGateways: Async<List<Pusher>> = Uninitialized val pushGateways: Async<List<Pusher>> = Uninitialized
) : MvRxState ) : MvRxState


class PushGatewaysViewModel(initialState: PushGatewayViewState) : VectorViewModel<PushGatewayViewState>(initialState) { class PushGatewaysViewModel @AssistedInject constructor(@Assisted initialState: PushGatewayViewState,
private val session: Session) : VectorViewModel<PushGatewayViewState>(initialState) {

@AssistedInject.Factory
interface Factory {
fun create(initialState: PushGatewayViewState): PushGatewaysViewModel
}


companion object : MvRxViewModelFactory<PushGatewaysViewModel, PushGatewayViewState> { companion object : MvRxViewModelFactory<PushGatewaysViewModel, PushGatewayViewState> {


override fun create(viewModelContext: ViewModelContext, state: PushGatewayViewState): PushGatewaysViewModel? { override fun create(viewModelContext: ViewModelContext, state: PushGatewayViewState): PushGatewaysViewModel? {
val session = viewModelContext.activity.get<Session>() val fragment: PushGatewaysFragment = (viewModelContext as FragmentViewModelContext).fragment()
val fragment = (viewModelContext as FragmentViewModelContext).fragment return fragment.pushGatewaysViewModelFactory.create(state)

val livePushers = session.livePushers()

val viewModel = PushGatewaysViewModel(state)

livePushers.observe(fragment, Observer {
viewModel.setState {
PushGatewayViewState(Success(it))
}
})
return viewModel
} }
}


init {
observePushers()
}

private fun observePushers() {
RxSession(session)
.livePushers()
.execute {
copy(pushGateways = it)
}
} }


} }

View File

@ -19,23 +19,20 @@ import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext import com.airbnb.mvrx.ViewModelContext
import im.vector.matrix.android.api.pushrules.rest.PushRule import im.vector.matrix.android.api.pushrules.rest.PushRule
import im.vector.matrix.android.api.session.Session import im.vector.riotredesign.core.di.HasScreenInjector
import im.vector.riotredesign.core.platform.VectorViewModel import im.vector.riotredesign.core.platform.VectorViewModel
import org.koin.android.ext.android.get


data class PushRulesViewState( data class PushRulesViewState(
val rules: List<PushRule> = emptyList() val rules: List<PushRule> = emptyList()
) : MvRxState ) : MvRxState



class PushRulesViewModel(initialState: PushRulesViewState) : VectorViewModel<PushRulesViewState>(initialState) { class PushRulesViewModel(initialState: PushRulesViewState) : VectorViewModel<PushRulesViewState>(initialState) {


companion object : MvRxViewModelFactory<PushRulesViewModel, PushRulesViewState> { companion object : MvRxViewModelFactory<PushRulesViewModel, PushRulesViewState> {


override fun initialState(viewModelContext: ViewModelContext): PushRulesViewState? { override fun initialState(viewModelContext: ViewModelContext): PushRulesViewState? {
val session = viewModelContext.activity.get<Session>() val session = (viewModelContext.activity as HasScreenInjector).injector().session()
val rules = session.getPushRules() val rules = session.getPushRules()

return PushRulesViewState(rules) return PushRulesViewState(rules)
} }