Initialize AppDependencies if needed in AvatarProvider.

This commit is contained in:
Greyson Parrelli
2024-09-05 15:45:48 -04:00
committed by Cody Henthorne
parent 9261c34213
commit 6112ee9bd3
26 changed files with 154 additions and 146 deletions

View File

@@ -91,6 +91,7 @@ android {
kotlinOptions {
jvmTarget = signalKotlinJvmTarget
freeCompilerArgs = listOf("-Xjvm-default=all")
}
keystores["debug"]?.let { properties ->

View File

@@ -167,7 +167,7 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
.addBlocking("scrubber", () -> Scrubber.setIdentifierHmacKeyProvider(() -> SignalStore.svr().getOrCreateMasterKey().deriveLoggingKey()))
.addBlocking("first-launch", this::initializeFirstEverAppLaunch)
.addBlocking("app-migrations", this::initializeApplicationMigrations)
.addBlocking("lifecycle-observer", () -> AppDependencies.getAppForegroundObserver().addListener(this))
.addBlocking("lifecycle-observer", () -> AppForegroundObserver.addListener(this))
.addBlocking("message-retriever", this::initializeMessageRetrieval)
.addBlocking("dynamic-theme", () -> DynamicTheme.setDefaultDayNightMode(this))
.addBlocking("proxy-init", () -> {
@@ -366,7 +366,11 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
@VisibleForTesting
void initializeAppDependencies() {
AppDependencies.init(this, new ApplicationDependencyProvider(this));
if (!AppDependencies.isInitialized()) {
Log.i(TAG, "Initializing AppDependencies.");
AppDependencies.init(this, new ApplicationDependencyProvider(this));
}
AppForegroundObserver.begin();
}
private void initializeFirstEverAppLaunch() {

View File

@@ -31,6 +31,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.registration.ui.RegistrationActivity;
import org.thoughtcrime.securesms.restore.RestoreActivity;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.thoughtcrime.securesms.util.AppStartup;
import org.thoughtcrime.securesms.util.RemoteConfig;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@@ -92,8 +93,8 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
@Override
public void onMasterSecretCleared() {
Log.d(TAG, "onMasterSecretCleared()");
if (AppDependencies.getAppForegroundObserver().isForegrounded()) routeApplicationState(true);
else finish();
if (AppForegroundObserver.isForegrounded()) routeApplicationState(true);
else finish();
}
protected <T extends Fragment> T initFragment(@IdRes int target,
@@ -209,7 +210,7 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
private Intent getPromptPassphraseIntent() {
Intent intent = getRoutedIntent(PassphrasePromptActivity.class, getIntent());
intent.putExtra(PassphrasePromptActivity.FROM_FOREGROUND, AppDependencies.getAppForegroundObserver().isForegrounded());
intent.putExtra(PassphrasePromptActivity.FROM_FOREGROUND, AppForegroundObserver.isForegrounded());
return intent;
}

View File

@@ -17,6 +17,7 @@ import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.jobs.ApkUpdateJob
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.AppForegroundObserver
import org.thoughtcrime.securesms.util.Environment
import org.thoughtcrime.securesms.util.FileUtils
import java.io.FileInputStream
@@ -61,7 +62,7 @@ object ApkUpdateInstaller {
}
if (!userInitiated && !shouldAutoUpdate()) {
Log.w(TAG, "Not user-initiated and not eligible for auto-update. Prompting. (API=${Build.VERSION.SDK_INT}, Foreground=${AppDependencies.appForegroundObserver.isForegrounded}, AutoUpdate=${SignalStore.apkUpdate.autoUpdate})")
Log.w(TAG, "Not user-initiated and not eligible for auto-update. Prompting. (API=${Build.VERSION.SDK_INT}, Foreground=${AppForegroundObserver.isForegrounded()}, AutoUpdate=${SignalStore.apkUpdate.autoUpdate})")
ApkUpdateNotifications.showInstallPrompt(context, downloadId)
return
}
@@ -145,6 +146,6 @@ object ApkUpdateInstaller {
private fun shouldAutoUpdate(): Boolean {
// TODO Auto-updates temporarily restricted to nightlies. Once we have designs for allowing users to opt-out of auto-updates, we can re-enable this
return Environment.IS_NIGHTLY && Build.VERSION.SDK_INT >= 31 && SignalStore.apkUpdate.autoUpdate && !AppDependencies.appForegroundObserver.isForegrounded
return Environment.IS_NIGHTLY && Build.VERSION.SDK_INT >= 31 && SignalStore.apkUpdate.autoUpdate && !AppForegroundObserver.isForegrounded()
}
}

View File

@@ -529,7 +529,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
@Override
public void onStart() {
super.onStart();
AppDependencies.getAppForegroundObserver().addListener(appForegroundObserver);
AppForegroundObserver.addListener(appForegroundObserver);
itemAnimator.disable();
}
@@ -546,7 +546,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
@Override
public void onStop() {
super.onStop();
AppDependencies.getAppForegroundObserver().removeListener(appForegroundObserver);
AppForegroundObserver.removeListener(appForegroundObserver);
}
@Override

View File

@@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.dependencies
import android.annotation.SuppressLint
import android.app.Application
import androidx.annotation.MainThread
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.subjects.BehaviorSubject
import io.reactivex.rxjava3.subjects.Subject
@@ -35,7 +34,6 @@ import org.thoughtcrime.securesms.service.ScheduledMessageManager
import org.thoughtcrime.securesms.service.TrimThreadsByDateManager
import org.thoughtcrime.securesms.service.webrtc.SignalCallManager
import org.thoughtcrime.securesms.shakereport.ShakeToReport
import org.thoughtcrime.securesms.util.AppForegroundObserver
import org.thoughtcrime.securesms.util.EarlyMessageCache
import org.thoughtcrime.securesms.util.FrameRateTracker
import org.thoughtcrime.securesms.video.exo.GiphyMp4Cache
@@ -69,21 +67,15 @@ object AppDependencies {
private lateinit var _application: Application
private lateinit var provider: Provider
// Needs special initialization because it needs to be created on the main thread
private lateinit var _appForegroundObserver: AppForegroundObserver
@JvmStatic
@MainThread
@Synchronized
fun init(application: Application, provider: Provider) {
if (this::_application.isInitialized || this::provider.isInitialized) {
throw IllegalStateException("Already initialized!")
return
}
_application = application
AppDependencies.provider = provider
_appForegroundObserver = provider.provideAppForegroundObserver()
_appForegroundObserver.begin()
}
@JvmStatic
@@ -94,10 +86,6 @@ object AppDependencies {
val application: Application
get() = _application
@JvmStatic
val appForegroundObserver: AppForegroundObserver
get() = _appForegroundObserver
@JvmStatic
val recipientCache: LiveRecipientCache by lazy {
provider.provideRecipientCache()
@@ -339,7 +327,6 @@ object AppDependencies {
fun provideDatabaseObserver(): DatabaseObserver
fun providePayments(signalServiceAccountManager: SignalServiceAccountManager): Payments
fun provideShakeToReport(): ShakeToReport
fun provideAppForegroundObserver(): AppForegroundObserver
fun provideSignalCallManager(): SignalCallManager
fun providePendingRetryReceiptManager(): PendingRetryReceiptManager
fun providePendingRetryReceiptCache(): PendingRetryReceiptCache

View File

@@ -275,11 +275,6 @@ public class ApplicationDependencyProvider implements AppDependencies.Provider {
return new ShakeToReport(context);
}
@Override
public @NonNull AppForegroundObserver provideAppForegroundObserver() {
return new AppForegroundObserver();
}
@Override
public @NonNull SignalCallManager provideSignalCallManager() {
return new SignalCallManager(context);

View File

@@ -13,6 +13,7 @@ import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.messages.WebSocketDrainer;
import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.thoughtcrime.securesms.util.ServiceUtil;
/**
@@ -39,7 +40,7 @@ public class FcmJobService extends JobService {
public boolean onStartJob(JobParameters params) {
Log.d(TAG, "onStartJob()");
if (AppDependencies.getAppForegroundObserver().isForegrounded()) {
if (AppForegroundObserver.isForegrounded()) {
Log.i(TAG, "App is foregrounded. No need to run.");
return false;
}

View File

@@ -12,9 +12,9 @@ import androidx.annotation.WorkerThread
import androidx.core.content.ContextCompat
import org.signal.core.util.PendingIntentFlags
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.service.GenericForegroundService
import org.thoughtcrime.securesms.service.NotificationController
import org.thoughtcrime.securesms.util.AppForegroundObserver
import org.thoughtcrime.securesms.util.ServiceUtil
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
@@ -137,7 +137,7 @@ object ForegroundServiceUtil {
private fun blockUntilCapable(context: Context, timeout: Long = DEFAULT_TIMEOUT): Boolean {
val alarmManager = ServiceUtil.getAlarmManager(context)
if (Build.VERSION.SDK_INT < 31 || AppDependencies.appForegroundObserver.isForegrounded) {
if (Build.VERSION.SDK_INT < 31 || AppForegroundObserver.isForegrounded()) {
return true
}

View File

@@ -125,7 +125,7 @@ public final class MessageFetchJob extends BaseJob {
@Override
public void close() {
AppDependencies.getAppForegroundObserver().removeListener(this);
AppForegroundObserver.removeListener(this);
closeNotificationController();
}
@@ -157,7 +157,7 @@ public final class MessageFetchJob extends BaseJob {
static ForegroundServiceController create(@NonNull Context context) {
ForegroundServiceController instance = new ForegroundServiceController(context);
AppDependencies.getAppForegroundObserver().addListener(instance);
AppForegroundObserver.addListener(instance);
return instance;
}

View File

@@ -29,6 +29,7 @@ import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
@@ -196,7 +197,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob {
private void generateFullContactUpdate()
throws IOException, UntrustedIdentityException, NetworkException
{
boolean isAppVisible = AppDependencies.getAppForegroundObserver().isForegrounded();
boolean isAppVisible = AppForegroundObserver.isForegrounded();
long timeSinceLastSync = System.currentTimeMillis() - TextSecurePreferences.getLastFullContactSyncTime(context);
Log.d(TAG, "Requesting a full contact sync. forced = " + forceSync + ", appVisible = " + isAppVisible + ", timeSinceLastSync = " + timeSinceLastSync + " ms");

View File

@@ -18,6 +18,7 @@ import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.jobmanager.JobTracker;
import org.thoughtcrime.securesms.jobs.MessageFetchJob;
import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.thoughtcrime.securesms.util.RemoteConfig;
import java.util.Locale;
@@ -43,7 +44,7 @@ public final class RoutineMessageFetchReceiver extends BroadcastReceiver {
startOrUpdateAlarm(context);
} else if (BROADCAST_ACTION.equals(intent.getAction())) {
if (AppDependencies.getAppForegroundObserver().isForegrounded()) {
if (AppForegroundObserver.isForegrounded()) {
Log.i(TAG, "App is foregrounded");
return;
}

View File

@@ -130,7 +130,7 @@ class IncomingMessageObserver(private val context: Application) {
}
}
AppDependencies.appForegroundObserver.addListener(object : AppForegroundObserver.Listener {
AppForegroundObserver.addListener(object : AppForegroundObserver.Listener {
override fun onForeground() {
onAppForegrounded()
}

View File

@@ -15,6 +15,7 @@ import android.graphics.Bitmap
import android.net.Uri
import android.os.ParcelFileDescriptor
import org.signal.core.util.concurrent.SignalExecutors
import org.signal.core.util.logging.AndroidLogger
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.BuildConfig
@@ -22,7 +23,10 @@ import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider
import org.thoughtcrime.securesms.crypto.DatabaseSecretProvider
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.SqlCipherLibraryLoader
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.dependencies.ApplicationDependencyProvider
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.logging.PersistentLogger
import org.thoughtcrime.securesms.profiles.AvatarHelper
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientCreator
@@ -31,6 +35,7 @@ import org.thoughtcrime.securesms.service.KeyCachingService
import org.thoughtcrime.securesms.util.AdaptiveBitmapMetrics
import org.thoughtcrime.securesms.util.AvatarUtil
import org.thoughtcrime.securesms.util.MediaUtil
import org.thoughtcrime.securesms.util.RemoteConfig
import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
@@ -73,6 +78,13 @@ class AvatarProvider : BaseContentProvider() {
SignalStore.init(application)
Log.initialize(RemoteConfig::internalUser, AndroidLogger(), PersistentLogger(application))
if (!AppDependencies.isInitialized) {
Log.i(TAG, "Initializing AppDependencies.")
AppDependencies.init(application, ApplicationDependencyProvider(application))
}
return application
}

View File

@@ -46,6 +46,7 @@ import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.migrations.ApplicationMigrations;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.ServiceUtil;
@@ -229,7 +230,7 @@ public class KeyCachingService extends Service {
}
private static void startTimeoutIfAppropriate(@NonNull Context context) {
boolean appVisible = AppDependencies.getAppForegroundObserver().isForegrounded();
boolean appVisible = AppForegroundObserver.isForegrounded();
boolean secretSet = KeyCachingService.masterSecret != null;
boolean timeoutEnabled = SignalStore.settings().getPassphraseTimeoutEnabled();

View File

@@ -10,6 +10,7 @@ import org.thoughtcrime.securesms.events.WebRtcViewModel;
import org.thoughtcrime.securesms.ringrtc.Camera;
import org.thoughtcrime.securesms.ringrtc.RemotePeer;
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState;
import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager;
import org.thoughtcrime.securesms.webrtc.locks.LockManager;
@@ -43,7 +44,7 @@ public class CallSetupActionProcessorDelegate extends WebRtcActionProcessor {
currentState.getCallSetupState(activePeer).isAcceptWithVideo() || currentState.getLocalDeviceState().getCameraState().isEnabled()
);
AppDependencies.getAppForegroundObserver().removeListener(webRtcInteractor.getForegroundListener());
AppForegroundObserver.removeListener(webRtcInteractor.getForegroundListener());
webRtcInteractor.startAudioCommunication();
webRtcInteractor.activateCall(activePeer.getId());

View File

@@ -25,6 +25,7 @@ import org.thoughtcrime.securesms.ringrtc.RemotePeer;
import org.thoughtcrime.securesms.service.webrtc.state.CallSetupState;
import org.thoughtcrime.securesms.service.webrtc.state.VideoState;
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState;
import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.thoughtcrime.securesms.util.NetworkUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.webrtc.locks.LockManager;
@@ -191,7 +192,7 @@ public class IncomingCallActionProcessor extends DeviceAwareActionProcessor {
boolean started = webRtcInteractor.startWebRtcCallActivityIfPossible();
if (!started) {
Log.i(TAG, "Unable to start call activity due to OS version or not being in the foreground");
AppDependencies.getAppForegroundObserver().addListener(webRtcInteractor.getForegroundListener());
AppForegroundObserver.addListener(webRtcInteractor.getForegroundListener());
}
}

View File

@@ -23,6 +23,7 @@ import org.thoughtcrime.securesms.notifications.DoNotDisturbUtil;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.ringrtc.RemotePeer;
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState;
import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.thoughtcrime.securesms.util.NetworkUtil;
import org.thoughtcrime.securesms.webrtc.locks.LockManager;
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
@@ -128,7 +129,7 @@ public final class IncomingGroupCallActionProcessor extends DeviceAwareActionPro
boolean started = webRtcInteractor.startWebRtcCallActivityIfPossible();
if (!started) {
Log.i(TAG, "Unable to start call activity due to OS version or not being in the foreground");
AppDependencies.getAppForegroundObserver().addListener(webRtcInteractor.getForegroundListener());
AppForegroundObserver.addListener(webRtcInteractor.getForegroundListener());
}
}

View File

@@ -991,7 +991,7 @@ public final class SignalCallManager implements CallManager.Observer, GroupCall.
Log.i(TAG, "Starting call activity from foreground listener");
startCallCardActivityIfPossible();
}
AppDependencies.getAppForegroundObserver().removeListener(this);
AppForegroundObserver.removeListener(this);
return s;
});
}
@@ -1218,7 +1218,7 @@ public final class SignalCallManager implements CallManager.Observer, GroupCall.
}
public void relaunchPipOnForeground() {
AppDependencies.getAppForegroundObserver().addListener(new RelaunchListener(AppDependencies.getAppForegroundObserver().isForegrounded()));
AppForegroundObserver.addListener(new RelaunchListener(AppForegroundObserver.isForegrounded()));
}
private void processSendMessageFailureWithChangeDetection(@NonNull RemotePeer remotePeer,
@@ -1265,7 +1265,7 @@ public final class SignalCallManager implements CallManager.Observer, GroupCall.
return s;
});
}
AppDependencies.getAppForegroundObserver().removeListener(this);
AppForegroundObserver.removeListener(this);
}
}

View File

@@ -44,6 +44,7 @@ import org.thoughtcrime.securesms.service.webrtc.WebRtcData.ReceivedOfferMetadat
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcEphemeralState;
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState;
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceStateBuilder;
import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.thoughtcrime.securesms.util.NetworkUtil;
import org.thoughtcrime.securesms.util.TelephonyUtil;
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager;
@@ -692,7 +693,7 @@ public abstract class WebRtcActionProcessor {
activePeer = remotePeer;
}
AppDependencies.getAppForegroundObserver().removeListener(webRtcInteractor.getForegroundListener());
AppForegroundObserver.removeListener(webRtcInteractor.getForegroundListener());
if (activePeer.getState() != CallState.IDLE) {
webRtcInteractor.updatePhoneState(LockManager.PhoneState.PROCESSING);

View File

@@ -21,6 +21,7 @@ import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.logsubmit.SubmitDebugLogRepository;
import org.thoughtcrime.securesms.sharing.MultiShareArgs;
import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.views.SimpleProgressDialog;
@@ -142,7 +143,7 @@ public final class ShakeToReport implements ShakeDetector.Listener {
}
private void enableIfVisible() {
if (AppDependencies.getAppForegroundObserver().isForegrounded()) {
if (AppForegroundObserver.isForegrounded()) {
enable();
}
}

View File

@@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.stories.viewer
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.util.AppForegroundObserver
/**
@@ -11,7 +10,7 @@ object StoryMutePolicy : AppForegroundObserver.Listener {
var isContentMuted: Boolean = true
fun initialize() {
AppDependencies.appForegroundObserver.addListener(this)
AppForegroundObserver.addListener(this)
}
override fun onBackground() {

View File

@@ -1,92 +0,0 @@
package org.thoughtcrime.securesms.util;
import androidx.annotation.AnyThread;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ProcessLifecycleOwner;
import org.signal.core.util.ThreadUtil;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* A wrapper around {@link ProcessLifecycleOwner} that allows for safely adding/removing observers
* on multiple threads.
*/
public class AppForegroundObserver {
private final Set<Listener> listeners = new CopyOnWriteArraySet<>();
private volatile Boolean isForegrounded = null;
@MainThread
public void begin() {
ThreadUtil.assertMainThread();
ProcessLifecycleOwner.get().getLifecycle().addObserver(new DefaultLifecycleObserver() {
@Override
public void onStart(@NonNull LifecycleOwner owner) {
onForeground();
}
@Override
public void onStop(@NonNull LifecycleOwner owner) {
onBackground();
}
});
}
/**
* Adds a listener to be notified of when the app moves between the background and the foreground.
* To mimic the behavior of subscribing to {@link ProcessLifecycleOwner}, this listener will be
* immediately notified of the foreground state if we've experienced a foreground/background event
* already.
*/
@AnyThread
public void addListener(@NonNull Listener listener) {
listeners.add(listener);
if (isForegrounded != null) {
if (isForegrounded) {
listener.onForeground();
} else {
listener.onBackground();
}
}
}
@AnyThread
public void removeListener(@NonNull Listener listener) {
listeners.remove(listener);
}
public boolean isForegrounded() {
return isForegrounded != null && isForegrounded;
}
@MainThread
private void onForeground() {
isForegrounded = true;
for (Listener listener : listeners) {
listener.onForeground();
}
}
@MainThread
private void onBackground() {
isForegrounded = false;
for (Listener listener : listeners) {
listener.onBackground();
}
}
public interface Listener {
default void onForeground() {}
default void onBackground() {}
}
}

View File

@@ -0,0 +1,96 @@
package org.thoughtcrime.securesms.util
import androidx.annotation.AnyThread
import androidx.annotation.MainThread
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ProcessLifecycleOwner
import org.signal.core.util.ThreadUtil
import java.util.concurrent.CopyOnWriteArraySet
import kotlin.concurrent.Volatile
/**
* A wrapper around [ProcessLifecycleOwner] that allows for safely adding/removing observers
* on multiple threads.
*/
object AppForegroundObserver {
private val listeners: MutableSet<Listener> = CopyOnWriteArraySet()
@Volatile
private var isInitialized: Boolean = false
@Volatile
private var isForegrounded: Boolean = false
@MainThread
@JvmStatic
fun begin() {
ThreadUtil.assertMainThread()
ProcessLifecycleOwner.get().lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onStart(owner: LifecycleOwner) {
onForeground()
}
override fun onStop(owner: LifecycleOwner) {
onBackground()
}
})
isInitialized = true
}
/**
* Adds a listener to be notified of when the app moves between the background and the foreground.
* To mimic the behavior of subscribing to [ProcessLifecycleOwner], this listener will be
* immediately notified of the foreground state if we've experienced a foreground/background event
* already.
*/
@AnyThread
@JvmStatic
fun addListener(listener: Listener) {
listeners.add(listener)
if (isInitialized) {
if (isForegrounded) {
listener.onForeground()
} else {
listener.onBackground()
}
}
}
@AnyThread
@JvmStatic
fun removeListener(listener: Listener) {
listeners.remove(listener)
}
@JvmStatic
fun isForegrounded(): Boolean {
return isInitialized && isForegrounded
}
@MainThread
private fun onForeground() {
isForegrounded = true
for (listener in listeners) {
listener.onForeground()
}
}
@MainThread
private fun onBackground() {
isForegrounded = false
for (listener in listeners) {
listener.onBackground()
}
}
interface Listener {
fun onForeground() {}
fun onBackground() {}
}
}

View File

@@ -30,7 +30,7 @@ class SimpleExoPlayerPool(context: Context) : ExoPlayerPool<ExoPlayer>(MAXIMUM_R
private val mediaSourceFactory: MediaSource.Factory = DefaultMediaSourceFactory(dataSourceFactory)
init {
AppDependencies.appForegroundObserver.addListener(this)
AppForegroundObserver.addListener(this)
}
/**

View File

@@ -28,7 +28,6 @@ import org.thoughtcrime.securesms.service.ScheduledMessageManager
import org.thoughtcrime.securesms.service.TrimThreadsByDateManager
import org.thoughtcrime.securesms.service.webrtc.SignalCallManager
import org.thoughtcrime.securesms.shakereport.ShakeToReport
import org.thoughtcrime.securesms.util.AppForegroundObserver
import org.thoughtcrime.securesms.util.EarlyMessageCache
import org.thoughtcrime.securesms.util.FrameRateTracker
import org.thoughtcrime.securesms.video.exo.GiphyMp4Cache
@@ -141,10 +140,6 @@ class MockApplicationDependencyProvider : AppDependencies.Provider {
return mockk()
}
override fun provideAppForegroundObserver(): AppForegroundObserver {
return Mockito.mock(AppForegroundObserver::class.java)
}
override fun provideSignalCallManager(): SignalCallManager {
return mockk()
}