Merge pull request #1033 from session-foundation/db-kotlin
Convert ApplicationContext into kotlinpull/1712/head
parent
a312b901b2
commit
791c233bf5
@ -1,502 +0,0 @@
|
|||||||
/* 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;
|
|
||||||
|
|
||||||
import static nl.komponents.kovenant.android.KovenantAndroid.startKovenant;
|
|
||||||
import static nl.komponents.kovenant.android.KovenantAndroid.stopKovenant;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.app.Application;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.HandlerThread;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.StringRes;
|
|
||||||
import androidx.core.content.pm.ShortcutInfoCompat;
|
|
||||||
import androidx.core.content.pm.ShortcutManagerCompat;
|
|
||||||
import androidx.core.graphics.drawable.IconCompat;
|
|
||||||
import androidx.hilt.work.HiltWorkerFactory;
|
|
||||||
import androidx.lifecycle.DefaultLifecycleObserver;
|
|
||||||
import androidx.lifecycle.LifecycleOwner;
|
|
||||||
import androidx.lifecycle.ProcessLifecycleOwner;
|
|
||||||
import androidx.work.Configuration;
|
|
||||||
|
|
||||||
import com.squareup.phrase.Phrase;
|
|
||||||
|
|
||||||
import org.conscrypt.Conscrypt;
|
|
||||||
import org.session.libsession.database.MessageDataProvider;
|
|
||||||
import org.session.libsession.messaging.MessagingModuleConfiguration;
|
|
||||||
import org.session.libsession.messaging.groups.GroupManagerV2;
|
|
||||||
import org.session.libsession.messaging.groups.LegacyGroupDeprecationManager;
|
|
||||||
import org.session.libsession.messaging.notifications.TokenFetcher;
|
|
||||||
import org.session.libsession.messaging.sending_receiving.notifications.MessageNotifier;
|
|
||||||
import org.session.libsession.messaging.sending_receiving.pollers.LegacyClosedGroupPollerV2;
|
|
||||||
import org.session.libsession.messaging.sending_receiving.pollers.Poller;
|
|
||||||
import org.session.libsession.snode.SnodeClock;
|
|
||||||
import org.session.libsession.snode.SnodeModule;
|
|
||||||
import org.session.libsession.utilities.Device;
|
|
||||||
import org.session.libsession.utilities.Environment;
|
|
||||||
import org.session.libsession.utilities.ProfilePictureUtilities;
|
|
||||||
import org.session.libsession.utilities.SSKEnvironment;
|
|
||||||
import org.session.libsession.utilities.TextSecurePreferences;
|
|
||||||
import org.session.libsession.utilities.Toaster;
|
|
||||||
import org.session.libsession.utilities.UsernameUtils;
|
|
||||||
import org.session.libsession.utilities.Util;
|
|
||||||
import org.session.libsession.utilities.WindowDebouncer;
|
|
||||||
import org.session.libsignal.utilities.HTTP;
|
|
||||||
import org.session.libsignal.utilities.JsonUtil;
|
|
||||||
import org.session.libsignal.utilities.Log;
|
|
||||||
import org.session.libsignal.utilities.ThreadUtils;
|
|
||||||
import org.signal.aesgcmprovider.AesGcmProvider;
|
|
||||||
import org.thoughtcrime.securesms.components.TypingStatusSender;
|
|
||||||
import org.thoughtcrime.securesms.configs.ConfigUploader;
|
|
||||||
import org.thoughtcrime.securesms.database.EmojiSearchDatabase;
|
|
||||||
import org.thoughtcrime.securesms.database.LastSentTimestampCache;
|
|
||||||
import org.thoughtcrime.securesms.database.LokiAPIDatabase;
|
|
||||||
import org.thoughtcrime.securesms.database.Storage;
|
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
|
||||||
import org.thoughtcrime.securesms.database.model.EmojiSearchData;
|
|
||||||
import org.thoughtcrime.securesms.debugmenu.DebugActivity;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.AppComponent;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.ConfigFactory;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseModule;
|
|
||||||
import org.thoughtcrime.securesms.emoji.EmojiSource;
|
|
||||||
import org.thoughtcrime.securesms.groups.ExpiredGroupManager;
|
|
||||||
import org.thoughtcrime.securesms.groups.OpenGroupManager;
|
|
||||||
import org.thoughtcrime.securesms.groups.handler.AdminStateSync;
|
|
||||||
import org.thoughtcrime.securesms.groups.handler.CleanupInvitationHandler;
|
|
||||||
import org.thoughtcrime.securesms.groups.handler.DestroyedGroupSync;
|
|
||||||
import org.thoughtcrime.securesms.groups.GroupPollerManager;
|
|
||||||
import org.thoughtcrime.securesms.groups.handler.RemoveGroupMemberHandler;
|
|
||||||
import org.thoughtcrime.securesms.home.HomeActivity;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
|
||||||
import org.thoughtcrime.securesms.logging.AndroidLogger;
|
|
||||||
import org.thoughtcrime.securesms.logging.PersistentLogger;
|
|
||||||
import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger;
|
|
||||||
import org.thoughtcrime.securesms.notifications.BackgroundPollManager;
|
|
||||||
import org.thoughtcrime.securesms.notifications.BackgroundPollWorker;
|
|
||||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
|
||||||
import org.thoughtcrime.securesms.notifications.PushRegistrationHandler;
|
|
||||||
import org.thoughtcrime.securesms.providers.BlobProvider;
|
|
||||||
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
|
||||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
|
||||||
import org.thoughtcrime.securesms.webrtc.WebRtcCallBridge;
|
|
||||||
import org.thoughtcrime.securesms.sskenvironment.ReadReceiptManager;
|
|
||||||
import org.thoughtcrime.securesms.sskenvironment.TypingStatusRepository;
|
|
||||||
import org.thoughtcrime.securesms.util.AppVisibilityManager;
|
|
||||||
import org.thoughtcrime.securesms.util.Broadcaster;
|
|
||||||
import org.thoughtcrime.securesms.util.VersionDataFetcher;
|
|
||||||
import org.thoughtcrime.securesms.webrtc.CallMessageProcessor;
|
|
||||||
import org.webrtc.PeerConnectionFactory;
|
|
||||||
import org.webrtc.PeerConnectionFactory.InitializationOptions;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.security.Security;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Timer;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Provider;
|
|
||||||
|
|
||||||
import dagger.Lazy;
|
|
||||||
import dagger.hilt.EntryPoints;
|
|
||||||
import dagger.hilt.android.HiltAndroidApp;
|
|
||||||
import kotlin.Deprecated;
|
|
||||||
import kotlin.Unit;
|
|
||||||
import network.loki.messenger.BuildConfig;
|
|
||||||
import network.loki.messenger.R;
|
|
||||||
import network.loki.messenger.libsession_util.util.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Will be called once when the TextSecure process is created.
|
|
||||||
* <p>
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
@HiltAndroidApp
|
|
||||||
public class ApplicationContext extends Application implements DefaultLifecycleObserver, Toaster, Configuration.Provider {
|
|
||||||
|
|
||||||
public static final String PREFERENCES_NAME = "SecureSMS-Preferences";
|
|
||||||
|
|
||||||
private static final String TAG = ApplicationContext.class.getSimpleName();
|
|
||||||
|
|
||||||
public Poller poller = null;
|
|
||||||
public Broadcaster broadcaster = null;
|
|
||||||
private WindowDebouncer conversationListDebouncer;
|
|
||||||
private HandlerThread conversationListHandlerThread;
|
|
||||||
private Handler conversationListHandler;
|
|
||||||
private PersistentLogger persistentLogger;
|
|
||||||
|
|
||||||
@Inject HiltWorkerFactory workerFactory;
|
|
||||||
@Inject LokiAPIDatabase lokiAPIDatabase;
|
|
||||||
@Inject public Storage storage;
|
|
||||||
@Inject Device device;
|
|
||||||
@Inject MessageDataProvider messageDataProvider;
|
|
||||||
@Inject TextSecurePreferences textSecurePreferences;
|
|
||||||
@Inject ConfigFactory configFactory;
|
|
||||||
@Inject LastSentTimestampCache lastSentTimestampCache;
|
|
||||||
@Inject VersionDataFetcher versionDataFetcher;
|
|
||||||
@Inject PushRegistrationHandler pushRegistrationHandler;
|
|
||||||
@Inject TokenFetcher tokenFetcher;
|
|
||||||
@Inject GroupManagerV2 groupManagerV2;
|
|
||||||
@Inject SSKEnvironment.ProfileManagerProtocol profileManager;
|
|
||||||
@Inject CallMessageProcessor callMessageProcessor;
|
|
||||||
MessagingModuleConfiguration messagingModuleConfiguration;
|
|
||||||
@Inject ConfigUploader configUploader;
|
|
||||||
@Inject AdminStateSync adminStateSync;
|
|
||||||
@Inject DestroyedGroupSync destroyedGroupSync;
|
|
||||||
@Inject RemoveGroupMemberHandler removeGroupMemberHandler; // Exists here only to start upon app starts
|
|
||||||
@Inject SnodeClock snodeClock;
|
|
||||||
@Inject ExpiringMessageManager expiringMessageManager;
|
|
||||||
@Inject TypingStatusRepository typingStatusRepository;
|
|
||||||
@Inject TypingStatusSender typingStatusSender;
|
|
||||||
@Inject ReadReceiptManager readReceiptManager;
|
|
||||||
@Inject Lazy<MessageNotifier> messageNotifierLazy;
|
|
||||||
@Inject LokiAPIDatabase apiDB;
|
|
||||||
@Inject EmojiSearchDatabase emojiSearchDb;
|
|
||||||
@Inject WebRtcCallBridge webRtcCallBridge;
|
|
||||||
@Inject LegacyClosedGroupPollerV2 legacyClosedGroupPollerV2;
|
|
||||||
@Inject LegacyGroupDeprecationManager legacyGroupDeprecationManager;
|
|
||||||
@Inject CleanupInvitationHandler cleanupInvitationHandler;
|
|
||||||
@Inject UsernameUtils usernameUtils;
|
|
||||||
@Inject BackgroundPollManager backgroundPollManager; // Exists here only to start upon app starts
|
|
||||||
@Inject AppVisibilityManager appVisibilityManager; // Exists here only to start upon app starts
|
|
||||||
@Inject GroupPollerManager groupPollerManager; // Exists here only to start upon app starts
|
|
||||||
@Inject ExpiredGroupManager expiredGroupManager; // Exists here only to start upon app starts
|
|
||||||
|
|
||||||
public volatile boolean isAppVisible;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getSystemService(String name) {
|
|
||||||
if (MessagingModuleConfiguration.MESSAGING_MODULE_SERVICE.equals(name)) {
|
|
||||||
return messagingModuleConfiguration;
|
|
||||||
}
|
|
||||||
return super.getSystemService(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ApplicationContext getInstance(Context context) {
|
|
||||||
return (ApplicationContext) context.getApplicationContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated(message = "Use proper DI to inject this component")
|
|
||||||
public TextSecurePreferences getPrefs() {
|
|
||||||
return EntryPoints.get(getApplicationContext(), AppComponent.class).getPrefs();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated(message = "Use proper DI to inject this component")
|
|
||||||
public DatabaseComponent getDatabaseComponent() {
|
|
||||||
return EntryPoints.get(getApplicationContext(), DatabaseComponent.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated(message = "Use proper DI to inject this component")
|
|
||||||
public MessageNotifier getMessageNotifier() {
|
|
||||||
return messageNotifierLazy.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Handler getConversationListNotificationHandler() {
|
|
||||||
if (this.conversationListHandlerThread == null) {
|
|
||||||
conversationListHandlerThread = new HandlerThread("ConversationListHandler");
|
|
||||||
conversationListHandlerThread.start();
|
|
||||||
}
|
|
||||||
if (this.conversationListHandler == null) {
|
|
||||||
conversationListHandler = new Handler(conversationListHandlerThread.getLooper());
|
|
||||||
}
|
|
||||||
return conversationListHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public WindowDebouncer getConversationListDebouncer() {
|
|
||||||
if (conversationListDebouncer == null) {
|
|
||||||
conversationListDebouncer = new WindowDebouncer(1000, new Timer());
|
|
||||||
}
|
|
||||||
return conversationListDebouncer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PersistentLogger getPersistentLogger() {
|
|
||||||
return this.persistentLogger;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void toast(@StringRes int stringRes, int toastLength, @NonNull Map<String, String> parameters) {
|
|
||||||
Phrase builder = Phrase.from(this, stringRes);
|
|
||||||
for (Map.Entry<String,String> entry : parameters.entrySet()) {
|
|
||||||
builder.put(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
Toast.makeText(this, builder.format(), toastLength).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void toast(@NonNull CharSequence message, int toastLength) {
|
|
||||||
Toast.makeText(this, message, toastLength).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate() {
|
|
||||||
TextSecurePreferences.setPushSuffix(BuildConfig.PUSH_KEY_SUFFIX);
|
|
||||||
|
|
||||||
DatabaseModule.init(this);
|
|
||||||
MessagingModuleConfiguration.configure(this);
|
|
||||||
super.onCreate();
|
|
||||||
|
|
||||||
messagingModuleConfiguration = new MessagingModuleConfiguration(
|
|
||||||
this,
|
|
||||||
storage,
|
|
||||||
device,
|
|
||||||
messageDataProvider,
|
|
||||||
configFactory,
|
|
||||||
lastSentTimestampCache,
|
|
||||||
this,
|
|
||||||
tokenFetcher,
|
|
||||||
groupManagerV2,
|
|
||||||
snodeClock,
|
|
||||||
textSecurePreferences,
|
|
||||||
legacyClosedGroupPollerV2,
|
|
||||||
legacyGroupDeprecationManager,
|
|
||||||
usernameUtils
|
|
||||||
);
|
|
||||||
|
|
||||||
Log.i(TAG, "onCreate()");
|
|
||||||
startKovenant();
|
|
||||||
initializeSecurityProvider();
|
|
||||||
initializeLogging();
|
|
||||||
initializeCrashHandling();
|
|
||||||
NotificationChannels.create(this);
|
|
||||||
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
|
|
||||||
AppContext.INSTANCE.configureKovenant();
|
|
||||||
broadcaster = new Broadcaster(this);
|
|
||||||
boolean useTestNet = textSecurePreferences.getEnvironment() == Environment.TEST_NET;
|
|
||||||
SnodeModule.Companion.configure(apiDB, broadcaster, useTestNet);
|
|
||||||
SSKEnvironment.Companion.configure(typingStatusRepository, readReceiptManager, profileManager, getMessageNotifier(), expiringMessageManager);
|
|
||||||
initializeWebRtc();
|
|
||||||
initializeBlobProvider();
|
|
||||||
resubmitProfilePictureIfNeeded();
|
|
||||||
loadEmojiSearchIndexIfNeeded();
|
|
||||||
EmojiSource.refresh();
|
|
||||||
|
|
||||||
NetworkConstraint networkConstraint = new NetworkConstraint.Factory(this).create();
|
|
||||||
HTTP.INSTANCE.setConnectedToNetwork(networkConstraint::isMet);
|
|
||||||
|
|
||||||
snodeClock.start();
|
|
||||||
pushRegistrationHandler.run();
|
|
||||||
configUploader.start();
|
|
||||||
destroyedGroupSync.start();
|
|
||||||
adminStateSync.start();
|
|
||||||
cleanupInvitationHandler.start();
|
|
||||||
|
|
||||||
// add our shortcut debug menu if we are not in a release build
|
|
||||||
if (BuildConfig.BUILD_TYPE != "release") {
|
|
||||||
// add the config settings shortcut
|
|
||||||
Intent intent = new Intent(this, DebugActivity.class);
|
|
||||||
intent.setAction(Intent.ACTION_VIEW);
|
|
||||||
|
|
||||||
ShortcutInfoCompat shortcut = new ShortcutInfoCompat.Builder(this, "shortcut_debug_menu")
|
|
||||||
.setShortLabel("Debug Menu")
|
|
||||||
.setLongLabel("Debug Menu")
|
|
||||||
.setIcon(IconCompat.createWithResource(this, R.drawable.ic_settings))
|
|
||||||
.setIntent(intent)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
ShortcutManagerCompat.pushDynamicShortcut(this, shortcut);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Configuration getWorkManagerConfiguration() {
|
|
||||||
return new Configuration.Builder()
|
|
||||||
.setWorkerFactory(workerFactory)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStart(@NonNull LifecycleOwner owner) {
|
|
||||||
isAppVisible = true;
|
|
||||||
Log.i(TAG, "App is now visible.");
|
|
||||||
KeyCachingService.onAppForegrounded(this);
|
|
||||||
|
|
||||||
// If the user account hasn't been created or onboarding wasn't finished then don't start
|
|
||||||
// the pollers
|
|
||||||
if (textSecurePreferences.getLocalNumber() == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
startPollingIfNeeded();
|
|
||||||
|
|
||||||
ThreadUtils.queue(()->{
|
|
||||||
OpenGroupManager.INSTANCE.startPolling();
|
|
||||||
return Unit.INSTANCE;
|
|
||||||
});
|
|
||||||
|
|
||||||
// fetch last version data
|
|
||||||
versionDataFetcher.startTimedVersionCheck();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStop(@NonNull LifecycleOwner owner) {
|
|
||||||
isAppVisible = false;
|
|
||||||
Log.i(TAG, "App is no longer visible.");
|
|
||||||
KeyCachingService.onAppBackgrounded(this);
|
|
||||||
getMessageNotifier().setVisibleThread(-1);
|
|
||||||
if (poller != null) {
|
|
||||||
poller.stopIfNeeded();
|
|
||||||
}
|
|
||||||
legacyClosedGroupPollerV2.stopAll();
|
|
||||||
versionDataFetcher.stopTimedVersionCheck();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTerminate() {
|
|
||||||
stopKovenant(); // Loki
|
|
||||||
OpenGroupManager.INSTANCE.stopPolling();
|
|
||||||
versionDataFetcher.stopTimedVersionCheck();
|
|
||||||
super.onTerminate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated(message = "Use proper DI to inject this component")
|
|
||||||
public ExpiringMessageManager getExpiringMessageManager() {
|
|
||||||
return expiringMessageManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated(message = "Use proper DI to inject this component")
|
|
||||||
public TypingStatusRepository getTypingStatusRepository() {
|
|
||||||
return typingStatusRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated(message = "Use proper DI to inject this component")
|
|
||||||
public TypingStatusSender getTypingStatusSender() {
|
|
||||||
return typingStatusSender;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated(message = "Use proper DI to inject this component")
|
|
||||||
public TextSecurePreferences getTextSecurePreferences() {
|
|
||||||
return textSecurePreferences;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated(message = "Use proper DI to inject this component")
|
|
||||||
public ReadReceiptManager getReadReceiptManager() {
|
|
||||||
return readReceiptManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public boolean isAppVisible() {
|
|
||||||
return isAppVisible;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loki
|
|
||||||
|
|
||||||
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() {
|
|
||||||
if (persistentLogger == null) {
|
|
||||||
persistentLogger = new PersistentLogger(this);
|
|
||||||
}
|
|
||||||
Log.initialize(new AndroidLogger(), persistentLogger);
|
|
||||||
Logger.initLogger();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeCrashHandling() {
|
|
||||||
final Thread.UncaughtExceptionHandler originalHandler = Thread.getDefaultUncaughtExceptionHandler();
|
|
||||||
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionLogger(originalHandler));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeWebRtc() {
|
|
||||||
try {
|
|
||||||
PeerConnectionFactory.initialize(InitializationOptions.builder(this).createInitializationOptions());
|
|
||||||
} catch (UnsatisfiedLinkError e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeBlobProvider() {
|
|
||||||
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
|
||||||
BlobProvider.getInstance().onSessionStart(this);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ProviderInitializationException extends RuntimeException { }
|
|
||||||
private void setUpPollingIfNeeded() {
|
|
||||||
String userPublicKey = textSecurePreferences.getLocalNumber();
|
|
||||||
if (userPublicKey == null) return;
|
|
||||||
if(poller == null) {
|
|
||||||
poller = new Poller(configFactory, storage, lokiAPIDatabase);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void startPollingIfNeeded() {
|
|
||||||
setUpPollingIfNeeded();
|
|
||||||
if (poller != null) {
|
|
||||||
poller.startIfNeeded();
|
|
||||||
}
|
|
||||||
legacyClosedGroupPollerV2.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void retrieveUserProfile() {
|
|
||||||
setUpPollingIfNeeded();
|
|
||||||
if (poller != null) {
|
|
||||||
poller.retrieveUserProfile();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void resubmitProfilePictureIfNeeded() {
|
|
||||||
ProfilePictureUtilities.INSTANCE.resubmitProfilePictureIfNeeded(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadEmojiSearchIndexIfNeeded() {
|
|
||||||
Executors.newSingleThreadExecutor().execute(() -> {
|
|
||||||
if (emojiSearchDb.query("face", 1).isEmpty()) {
|
|
||||||
try (InputStream inputStream = getAssets().open("emoji/emoji_search_index.json")) {
|
|
||||||
List<EmojiSearchData> searchIndex = Arrays.asList(JsonUtil.fromJson(inputStream, EmojiSearchData[].class));
|
|
||||||
emojiSearchDb.setSearchIndex(searchIndex);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e("Loki", "Failed to load emoji search index");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
}
|
|
@ -0,0 +1,500 @@
|
|||||||
|
/* 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
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.AsyncTask
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.HandlerThread
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.core.content.pm.ShortcutInfoCompat
|
||||||
|
import androidx.core.content.pm.ShortcutManagerCompat
|
||||||
|
import androidx.core.graphics.drawable.IconCompat
|
||||||
|
import androidx.hilt.work.HiltWorkerFactory
|
||||||
|
import androidx.lifecycle.DefaultLifecycleObserver
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.lifecycle.ProcessLifecycleOwner
|
||||||
|
import androidx.work.Configuration
|
||||||
|
import com.squareup.phrase.Phrase
|
||||||
|
import dagger.Lazy
|
||||||
|
import dagger.hilt.EntryPoints
|
||||||
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
|
import network.loki.messenger.BuildConfig
|
||||||
|
import network.loki.messenger.R
|
||||||
|
import network.loki.messenger.libsession_util.util.Logger.initLogger
|
||||||
|
import nl.komponents.kovenant.android.startKovenant
|
||||||
|
import nl.komponents.kovenant.android.stopKovenant
|
||||||
|
import org.conscrypt.Conscrypt
|
||||||
|
import org.session.libsession.database.MessageDataProvider
|
||||||
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
|
import org.session.libsession.messaging.MessagingModuleConfiguration.Companion.configure
|
||||||
|
import org.session.libsession.messaging.groups.GroupManagerV2
|
||||||
|
import org.session.libsession.messaging.groups.LegacyGroupDeprecationManager
|
||||||
|
import org.session.libsession.messaging.notifications.TokenFetcher
|
||||||
|
import org.session.libsession.messaging.sending_receiving.notifications.MessageNotifier
|
||||||
|
import org.session.libsession.messaging.sending_receiving.pollers.LegacyClosedGroupPollerV2
|
||||||
|
import org.session.libsession.messaging.sending_receiving.pollers.Poller
|
||||||
|
import org.session.libsession.snode.SnodeClock
|
||||||
|
import org.session.libsession.snode.SnodeModule.Companion.configure
|
||||||
|
import org.session.libsession.utilities.Device
|
||||||
|
import org.session.libsession.utilities.Environment
|
||||||
|
import org.session.libsession.utilities.ProfilePictureUtilities.resubmitProfilePictureIfNeeded
|
||||||
|
import org.session.libsession.utilities.SSKEnvironment.Companion.configure
|
||||||
|
import org.session.libsession.utilities.SSKEnvironment.ProfileManagerProtocol
|
||||||
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
|
import org.session.libsession.utilities.TextSecurePreferences.Companion.pushSuffix
|
||||||
|
import org.session.libsession.utilities.Toaster
|
||||||
|
import org.session.libsession.utilities.UsernameUtils
|
||||||
|
import org.session.libsession.utilities.WindowDebouncer
|
||||||
|
import org.session.libsignal.utilities.HTTP.isConnectedToNetwork
|
||||||
|
import org.session.libsignal.utilities.JsonUtil
|
||||||
|
import org.session.libsignal.utilities.Log
|
||||||
|
import org.session.libsignal.utilities.ThreadUtils.queue
|
||||||
|
import org.signal.aesgcmprovider.AesGcmProvider
|
||||||
|
import org.thoughtcrime.securesms.AppContext.configureKovenant
|
||||||
|
import org.thoughtcrime.securesms.components.TypingStatusSender
|
||||||
|
import org.thoughtcrime.securesms.configs.ConfigUploader
|
||||||
|
import org.thoughtcrime.securesms.database.EmojiSearchDatabase
|
||||||
|
import org.thoughtcrime.securesms.database.LastSentTimestampCache
|
||||||
|
import org.thoughtcrime.securesms.database.LokiAPIDatabase
|
||||||
|
import org.thoughtcrime.securesms.database.Storage
|
||||||
|
import org.thoughtcrime.securesms.database.model.EmojiSearchData
|
||||||
|
import org.thoughtcrime.securesms.debugmenu.DebugActivity
|
||||||
|
import org.thoughtcrime.securesms.dependencies.AppComponent
|
||||||
|
import org.thoughtcrime.securesms.dependencies.ConfigFactory
|
||||||
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||||
|
import org.thoughtcrime.securesms.dependencies.DatabaseModule.init
|
||||||
|
import org.thoughtcrime.securesms.emoji.EmojiSource.Companion.refresh
|
||||||
|
import org.thoughtcrime.securesms.groups.ExpiredGroupManager
|
||||||
|
import org.thoughtcrime.securesms.groups.GroupPollerManager
|
||||||
|
import org.thoughtcrime.securesms.groups.OpenGroupManager.startPolling
|
||||||
|
import org.thoughtcrime.securesms.groups.OpenGroupManager.stopPolling
|
||||||
|
import org.thoughtcrime.securesms.groups.handler.AdminStateSync
|
||||||
|
import org.thoughtcrime.securesms.groups.handler.CleanupInvitationHandler
|
||||||
|
import org.thoughtcrime.securesms.groups.handler.DestroyedGroupSync
|
||||||
|
import org.thoughtcrime.securesms.groups.handler.RemoveGroupMemberHandler
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
||||||
|
import org.thoughtcrime.securesms.logging.AndroidLogger
|
||||||
|
import org.thoughtcrime.securesms.logging.PersistentLogger
|
||||||
|
import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger
|
||||||
|
import org.thoughtcrime.securesms.notifications.BackgroundPollManager
|
||||||
|
import org.thoughtcrime.securesms.notifications.NotificationChannels
|
||||||
|
import org.thoughtcrime.securesms.notifications.PushRegistrationHandler
|
||||||
|
import org.thoughtcrime.securesms.providers.BlobProvider
|
||||||
|
import org.thoughtcrime.securesms.service.ExpiringMessageManager
|
||||||
|
import org.thoughtcrime.securesms.service.KeyCachingService
|
||||||
|
import org.thoughtcrime.securesms.sskenvironment.ReadReceiptManager
|
||||||
|
import org.thoughtcrime.securesms.sskenvironment.TypingStatusRepository
|
||||||
|
import org.thoughtcrime.securesms.util.AppVisibilityManager
|
||||||
|
import org.thoughtcrime.securesms.util.Broadcaster
|
||||||
|
import org.thoughtcrime.securesms.util.VersionDataFetcher
|
||||||
|
import org.thoughtcrime.securesms.webrtc.CallMessageProcessor
|
||||||
|
import org.thoughtcrime.securesms.webrtc.WebRtcCallBridge
|
||||||
|
import org.webrtc.PeerConnectionFactory
|
||||||
|
import org.webrtc.PeerConnectionFactory.InitializationOptions
|
||||||
|
import java.io.IOException
|
||||||
|
import java.security.Security
|
||||||
|
import java.util.Arrays
|
||||||
|
import java.util.Timer
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlin.concurrent.Volatile
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
@HiltAndroidApp
|
||||||
|
class ApplicationContext : Application(), DefaultLifecycleObserver,
|
||||||
|
Toaster, Configuration.Provider {
|
||||||
|
@JvmField
|
||||||
|
var poller: Poller? = null
|
||||||
|
var broadcaster: Broadcaster? = null
|
||||||
|
var conversationListDebouncer: WindowDebouncer? = null
|
||||||
|
get() {
|
||||||
|
if (field == null) {
|
||||||
|
field = WindowDebouncer(1000, Timer())
|
||||||
|
}
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
private set
|
||||||
|
private var conversationListHandlerThread: HandlerThread? = null
|
||||||
|
private var conversationListHandler: Handler? = null
|
||||||
|
lateinit var persistentLogger: PersistentLogger
|
||||||
|
|
||||||
|
@Inject lateinit var workerFactory: HiltWorkerFactory
|
||||||
|
@Inject lateinit var lokiAPIDatabase: LokiAPIDatabase
|
||||||
|
@Inject lateinit var storage: Storage
|
||||||
|
@Inject lateinit var device: Device
|
||||||
|
@Inject lateinit var messageDataProvider: MessageDataProvider
|
||||||
|
@Inject lateinit var textSecurePreferences: TextSecurePreferences
|
||||||
|
@Inject lateinit var configFactory: ConfigFactory
|
||||||
|
@Inject lateinit var lastSentTimestampCache: LastSentTimestampCache
|
||||||
|
@Inject lateinit var versionDataFetcher: VersionDataFetcher
|
||||||
|
@Inject lateinit var pushRegistrationHandler: PushRegistrationHandler
|
||||||
|
@Inject lateinit var tokenFetcher: TokenFetcher
|
||||||
|
@Inject lateinit var groupManagerV2: GroupManagerV2
|
||||||
|
@Inject lateinit var profileManager: ProfileManagerProtocol
|
||||||
|
@Inject lateinit var callMessageProcessor: CallMessageProcessor
|
||||||
|
private var messagingModuleConfiguration: MessagingModuleConfiguration? = null
|
||||||
|
|
||||||
|
@Inject lateinit var configUploader: ConfigUploader
|
||||||
|
@Inject lateinit var adminStateSync: AdminStateSync
|
||||||
|
@Inject lateinit var destroyedGroupSync: DestroyedGroupSync
|
||||||
|
@Inject lateinit var removeGroupMemberHandler: RemoveGroupMemberHandler // Exists here only to start upon app starts
|
||||||
|
@Inject lateinit var snodeClock: SnodeClock
|
||||||
|
|
||||||
|
@get:Deprecated(message = "Use proper DI to inject this component")
|
||||||
|
@Inject
|
||||||
|
lateinit var expiringMessageManager: ExpiringMessageManager
|
||||||
|
|
||||||
|
@get:Deprecated(message = "Use proper DI to inject this component")
|
||||||
|
@Inject
|
||||||
|
lateinit var typingStatusRepository: TypingStatusRepository
|
||||||
|
|
||||||
|
@get:Deprecated(message = "Use proper DI to inject this component")
|
||||||
|
@Inject
|
||||||
|
lateinit var typingStatusSender: TypingStatusSender
|
||||||
|
|
||||||
|
@get:Deprecated(message = "Use proper DI to inject this component")
|
||||||
|
@Inject
|
||||||
|
lateinit var readReceiptManager: ReadReceiptManager
|
||||||
|
|
||||||
|
@Inject lateinit var messageNotifierLazy: Lazy<MessageNotifier>
|
||||||
|
@Inject lateinit var apiDB: LokiAPIDatabase
|
||||||
|
@Inject lateinit var emojiSearchDb: EmojiSearchDatabase
|
||||||
|
@Inject lateinit var webRtcCallBridge: WebRtcCallBridge
|
||||||
|
@Inject lateinit var legacyClosedGroupPollerV2: LegacyClosedGroupPollerV2
|
||||||
|
@Inject lateinit var legacyGroupDeprecationManager: LegacyGroupDeprecationManager
|
||||||
|
@Inject lateinit var cleanupInvitationHandler: CleanupInvitationHandler
|
||||||
|
@Inject lateinit var usernameUtils: UsernameUtils
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var backgroundPollManager: BackgroundPollManager // Exists here only to start upon app starts
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var appVisibilityManager: AppVisibilityManager // Exists here only to start upon app starts
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var groupPollerManager: GroupPollerManager // Exists here only to start upon app starts
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var expiredGroupManager: ExpiredGroupManager // Exists here only to start upon app starts
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
var isAppVisible: Boolean = false
|
||||||
|
|
||||||
|
override fun getSystemService(name: String): Any {
|
||||||
|
if (MessagingModuleConfiguration.MESSAGING_MODULE_SERVICE == name) {
|
||||||
|
return messagingModuleConfiguration!!
|
||||||
|
}
|
||||||
|
return super.getSystemService(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Deprecated(message = "Use proper DI to inject this component")
|
||||||
|
val prefs: TextSecurePreferences
|
||||||
|
get() = EntryPoints.get(
|
||||||
|
applicationContext,
|
||||||
|
AppComponent::class.java
|
||||||
|
).getPrefs()
|
||||||
|
|
||||||
|
@get:Deprecated(message = "Use proper DI to inject this component")
|
||||||
|
val databaseComponent: DatabaseComponent
|
||||||
|
get() = EntryPoints.get(
|
||||||
|
applicationContext,
|
||||||
|
DatabaseComponent::class.java
|
||||||
|
)
|
||||||
|
|
||||||
|
@get:Deprecated(message = "Use proper DI to inject this component")
|
||||||
|
val messageNotifier: MessageNotifier
|
||||||
|
get() = messageNotifierLazy.get()
|
||||||
|
|
||||||
|
val conversationListNotificationHandler: Handler
|
||||||
|
get() {
|
||||||
|
if (this.conversationListHandlerThread == null) {
|
||||||
|
conversationListHandlerThread = HandlerThread("ConversationListHandler")
|
||||||
|
conversationListHandlerThread!!.start()
|
||||||
|
}
|
||||||
|
if (this.conversationListHandler == null) {
|
||||||
|
conversationListHandler =
|
||||||
|
Handler(conversationListHandlerThread!!.looper)
|
||||||
|
}
|
||||||
|
return conversationListHandler!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toast(
|
||||||
|
@StringRes stringRes: Int,
|
||||||
|
toastLength: Int,
|
||||||
|
parameters: Map<String, String>
|
||||||
|
) {
|
||||||
|
val builder = Phrase.from(this, stringRes)
|
||||||
|
for ((key, value) in parameters) {
|
||||||
|
builder.put(key, value)
|
||||||
|
}
|
||||||
|
Toast.makeText(this, builder.format(), toastLength).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toast(message: CharSequence, toastLength: Int) {
|
||||||
|
Toast.makeText(this, message, toastLength).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
pushSuffix = BuildConfig.PUSH_KEY_SUFFIX
|
||||||
|
|
||||||
|
init(this)
|
||||||
|
configure(this)
|
||||||
|
super<Application>.onCreate()
|
||||||
|
|
||||||
|
messagingModuleConfiguration = MessagingModuleConfiguration(
|
||||||
|
this,
|
||||||
|
storage,
|
||||||
|
device,
|
||||||
|
messageDataProvider,
|
||||||
|
configFactory,
|
||||||
|
lastSentTimestampCache,
|
||||||
|
this,
|
||||||
|
tokenFetcher,
|
||||||
|
groupManagerV2,
|
||||||
|
snodeClock,
|
||||||
|
textSecurePreferences,
|
||||||
|
legacyClosedGroupPollerV2,
|
||||||
|
legacyGroupDeprecationManager,
|
||||||
|
usernameUtils
|
||||||
|
)
|
||||||
|
|
||||||
|
startKovenant()
|
||||||
|
initializeSecurityProvider()
|
||||||
|
initializeLogging()
|
||||||
|
initializeCrashHandling()
|
||||||
|
NotificationChannels.create(this)
|
||||||
|
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
|
||||||
|
configureKovenant()
|
||||||
|
broadcaster = Broadcaster(this)
|
||||||
|
val useTestNet = textSecurePreferences.getEnvironment() == Environment.TEST_NET
|
||||||
|
configure(apiDB, broadcaster!!, useTestNet)
|
||||||
|
configure(
|
||||||
|
typingStatusRepository, readReceiptManager, profileManager,
|
||||||
|
messageNotifier, expiringMessageManager
|
||||||
|
)
|
||||||
|
initializeWebRtc()
|
||||||
|
initializeBlobProvider()
|
||||||
|
resubmitProfilePictureIfNeeded()
|
||||||
|
loadEmojiSearchIndexIfNeeded()
|
||||||
|
refresh()
|
||||||
|
|
||||||
|
val networkConstraint = NetworkConstraint.Factory(this).create()
|
||||||
|
isConnectedToNetwork = { networkConstraint.isMet }
|
||||||
|
|
||||||
|
snodeClock.start()
|
||||||
|
pushRegistrationHandler.run()
|
||||||
|
configUploader.start()
|
||||||
|
destroyedGroupSync.start()
|
||||||
|
adminStateSync.start()
|
||||||
|
cleanupInvitationHandler.start()
|
||||||
|
|
||||||
|
// add our shortcut debug menu if we are not in a release build
|
||||||
|
if (BuildConfig.BUILD_TYPE != "release") {
|
||||||
|
// add the config settings shortcut
|
||||||
|
val intent = Intent(this, DebugActivity::class.java)
|
||||||
|
intent.setAction(Intent.ACTION_VIEW)
|
||||||
|
|
||||||
|
val shortcut = ShortcutInfoCompat.Builder(this, "shortcut_debug_menu")
|
||||||
|
.setShortLabel("Debug Menu")
|
||||||
|
.setLongLabel("Debug Menu")
|
||||||
|
.setIcon(IconCompat.createWithResource(this, R.drawable.ic_settings))
|
||||||
|
.setIntent(intent)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
ShortcutManagerCompat.pushDynamicShortcut(this, shortcut)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getWorkManagerConfiguration(): Configuration {
|
||||||
|
return Configuration.Builder()
|
||||||
|
.setWorkerFactory(workerFactory)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStart(owner: LifecycleOwner) {
|
||||||
|
isAppVisible = true
|
||||||
|
Log.i(TAG, "App is now visible.")
|
||||||
|
KeyCachingService.onAppForegrounded(this)
|
||||||
|
|
||||||
|
// If the user account hasn't been created or onboarding wasn't finished then don't start
|
||||||
|
// the pollers
|
||||||
|
if (textSecurePreferences.getLocalNumber() == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
startPollingIfNeeded()
|
||||||
|
|
||||||
|
queue {
|
||||||
|
startPolling()
|
||||||
|
Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch last version data
|
||||||
|
versionDataFetcher.startTimedVersionCheck()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStop(owner: LifecycleOwner) {
|
||||||
|
isAppVisible = false
|
||||||
|
Log.i(TAG, "App is no longer visible.")
|
||||||
|
KeyCachingService.onAppBackgrounded(this)
|
||||||
|
messageNotifier.setVisibleThread(-1)
|
||||||
|
if (poller != null) {
|
||||||
|
poller!!.stopIfNeeded()
|
||||||
|
}
|
||||||
|
legacyClosedGroupPollerV2.stopAll()
|
||||||
|
versionDataFetcher.stopTimedVersionCheck()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTerminate() {
|
||||||
|
stopKovenant() // Loki
|
||||||
|
stopPolling()
|
||||||
|
versionDataFetcher.stopTimedVersionCheck()
|
||||||
|
super.onTerminate()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Loki
|
||||||
|
private fun initializeSecurityProvider() {
|
||||||
|
try {
|
||||||
|
Class.forName("org.signal.aesgcmprovider.AesGcmCipher")
|
||||||
|
} catch (e: ClassNotFoundException) {
|
||||||
|
Log.e(TAG, "Failed to find AesGcmCipher class")
|
||||||
|
throw ProviderInitializationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
val aesPosition = Security.insertProviderAt(AesGcmProvider(), 1)
|
||||||
|
Log.i(
|
||||||
|
TAG,
|
||||||
|
"Installed AesGcmProvider: $aesPosition"
|
||||||
|
)
|
||||||
|
|
||||||
|
if (aesPosition < 0) {
|
||||||
|
Log.e(TAG, "Failed to install AesGcmProvider()")
|
||||||
|
throw ProviderInitializationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
val 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 fun initializeLogging() {
|
||||||
|
persistentLogger = PersistentLogger(this)
|
||||||
|
Log.initialize(AndroidLogger(), persistentLogger)
|
||||||
|
initLogger()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initializeCrashHandling() {
|
||||||
|
val originalHandler = Thread.getDefaultUncaughtExceptionHandler()
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler(UncaughtExceptionLogger(originalHandler!!))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initializeWebRtc() {
|
||||||
|
try {
|
||||||
|
PeerConnectionFactory.initialize(
|
||||||
|
InitializationOptions.builder(this).createInitializationOptions()
|
||||||
|
)
|
||||||
|
} catch (e: UnsatisfiedLinkError) {
|
||||||
|
Log.w(TAG, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initializeBlobProvider() {
|
||||||
|
AsyncTask.THREAD_POOL_EXECUTOR.execute {
|
||||||
|
BlobProvider.getInstance().onSessionStart(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ProviderInitializationException : RuntimeException()
|
||||||
|
|
||||||
|
private fun setUpPollingIfNeeded() {
|
||||||
|
val userPublicKey = textSecurePreferences!!.getLocalNumber() ?: return
|
||||||
|
if (poller == null) {
|
||||||
|
poller = Poller(configFactory!!, storage!!, lokiAPIDatabase!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startPollingIfNeeded() {
|
||||||
|
setUpPollingIfNeeded()
|
||||||
|
if (poller != null) {
|
||||||
|
poller!!.startIfNeeded()
|
||||||
|
}
|
||||||
|
legacyClosedGroupPollerV2!!.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun retrieveUserProfile() {
|
||||||
|
setUpPollingIfNeeded()
|
||||||
|
if (poller != null) {
|
||||||
|
poller!!.retrieveUserProfile()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resubmitProfilePictureIfNeeded() {
|
||||||
|
resubmitProfilePictureIfNeeded(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadEmojiSearchIndexIfNeeded() {
|
||||||
|
Executors.newSingleThreadExecutor().execute {
|
||||||
|
if (emojiSearchDb.query("face", 1).isEmpty()) {
|
||||||
|
try {
|
||||||
|
assets.open("emoji/emoji_search_index.json").use { inputStream ->
|
||||||
|
val searchIndex = Arrays.asList(
|
||||||
|
*JsonUtil.fromJson(
|
||||||
|
inputStream,
|
||||||
|
Array<EmojiSearchData>::class.java
|
||||||
|
)
|
||||||
|
)
|
||||||
|
emojiSearchDb.setSearchIndex(searchIndex)
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.e(
|
||||||
|
"Loki",
|
||||||
|
"Failed to load emoji search index"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // endregion
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val PREFERENCES_NAME: String = "SecureSMS-Preferences"
|
||||||
|
|
||||||
|
private val TAG: String = ApplicationContext::class.java.simpleName
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getInstance(context: Context): ApplicationContext {
|
||||||
|
return context.applicationContext as ApplicationContext
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue