/**
   * 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);
          }
        });
  }
Beispiel #3
0
 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;
  }
Beispiel #12
0
  /**
   * 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;
  }
Beispiel #16
0
 private SharedPreferences getSharedPreferences() {
   return GeckoSharedPrefs.forProfile(mContext);
 }
Beispiel #17
0
  @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;
  }