/** * Update prefs that depend on home panels state. * * <p>This includes the prefs that keep track of whether bookmarks or history are enabled, which * are used to control the visibility of the corresponding menu items. */ private void updatePrefsFromConfig(JSONArray panelsArray) { final SharedPreferences prefs = GeckoSharedPrefs.forProfile(mContext); if (!prefs.contains(HomeConfig.PREF_KEY_BOOKMARKS_PANEL_ENABLED) || !prefs.contains(HomeConfig.PREF_KEY_HISTORY_PANEL_ENABLED)) { final String bookmarkType = PanelType.BOOKMARKS.toString(); final String historyType = PanelType.COMBINED_HISTORY.toString(); try { for (int i = 0; i < panelsArray.length(); i++) { final JSONObject panelObj = panelsArray.getJSONObject(i); final String panelType = panelObj.optString(PanelConfig.JSON_KEY_TYPE, null); if (panelType == null) { break; } final boolean isDisabled = panelObj.optBoolean(PanelConfig.JSON_KEY_DISABLED, false); if (bookmarkType.equals(panelType)) { prefs .edit() .putBoolean(HomeConfig.PREF_KEY_BOOKMARKS_PANEL_ENABLED, !isDisabled) .apply(); } else if (historyType.equals(panelType)) { prefs.edit().putBoolean(HomeConfig.PREF_KEY_HISTORY_PANEL_ENABLED, !isDisabled).apply(); } } } catch (JSONException e) { Log.e(LOGTAG, "Error fetching panel from config to update prefs"); } } }
private void openNow(Intent intent) { Intent forwardIntent = new Intent(intent); forwardIntent.setClassName( getApplicationContext(), AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS); forwardIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(forwardIntent); TabQueueHelper.removeNotification(getApplicationContext()); GeckoSharedPrefs.forApp(getApplicationContext()) .edit() .remove(GeckoPreferences.PREFS_TAB_QUEUE_LAST_SITE) .remove(GeckoPreferences.PREFS_TAB_QUEUE_LAST_TIME) .apply(); Telemetry.sendUIEvent( TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "tabqueue-now"); executorService.submit( new Runnable() { @Override public void run() { int queuedTabCount = TabQueueHelper.getTabQueueLength(TabQueueService.this); Telemetry.addToHistogram("FENNEC_TABQUEUE_QUEUESIZE", queuedTabCount); } }); }
private SharedPreferences getSharedPreferences() { final SharedPreferences settings; if (prefsBranch == null) { settings = GeckoSharedPrefs.forApp(context); } else { settings = context.getSharedPreferences(prefsBranch, Activity.MODE_PRIVATE); } return settings; }
private void saveToPrefs() { GeckoSharedPrefs.forProfile(mApplication) .edit() .putString(PREFS_URL, mHeaderURL) .putString(PREFS_COLOR, mColor) .apply(); // Let's keep the saved data in sync. mSavedURL = mHeaderURL; mSavedColor = mColor; }
public CombinedHistoryAdapter(Context context, int savedParentIndex) { super(); this.context = context; sectionHeaders = new SparseArray<>(); // This races when multiple Fragments are created. That's okay: one // will win, and thereafter, all will be okay. If we create and then // drop an instance the shared SharedPreferences backing all the // instances will maintain the state for us. Since everything happens on // the UI thread, this doesn't even need to be volatile. if (sState == null) { sState = new RemoteTabsExpandableListState(GeckoSharedPrefs.forProfile(context)); } remoteClientIndexOfParent = savedParentIndex; }
private static void removeGuestProfile(Context context) { boolean success = false; try { File guestDir = getGuestDir(context); if (guestDir.exists()) { success = delete(guestDir); } } catch (Exception ex) { Log.e(LOGTAG, "Error removing guest profile", ex); } if (success) { // Clear all shared prefs for the guest profile. GeckoSharedPrefs.forProfileName(context, GUEST_PROFILE).edit().clear().commit(); } }
public void testDisabledState() { resources.setSuggestedSitesResource(generateSites(3)); Cursor c = new SuggestedSites(context).get(DEFAULT_LIMIT); assertEquals(3, c.getCount()); c.close(); // Disable suggested sites GeckoSharedPrefs.forApp(context) .edit() .putBoolean(GeckoPreferences.PREFS_SUGGESTED_SITES, false) .commit(); c = new SuggestedSites(context).get(DEFAULT_LIMIT); assertNotNull(c); assertEquals(0, c.getCount()); c.close(); }
public static boolean removeProfile(Context context, String profileName) { if (profileName == null) { Log.w(LOGTAG, "Unable to remove profile: null profile name."); return false; } final GeckoProfile profile = get(context, profileName); if (profile == null) { return false; } final boolean success = profile.remove(); if (success) { // Clear all shared prefs for the given profile. GeckoSharedPrefs.forProfileName(context, profileName).edit().clear().commit(); } return success; }
@WorkerThread private void uploadCorePing( @NonNull final String docId, final int seq, @NonNull final String profileName, @NonNull final String profilePath, @Nullable final String defaultSearchEngine) { final GeckoProfile profile = GeckoProfile.get(this, profileName, profilePath); final String clientId; try { clientId = profile.getClientId(); } catch (final IOException e) { Log.w(LOGTAG, "Unable to get client ID to generate core ping: returning.", e); return; } // Each profile can have different telemetry data so we intentionally grab the shared prefs for // the profile. final SharedPreferences sharedPrefs = GeckoSharedPrefs.forProfileName(this, profileName); // TODO (bug 1241685): Sync this preference with the gecko preference. final String serverURLSchemeHostPort = sharedPrefs.getString( TelemetryConstants.PREF_SERVER_URL, TelemetryConstants.DEFAULT_SERVER_URL); final long profileCreationDate = getProfileCreationDate(profile); final TelemetryCorePingBuilder builder = new TelemetryCorePingBuilder(this, serverURLSchemeHostPort) .setClientID(clientId) .setDefaultSearchEngine( TextUtils.isEmpty(defaultSearchEngine) ? null : defaultSearchEngine) .setProfileCreationDate(profileCreationDate < 0 ? null : profileCreationDate) .setSequenceNumber(seq); final String distributionId = sharedPrefs.getString(DistributionStoreCallback.PREF_DISTRIBUTION_ID, null); if (distributionId != null) { builder.setOptDistributionID(distributionId); } final TelemetryPing corePing = builder.build(); final CorePingResultDelegate resultDelegate = new CorePingResultDelegate(); uploadPing(corePing, resultDelegate); }
public void testHiddenSites() { resources.setSuggestedSitesResource(generateSites(6)); List<String> visibleUrls = new ArrayList<String>(3); visibleUrls.add("url3"); visibleUrls.add("url4"); visibleUrls.add("url5"); List<String> hiddenUrls = new ArrayList<String>(3); hiddenUrls.add("url0"); hiddenUrls.add("url1"); hiddenUrls.add("url2"); // Add mocked hidden sites to SharedPreferences. StringBuilder hiddenUrlBuilder = new StringBuilder(); for (String s : hiddenUrls) { hiddenUrlBuilder.append(" "); hiddenUrlBuilder.append(Uri.encode(s)); } final String hiddenPref = hiddenUrlBuilder.toString(); GeckoSharedPrefs.forProfile(context) .edit() .putString(SuggestedSites.PREF_SUGGESTED_SITES_HIDDEN, hiddenPref) .commit(); Cursor c = new SuggestedSites(context).get(DEFAULT_LIMIT); assertEquals(Math.min(3, DEFAULT_LIMIT), c.getCount()); c.moveToPosition(-1); while (c.moveToNext()) { String url = c.getString(c.getColumnIndexOrThrow(BrowserContract.SuggestedSites.URL)); assertFalse(hiddenUrls.contains(url)); assertTrue(visibleUrls.contains(url)); } c.close(); }
private File createProfileDir() throws IOException { INIParser parser = GeckoProfileDirectories.getProfilesINI(mMozillaDir); // Salt the name of our requested profile String saltedName = GeckoProfileDirectories.saltProfileName(mName); File profileDir = new File(mMozillaDir, saltedName); while (profileDir.exists()) { saltedName = GeckoProfileDirectories.saltProfileName(mName); profileDir = new File(mMozillaDir, saltedName); } // Attempt to create the salted profile dir if (!profileDir.mkdirs()) { throw new IOException("Unable to create profile."); } Log.d(LOGTAG, "Created new profile dir."); // Now update profiles.ini // If this is the first time its created, we also add a General section // look for the first profile number that isn't taken yet int profileNum = 0; boolean isDefaultSet = false; INISection profileSection; while ((profileSection = parser.getSection("Profile" + profileNum)) != null) { profileNum++; if (profileSection.getProperty("Default") != null) { isDefaultSet = true; } } profileSection = new INISection("Profile" + profileNum); profileSection.setProperty("Name", mName); profileSection.setProperty("IsRelative", 1); profileSection.setProperty("Path", saltedName); if (parser.getSection("General") == null) { INISection generalSection = new INISection("General"); generalSection.setProperty("StartWithLastProfile", 1); parser.addSection(generalSection); } if (!isDefaultSet && !mIsWebAppProfile) { // only set as default if this is the first non-webapp // profile we're creating profileSection.setProperty("Default", 1); // We have no intention of stopping this session. The FIRSTRUN session // ends when the browsing session/activity has ended. All events // during firstrun will be tagged as FIRSTRUN. Telemetry.startUISession(TelemetryContract.Session.FIRSTRUN); } parser.addSection(profileSection); parser.write(); // Trigger init for non-webapp profiles. if (!mIsWebAppProfile) { enqueueInitialization(); } // Write out profile creation time, mirroring the logic in nsToolkitProfileService. try { FileOutputStream stream = new FileOutputStream(profileDir.getAbsolutePath() + File.separator + "times.json"); OutputStreamWriter writer = new OutputStreamWriter(stream, Charset.forName("UTF-8")); try { writer.append("{\"created\": " + System.currentTimeMillis() + "}\n"); } finally { writer.close(); } } catch (Exception e) { // Best-effort. Log.w(LOGTAG, "Couldn't write times.json.", e); } // Initialize pref flag for displaying the start pane for a new non-webapp profile. if (!mIsWebAppProfile) { final SharedPreferences prefs = GeckoSharedPrefs.forProfile(mApplicationContext); prefs.edit().putBoolean(BrowserApp.PREF_STARTPANE_ENABLED, true).apply(); } return profileDir; }
/** * Migrates JSON config data storage. * * @param context Context used to get shared preferences and create built-in panel. * @param jsonString String currently stored in preferences. * @return JSONArray array representing new set of panel configs. */ private static synchronized JSONArray maybePerformMigration(Context context, String jsonString) throws JSONException { // If the migration is already done, we're at the current version. if (sMigrationDone) { final JSONObject json = new JSONObject(jsonString); return json.getJSONArray(JSON_KEY_PANELS); } // Make sure we only do this version check once. sMigrationDone = true; final JSONArray originalJsonPanels; final int version; final SharedPreferences prefs = GeckoSharedPrefs.forProfile(context); if (prefs.contains(PREFS_CONFIG_KEY_OLD)) { // Our original implementation did not contain versioning, so this is implicitly version 0. originalJsonPanels = new JSONArray(jsonString); version = 0; } else { final JSONObject json = new JSONObject(jsonString); originalJsonPanels = json.getJSONArray(JSON_KEY_PANELS); version = json.getInt(JSON_KEY_VERSION); } if (version == VERSION) { return originalJsonPanels; } Log.d(LOGTAG, "Performing migration"); final JSONArray newJsonPanels = new JSONArray(); final SharedPreferences.Editor prefsEditor = prefs.edit(); for (int v = version + 1; v <= VERSION; v++) { Log.d(LOGTAG, "Migrating to version = " + v); switch (v) { case 1: // Add "Recent Tabs" panel final PanelConfig recentTabsConfig = createBuiltinPanelConfig(context, PanelType.RECENT_TABS); final JSONObject jsonRecentTabsConfig = recentTabsConfig.toJSON(); // Add the new panel to the front of the array on phones. if (!HardwareUtils.isTablet()) { newJsonPanels.put(jsonRecentTabsConfig); } // Copy the original panel configs. final int count = originalJsonPanels.length(); for (int i = 0; i < count; i++) { final JSONObject jsonPanelConfig = originalJsonPanels.getJSONObject(i); newJsonPanels.put(jsonPanelConfig); } // Add the new panel to the end of the array on tablets. if (HardwareUtils.isTablet()) { newJsonPanels.put(jsonRecentTabsConfig); } // Remove the old pref key. prefsEditor.remove(PREFS_CONFIG_KEY_OLD); break; } } // Save the new panel config and the new version number. final JSONObject newJson = new JSONObject(); newJson.put(JSON_KEY_PANELS, newJsonPanels); newJson.put(JSON_KEY_VERSION, VERSION); prefsEditor.putString(PREFS_CONFIG_KEY, newJson.toString()); prefsEditor.commit(); return newJsonPanels; }
private void loadFromPrefs() { SharedPreferences prefs = GeckoSharedPrefs.forProfile(mApplication); mSavedURL = prefs.getString(PREFS_URL, null); mSavedColor = prefs.getString(PREFS_COLOR, null); }
/** Clear the data stored in preferences for fast path loading during startup */ private void clearPrefs() { GeckoSharedPrefs.forProfile(mApplication).edit().remove(PREFS_URL).remove(PREFS_COLOR).apply(); }
/** * Migrates JSON config data storage. * * @param context Context used to get shared preferences and create built-in panel. * @param jsonString String currently stored in preferences. * @return JSONArray array representing new set of panel configs. */ private static synchronized JSONArray maybePerformMigration(Context context, String jsonString) throws JSONException { // If the migration is already done, we're at the current version. if (sMigrationDone) { final JSONObject json = new JSONObject(jsonString); return json.getJSONArray(JSON_KEY_PANELS); } // Make sure we only do this version check once. sMigrationDone = true; JSONArray jsonPanels; final int version; final SharedPreferences prefs = GeckoSharedPrefs.forProfile(context); if (prefs.contains(PREFS_CONFIG_KEY_OLD)) { // Our original implementation did not contain versioning, so this is implicitly version 0. jsonPanels = new JSONArray(jsonString); version = 0; } else { final JSONObject json = new JSONObject(jsonString); jsonPanels = json.getJSONArray(JSON_KEY_PANELS); version = json.getInt(JSON_KEY_VERSION); } if (version == VERSION) { return jsonPanels; } Log.d(LOGTAG, "Performing migration"); final SharedPreferences.Editor prefsEditor = prefs.edit(); for (int v = version + 1; v <= VERSION; v++) { Log.d(LOGTAG, "Migrating to version = " + v); switch (v) { case 1: // Add "Recent Tabs" panel. addBuiltinPanelConfig( context, jsonPanels, PanelType.RECENT_TABS, Position.FRONT, Position.BACK); // Remove the old pref key. prefsEditor.remove(PREFS_CONFIG_KEY_OLD); break; case 2: // Add "Remote Tabs"/"Synced Tabs" panel. addBuiltinPanelConfig( context, jsonPanels, PanelType.DEPRECATED_REMOTE_TABS, Position.FRONT, Position.BACK); break; case 3: // Add the "Reading List" panel if it does not exist. At one time, // the Reading List panel was shown only to devices that were not // considered "low memory". Now, we expose the panel to all devices. // This migration should only occur for "low memory" devices. // Note: This will not agree with the default configuration, which // has DEPRECATED_REMOTE_TABS after READING_LIST on some devices. if (!readingListPanelExists(jsonPanels)) { addBuiltinPanelConfig( context, jsonPanels, PanelType.READING_LIST, Position.BACK, Position.BACK); } break; case 4: // Combine the History and Sync panels. In order to minimize an unexpected reordering // of panels, we try to replace the History panel if it's visible, and fall back to // the Sync panel if that's visible. jsonPanels = combineHistoryAndSyncPanels(context, jsonPanels); break; case 5: // This is the fix for bug 1264136 where we lost track of the default panel during some // migrations. ensureDefaultPanelForV5(context, jsonPanels); break; } } // Save the new panel config and the new version number. final JSONObject newJson = new JSONObject(); newJson.put(JSON_KEY_PANELS, jsonPanels); newJson.put(JSON_KEY_VERSION, VERSION); prefsEditor.putString(PREFS_CONFIG_KEY, newJson.toString()); prefsEditor.apply(); return jsonPanels; }
private SharedPreferences getSharedPreferences() { return GeckoSharedPrefs.forProfile(mContext); }
@Override public int onStartCommand(final Intent intent, final int flags, final int startId) { // If this is a redelivery then lets bypass the entire double tap to open now code as that's a // big can of worms, // we also don't expect redeliveries because of the short time window associated with this // feature. if (flags != START_FLAG_REDELIVERY) { final Context applicationContext = getApplicationContext(); final SharedPreferences sharedPreferences = GeckoSharedPrefs.forApp(applicationContext); final String lastUrl = sharedPreferences.getString(GeckoPreferences.PREFS_TAB_QUEUE_LAST_SITE, ""); final ContextUtils.SafeIntent safeIntent = new ContextUtils.SafeIntent(intent); final String intentUrl = safeIntent.getDataString(); final long lastRunTime = sharedPreferences.getLong(GeckoPreferences.PREFS_TAB_QUEUE_LAST_TIME, 0); final boolean isWithinDoubleTapTimeLimit = System.currentTimeMillis() - lastRunTime < TOAST_DOUBLE_TAP_TIMEOUT_MILLIS; if (!TextUtils.isEmpty(lastUrl) && lastUrl.equals(intentUrl) && isWithinDoubleTapTimeLimit) { // Background thread because we could do some file IO if we have to remove a url from the // list. tabQueueHandler.post( new Runnable() { @Override public void run() { // If there is a runnable around, that means that the previous process hasn't yet // completed, so // we will need to prevent it from running and remove the view from the window // manager. // If there is no runnable around then the url has already been added to the list, // so we'll // need to remove it before proceeding or that url will open multiple times. if (stopServiceRunnable != null) { tabQueueHandler.removeCallbacks(stopServiceRunnable); stopSelfResult(stopServiceRunnable.getStartId()); stopServiceRunnable = null; removeView(); } else { TabQueueHelper.removeURLFromFile( applicationContext, intentUrl, TabQueueHelper.FILE_NAME); } openNow(safeIntent.getUnsafe()); stopSelfResult(startId); } }); return START_REDELIVER_INTENT; } sharedPreferences .edit() .putString(GeckoPreferences.PREFS_TAB_QUEUE_LAST_SITE, intentUrl) .putLong(GeckoPreferences.PREFS_TAB_QUEUE_LAST_TIME, System.currentTimeMillis()) .apply(); } if (stopServiceRunnable != null) { // If we're already displaying a toast, keep displaying it but store the previous url. // The open button will refer to the most recently opened link. tabQueueHandler.removeCallbacks(stopServiceRunnable); stopServiceRunnable.run(false); } else { try { windowManager.addView(toastLayout, toastLayoutParams); } catch (final SecurityException e) { Toast.makeText(this, getText(R.string.tab_queue_toast_message), Toast.LENGTH_SHORT).show(); } } stopServiceRunnable = new StopServiceRunnable(startId) { @Override public void onRun() { addURLToTabQueue(intent, TabQueueHelper.FILE_NAME); stopServiceRunnable = null; } }; openNowButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick(final View view) { tabQueueHandler.removeCallbacks(stopServiceRunnable); stopServiceRunnable = null; removeView(); openNow(intent); stopSelfResult(startId); } }); tabQueueHandler.postDelayed(stopServiceRunnable, TOAST_TIMEOUT); return START_REDELIVER_INTENT; }