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.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.sync.SyncState
import io.reactivex.Observable
@ -36,6 +37,10 @@ class RxSession(private val session: Session) {
return session.syncState().asObservable()
}

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

}

fun Session.rx(): RxSession {

View File

@ -54,6 +54,10 @@ interface Session :
*/
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.
*/

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.internal.crypto.CryptoModule
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.content.ContentModule
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.signout.SignOutModule
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.user.UserModule
import im.vector.matrix.android.internal.task.TaskExecutor

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

fun session(): Session

fun syncTask(): SyncTask

fun syncTokenStore(): SyncTokenStore

fun networkConnectivityChecker(): NetworkConnectivityChecker

fun taskExecutor(): TaskExecutor

fun inject(sendEventWorker: SendEventWorker)

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

fun inject(addHttpPusherWorker: AddHttpPusherWorker)


@Component.Factory
interface Factory {
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.PusherEntityFields
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.UpdatePushRuleEnableStatusTask
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 javax.inject.Inject


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


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


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

View File

@ -19,7 +19,9 @@ import android.app.Service
import android.content.Intent
import android.os.Binder
import android.os.IBinder
import androidx.work.ListenableWorker
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.failure.Failure
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.TaskThread
import im.vector.matrix.android.internal.task.configureWith
import im.vector.matrix.android.internal.worker.getSessionComponent
import timber.log.Timber
import java.net.SocketTimeoutException
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.
* 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 cancelableTask: Cancelable? = null

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

private var localBinder = LocalBinder()

@ -68,6 +71,12 @@ internal open class SyncService : Service() {
nextBatchDelay = 60_000L
timeout = 0
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) {
timer.cancel()
timer = Timer()
@ -191,7 +200,6 @@ internal open class SyncService : Service() {
}

internal fun notifySyncFinish() {

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

companion object {

const val EXTRA_USER_ID = "EXTRA_USER_ID"

fun startLongPool(delay: Long) {

}

View File

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

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

override fun onReceive(context: Context, intent: Intent) {
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)
fun entersForeground() {
AlarmSyncBroadcastReceiver.cancelAlarm(appContext)
activeSessionHolder.getActiveSession().also {
activeSessionHolder.getSafeActiveSession()?.also {
it.stopAnyBackgroundSync()
}
}
@ -128,8 +128,9 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
} else {
//TODO check if notifications are enabled for this device
//We need to use alarm in this mode
if (PreferencesManager.areNotificationEnabledForDevice(applicationContext)) {
AlarmSyncBroadcastReceiver.scheduleAlarm(applicationContext, 4_000L)
val activeSession = activeSessionHolder.getSafeActiveSession()
if (activeSession != null && PreferencesManager.areNotificationEnabledForDevice(applicationContext)) {
AlarmSyncBroadcastReceiver.scheduleAlarm(applicationContext, activeSession.myUserId, 4_000L)
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.roompreview.RoomPreviewNoPreviewFragment
import im.vector.riotredesign.features.settings.VectorSettingsActivity
import im.vector.riotredesign.features.settings.VectorSettingsNotificationPreferenceFragment
import im.vector.riotredesign.features.settings.VectorSettingsPreferencesFragment

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

fun inject(videoMediaViewerActivity: VideoMediaViewerActivity)

fun inject(vectorSettingsNotificationPreferenceFragment: VectorSettingsNotificationPreferenceFragment)

@Component.Factory
interface Factory {
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.group.SelectedGroupStore
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.PushRuleTriggerListener
import im.vector.riotredesign.features.rageshake.BugReporter
import im.vector.riotredesign.features.rageshake.RageShake
import im.vector.riotredesign.features.rageshake.VectorUncaughtExceptionHandler
@ -43,6 +45,8 @@ import javax.inject.Singleton
@Singleton
interface VectorComponent {

fun inject(vectorApplication: NotificationBroadcastReceiver)

fun inject(vectorApplication: VectorApplication)

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

fun vectorUncaughtExceptionHandler(): VectorUncaughtExceptionHandler

fun pushRuleTriggerListener(): PushRuleTriggerListener

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

View File

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

@Module
interface ViewModelModule {

@Binds
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.LocaleProvider
import im.vector.riotredesign.core.resources.StringProvider
import javax.inject.Inject

private const val DEFAULT_PUSHER_FILE_TAG = "mobile"

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

View File

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

import android.content.Context
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 {
try {

View File

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

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.
Timber.d("RestartBroadcastReceiver received intent")
Intent(context, VectorSyncService::class.java).also {
it.action = "SLOW"
it.putExtra(SyncService.EXTRA_USER_ID, userId)
context.startService(it)
try {
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")
}
@ -48,11 +50,13 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
companion object {
const val REQUEST_CODE = 0

fun scheduleAlarm(context: Context, delay: Long) {
fun scheduleAlarm(context: Context, userId: String, delay: Long) {
//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,
intent, PendingIntent.FLAG_UPDATE_CURRENT)
intent, PendingIntent.FLAG_UPDATE_CURRENT)
val firstMillis = System.currentTimeMillis() + delay
val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
@ -65,7 +69,7 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
fun cancelAlarm(context: Context) {
val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java)
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
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.session.Session
import im.vector.riotredesign.features.navigation.Navigator
import javax.inject.Inject

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

fun launch(context: Context, deepLink: String?, navigateToRoomInterceptor: NavigateToRoomInterceptor? = null): Boolean {
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.util.MessageInformationDataFactory
import me.gujun.android.span.span
import javax.inject.Inject

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

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

View File

@ -30,6 +30,7 @@ import im.vector.riotredesign.R
import im.vector.riotredesign.core.resources.StringProvider
import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter
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.
@ -37,8 +38,8 @@ import timber.log.Timber
* 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.
*/
class NotifiableEventResolver(private val stringProvider: StringProvider,
private val noticeEventFormatter: NoticeEventFormatter) {
class NotifiableEventResolver @Inject constructor(private val stringProvider: StringProvider,
private val noticeEventFormatter: NoticeEventFormatter) {

//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
val body =
event.annotations?.editSummary?.aggregatedContent?.toModel<MessageContent>()?.body
?: event.root.getClearContent().toModel<MessageContent>()?.body
?: stringProvider.getString(R.string.notification_unknown_new_event)
?: event.root.getClearContent().toModel<MessageContent>()?.body
?: stringProvider.getString(R.string.notification_unknown_new_event)
val roomName = stringProvider.getString(R.string.notification_unknown_room_name)
val senderDisplayName = event.senderName

@ -114,8 +115,8 @@ class NotifiableEventResolver(private val stringProvider: StringProvider,
return notifiableEvent
} else {
val body = event.annotations?.editSummary?.aggregatedContent?.toModel<MessageContent>()?.body
?: event.root.getClearContent().toModel<MessageContent>()?.body
?: stringProvider.getString(R.string.notification_unknown_new_event)
?: event.root.getClearContent().toModel<MessageContent>()?.body
?: stringProvider.getString(R.string.notification_unknown_new_event)
val roomName = room.roomSummary?.displayName ?: ""
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)
notifiableEvent.roomAvatarPath = session.contentUrlResolver()
.resolveThumbnail(room.roomSummary?.avatarUrl,
250,
250,
ContentUrlResolver.ThumbnailMethod.SCALE)
250,
250,
ContentUrlResolver.ThumbnailMethod.SCALE)

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

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

View File

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


override fun onReceive(context: Context?, intent: Intent?) {
if (intent == null || context == null) return
Timber.v("NotificationBroadcastReceiver received : $intent")

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

private fun handleMarkAsRead(context: Context, roomId: String) {
activeSessionHolder.getActiveSession().let { session ->
activeSessionHolder.getActiveSession().let { session ->
session.getRoom(roomId)
?.markAllAsRead(object : MatrixCallback<Unit> {})
}
@ -95,7 +99,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
false,
System.currentTimeMillis(),
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,
message,
room.roomId,

View File

@ -15,10 +15,10 @@
*/
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

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

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

View File

@ -16,32 +16,43 @@

package im.vector.riotredesign.features.settings

import android.content.Context
import android.os.Bundle
import androidx.preference.Preference
import androidx.preference.SwitchPreference
import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.MatrixCallback
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.pushers.PushersManager
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
class VectorSettingsNotificationPreferenceFragment : VectorPreferenceFragment() {


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?) {
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() {
super.onResume()
Matrix.getInstance().currentSession?.refreshPushers()
activeSessionHolder.getSafeActiveSession()?.refreshPushers()
}

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

View File

@ -35,12 +35,21 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
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.core.content.ContextCompat
import androidx.core.content.edit
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.material.textfield.TextInputEditText
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.UserAvatarPreference
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.configuration.VectorConfiguration
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.themes.ThemeUtils
import im.vector.riotredesign.features.version.getVersion
import org.koin.android.ext.android.inject
import timber.log.Timber
import java.lang.ref.WeakReference
import java.text.DateFormat
@ -314,7 +334,7 @@ class VectorSettingsPreferencesFragment : VectorPreferenceFragment(), SharedPref
// It does not work on XML, do it here
it.icon = activity?.let {
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
@ -331,7 +351,7 @@ class VectorSettingsPreferencesFragment : VectorPreferenceFragment(), SharedPref
// It does not work on XML, do it here
it.icon = activity?.let {
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 {
@ -713,7 +733,7 @@ class VectorSettingsPreferencesFragment : VectorPreferenceFragment(), SharedPref
context?.let { context: Context ->
AlertDialog.Builder(context)
.setSingleChoiceItems(R.array.media_saving_choice,
PreferencesManager.getSelectedMediasSavingPeriod(activity)) { d, n ->
PreferencesManager.getSelectedMediasSavingPeriod(activity)) { d, n ->
PreferencesManager.setSelectedMediasSavingPeriod(activity, n)
d.cancel()

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

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

var order = addEmailBtn.order

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

// disable the deletion for our own device
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)
.setOnKeyListener(DialogInterface.OnKeyListener { dialog, keyCode, event ->
@ -2746,8 +2768,8 @@ class VectorSettingsPreferencesFragment : VectorPreferenceFragment(), SharedPref

AlertDialog.Builder(thisActivity)
.setMessage(getString(R.string.encryption_import_room_keys_success,
data.successfullyNumberOfImportedKeys,
data.totalNumberOfKeys))
data.successfullyNumberOfImportedKeys,
data.totalNumberOfKeys))
.setPositiveButton(R.string.ok) { dialog, _ -> dialog.dismiss() }
.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.ui.list.genericFooterItem
import kotlinx.android.synthetic.main.fragment_generic_recycler_epoxy.*
import javax.inject.Inject

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

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 epoxyController by lazy { PushGateWayController(StringProvider(requireContext().resources)) }

override fun onResume() {

View File

@ -16,38 +16,50 @@

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

import androidx.lifecycle.Observer
import com.airbnb.mvrx.*
import com.airbnb.mvrx.Async
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.pushers.Pusher
import im.vector.matrix.rx.RxSession
import im.vector.riotredesign.core.platform.VectorViewModel
import org.koin.android.ext.android.get


data class PushGatewayViewState(
val pushGateways: Async<List<Pusher>> = Uninitialized
) : 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> {

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

val livePushers = session.livePushers()

val viewModel = PushGatewaysViewModel(state)

livePushers.observe(fragment, Observer {
viewModel.setState {
PushGatewayViewState(Success(it))
}
})
return viewModel
val fragment: PushGatewaysFragment = (viewModelContext as FragmentViewModelContext).fragment()
return fragment.pushGatewaysViewModelFactory.create(state)
}
}

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.ViewModelContext
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 org.koin.android.ext.android.get

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


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

companion object : MvRxViewModelFactory<PushRulesViewModel, 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()

return PushRulesViewState(rules)
}