forked from GitHub-Mirror/riotX-android
Basic FCM vs fdroid mode
This commit is contained in:
@ -2,14 +2,24 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="im.vector.riotredesign">
|
||||
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
|
||||
<application>
|
||||
|
||||
<receiver android:name=".receiver.OnApplicationUpgradeReceiver">
|
||||
<receiver android:name=".receiver.OnApplicationUpgradeOrRebootReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name=".core.services.AlarmSyncBroadcastReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="false">
|
||||
</receiver>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -14,24 +14,24 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package im.vector.riotredesign.push.fcm;
|
||||
package im.vector.riotredesign.push.fcm
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import im.vector.riotredesign.core.pushers.PushersManager
|
||||
|
||||
public class FcmHelper {
|
||||
object FcmHelper {
|
||||
|
||||
fun isPushSupported(): Boolean = false
|
||||
|
||||
/**
|
||||
* Retrieves the FCM registration token.
|
||||
*
|
||||
* @return the FCM token or null if not received from FCM
|
||||
*/
|
||||
@Nullable
|
||||
public static String getFcmToken(Context context) {
|
||||
return null;
|
||||
fun getFcmToken(context: Context): String? {
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,8 +40,7 @@ public class FcmHelper {
|
||||
* @param context android context
|
||||
* @param token the token to store
|
||||
*/
|
||||
public static void storeFcmToken(@NonNull Context context,
|
||||
@Nullable String token) {
|
||||
fun storeFcmToken(context: Context, token: String?) {
|
||||
// No op
|
||||
}
|
||||
|
||||
@ -50,7 +49,7 @@ public class FcmHelper {
|
||||
*
|
||||
* @param activity the first launch Activity
|
||||
*/
|
||||
public static void ensureFcmTokenIsRetrieved(final Activity activity, PushersManager pushersManager) {
|
||||
fun ensureFcmTokenIsRetrieved(activity: Activity, pushersManager: PushersManager) {
|
||||
// No op
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2018 New Vector Ltd
|
||||
* 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.
|
||||
@ -14,21 +15,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotredesign.receiver;
|
||||
package im.vector.riotredesign.receiver
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import im.vector.riotredesign.core.services.AlarmSyncBroadcastReceiver
|
||||
import timber.log.Timber
|
||||
|
||||
import timber.log.Timber;
|
||||
class OnApplicationUpgradeOrRebootReceiver : BroadcastReceiver() {
|
||||
|
||||
public class OnApplicationUpgradeReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Timber.v("## onReceive() : Application has been upgraded, restart event stream service.");
|
||||
|
||||
// Start Event stream
|
||||
// TODO EventStreamServiceX.Companion.onApplicationUpgrade(context);
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
Timber.v("## onReceive() ${intent.action}")
|
||||
AlarmSyncBroadcastReceiver.scheduleAlarm(context, 10)
|
||||
}
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014 OpenMarket Ltd
|
||||
* Copyright 2017 Vector Creations Ltd
|
||||
* Copyright 2018 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.push.fcm;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.GoogleApiAvailability;
|
||||
import com.google.firebase.iid.FirebaseInstanceId;
|
||||
|
||||
import im.vector.riotredesign.R;
|
||||
import im.vector.riotredesign.core.pushers.PushersManager;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* This class store the FCM token in SharedPrefs and ensure this token is retrieved.
|
||||
* It has an alter ego in the fdroid variant.
|
||||
*/
|
||||
public class FcmHelper {
|
||||
private static final String LOG_TAG = FcmHelper.class.getSimpleName();
|
||||
|
||||
private static final String PREFS_KEY_FCM_TOKEN = "FCM_TOKEN";
|
||||
|
||||
/**
|
||||
* Retrieves the FCM registration token.
|
||||
*
|
||||
* @return the FCM token or null if not received from FCM
|
||||
*/
|
||||
@Nullable
|
||||
public static String getFcmToken(Context context) {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context).getString(PREFS_KEY_FCM_TOKEN, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store FCM token to the SharedPrefs
|
||||
*
|
||||
* @param context android context
|
||||
* @param token the token to store
|
||||
*/
|
||||
public static void storeFcmToken(@NonNull Context context,
|
||||
@Nullable String token) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.edit()
|
||||
.putString(PREFS_KEY_FCM_TOKEN, token)
|
||||
.apply();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* onNewToken may not be called on application upgrade, so ensure my shared pref is set
|
||||
*
|
||||
* @param activity the first launch Activity
|
||||
*/
|
||||
public static void ensureFcmTokenIsRetrieved(final Activity activity, PushersManager pushersManager) {
|
||||
// if (TextUtils.isEmpty(getFcmToken(activity))) {
|
||||
|
||||
|
||||
//vfe: according to firebase doc
|
||||
//'app should always check the device for a compatible Google Play services APK before accessing Google Play services features'
|
||||
if (checkPlayServices(activity)) {
|
||||
try {
|
||||
FirebaseInstanceId.getInstance().getInstanceId()
|
||||
.addOnSuccessListener(activity, instanceIdResult -> {
|
||||
storeFcmToken(activity, instanceIdResult.getToken());
|
||||
pushersManager.registerPusherWithFcmKey(instanceIdResult.getToken());
|
||||
})
|
||||
.addOnFailureListener(activity, e -> Timber.e(e, "## ensureFcmTokenIsRetrieved() : failed " + e.getMessage()));
|
||||
} catch (Throwable e) {
|
||||
Timber.e(e, "## ensureFcmTokenIsRetrieved() : failed " + e.getMessage());
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(activity, R.string.no_valid_google_play_services_apk, Toast.LENGTH_SHORT).show();
|
||||
Timber.e("No valid Google Play Services found. Cannot use FCM.");
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the device to make sure it has the Google Play Services APK. If
|
||||
* it doesn't, display a dialog that allows users to download the APK from
|
||||
* the Google Play Store or enable it in the device's system settings.
|
||||
*/
|
||||
private static boolean checkPlayServices(Activity activity) {
|
||||
GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
|
||||
int resultCode = apiAvailability.isGooglePlayServicesAvailable(activity);
|
||||
if (resultCode != ConnectionResult.SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
103
vector/src/gplay/java/im/vector/riotredesign/push/fcm/FcmHelper.kt
Executable file
103
vector/src/gplay/java/im/vector/riotredesign/push/fcm/FcmHelper.kt
Executable file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2014 OpenMarket Ltd
|
||||
* Copyright 2017 Vector Creations Ltd
|
||||
* Copyright 2018 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.push.fcm
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.preference.PreferenceManager
|
||||
import android.widget.Toast
|
||||
import com.google.android.gms.common.ConnectionResult
|
||||
import com.google.android.gms.common.GoogleApiAvailability
|
||||
import com.google.firebase.iid.FirebaseInstanceId
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.pushers.PushersManager
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* This class store the FCM token in SharedPrefs and ensure this token is retrieved.
|
||||
* It has an alter ego in the fdroid variant.
|
||||
*/
|
||||
object FcmHelper {
|
||||
private val LOG_TAG = FcmHelper::class.java.simpleName
|
||||
|
||||
private val PREFS_KEY_FCM_TOKEN = "FCM_TOKEN"
|
||||
|
||||
|
||||
fun isPushSupported(): Boolean = true
|
||||
|
||||
/**
|
||||
* Retrieves the FCM registration token.
|
||||
*
|
||||
* @return the FCM token or null if not received from FCM
|
||||
*/
|
||||
fun getFcmToken(context: Context): String? {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context).getString(PREFS_KEY_FCM_TOKEN, null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Store FCM token to the SharedPrefs
|
||||
*
|
||||
* @param context android context
|
||||
* @param token the token to store
|
||||
*/
|
||||
fun storeFcmToken(context: Context,
|
||||
token: String?) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.edit()
|
||||
.putString(PREFS_KEY_FCM_TOKEN, token)
|
||||
.apply()
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* onNewToken may not be called on application upgrade, so ensure my shared pref is set
|
||||
*
|
||||
* @param activity the first launch Activity
|
||||
*/
|
||||
fun ensureFcmTokenIsRetrieved(activity: Activity, pushersManager: PushersManager) {
|
||||
// if (TextUtils.isEmpty(getFcmToken(activity))) {
|
||||
//'app should always check the device for a compatible Google Play services APK before accessing Google Play services features'
|
||||
if (checkPlayServices(activity)) {
|
||||
try {
|
||||
FirebaseInstanceId.getInstance().instanceId
|
||||
.addOnSuccessListener(activity) { instanceIdResult ->
|
||||
storeFcmToken(activity, instanceIdResult.token)
|
||||
pushersManager.registerPusherWithFcmKey(instanceIdResult.token)
|
||||
}
|
||||
.addOnFailureListener(activity) { e -> Timber.e(e, "## ensureFcmTokenIsRetrieved() : failed " + e.message) }
|
||||
} catch (e: Throwable) {
|
||||
Timber.e(e, "## ensureFcmTokenIsRetrieved() : failed " + e.message)
|
||||
}
|
||||
|
||||
} else {
|
||||
Toast.makeText(activity, R.string.no_valid_google_play_services_apk, Toast.LENGTH_SHORT).show()
|
||||
Timber.e("No valid Google Play Services found. Cannot use FCM.")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the device to make sure it has the Google Play Services APK. If
|
||||
* it doesn't, display a dialog that allows users to download the APK from
|
||||
* the Google Play Store or enable it in the device's system settings.
|
||||
*/
|
||||
private fun checkPlayServices(activity: Activity): Boolean {
|
||||
val apiAvailability = GoogleApiAvailability.getInstance()
|
||||
val resultCode = apiAvailability.isGooglePlayServicesAvailable(activity)
|
||||
return resultCode == ConnectionResult.SUCCESS
|
||||
}
|
||||
}
|
@ -144,14 +144,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
||||
Timber.i("Ignoring push, event already knwown")
|
||||
} else {
|
||||
Timber.v("Requesting background sync")
|
||||
val workRequest = OneTimeWorkRequestBuilder<SyncWorker>()
|
||||
.setInputData(Data.Builder().put("timeout", 0L).build())
|
||||
.setConstraints(Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build())
|
||||
.setBackoffCriteria(BackoffPolicy.LINEAR, 10_000, TimeUnit.MILLISECONDS)
|
||||
.build()
|
||||
WorkManager.getInstance().enqueueUniqueWork("BG_SYNCP", ExistingWorkPolicy.REPLACE, workRequest)
|
||||
session.requireBackgroundSync(0L)
|
||||
}
|
||||
}
|
||||
|
||||
@ -214,7 +207,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
||||
isPushGatewayEvent = true
|
||||
)
|
||||
notificationDrawerManager.onNotifiableEventReceived(simpleNotifiableEvent)
|
||||
notificationDrawerManager.refreshNotificationDrawer(null)
|
||||
notificationDrawerManager.refreshNotificationDrawer()
|
||||
|
||||
return
|
||||
} else {
|
||||
@ -249,7 +242,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
||||
notifiableEvent.isPushGatewayEvent = true
|
||||
notifiableEvent.matrixID = session.sessionParams.credentials.userId
|
||||
notificationDrawerManager.onNotifiableEventReceived(notifiableEvent)
|
||||
notificationDrawerManager.refreshNotificationDrawer(null)
|
||||
notificationDrawerManager.refreshNotificationDrawer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,12 +15,6 @@
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme.Light"
|
||||
tools:replace="android:allowBackup">
|
||||
<receiver
|
||||
android:name=".core.services.RestartBroadcastReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="false">
|
||||
|
||||
</receiver>
|
||||
|
||||
<activity
|
||||
android:name=".features.MainActivity"
|
||||
|
@ -34,7 +34,6 @@ import androidx.lifecycle.LifecycleObserver
|
||||
import androidx.lifecycle.OnLifecycleEvent
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import androidx.multidex.MultiDex
|
||||
import androidx.work.*
|
||||
import com.airbnb.epoxy.EpoxyAsyncUtil
|
||||
import com.airbnb.epoxy.EpoxyController
|
||||
import com.facebook.stetho.Stetho
|
||||
@ -43,9 +42,8 @@ import com.github.piasy.biv.loader.glide.GlideImageLoader
|
||||
import com.jakewharton.threetenabp.AndroidThreeTen
|
||||
import im.vector.matrix.android.api.Matrix
|
||||
import im.vector.matrix.android.internal.session.sync.job.SyncService
|
||||
import im.vector.matrix.android.internal.session.sync.job.SyncWorker
|
||||
import im.vector.riotredesign.core.di.AppModule
|
||||
import im.vector.riotredesign.core.services.RestartBroadcastReceiver
|
||||
import im.vector.riotredesign.core.services.AlarmSyncBroadcastReceiver
|
||||
import im.vector.riotredesign.core.services.VectorSyncService
|
||||
import im.vector.riotredesign.features.configuration.VectorConfiguration
|
||||
import im.vector.riotredesign.features.crypto.keysbackup.KeysBackupModule
|
||||
@ -57,6 +55,7 @@ import im.vector.riotredesign.features.rageshake.VectorFileLogger
|
||||
import im.vector.riotredesign.features.rageshake.VectorUncaughtExceptionHandler
|
||||
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryModule
|
||||
import im.vector.riotredesign.features.version.getVersion
|
||||
import im.vector.riotredesign.push.fcm.FcmHelper
|
||||
import org.koin.android.ext.android.get
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.koin.log.EmptyLogger
|
||||
@ -66,7 +65,7 @@ import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class VectorApplication : Application(), SyncService.SyncListener {
|
||||
class VectorApplication : Application() {
|
||||
|
||||
|
||||
lateinit var appContext: Context
|
||||
@ -75,26 +74,6 @@ class VectorApplication : Application(), SyncService.SyncListener {
|
||||
|
||||
val vectorConfiguration: VectorConfiguration by inject()
|
||||
|
||||
private var mBinder: SyncService.LocalBinder? = null
|
||||
|
||||
private val connection = object : ServiceConnection {
|
||||
override fun onServiceDisconnected(name: ComponentName?) {
|
||||
Timber.v("Service unbounded")
|
||||
mBinder?.removeListener(this@VectorApplication)
|
||||
mBinder = null
|
||||
}
|
||||
|
||||
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
||||
Timber.v("Service bounded")
|
||||
mBinder = service as SyncService.LocalBinder
|
||||
mBinder?.addListener(this@VectorApplication)
|
||||
mBinder?.getService()?.nextBatchDelay = 0
|
||||
mBinder?.getService()?.timeout = 10_000L
|
||||
mBinder?.getService()?.doSync()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// var slowMode = false
|
||||
|
||||
|
||||
@ -141,92 +120,26 @@ class VectorApplication : Application(), SyncService.SyncListener {
|
||||
|
||||
ProcessLifecycleOwner.get().lifecycle.addObserver(object : LifecycleObserver {
|
||||
|
||||
fun cancelAlarm() {
|
||||
val intent = Intent(applicationContext, RestartBroadcastReceiver::class.java)
|
||||
val pIntent = PendingIntent.getBroadcast(applicationContext, RestartBroadcastReceiver.REQUEST_CODE,
|
||||
intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val alarm = getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
alarm.cancel(pIntent)
|
||||
}
|
||||
|
||||
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
||||
fun entersForeground() {
|
||||
// HttpLongPoolingSyncService.startService(applicationContext)
|
||||
// cancelAlarm()
|
||||
if (Matrix.getInstance().currentSession == null) return
|
||||
WorkManager.getInstance().cancelAllWorkByTag("BG_SYNC")
|
||||
Intent(applicationContext, VectorSyncService::class.java).also { intent ->
|
||||
// intent.action = "NORMAL"
|
||||
// try {
|
||||
// startService(intent)
|
||||
// } catch (e: Throwable) {
|
||||
// Timber.e("Failed to launch sync service")
|
||||
// }
|
||||
bindService(intent, connection, Context.BIND_AUTO_CREATE)
|
||||
|
||||
AlarmSyncBroadcastReceiver.cancelAlarm(appContext)
|
||||
Matrix.getInstance().currentSession?.also {
|
||||
it.stopAnyBackgroundSync()
|
||||
}
|
||||
}
|
||||
|
||||
var isPushAvailable = true
|
||||
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
|
||||
fun entersBackground() {
|
||||
Timber.i("App entered background")
|
||||
//we have here 3 modes
|
||||
|
||||
if (isPushAvailable) {
|
||||
// PUSH IS AVAILABLE:
|
||||
// Just stop the service, we will sync when a notification is received
|
||||
try {
|
||||
unbindService(connection)
|
||||
mBinder?.getService()?.stopMe()
|
||||
mBinder = null
|
||||
} catch (t: Throwable) {
|
||||
Timber.e(t)
|
||||
}
|
||||
if (FcmHelper.isPushSupported()) {
|
||||
//TODO FCM fallback
|
||||
} else {
|
||||
|
||||
// NO PUSH, and don't care about battery
|
||||
// unbindService(connection)
|
||||
// mBinder?.getService()?.stopMe()// kill also
|
||||
// mBinder = null
|
||||
//In this case we will keep a permanent
|
||||
|
||||
//TODO if no push schedule reccuring alarm
|
||||
|
||||
// val workRequest = PeriodicWorkRequestBuilder<SyncWorker>(1, TimeUnit.MINUTES)
|
||||
// .setConstraints(Constraints.Builder()
|
||||
// .setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
// .build())
|
||||
// .setBackoffCriteria(BackoffPolicy.LINEAR, 10_000, TimeUnit.MILLISECONDS)
|
||||
// .build()
|
||||
// WorkManager.getInstance().enqueueUniquePeriodicWork(
|
||||
// "BG_SYNC",
|
||||
// ExistingPeriodicWorkPolicy.KEEP,
|
||||
// workRequest)
|
||||
val workRequest = OneTimeWorkRequestBuilder<SyncWorker>()
|
||||
// .setInitialDelay(30_000, TimeUnit.MILLISECONDS)
|
||||
.setInputData(Data.Builder().put("timeout", 0L).build())
|
||||
.setConstraints(Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build())
|
||||
.setBackoffCriteria(BackoffPolicy.LINEAR, 10_000, TimeUnit.MILLISECONDS)
|
||||
.build()
|
||||
WorkManager.getInstance().enqueueUniqueWork("BG_SYNCP", ExistingWorkPolicy.REPLACE, workRequest)
|
||||
|
||||
// val intent = Intent(applicationContext, RestartBroadcastReceiver::class.java)
|
||||
// // Create a PendingIntent to be triggered when the alarm goes off
|
||||
// val pIntent = PendingIntent.getBroadcast(applicationContext, RestartBroadcastReceiver.REQUEST_CODE,
|
||||
// intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
// // Setup periodic alarm every every half hour from this point onwards
|
||||
// val firstMillis = System.currentTimeMillis(); // alarm is set right away
|
||||
// val alarmMgr = getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
// // First parameter is the type: ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP, RTC_WAKEUP
|
||||
// // Interval can be INTERVAL_FIFTEEN_MINUTES, INTERVAL_HALF_HOUR, INTERVAL_HOUR, INTERVAL_DAY
|
||||
//// alarm.setInexactRepeating(AlarmManager.RTC_WAKEUP, firstMillis,
|
||||
//// 30_000L, pIntent)
|
||||
// alarmMgr.set(AlarmManager.RTC_WAKEUP, firstMillis, pIntent);
|
||||
|
||||
//TODO check if notifications are enabled for this device
|
||||
//We need to use alarm in this mode
|
||||
AlarmSyncBroadcastReceiver.scheduleAlarm(applicationContext,4_000L)
|
||||
Timber.i("Alarm scheduled to restart service")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,36 +186,4 @@ class VectorApplication : Application(), SyncService.SyncListener {
|
||||
return mFontThreadHandler!!
|
||||
}
|
||||
|
||||
override fun onSyncFinsh() {
|
||||
//in foreground sync right now!!
|
||||
Timber.v("Sync just finished")
|
||||
// mBinder?.getService()?.doSync()
|
||||
}
|
||||
|
||||
override fun networkNotAvailable() {
|
||||
//we then want to retry in 10s?
|
||||
}
|
||||
|
||||
override fun onFailed(failure: Throwable) {
|
||||
//stop it also?
|
||||
// if (failure is Failure.NetworkConnection
|
||||
// && failure.cause is SocketTimeoutException) {
|
||||
// // Timeout are not critical just retry?
|
||||
// //TODO
|
||||
// }
|
||||
//
|
||||
// if (failure !is Failure.NetworkConnection
|
||||
// || failure.cause is JsonEncodingException) {
|
||||
// //TODO Retry in 10S?
|
||||
// }
|
||||
//
|
||||
// if (failure is Failure.ServerError
|
||||
// && (failure.error.code == MatrixError.UNKNOWN_TOKEN || failure.error.code == MatrixError.MISSING_TOKEN)) {
|
||||
// // No token or invalid token, stop the thread
|
||||
// mBinder?.getService()?.unbindService(connection)
|
||||
// mBinder?.getService()?.stopMe()
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -37,6 +37,7 @@ import im.vector.riotredesign.features.navigation.DefaultNavigator
|
||||
import im.vector.riotredesign.features.navigation.Navigator
|
||||
import im.vector.riotredesign.features.notifications.NotifiableEventResolver
|
||||
import im.vector.riotredesign.features.notifications.NotificationDrawerManager
|
||||
import im.vector.riotredesign.features.notifications.OutdatedEventDetector
|
||||
import im.vector.riotredesign.features.notifications.PushRuleTriggerListener
|
||||
import org.koin.dsl.module.module
|
||||
|
||||
@ -85,11 +86,15 @@ class AppModule(private val context: Context) {
|
||||
}
|
||||
|
||||
single {
|
||||
PushRuleTriggerListener(get(),get())
|
||||
PushRuleTriggerListener(get(), get())
|
||||
}
|
||||
|
||||
single {
|
||||
NotificationDrawerManager(context)
|
||||
OutdatedEventDetector(context)
|
||||
}
|
||||
|
||||
single {
|
||||
NotificationDrawerManager(context, get())
|
||||
}
|
||||
|
||||
single {
|
||||
|
@ -0,0 +1,74 @@
|
||||
package im.vector.riotredesign.core.services
|
||||
|
||||
import android.app.AlarmManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import android.os.PowerManager
|
||||
import androidx.core.content.ContextCompat
|
||||
import timber.log.Timber
|
||||
|
||||
class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
|
||||
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
|
||||
//Aquire a lock to give enough time for the sync :/
|
||||
(context.getSystemService(Context.POWER_SERVICE) as PowerManager).run {
|
||||
newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "riotx:fdroidSynclock").apply {
|
||||
acquire((10_000).toLong())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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"
|
||||
context.startService(it)
|
||||
try {
|
||||
if (SDK_INT >= Build.VERSION_CODES.O) {
|
||||
ContextCompat.startForegroundService(context, intent)
|
||||
} else {
|
||||
context.startService(intent)
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
//TODO
|
||||
Timber.e(ex)
|
||||
}
|
||||
}
|
||||
|
||||
scheduleAlarm(context,30_000L)
|
||||
|
||||
Timber.i("Alarm scheduled to restart service")
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val REQUEST_CODE = 0
|
||||
|
||||
fun scheduleAlarm(context: Context, delay: Long) {
|
||||
//Reschedule
|
||||
val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java)
|
||||
val pIntent = PendingIntent.getBroadcast(context, AlarmSyncBroadcastReceiver.REQUEST_CODE,
|
||||
intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val firstMillis = System.currentTimeMillis() + delay
|
||||
val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
if (SDK_INT >= Build.VERSION_CODES.M) {
|
||||
alarmMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, firstMillis, pIntent)
|
||||
} else {
|
||||
alarmMgr.set(AlarmManager.RTC_WAKEUP, firstMillis, pIntent)
|
||||
}
|
||||
}
|
||||
|
||||
fun cancelAlarm(context: Context) {
|
||||
val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java)
|
||||
val pIntent = PendingIntent.getBroadcast(context, AlarmSyncBroadcastReceiver.REQUEST_CODE,
|
||||
intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
alarmMgr.cancel(pIntent)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,582 +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 org.koin.android.ext.android.inject
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
private val mSession by inject<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 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 == mSession) {
|
||||
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(mSession!!)
|
||||
|
||||
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 (mSession != 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
//package im.vector.riotredesign.core.services
|
||||
//
|
||||
//import android.app.NotificationManager
|
||||
//import android.content.Context
|
||||
//import android.content.Intent
|
||||
//import android.os.Build.VERSION.SDK_INT
|
||||
//import android.os.Build.VERSION_CODES
|
||||
//import android.os.Handler
|
||||
//import android.os.HandlerThread
|
||||
//import android.os.Looper
|
||||
//import androidx.core.content.ContextCompat.startForegroundService
|
||||
//import im.vector.matrix.android.api.Matrix
|
||||
//import im.vector.matrix.android.api.session.Session
|
||||
//import im.vector.riotredesign.R
|
||||
//import im.vector.riotredesign.features.notifications.NotificationUtils
|
||||
//import timber.log.Timber
|
||||
//import java.net.HttpURLConnection
|
||||
//import java.net.URL
|
||||
//
|
||||
//
|
||||
///**
|
||||
// *
|
||||
// * This is used to display message notifications to the user when Push is not enabled (or not configured)
|
||||
// *
|
||||
// * This service is used to implement a long pooling mechanism in order to get messages from
|
||||
// * the home server when the user is not interacting with the app.
|
||||
// *
|
||||
// * It is intended to be started when the app enters background, and stopped when app is in foreground.
|
||||
// *
|
||||
// * When in foreground, the app uses another mechanism to get messages (doing sync wia a thread).
|
||||
// *
|
||||
// */
|
||||
//class HttpLongPoolingSyncService : VectorService() {
|
||||
//
|
||||
// private var mServiceLooper: Looper? = null
|
||||
// private var mHandler: Handler? = null
|
||||
// private val currentSessions = ArrayList<Session>()
|
||||
// private var mCount = 0
|
||||
// private var lastTimeMs = System.currentTimeMillis()
|
||||
//
|
||||
// lateinit var myRun: () -> Unit
|
||||
// override fun onCreate() {
|
||||
// //Add the permanent listening notification
|
||||
// super.onCreate()
|
||||
//
|
||||
// if (SDK_INT >= VERSION_CODES.O) {
|
||||
// val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
// val notification = NotificationUtils.buildForegroundServiceNotification(applicationContext, R.string.notification_listening_for_events, false)
|
||||
// startForeground(NotificationUtils.NOTIFICATION_ID_FOREGROUND_SERVICE, notification)
|
||||
// }
|
||||
// val thread = HandlerThread("My service Handler")
|
||||
// thread.start()
|
||||
//
|
||||
// mServiceLooper = thread.looper
|
||||
// mHandler = Handler(mServiceLooper)
|
||||
// myRun = {
|
||||
// val diff = System.currentTimeMillis() - lastTimeMs
|
||||
// lastTimeMs = System.currentTimeMillis()
|
||||
// val isAlive = Matrix.getInstance().currentSession?.isSyncThreadAlice()
|
||||
// val state = Matrix.getInstance().currentSession?.syncThreadState()
|
||||
// Timber.w(" timeDiff[${diff/1000}] Yo me here $mCount, sync thread is Alive? $isAlive, state:$state")
|
||||
// mCount++
|
||||
// mHandler?.postDelayed(Runnable { myRun() }, 10_000L)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
// //START_STICKY mode makes sense for things that will be explicitly started
|
||||
// //and stopped to run for arbitrary periods of time
|
||||
//
|
||||
// mHandler?.post {
|
||||
// myRun()
|
||||
// }
|
||||
// return START_STICKY
|
||||
// }
|
||||
//
|
||||
//
|
||||
// override fun onDestroy() {
|
||||
// //TODO test if this service should be relaunched (preference)
|
||||
// Timber.i("Service is destroyed, relaunch asap")
|
||||
// Intent(applicationContext, RestartBroadcastReceiver::class.java).also { sendBroadcast(it) }
|
||||
// super.onDestroy()
|
||||
// }
|
||||
//
|
||||
// companion object {
|
||||
//
|
||||
// fun startService(context: Context) {
|
||||
// Timber.i("Start sync service")
|
||||
// val intent = Intent(context, HttpLongPoolingSyncService::class.java)
|
||||
// try {
|
||||
// if (SDK_INT >= VERSION_CODES.O) {
|
||||
// startForegroundService(context, intent)
|
||||
// } else {
|
||||
// context.startService(intent)
|
||||
// }
|
||||
// } catch (ex: Throwable) {
|
||||
// //TODO
|
||||
// Timber.e(ex)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
@ -1,36 +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 androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
|
||||
/**
|
||||
* This class simulate push event when FCM is not working/disabled
|
||||
*/
|
||||
class PushSimulatorWorker(val context: Context,
|
||||
workerParams: WorkerParameters) : Worker(context, workerParams) {
|
||||
|
||||
override fun doWork(): Result {
|
||||
// Simulate a Push
|
||||
EventStreamServiceX.onSimulatedPushReceived(context)
|
||||
|
||||
// Indicate whether the task finished successfully with the Result
|
||||
return Result.success()
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package im.vector.riotredesign.core.services
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.legacy.content.WakefulBroadcastReceiver
|
||||
import im.vector.matrix.android.internal.session.sync.job.SyncService
|
||||
import timber.log.Timber
|
||||
|
||||
class RestartBroadcastReceiver : BroadcastReceiver() {
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
// 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"
|
||||
context.startService(it)
|
||||
try {
|
||||
if (SDK_INT >= Build.VERSION_CODES.O) {
|
||||
ContextCompat.startForegroundService(context, intent)
|
||||
} else {
|
||||
context.startService(intent)
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
//TODO
|
||||
Timber.e(ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val REQUEST_CODE = 0
|
||||
}
|
||||
}
|
@ -76,9 +76,7 @@ class LoginActivity : VectorBaseActivity() {
|
||||
Matrix.getInstance().currentSession = data
|
||||
data.open()
|
||||
data.setFilter(FilterService.FilterPreset.RiotFilter)
|
||||
//TODO sync
|
||||
// data.shoudPauseOnBackground(false)
|
||||
// data.startSync()
|
||||
data.startSync()
|
||||
get<PushRuleTriggerListener>().startWithSession(data)
|
||||
goToHome()
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ import java.io.FileOutputStream
|
||||
* organise them in order to display them in the notification drawer.
|
||||
* Events can be grouped into the same notification, old (already read) events can be removed to do some cleaning.
|
||||
*/
|
||||
class NotificationDrawerManager(val context: Context) {
|
||||
class NotificationDrawerManager(val context: Context, private val outdatedDetector: OutdatedEventDetector?) {
|
||||
|
||||
//The first time the notification drawer is refreshed, we force re-render of all notifications
|
||||
private var firstTime = true
|
||||
@ -53,7 +53,7 @@ class NotificationDrawerManager(val context: Context) {
|
||||
object : IconLoader.IconLoaderListener {
|
||||
override fun onIconsLoaded() {
|
||||
// Force refresh
|
||||
refreshNotificationDrawer(null)
|
||||
refreshNotificationDrawer()
|
||||
}
|
||||
})
|
||||
|
||||
@ -123,7 +123,7 @@ class NotificationDrawerManager(val context: Context) {
|
||||
synchronized(eventList) {
|
||||
eventList.clear()
|
||||
}
|
||||
refreshNotificationDrawer(null)
|
||||
refreshNotificationDrawer()
|
||||
}
|
||||
|
||||
/** Clear all known message events for this room and refresh the notification drawer */
|
||||
@ -139,7 +139,7 @@ class NotificationDrawerManager(val context: Context) {
|
||||
}
|
||||
NotificationUtils.cancelNotificationMessage(context, roomId, ROOM_MESSAGES_NOTIFICATION_ID)
|
||||
}
|
||||
refreshNotificationDrawer(null)
|
||||
refreshNotificationDrawer()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -177,7 +177,7 @@ class NotificationDrawerManager(val context: Context) {
|
||||
}
|
||||
|
||||
|
||||
fun refreshNotificationDrawer(outdatedDetector: OutdatedEventDetector?) {
|
||||
fun refreshNotificationDrawer() {
|
||||
if (myUserDisplayName.isBlank()) {
|
||||
// TODO
|
||||
// initWithSession(Matrix.getInstance(context).defaultSession)
|
||||
|
@ -16,6 +16,7 @@
|
||||
package im.vector.riotredesign.features.notifications
|
||||
|
||||
import android.content.Context
|
||||
import im.vector.matrix.android.api.Matrix
|
||||
|
||||
class OutdatedEventDetector(val context: Context) {
|
||||
|
||||
@ -28,20 +29,9 @@ class OutdatedEventDetector(val context: Context) {
|
||||
if (notifiableEvent is NotifiableMessageEvent) {
|
||||
val eventID = notifiableEvent.eventId
|
||||
val roomID = notifiableEvent.roomId
|
||||
/*
|
||||
TODO
|
||||
Matrix.getMXSession(context.applicationContext, notifiableEvent.matrixID)?.let { session ->
|
||||
//find the room
|
||||
if (session.isAlive) {
|
||||
session.dataHandler.getRoom(roomID)?.let { room ->
|
||||
if (room.isEventRead(eventID)) {
|
||||
Timber.v("Notifiable Event $eventID is read, and should be removed")
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
val session = Matrix.getInstance().currentSession ?: return false
|
||||
val room = session.getRoom(roomID) ?: return false
|
||||
return room.isEventRead(eventID)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ class PushRuleTriggerListener(
|
||||
}
|
||||
|
||||
override fun batchFinish() {
|
||||
drawerManager.refreshNotificationDrawer(null)
|
||||
drawerManager.refreshNotificationDrawer()
|
||||
}
|
||||
|
||||
fun startWithSession(session: Session) {
|
||||
@ -41,6 +41,6 @@ class PushRuleTriggerListener(
|
||||
session?.removePushRuleListener(this)
|
||||
session = null
|
||||
drawerManager.clearAllEvents()
|
||||
drawerManager.refreshNotificationDrawer(null)
|
||||
drawerManager.refreshNotificationDrawer()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user