Improve and streamline Application#onCreate.

fork-5.53.8
Greyson Parrelli 2020-12-20 14:45:51 -05:00 zatwierdzone przez Alan Evans
rodzic c27300c19d
commit cdd7b2deb9
13 zmienionych plików z 205 dodań i 117 usunięć

Wyświetl plik

@ -16,12 +16,12 @@
*/
package org.thoughtcrime.securesms;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
@ -44,6 +44,7 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencyProvider;
import org.thoughtcrime.securesms.gcm.FcmJobService;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
import org.thoughtcrime.securesms.jobs.FcmRefreshJob;
import org.thoughtcrime.securesms.jobs.GroupV1MigrationJob;
@ -69,6 +70,7 @@ import org.thoughtcrime.securesms.service.RotateSenderCertificateListener;
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.util.AppStartup;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.SignalUncaughtExceptionHandler;
@ -118,40 +120,42 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
super.onCreate();
initializeSecurityProvider();
initializeLogging();
Log.i(TAG, "onCreate()");
initializeCrashHandling();
initializeAppDependencies();
initializeFirstEverAppLaunch();
initializeApplicationMigrations();
initializeMessageRetrieval();
initializeExpiringMessageManager();
initializeRevealableMessageManager();
initializeGcmCheck();
initializeSignedPreKeyCheck();
initializePeriodicTasks();
initializeCircumvention();
initializeRingRtc();
initializePendingMessages();
initializeBlobProvider();
initializeCleanup();
initializeGlideCodecs();
FeatureFlags.init();
NotificationChannels.create(this);
RefreshPreKeysJob.scheduleIfNecessary();
StorageSyncHelper.scheduleRoutineSync();
RegistrationUtil.maybeMarkRegistrationComplete(this);
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
if (Build.VERSION.SDK_INT < 21) {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
ApplicationDependencies.getJobManager().beginJobLoop();
DynamicTheme.setDefaultDayNightMode(this);
new AppStartup().addBlocking("security-provider", this::initializeSecurityProvider)
.addBlocking("logging", () -> {
initializeLogging();
Log.i(TAG, "onCreate()");
})
.addBlocking("crash-handling", this::initializeCrashHandling)
.addBlocking("eat-db", () -> DatabaseFactory.getInstance(this))
.addBlocking("app-dependencies", this::initializeAppDependencies)
.addBlocking("first-launch", this::initializeFirstEverAppLaunch)
.addBlocking("app-migrations", this::initializeApplicationMigrations)
.addBlocking("ring-rtc", this::initializeRingRtc)
.addBlocking("mark-registration", () -> RegistrationUtil.maybeMarkRegistrationComplete(this))
.addBlocking("lifecycle-observer", () -> ProcessLifecycleOwner.get().getLifecycle().addObserver(this))
.addBlocking("dynamic-theme", () -> DynamicTheme.setDefaultDayNightMode(this))
.addBlocking("vector-compat", () -> {
if (Build.VERSION.SDK_INT < 21) {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
})
.addDeferred(this::initializeMessageRetrieval)
.addDeferred(this::initializeExpiringMessageManager)
.addDeferred(this::initializeRevealableMessageManager)
.addDeferred(this::initializeGcmCheck)
.addDeferred(this::initializeSignedPreKeyCheck)
.addDeferred(this::initializePeriodicTasks)
.addDeferred(this::initializeCircumvention)
.addDeferred(this::initializePendingMessages)
.addDeferred(this::initializeBlobProvider)
.addDeferred(this::initializeCleanup)
.addDeferred(this::initializeGlideCodecs)
.addDeferred(FeatureFlags::init)
.addDeferred(() -> NotificationChannels.create(this))
.addDeferred(RefreshPreKeysJob::scheduleIfNecessary)
.addDeferred(StorageSyncHelper::scheduleRoutineSync)
.addDeferred(() -> ApplicationDependencies.getJobManager().beginJobLoop())
.execute();
Log.d(TAG, "onCreate() took " + (System.currentTimeMillis() - startTime) + " ms");
Tracer.getInstance().end("Application#onCreate()");
@ -159,17 +163,24 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
@Override
public void onStart(@NonNull LifecycleOwner owner) {
long startTime = System.currentTimeMillis();
isAppVisible = true;
Log.i(TAG, "App is now visible.");
FeatureFlags.refreshIfNecessary();
ApplicationDependencies.getRecipientCache().warmUp();
RetrieveProfileJob.enqueueRoutineFetchIfNecessary(this);
GroupV1MigrationJob.enqueueRoutineMigrationsIfNecessary(this);
executePendingContactSync();
KeyCachingService.onAppForegrounded(this);
ApplicationDependencies.getFrameRateTracker().begin();
ApplicationDependencies.getMegaphoneRepository().onAppForegrounded();
checkBuildExpiration();
SignalExecutors.BOUNDED.execute(() -> {
FeatureFlags.refreshIfNecessary();
ApplicationDependencies.getRecipientCache().warmUp();
RetrieveProfileJob.enqueueRoutineFetchIfNecessary(this);
GroupV1MigrationJob.enqueueRoutineMigrationsIfNecessary(this);
executePendingContactSync();
KeyCachingService.onAppForegrounded(this);
ApplicationDependencies.getMegaphoneRepository().onAppForegrounded();
checkBuildExpiration();
});
Log.d(TAG, "onStart() took " + (System.currentTimeMillis() - startTime) + " ms");
}
@Override
@ -335,17 +346,15 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
}
}
@SuppressLint("StaticFieldLeak")
@WorkerThread
private void initializeCircumvention() {
SignalExecutors.BOUNDED.execute(() -> {
if (new SignalServiceNetworkAccess(ApplicationContext.this).isCensored(ApplicationContext.this)) {
try {
ProviderInstaller.installIfNeeded(ApplicationContext.this);
} catch (Throwable t) {
Log.w(TAG, t);
}
if (new SignalServiceNetworkAccess(ApplicationContext.this).isCensored(ApplicationContext.this)) {
try {
ProviderInstaller.installIfNeeded(ApplicationContext.this);
} catch (Throwable t) {
Log.w(TAG, t);
}
});
}
}
private void executePendingContactSync() {
@ -366,17 +375,15 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
}
}
@WorkerThread
private void initializeBlobProvider() {
SignalExecutors.BOUNDED.execute(() -> {
BlobProvider.getInstance().onSessionStart(this);
});
BlobProvider.getInstance().onSessionStart(this);
}
@WorkerThread
private void initializeCleanup() {
SignalExecutors.BOUNDED.execute(() -> {
int deleted = DatabaseFactory.getAttachmentDatabase(this).deleteAbandonedPreuploadedAttachments();
Log.i(TAG, "Deleted " + deleted + " abandoned attachments.");
});
int deleted = DatabaseFactory.getAttachmentDatabase(this).deleteAbandonedPreuploadedAttachments();
Log.i(TAG, "Deleted " + deleted + " abandoned attachments.");
}
private void initializeGlideCodecs() {

Wyświetl plik

@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.components;
import android.annotation.SuppressLint;
import android.app.Application;
import android.content.Context;
import androidx.annotation.NonNull;
@ -21,11 +22,9 @@ public class TypingStatusSender {
private static final long REFRESH_TYPING_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
private static final long PAUSE_TYPING_TIMEOUT = TimeUnit.SECONDS.toMillis(3);
private final Context context;
private final Map<Long, TimerPair> selfTypingTimers;
public TypingStatusSender(@NonNull Context context) {
this.context = context;
public TypingStatusSender() {
this.selfTypingTimers = new HashMap<>();
}

Wyświetl plik

@ -38,7 +38,7 @@ public class DatabaseFactory {
private static final Object lock = new Object();
private static DatabaseFactory instance;
private static volatile DatabaseFactory instance;
private final SQLCipherOpenHelper databaseHelper;
private final SmsDatabase sms;
@ -67,12 +67,14 @@ public class DatabaseFactory {
private final MentionDatabase mentionDatabase;
public static DatabaseFactory getInstance(Context context) {
synchronized (lock) {
if (instance == null)
instance = new DatabaseFactory(context.getApplicationContext());
return instance;
if (instance == null) {
synchronized (lock) {
if (instance == null) {
instance = new DatabaseFactory(context.getApplicationContext());
}
}
}
return instance;
}
public static MmsSmsDatabase getMmsSmsDatabase(Context context) {
@ -193,8 +195,8 @@ public class DatabaseFactory {
private DatabaseFactory(@NonNull Context context) {
SQLiteDatabase.loadLibs(context);
DatabaseSecret databaseSecret = new DatabaseSecretProvider(context).getOrCreateDatabaseSecret();
AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret();
DatabaseSecret databaseSecret = new DatabaseSecretProvider(context).getOrCreateDatabaseSecret();
AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret();
this.databaseHelper = new SQLCipherOpenHelper(context, databaseSecret);
this.sms = new SmsDatabase(context, databaseHelper);

Wyświetl plik

@ -5,7 +5,6 @@ import android.app.Application;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import org.signal.core.util.tracing.Tracer;
import org.thoughtcrime.securesms.KbsEnclave;
import org.thoughtcrime.securesms.components.TypingStatusRepository;
import org.thoughtcrime.securesms.components.TypingStatusSender;
@ -24,7 +23,6 @@ import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
import org.thoughtcrime.securesms.recipients.LiveRecipientCache;
import org.thoughtcrime.securesms.service.TrimThreadsByDateManager;
import org.thoughtcrime.securesms.util.EarlyMessageCache;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.FrameRateTracker;
import org.thoughtcrime.securesms.util.Hex;
import org.thoughtcrime.securesms.util.IasKeyStore;
@ -47,11 +45,11 @@ public class ApplicationDependencies {
private static final Object LOCK = new Object();
private static final Object FRAME_RATE_TRACKER_LOCK = new Object();
private static final Object JOB_MANAGER_LOCK = new Object();
private static Application application;
private static Provider provider;
private static MessageNotifier messageNotifier;
private static TrimThreadsByDateManager trimThreadsByDateManager;
private static Application application;
private static Provider provider;
private static MessageNotifier messageNotifier;
private static volatile SignalServiceAccountManager accountManager;
private static volatile SignalServiceMessageSender messageSender;
@ -70,7 +68,7 @@ public class ApplicationDependencies {
private static volatile TypingStatusRepository typingStatusRepository;
private static volatile TypingStatusSender typingStatusSender;
private static volatile DatabaseObserver databaseObserver;
private static volatile Tracer tracer;
private static volatile TrimThreadsByDateManager trimThreadsByDateManager;
@MainThread
public static void init(@NonNull Application application, @NonNull Provider provider) {
@ -82,7 +80,6 @@ public class ApplicationDependencies {
ApplicationDependencies.application = application;
ApplicationDependencies.provider = provider;
ApplicationDependencies.messageNotifier = provider.provideMessageNotifier();
ApplicationDependencies.trimThreadsByDateManager = provider.provideTrimThreadsByDateManager();
}
}
@ -222,7 +219,7 @@ public class ApplicationDependencies {
public static @NonNull JobManager getJobManager() {
if (jobManager == null) {
synchronized (LOCK) {
synchronized (JOB_MANAGER_LOCK) {
if (jobManager == null) {
jobManager = provider.provideJobManager();
}
@ -285,7 +282,15 @@ public class ApplicationDependencies {
}
public static @NonNull TrimThreadsByDateManager getTrimThreadsByDateManager() {
return trimThreadsByDateManager;
if (trimThreadsByDateManager == null) {
synchronized (LOCK) {
if (trimThreadsByDateManager == null) {
trimThreadsByDateManager = provider.provideTrimThreadsByDateManager();
}
}
}
return trimThreadsByDateManager;
}
public static TypingStatusRepository getTypingStatusRepository() {

Wyświetl plik

@ -15,10 +15,14 @@ import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.DatabaseObserver;
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
import org.thoughtcrime.securesms.jobmanager.Constraint;
import org.thoughtcrime.securesms.jobmanager.ConstraintObserver;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobmanager.JobMigrator;
import org.thoughtcrime.securesms.jobmanager.impl.FactoryJobPredicate;
import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer;
import org.thoughtcrime.securesms.jobmanager.persistence.JobStorage;
import org.thoughtcrime.securesms.jobs.FastJobStorage;
import org.thoughtcrime.securesms.jobs.GroupCallUpdateSendJob;
import org.thoughtcrime.securesms.jobs.JobManagerFactories;
@ -140,16 +144,17 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr
@Override
public @NonNull JobManager provideJobManager() {
return new JobManager(context, new JobManager.Configuration.Builder()
.setDataSerializer(new JsonDataSerializer())
.setJobFactories(JobManagerFactories.getJobFactories(context))
.setConstraintFactories(JobManagerFactories.getConstraintFactories(context))
.setConstraintObservers(JobManagerFactories.getConstraintObservers(context))
.setJobStorage(new FastJobStorage(DatabaseFactory.getJobDatabase(context)))
.setJobMigrator(new JobMigrator(TextSecurePreferences.getJobManagerVersion(context), JobManager.CURRENT_VERSION, JobManagerFactories.getJobMigrations(context)))
.addReservedJobRunner(new FactoryJobPredicate(PushDecryptMessageJob.KEY, PushProcessMessageJob.KEY, MarkerJob.KEY))
.addReservedJobRunner(new FactoryJobPredicate(PushTextSendJob.KEY, PushMediaSendJob.KEY, PushGroupSendJob.KEY, ReactionSendJob.KEY, TypingSendJob.KEY, GroupCallUpdateSendJob.KEY))
.build());
JobManager.Configuration config = new JobManager.Configuration.Builder()
.setDataSerializer(new JsonDataSerializer())
.setJobFactories(JobManagerFactories.getJobFactories(context))
.setConstraintFactories(JobManagerFactories.getConstraintFactories(context))
.setConstraintObservers(JobManagerFactories.getConstraintObservers(context))
.setJobStorage(new FastJobStorage(DatabaseFactory.getJobDatabase(context)))
.setJobMigrator(new JobMigrator(TextSecurePreferences.getJobManagerVersion(context), JobManager.CURRENT_VERSION, JobManagerFactories.getJobMigrations(context)))
.addReservedJobRunner(new FactoryJobPredicate(PushDecryptMessageJob.KEY, PushProcessMessageJob.KEY, MarkerJob.KEY))
.addReservedJobRunner(new FactoryJobPredicate(PushTextSendJob.KEY, PushMediaSendJob.KEY, PushGroupSendJob.KEY, ReactionSendJob.KEY, TypingSendJob.KEY, GroupCallUpdateSendJob.KEY))
.build();
return new JobManager(context, config);
}
@Override
@ -188,7 +193,7 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr
@Override
public @NonNull TypingStatusSender provideTypingStatusSender() {
return new TypingStatusSender(context);
return new TypingStatusSender();
}
@Override

Wyświetl plik

@ -8,6 +8,7 @@ import android.telephony.TelephonyManager;
import androidx.annotation.NonNull;
import org.signal.core.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.jobmanager.ConstraintObserver;
public class CellServiceConstraintObserver implements ConstraintObserver {
@ -17,11 +18,15 @@ public class CellServiceConstraintObserver implements ConstraintObserver {
private volatile Notifier notifier;
private volatile ServiceState lastKnownState;
private static CellServiceConstraintObserver instance;
private static volatile CellServiceConstraintObserver instance;
public static synchronized CellServiceConstraintObserver getInstance(@NonNull Application application) {
public static CellServiceConstraintObserver getInstance(@NonNull Application application) {
if (instance == null) {
instance = new CellServiceConstraintObserver(application);
synchronized (CellServiceConstraintObserver.class) {
if (instance == null) {
instance = new CellServiceConstraintObserver(application);
}
}
}
return instance;
}
@ -30,7 +35,9 @@ public class CellServiceConstraintObserver implements ConstraintObserver {
TelephonyManager telephonyManager = (TelephonyManager) application.getSystemService(Context.TELEPHONY_SERVICE);
ServiceStateListener serviceStateListener = new ServiceStateListener();
telephonyManager.listen(serviceStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
SignalExecutors.BOUNDED.execute(() -> {
telephonyManager.listen(serviceStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
});
}
@Override

Wyświetl plik

@ -13,18 +13,10 @@ public class DecryptionsDrainedConstraintObserver implements ConstraintObserver
private static final String REASON = DecryptionsDrainedConstraintObserver.class.getSimpleName();
private volatile Notifier notifier;
public DecryptionsDrainedConstraintObserver() {
ApplicationDependencies.getIncomingMessageObserver().addDecryptionDrainedListener(() -> {
if (notifier != null) {
notifier.onConstraintMet(REASON);
}
});
}
@Override
public void register(@NonNull Notifier notifier) {
this.notifier = notifier;
ApplicationDependencies.getIncomingMessageObserver().addDecryptionDrainedListener(() -> {
notifier.onConstraintMet(REASON);
});
}
}

Wyświetl plik

@ -173,8 +173,7 @@ public final class PushGroupSilentUpdateSendJob extends BaseJob {
public static class Factory implements Job.Factory<PushGroupSilentUpdateSendJob> {
@Override
public @NonNull
PushGroupSilentUpdateSendJob create(@NonNull Parameters parameters, @NonNull Data data) {
public @NonNull PushGroupSilentUpdateSendJob create(@NonNull Parameters parameters, @NonNull Data data) {
List<RecipientId> recipients = RecipientId.fromSerializedList(data.getString(KEY_RECIPIENTS));
int initialRecipientCount = data.getInt(KEY_INITIAL_RECIPIENT_COUNT);
long timestamp = data.getLong(KEY_TIMESTAMP);

Wyświetl plik

@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.megaphone;
import android.app.Application;
import android.content.Context;
import androidx.annotation.AnyThread;
@ -28,14 +29,14 @@ import java.util.concurrent.Executor;
*/
public class MegaphoneRepository {
private final Context context;
private final Application context;
private final Executor executor;
private final MegaphoneDatabase database;
private final Map<Event, MegaphoneRecord> databaseCache;
private boolean enabled;
public MegaphoneRepository(@NonNull Context context) {
public MegaphoneRepository(@NonNull Application context) {
this.context = context;
this.executor = SignalExecutors.SERIAL;
this.database = DatabaseFactory.getMegaphoneDatabase(context);

Wyświetl plik

@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.messages;
import android.app.Application;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
@ -47,7 +48,7 @@ public class IncomingMessageObserver {
private static SignalServiceMessagePipe pipe = null;
private static SignalServiceMessagePipe unidentifiedPipe = null;
private final Context context;
private final Application context;
private final SignalServiceNetworkAccess networkAccess;
private final List<Runnable> decryptionDrainedListeners;
@ -56,7 +57,7 @@ public class IncomingMessageObserver {
private volatile boolean networkDrained;
private volatile boolean decryptionDrained;
public IncomingMessageObserver(@NonNull Context context) {
public IncomingMessageObserver(@NonNull Application context) {
this.context = context;
this.networkAccess = ApplicationDependencies.getSignalServiceNetworkAccess();
this.decryptionDrainedListeners = new CopyOnWriteArrayList<>();

Wyświetl plik

@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.messages;
import android.app.Application;
import android.content.Context;
import androidx.annotation.NonNull;
@ -27,10 +28,10 @@ public class IncomingMessageProcessor {
private static final String TAG = Log.tag(IncomingMessageProcessor.class);
private final Context context;
private final Application context;
private final ReentrantLock lock;
public IncomingMessageProcessor(@NonNull Context context) {
public IncomingMessageProcessor(@NonNull Application context) {
this.context = context;
this.lock = new ReentrantLock();
}

Wyświetl plik

@ -0,0 +1,68 @@
package org.thoughtcrime.securesms.util;
import androidx.annotation.NonNull;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import java.util.LinkedList;
import java.util.List;
public final class AppStartup {
private static final String TAG = Log.tag(AppStartup.class);
private final List<Task> blocking;
private final List<Task> deferred;
public AppStartup() {
this.blocking = new LinkedList<>();
this.deferred = new LinkedList<>();
}
public @NonNull
AppStartup addBlocking(@NonNull String name, @NonNull Runnable task) {
blocking.add(new Task(name, task));
return this;
}
public @NonNull
AppStartup addDeferred(@NonNull Runnable task) {
deferred.add(new Task("", task));
return this;
}
public void execute() {
Stopwatch stopwatch = new Stopwatch("init");
for (Task task : blocking) {
task.getRunnable().run();
stopwatch.split(task.getName());
}
for (Task task : deferred) {
SignalExecutors.BOUNDED.execute(task.getRunnable());
}
stopwatch.split("schedule-deferred");
stopwatch.stop(TAG);
}
private class Task {
private final String name;
private final Runnable runnable;
protected Task(@NonNull String name, @NonNull Runnable runnable) {
this.name = name;
this.runnable = runnable;
}
@NonNull String getName() {
return name;
}
public @NonNull Runnable getRunnable() {
return runnable;
}
}
}

Wyświetl plik

@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.util;
import android.app.Application;
import android.content.Context;
import android.view.Choreographer;
import android.view.Display;
@ -23,7 +24,7 @@ public class FrameRateTracker {
private static final int MAX_CONSECUTIVE_FRAME_LOGS = 10;
private final Context context;
private final Application context;
private double refreshRate;
private long idealTimePerFrameNanos;
@ -33,8 +34,8 @@ public class FrameRateTracker {
private long consecutiveFrameWarnings;
public FrameRateTracker(@NonNull Context context) {
this.context = context;
public FrameRateTracker(@NonNull Application application) {
this.context = application;
updateRefreshRate();
}