session-android/src/org/thoughtcrime/securesms/ApplicationContext.java

586 lines
23 KiB
Java
Raw Normal View History

/*
* Copyright (C) 2013 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms;
2017-12-22 00:37:07 +00:00
import android.annotation.SuppressLint;
import android.arch.lifecycle.DefaultLifecycleObserver;
import android.arch.lifecycle.LifecycleOwner;
import android.arch.lifecycle.ProcessLifecycleOwner;
import android.content.Context;
2019-09-10 06:49:17 +00:00
import android.database.ContentObserver;
import android.os.AsyncTask;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
2017-01-08 17:43:43 +00:00
import android.support.multidex.MultiDexApplication;
2019-09-06 04:00:36 +00:00
import com.crashlytics.android.Crashlytics;
import com.google.android.gms.security.ProviderInstaller;
2019-09-04 06:44:19 +00:00
import com.mixpanel.android.mpmetrics.MixpanelAPI;
import org.conscrypt.Conscrypt;
2019-06-07 05:21:25 +00:00
import org.jetbrains.annotations.NotNull;
import org.signal.aesgcmprovider.AesGcmProvider;
2018-10-29 22:14:31 +00:00
import org.thoughtcrime.securesms.components.TypingStatusRepository;
import org.thoughtcrime.securesms.components.TypingStatusSender;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
2019-09-10 06:49:17 +00:00
import org.thoughtcrime.securesms.database.DatabaseContentProviders;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.dependencies.AxolotlStorageModule;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule;
2019-08-19 00:37:17 +00:00
import org.thoughtcrime.securesms.groups.GroupManager;
2019-03-28 15:56:35 +00:00
import org.thoughtcrime.securesms.jobmanager.DependencyInjector;
import org.thoughtcrime.securesms.jobmanager.JobManager;
2019-03-28 15:56:35 +00:00
import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer;
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
import org.thoughtcrime.securesms.jobs.FastJobStorage;
2019-01-24 11:04:28 +00:00
import org.thoughtcrime.securesms.jobs.FcmRefreshJob;
import org.thoughtcrime.securesms.jobs.JobManagerFactories;
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
2019-07-01 06:49:42 +00:00
import org.thoughtcrime.securesms.jobs.PushContentReceiveJob;
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
2018-10-11 23:45:22 +00:00
import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob;
import org.thoughtcrime.securesms.logging.AndroidLogger;
import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger;
2018-08-01 15:09:24 +00:00
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.logging.PersistentLogger;
import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger;
import org.thoughtcrime.securesms.loki.BackgroundPollWorker;
2019-06-18 02:49:21 +00:00
import org.thoughtcrime.securesms.loki.LokiAPIDatabase;
import org.thoughtcrime.securesms.loki.LokiPublicChatManager;
2019-08-28 04:19:08 +00:00
import org.thoughtcrime.securesms.loki.LokiRSSFeedPoller;
import org.thoughtcrime.securesms.loki.LokiUserDatabase;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
import org.thoughtcrime.securesms.service.DirectoryRefreshListener;
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.service.IncomingMessageObserver;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.service.LocalBackupListener;
2018-05-22 09:13:10 +00:00
import org.thoughtcrime.securesms.service.RotateSenderCertificateListener;
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper;
import org.webrtc.PeerConnectionFactory;
2017-12-22 00:37:07 +00:00
import org.webrtc.PeerConnectionFactory.InitializationOptions;
import org.webrtc.voiceengine.WebRtcAudioManager;
import org.webrtc.voiceengine.WebRtcAudioUtils;
import org.whispersystems.libsignal.logging.SignalProtocolLoggerProvider;
2019-06-25 03:51:24 +00:00
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
2019-10-07 04:30:20 +00:00
import org.whispersystems.signalservice.loki.api.LokiAPIDatabaseProtocol;
import org.whispersystems.signalservice.loki.api.LokiGroupChat;
import org.whispersystems.signalservice.loki.api.LokiGroupChatAPI;
import org.whispersystems.signalservice.loki.api.LokiLongPoller;
import org.whispersystems.signalservice.loki.api.LokiP2PAPI;
import org.whispersystems.signalservice.loki.api.LokiP2PAPIDelegate;
import org.whispersystems.signalservice.loki.api.LokiRSSFeed;
import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
2019-09-04 06:44:19 +00:00
import org.whispersystems.signalservice.loki.utilities.Analytics;
import java.security.Security;
2019-08-28 04:19:08 +00:00
import java.util.ArrayList;
2019-09-04 06:44:19 +00:00
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import dagger.ObjectGraph;
2019-09-06 04:00:36 +00:00
import io.fabric.sdk.android.Fabric;
2019-06-25 03:51:24 +00:00
import kotlin.Unit;
2019-09-04 06:44:19 +00:00
import kotlin.jvm.functions.Function1;
2019-07-24 02:30:23 +00:00
import network.loki.messenger.BuildConfig;
2019-09-19 05:00:52 +00:00
import static nl.komponents.kovenant.android.KovenantAndroid.startKovenant;
import static nl.komponents.kovenant.android.KovenantAndroid.stopKovenant;
/**
* Will be called once when the TextSecure process is created.
*
* We're using this as an insertion point to patch up the Android PRNG disaster,
* to initialize the job manager, and to check for GCM registration freshness.
*
* @author Moxie Marlinspike
*/
2019-06-07 05:21:25 +00:00
public class ApplicationContext extends MultiDexApplication implements DependencyInjector, DefaultLifecycleObserver, LokiP2PAPIDelegate {
private static final String TAG = ApplicationContext.class.getSimpleName();
private ExpiringMessageManager expiringMessageManager;
2018-10-29 22:14:31 +00:00
private TypingStatusRepository typingStatusRepository;
private TypingStatusSender typingStatusSender;
private JobManager jobManager;
private IncomingMessageObserver incomingMessageObserver;
private ObjectGraph objectGraph;
private PersistentLogger persistentLogger;
2019-06-28 06:08:27 +00:00
// Loki
private LokiLongPoller lokiLongPoller = null;
2019-08-28 04:19:08 +00:00
private LokiRSSFeedPoller lokiNewsFeedPoller = null;
private LokiRSSFeedPoller lokiMessengerUpdatesFeedPoller = null;
private LokiPublicChatManager lokiPublicChatManager = null;
private LokiGroupChatAPI lokiGroupChatAPI = null;
2019-06-28 06:08:27 +00:00
public SignalCommunicationModule communicationModule;
2019-09-04 06:44:19 +00:00
public MixpanelAPI mixpanel;
2019-06-18 02:49:21 +00:00
private volatile boolean isAppVisible;
public static ApplicationContext getInstance(Context context) {
return (ApplicationContext)context.getApplicationContext();
}
@Override
public void onCreate() {
super.onCreate();
2019-09-19 05:00:52 +00:00
startKovenant();
Log.i(TAG, "onCreate()");
initializeSecurityProvider();
initializeLogging();
initializeCrashHandling();
initializeDependencyInjection();
initializeJobManager();
initializeMessageRetrieval();
initializeExpiringMessageManager();
2018-10-29 22:14:31 +00:00
initializeTypingStatusRepository();
initializeTypingStatusSender();
initializeGcmCheck();
initializeSignedPreKeyCheck();
initializePeriodicTasks();
initializeCircumvention();
initializeWebRtc();
initializePendingMessages();
2018-10-11 23:45:22 +00:00
initializeUnidentifiedDeliveryAbilityRefresh();
initializeBlobProvider();
NotificationChannels.create(this);
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
// Loki - Set up P2P API if needed
setUpP2PAPI();
2019-09-04 06:44:19 +00:00
// Loki - Set up beta analytics
2019-09-18 03:56:50 +00:00
if (!BuildConfig.DEBUG) {
Fabric.with(this, new Crashlytics());
}
2019-09-04 06:44:19 +00:00
mixpanel = MixpanelAPI.getInstance(this, "59040b6707e5a1725f3fb6730fefca92");
Analytics.Companion.getShared().trackImplementation = (Function1<String, Unit>) event -> {
HashMap<String, Object> properties = new HashMap();
String configuration = BuildConfig.DEBUG ? "debug" : "production";
properties.put("configuration", configuration);
mixpanel.trackMap(event, properties);
return Unit.INSTANCE;
};
lokiPublicChatManager = new LokiPublicChatManager(this);
}
@Override
public void onStart(@NonNull LifecycleOwner owner) {
isAppVisible = true;
Log.i(TAG, "App is now visible.");
executePendingContactSync();
KeyCachingService.onAppForegrounded(this);
// Loki - Start long polling if needed
startLongPollingIfNeeded();
2019-09-25 04:04:25 +00:00
setUpStorageAPIIfNeeded();
}
@Override
public void onStop(@NonNull LifecycleOwner owner) {
isAppVisible = false;
Log.i(TAG, "App is no longer visible.");
KeyCachingService.onAppBackgrounded(this);
MessageNotifier.setVisibleThread(-1);
// Loki - Stop long polling if needed
2019-06-19 04:47:42 +00:00
if (lokiLongPoller != null) { lokiLongPoller.stopIfNeeded(); }
if (lokiPublicChatManager != null) { lokiPublicChatManager.stopPollers(); }
}
2019-09-19 05:00:52 +00:00
@Override
public void onTerminate() {
stopKovenant();
super.onTerminate();
}
@Override
public void injectDependencies(Object object) {
if (object instanceof InjectableType) {
objectGraph.inject(object);
}
}
public JobManager getJobManager() {
return jobManager;
}
public ExpiringMessageManager getExpiringMessageManager() {
return expiringMessageManager;
}
2018-10-29 22:14:31 +00:00
public TypingStatusRepository getTypingStatusRepository() {
return typingStatusRepository;
}
public TypingStatusSender getTypingStatusSender() {
return typingStatusSender;
}
public boolean isAppVisible() {
return isAppVisible;
}
public PersistentLogger getPersistentLogger() {
return persistentLogger;
}
public LokiPublicChatManager getLokiPublicChatManager() {
return lokiPublicChatManager;
}
public @Nullable LokiGroupChatAPI getLokiGroupChatAPI() {
2019-10-11 01:37:45 +00:00
if (lokiGroupChatAPI == null && IdentityKeyUtil.hasIdentityKey(this)) {
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize();
LokiAPIDatabase apiDatabase = DatabaseFactory.getLokiAPIDatabase(this);
LokiUserDatabase userDatabase = DatabaseFactory.getLokiUserDatabase(this);
lokiGroupChatAPI = new LokiGroupChatAPI(userHexEncodedPublicKey, userPrivateKey, apiDatabase, userDatabase);
}
return lokiGroupChatAPI;
}
private void initializeSecurityProvider() {
try {
Class.forName("org.signal.aesgcmprovider.AesGcmCipher");
} catch (ClassNotFoundException e) {
Log.e(TAG, "Failed to find AesGcmCipher class");
throw new ProviderInitializationException();
}
int aesPosition = Security.insertProviderAt(new AesGcmProvider(), 1);
Log.i(TAG, "Installed AesGcmProvider: " + aesPosition);
if (aesPosition < 0) {
Log.e(TAG, "Failed to install AesGcmProvider()");
throw new ProviderInitializationException();
}
int conscryptPosition = Security.insertProviderAt(Conscrypt.newProvider(), 2);
Log.i(TAG, "Installed Conscrypt provider: " + conscryptPosition);
if (conscryptPosition < 0) {
Log.w(TAG, "Did not install Conscrypt provider. May already be present.");
}
}
private void initializeLogging() {
persistentLogger = new PersistentLogger(this);
org.thoughtcrime.securesms.logging.Log.initialize(new AndroidLogger(), persistentLogger);
SignalProtocolLoggerProvider.setProvider(new CustomSignalProtocolLogger());
}
private void initializeCrashHandling() {
final Thread.UncaughtExceptionHandler originalHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionLogger(originalHandler));
}
private void initializeJobManager() {
2019-03-28 15:56:35 +00:00
this.jobManager = new JobManager(this, new JobManager.Configuration.Builder()
.setDataSerializer(new JsonDataSerializer())
.setJobFactories(JobManagerFactories.getJobFactories(this))
.setConstraintFactories(JobManagerFactories.getConstraintFactories(this))
.setConstraintObservers(JobManagerFactories.getConstraintObservers(this))
.setJobStorage(new FastJobStorage(DatabaseFactory.getJobDatabase(this)))
.setDependencyInjector(this)
.build());
}
public void initializeMessageRetrieval() {
this.incomingMessageObserver = new IncomingMessageObserver(this);
}
private void initializeDependencyInjection() {
2019-06-28 06:08:27 +00:00
communicationModule = new SignalCommunicationModule(this, new SignalServiceNetworkAccess(this));
this.objectGraph = ObjectGraph.create(communicationModule, new AxolotlStorageModule(this));
}
private void initializeGcmCheck() {
if (TextSecurePreferences.isPushRegistered(this)) {
2019-01-24 11:04:28 +00:00
long nextSetTime = TextSecurePreferences.getFcmTokenLastSetTime(this) + TimeUnit.HOURS.toMillis(6);
2019-01-24 11:04:28 +00:00
if (TextSecurePreferences.getFcmToken(this) == null || nextSetTime <= System.currentTimeMillis()) {
2019-03-28 15:56:35 +00:00
this.jobManager.add(new FcmRefreshJob());
}
}
}
private void initializeSignedPreKeyCheck() {
if (!TextSecurePreferences.isSignedPreKeyRegistered(this)) {
jobManager.add(new CreateSignedPreKeyJob(this));
}
}
private void initializeExpiringMessageManager() {
this.expiringMessageManager = new ExpiringMessageManager(this);
}
2018-10-29 22:14:31 +00:00
private void initializeTypingStatusRepository() {
this.typingStatusRepository = new TypingStatusRepository();
}
private void initializeTypingStatusSender() {
this.typingStatusSender = new TypingStatusSender(this);
}
private void initializePeriodicTasks() {
RotateSignedPreKeyListener.schedule(this);
DirectoryRefreshListener.schedule(this);
LocalBackupListener.schedule(this);
2018-05-22 09:13:10 +00:00
RotateSenderCertificateListener.schedule(this);
BackgroundPollWorker.schedule(this); // Loki
if (BuildConfig.PLAY_STORE_DISABLED) {
UpdateApkRefreshListener.schedule(this);
}
}
private void initializeWebRtc() {
try {
Set<String> HARDWARE_AEC_BLACKLIST = new HashSet<String>() {{
add("Pixel");
add("Pixel XL");
add("Moto G5");
add("Moto G (5S) Plus");
add("Moto G4");
add("TA-1053");
add("Mi A1");
add("E5823"); // Sony z5 compact
add("Redmi Note 5");
add("FP2"); // Fairphone FP2
2019-03-10 13:29:02 +00:00
add("MI 5");
}};
Set<String> OPEN_SL_ES_WHITELIST = new HashSet<String>() {{
add("Pixel");
add("Pixel XL");
}};
2017-12-22 00:37:07 +00:00
if (HARDWARE_AEC_BLACKLIST.contains(Build.MODEL)) {
WebRtcAudioUtils.setWebRtcBasedAcousticEchoCanceler(true);
}
2017-12-22 00:37:07 +00:00
if (!OPEN_SL_ES_WHITELIST.contains(Build.MODEL)) {
WebRtcAudioManager.setBlacklistDeviceForOpenSLESUsage(true);
}
2017-12-22 00:37:07 +00:00
2019-01-29 22:25:49 +00:00
PeerConnectionFactory.initialize(InitializationOptions.builder(this).createInitializationOptions());
} catch (UnsatisfiedLinkError e) {
Log.w(TAG, e);
}
}
2017-12-22 00:37:07 +00:00
@SuppressLint("StaticFieldLeak")
private void initializeCircumvention() {
AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
if (new SignalServiceNetworkAccess(ApplicationContext.this).isCensored(ApplicationContext.this)) {
try {
ProviderInstaller.installIfNeeded(ApplicationContext.this);
} catch (Throwable t) {
Log.w(TAG, t);
}
}
return null;
}
};
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void executePendingContactSync() {
if (TextSecurePreferences.needsFullContactSync(this)) {
ApplicationContext.getInstance(this).getJobManager().add(new MultiDeviceContactUpdateJob(this, true));
}
}
private void initializePendingMessages() {
if (TextSecurePreferences.getNeedsMessagePull(this)) {
Log.i(TAG, "Scheduling a message fetch.");
ApplicationContext.getInstance(this).getJobManager().add(new PushNotificationReceiveJob(this));
TextSecurePreferences.setNeedsMessagePull(this, false);
}
}
2018-10-11 23:45:22 +00:00
private void initializeUnidentifiedDeliveryAbilityRefresh() {
if (TextSecurePreferences.isMultiDevice(this) && !TextSecurePreferences.isUnidentifiedDeliveryEnabled(this)) {
2019-03-28 15:56:35 +00:00
jobManager.add(new RefreshUnidentifiedDeliveryAbilityJob());
2018-10-11 23:45:22 +00:00
}
}
private void initializeBlobProvider() {
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
BlobProvider.getInstance().onSessionStart(this);
});
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(DynamicLanguageContextWrapper.updateContext(base, TextSecurePreferences.getLanguage(base)));
}
private static class ProviderInitializationException extends RuntimeException {
}
2019-06-07 05:21:25 +00:00
// region Loki
2019-09-25 04:04:25 +00:00
public void setUpStorageAPIIfNeeded() {
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
2019-09-25 04:04:25 +00:00
if (userHexEncodedPublicKey != null && IdentityKeyUtil.hasIdentityKey(this)) {
2019-10-07 05:15:06 +00:00
boolean isDebugMode = BuildConfig.DEBUG;
2019-09-25 04:04:25 +00:00
byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize();
LokiAPIDatabaseProtocol database = DatabaseFactory.getLokiAPIDatabase(this);
2019-10-07 04:30:20 +00:00
LokiStorageAPI.Companion.configure(isDebugMode, userHexEncodedPublicKey, userPrivateKey, database);
2019-09-25 04:04:25 +00:00
}
2019-09-23 01:28:17 +00:00
}
public void setUpP2PAPI() {
String hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
if (hexEncodedPublicKey == null) { return; }
LokiP2PAPI.Companion.configure(hexEncodedPublicKey, (isOnline, contactPublicKey) -> {
// TODO: Implement
return null;
}, this);
}
2019-06-07 05:21:25 +00:00
@Override
public void ping(@NotNull String s) {
// TODO: Implement
}
private void setUpLongPollingIfNeeded() {
if (lokiLongPoller != null) return;
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
if (userHexEncodedPublicKey == null) return;
LokiAPIDatabase lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(this);
2019-06-25 03:51:24 +00:00
Context context = this;
2019-08-28 04:19:08 +00:00
lokiLongPoller = new LokiLongPoller(userHexEncodedPublicKey, lokiAPIDatabase, protos -> {
for (SignalServiceProtos.Envelope proto : protos) {
new PushContentReceiveJob(context).processEnvelope(new SignalServiceEnvelope(proto));
2019-06-25 03:51:24 +00:00
}
2019-08-28 04:19:08 +00:00
return Unit.INSTANCE;
2019-06-25 03:51:24 +00:00
});
}
2019-08-05 02:08:23 +00:00
public void startLongPollingIfNeeded() {
setUpLongPollingIfNeeded();
if (lokiLongPoller != null) { lokiLongPoller.startIfNeeded(); }
}
2019-08-28 04:19:08 +00:00
private LokiRSSFeed lokiNewsFeed() {
return new LokiRSSFeed("loki.network.feed", "https://loki.network/feed/", "Loki News", true);
}
private LokiRSSFeed lokiMessengerUpdatesFeed() {
return new LokiRSSFeed("loki.network.messenger-updates.feed", "https://loki.network/category/messenger-updates/feed", "Loki Messenger Updates", false);
}
public void createGroupChatsIfNeeded() {
List<LokiGroupChat> defaultChats = LokiGroupChat.Companion.defaultChats(BuildConfig.DEBUG);
for (LokiGroupChat chat : defaultChats) {
long threadID = GroupManager.getThreadId(chat.getId(), this);
String migrationKey = chat.getId() + "_migrated";
boolean isChatMigrated = TextSecurePreferences.getBooleanPreference(this, migrationKey, false);
boolean isChatSetUp = TextSecurePreferences.isChatSetUp(this, chat.getId());
if (!isChatSetUp || !chat.isDeletable()) {
lokiPublicChatManager.addChat(chat.getServer(), chat.getChannel(), chat.getDisplayName());
TextSecurePreferences.markChatSetUp(this, chat.getId());
TextSecurePreferences.setBooleanPreference(this, migrationKey, true);
} else if (threadID > -1 && !isChatMigrated) {
// Migrate the old public chats.
DatabaseFactory.getLokiThreadDatabase(this).setGroupChat(chat, threadID);
TextSecurePreferences.setBooleanPreference(this, migrationKey, true);
}
2019-08-28 04:19:08 +00:00
}
}
public void createRSSFeedsIfNeeded() {
ArrayList<LokiRSSFeed> feeds = new ArrayList<>();
feeds.add(lokiNewsFeed());
feeds.add(lokiMessengerUpdatesFeed());
for (LokiRSSFeed feed : feeds) {
boolean isFeedSetUp = TextSecurePreferences.isChatSetUp(this, feed.getId());
if (!isFeedSetUp || !feed.isDeletable()) {
GroupManager.createGroup(feed.getId(), this, new HashSet<>(), null, feed.getDisplayName(), false);
TextSecurePreferences.markChatSetUp(this, feed.getId());
}
}
}
private void createRSSFeedPollersIfNeeded() {
2019-09-11 23:59:15 +00:00
// Only create the RSS feed pollers if their threads aren't deleted
2019-09-10 06:49:17 +00:00
LokiRSSFeed lokiNewsFeed = lokiNewsFeed();
2019-09-11 23:59:15 +00:00
long lokiNewsFeedThreadID = GroupManager.getThreadId(lokiNewsFeed.getId(), this);
if (lokiNewsFeedThreadID >= 0 && lokiNewsFeedPoller == null) {
2019-09-10 06:49:17 +00:00
lokiNewsFeedPoller = new LokiRSSFeedPoller(this, lokiNewsFeed);
2019-09-11 23:59:15 +00:00
// Set up deletion listeners if needed
setUpThreadDeletionListeners(lokiNewsFeedThreadID, () -> {
2019-09-10 06:49:17 +00:00
if (lokiNewsFeedPoller != null) lokiNewsFeedPoller.stop();
lokiNewsFeedPoller = null;
});
}
2019-09-11 23:59:15 +00:00
// The user can't delete the Loki Messenger Updates RSS feed
if (lokiMessengerUpdatesFeedPoller == null) {
lokiMessengerUpdatesFeedPoller = new LokiRSSFeedPoller(this, lokiMessengerUpdatesFeed());
}
2019-09-10 06:49:17 +00:00
}
2019-09-11 23:59:15 +00:00
private void setUpThreadDeletionListeners(long threadID, Runnable onDelete) {
if (threadID < 0) { return; }
2019-09-10 06:49:17 +00:00
ContentObserver observer = new ContentObserver(null) {
2019-09-11 23:59:15 +00:00
2019-09-10 06:49:17 +00:00
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
2019-09-11 23:59:15 +00:00
// Stop the poller if thread is deleted
2019-09-10 06:49:17 +00:00
try {
2019-09-11 23:59:15 +00:00
if (!DatabaseFactory.getThreadDatabase(getApplicationContext()).hasThread(threadID)) {
2019-09-10 06:49:17 +00:00
onDelete.run();
getContentResolver().unregisterContentObserver(this);
}
} catch (Exception e) {
2019-09-11 23:59:15 +00:00
// TODO: Handle
2019-09-10 06:49:17 +00:00
}
}
};
2019-09-11 23:59:15 +00:00
this.getContentResolver().registerContentObserver(DatabaseContentProviders.Conversation.getUriForThread(threadID), true, observer);
2019-08-28 04:19:08 +00:00
}
public void startGroupChatPollersIfNeeded() {
lokiPublicChatManager.startPollersIfNeeded();
}
2019-08-28 04:19:08 +00:00
public void startRSSFeedPollersIfNeeded() {
createRSSFeedPollersIfNeeded();
2019-09-10 06:49:17 +00:00
if (lokiNewsFeedPoller != null) lokiNewsFeedPoller.startIfNeeded();
if (lokiMessengerUpdatesFeedPoller != null) lokiMessengerUpdatesFeedPoller.startIfNeeded();
2019-08-28 04:19:08 +00:00
}
// endregion
}