1
0
mirror of https://github.com/roytam1/UXP.git synced 2026-05-27 13:28:54 +00:00
Files
UXP/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
T

315 lines
12 KiB
Java

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko;
import android.app.Application;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
import com.squareup.leakcanary.LeakCanary;
import com.squareup.leakcanary.RefWatcher;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.db.LocalBrowserDB;
import org.mozilla.gecko.distribution.Distribution;
import org.mozilla.gecko.dlc.DownloadContentService;
import org.mozilla.gecko.home.HomePanelsManager;
import org.mozilla.gecko.lwt.LightweightTheme;
import org.mozilla.gecko.mdns.MulticastDNSManager;
import org.mozilla.gecko.media.AudioFocusAgent;
import org.mozilla.gecko.notifications.NotificationClient;
import org.mozilla.gecko.notifications.NotificationHelper;
import org.mozilla.gecko.preferences.DistroSharedPrefsImport;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.Clipboard;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.HardwareUtils;
import org.mozilla.gecko.util.ThreadUtils;
import java.io.File;
import java.lang.reflect.Method;
public class GeckoApplication extends Application
implements ContextGetter {
private static final String LOG_TAG = "GeckoApplication";
private static volatile GeckoApplication instance;
private boolean mInBackground;
private boolean mPausedGecko;
private LightweightTheme mLightweightTheme;
private RefWatcher mRefWatcher;
public GeckoApplication() {
super();
instance = this;
}
public static GeckoApplication get() {
return instance;
}
public static RefWatcher getRefWatcher(Context context) {
GeckoApplication app = (GeckoApplication) context.getApplicationContext();
return app.mRefWatcher;
}
public static void watchReference(Context context, Object object) {
if (context == null) {
return;
}
getRefWatcher(context).watch(object);
}
@Override
public Context getContext() {
return this;
}
@Override
public SharedPreferences getSharedPreferences() {
return GeckoSharedPrefs.forApp(this);
}
/**
* We need to do locale work here, because we need to intercept
* each hit to onConfigurationChanged.
*/
@Override
public void onConfigurationChanged(Configuration config) {
Log.d(LOG_TAG, "onConfigurationChanged: " + config.locale +
", background: " + mInBackground);
// Do nothing if we're in the background. It'll simply cause a loop
// (Bug 936756 Comment 11), and it's not necessary.
if (mInBackground) {
super.onConfigurationChanged(config);
return;
}
// Otherwise, correct the locale. This catches some cases that GeckoApp
// doesn't get a chance to.
try {
BrowserLocaleManager.getInstance().correctLocale(this, getResources(), config);
} catch (IllegalStateException ex) {
// GeckoApp hasn't started, so we have no ContextGetter in BrowserLocaleManager.
Log.w(LOG_TAG, "Couldn't correct locale.", ex);
}
super.onConfigurationChanged(config);
}
public void onActivityPause(GeckoActivityStatus activity) {
mInBackground = true;
if ((activity.isFinishing() == false) &&
(activity.isGeckoActivityOpened() == false)) {
// Notify Gecko that we are pausing; the cache service will be
// shutdown, closing the disk cache cleanly. If the android
// low memory killer subsequently kills us, the disk cache will
// be left in a consistent state, avoiding costly cleanup and
// re-creation.
GeckoThread.onPause();
mPausedGecko = true;
final BrowserDB db = BrowserDB.from(this);
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
db.expireHistory(getContentResolver(), BrowserContract.ExpirePriority.NORMAL);
}
});
}
GeckoNetworkManager.getInstance().stop();
}
public void onActivityResume(GeckoActivityStatus activity) {
if (mPausedGecko) {
GeckoThread.onResume();
mPausedGecko = false;
}
GeckoBatteryManager.getInstance().start(this);
GeckoNetworkManager.getInstance().start(this);
mInBackground = false;
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
AppConstants.maybeInstallMultiDex(base);
}
@Override
public void onCreate() {
Log.i(LOG_TAG, "zerdatime " + SystemClock.uptimeMillis() + " - Fennec application start");
mRefWatcher = LeakCanary.install(this);
final Context context = getApplicationContext();
GeckoAppShell.setApplicationContext(context);
HardwareUtils.init(context);
Clipboard.init(context);
FilePicker.init(context);
DownloadsIntegration.init();
HomePanelsManager.getInstance().init(context);
GlobalPageMetadata.getInstance().init();
// We need to set the notification client before launching Gecko, since Gecko could start
// sending notifications immediately after startup, which we don't want to lose/crash on.
GeckoAppShell.setNotificationListener(new NotificationClient(context));
// This getInstance call will force initialization of the NotificationHelper, but does nothing with the result
NotificationHelper.getInstance(context).init();
MulticastDNSManager.getInstance(context).init();
GeckoService.register();
EventDispatcher.getInstance().registerBackgroundThreadListener(new EventListener(),
"Profile:Create");
super.onCreate();
}
public void onDelayedStartup() {
if (AppConstants.MOZ_ANDROID_GCM) {
// TODO: only run in main process.
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
// It's fine to throw GCM initialization onto a background thread; the registration process requires
// network access, so is naturally asynchronous. This, of course, races against Gecko page load of
// content requiring GCM-backed services, like Web Push. There's nothing to be done here.
try {
final Class<?> clazz = Class.forName("org.mozilla.gecko.push.PushService");
final Method onCreate = clazz.getMethod("onCreate", Context.class);
onCreate.invoke(null, getApplicationContext()); // Method is static.
} catch (Exception e) {
Log.e(LOG_TAG, "Got exception during startup; ignoring.", e);
return;
}
}
});
}
if (AppConstants.MOZ_ANDROID_DOWNLOAD_CONTENT_SERVICE) {
DownloadContentService.startStudy(this);
}
GeckoAccessibility.setAccessibilityManagerListeners(this);
AudioFocusAgent.getInstance().attachToContext(this);
}
private class EventListener implements BundleEventListener
{
private void onProfileCreate(final String name, final String path) {
// Add everything when we're done loading the distribution.
final Context context = GeckoApplication.this;
final GeckoProfile profile = GeckoProfile.get(context, name);
final Distribution distribution = Distribution.getInstance(context);
distribution.addOnDistributionReadyCallback(new Distribution.ReadyCallback() {
@Override
public void distributionNotFound() {
this.distributionFound(null);
}
@Override
public void distributionFound(final Distribution distribution) {
Log.d(LOG_TAG, "Running post-distribution task: bookmarks.");
// Because we are running in the background, we want to synchronize on the
// GeckoProfile instance so that we don't race with main thread operations
// such as locking/unlocking/removing the profile.
synchronized (profile.getLock()) {
distributionFoundLocked(distribution);
}
}
@Override
public void distributionArrivedLate(final Distribution distribution) {
Log.d(LOG_TAG, "Running late distribution task: bookmarks.");
// Recover as best we can.
synchronized (profile.getLock()) {
distributionArrivedLateLocked(distribution);
}
}
private void distributionFoundLocked(final Distribution distribution) {
// Skip initialization if the profile directory has been removed.
if (!(new File(path)).exists()) {
return;
}
final ContentResolver cr = context.getContentResolver();
final LocalBrowserDB db = new LocalBrowserDB(profile.getName());
// We pass the number of added bookmarks to ensure that the
// indices of the distribution and default bookmarks are
// contiguous. Because there are always at least as many
// bookmarks as there are favicons, we can also guarantee that
// the favicon IDs won't overlap.
final int offset = distribution == null ? 0 :
db.addDistributionBookmarks(cr, distribution, 0);
db.addDefaultBookmarks(context, cr, offset);
Log.d(LOG_TAG, "Running post-distribution task: android preferences.");
DistroSharedPrefsImport.importPreferences(context, distribution);
}
private void distributionArrivedLateLocked(final Distribution distribution) {
// Skip initialization if the profile directory has been removed.
if (!(new File(path)).exists()) {
return;
}
final ContentResolver cr = context.getContentResolver();
final LocalBrowserDB db = new LocalBrowserDB(profile.getName());
// We assume we've been called very soon after startup, and so our offset
// into "Mobile Bookmarks" is the number of bookmarks in the DB.
final int offset = db.getCount(cr, "bookmarks");
db.addDistributionBookmarks(cr, distribution, offset);
Log.d(LOG_TAG, "Running late distribution task: android preferences.");
DistroSharedPrefsImport.importPreferences(context, distribution);
}
});
}
@Override // BundleEventListener
public void handleMessage(final String event, final Bundle message,
final EventCallback callback) {
if ("Profile:Create".equals(event)) {
onProfileCreate(message.getCharSequence("name").toString(),
message.getCharSequence("path").toString());
}
}
}
public boolean isApplicationInBackground() {
return mInBackground;
}
public LightweightTheme getLightweightTheme() {
return mLightweightTheme;
}
public void prepareLightweightTheme() {
mLightweightTheme = new LightweightTheme(this);
}
}