1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- 2 * This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 package org.mozilla.gecko.home; 7 8 import static org.mozilla.gecko.home.HomeConfig.createBuiltinPanelConfig; 9 10 import java.util.ArrayList; 11 import java.util.EnumSet; 12 import java.util.Locale; 13 14 import org.json.JSONArray; 15 import org.json.JSONException; 16 import org.json.JSONObject; 17 import org.mozilla.gecko.GeckoSharedPrefs; 18 import org.mozilla.gecko.home.HomeConfig.HomeConfigBackend; 19 import org.mozilla.gecko.home.HomeConfig.OnReloadListener; 20 import org.mozilla.gecko.home.HomeConfig.PanelConfig; 21 import org.mozilla.gecko.home.HomeConfig.PanelType; 22 import org.mozilla.gecko.home.HomeConfig.State; 23 import org.mozilla.gecko.util.HardwareUtils; 24 25 import android.content.BroadcastReceiver; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.content.SharedPreferences; 30 import android.support.annotation.CheckResult; 31 import android.support.annotation.VisibleForTesting; 32 import android.support.v4.content.LocalBroadcastManager; 33 import android.text.TextUtils; 34 import android.util.Log; 35 36 public class HomeConfigPrefsBackend implements HomeConfigBackend { 37 private static final String LOGTAG = "GeckoHomeConfigBackend"; 38 39 // Increment this to trigger a migration. 40 @VisibleForTesting 41 static final int VERSION = 8; 42 43 // This key was originally used to store only an array of panel configs. 44 public static final String PREFS_CONFIG_KEY_OLD = "home_panels"; 45 46 // This key is now used to store a version number with the array of panel configs. 47 public static final String PREFS_CONFIG_KEY = "home_panels_with_version"; 48 49 // Keys used with JSON object stored in prefs. 50 private static final String JSON_KEY_PANELS = "panels"; 51 private static final String JSON_KEY_VERSION = "version"; 52 53 private static final String PREFS_LOCALE_KEY = "home_locale"; 54 55 private static final String RELOAD_BROADCAST = "HomeConfigPrefsBackend:Reload"; 56 57 private final Context mContext; 58 private ReloadBroadcastReceiver mReloadBroadcastReceiver; 59 private OnReloadListener mReloadListener; 60 61 private static boolean sMigrationDone; 62 HomeConfigPrefsBackend(Context context)63 public HomeConfigPrefsBackend(Context context) { 64 mContext = context; 65 } 66 getSharedPreferences()67 private SharedPreferences getSharedPreferences() { 68 return GeckoSharedPrefs.forProfile(mContext); 69 } 70 loadDefaultConfig()71 private State loadDefaultConfig() { 72 final ArrayList<PanelConfig> panelConfigs = new ArrayList<PanelConfig>(); 73 74 panelConfigs.add(createBuiltinPanelConfig(mContext, PanelType.TOP_SITES, 75 EnumSet.of(PanelConfig.Flags.DEFAULT_PANEL))); 76 77 panelConfigs.add(createBuiltinPanelConfig(mContext, PanelType.BOOKMARKS)); 78 panelConfigs.add(createBuiltinPanelConfig(mContext, PanelType.COMBINED_HISTORY)); 79 80 81 return new State(panelConfigs, true); 82 } 83 84 /** 85 * Iterate through the panels to check if they are all disabled. 86 */ allPanelsAreDisabled(JSONArray jsonPanels)87 private static boolean allPanelsAreDisabled(JSONArray jsonPanels) throws JSONException { 88 final int count = jsonPanels.length(); 89 for (int i = 0; i < count; i++) { 90 final JSONObject jsonPanelConfig = jsonPanels.getJSONObject(i); 91 92 if (!jsonPanelConfig.optBoolean(PanelConfig.JSON_KEY_DISABLED, false)) { 93 return false; 94 } 95 } 96 97 return true; 98 } 99 100 protected enum Position { 101 NONE, // Not present. 102 FRONT, // At the front of the list of panels. 103 BACK, // At the back of the list of panels. 104 } 105 106 /** 107 * Create and insert a built-in panel configuration. 108 * 109 * @param context Android context. 110 * @param jsonPanels array of JSON panels to update in place. 111 * @param panelType to add. 112 * @param positionOnPhones where to place the new panel on phones. 113 * @param positionOnTablets where to place the new panel on tablets. 114 * @throws JSONException 115 */ addBuiltinPanelConfig(Context context, JSONArray jsonPanels, PanelType panelType, Position positionOnPhones, Position positionOnTablets)116 protected static void addBuiltinPanelConfig(Context context, JSONArray jsonPanels, 117 PanelType panelType, Position positionOnPhones, Position positionOnTablets) throws JSONException { 118 // Add the new panel. 119 final JSONObject jsonPanelConfig = 120 createBuiltinPanelConfig(context, panelType).toJSON(); 121 122 // If any panel is enabled, then we should make the new panel enabled. 123 jsonPanelConfig.put(PanelConfig.JSON_KEY_DISABLED, 124 allPanelsAreDisabled(jsonPanels)); 125 126 final boolean isTablet = HardwareUtils.isTablet(); 127 final boolean isPhone = !isTablet; 128 129 // Maybe add the new panel to the front of the array. 130 if ((isPhone && positionOnPhones == Position.FRONT) || 131 (isTablet && positionOnTablets == Position.FRONT)) { 132 // This is an inefficient way to stretch [a, b, c] to [a, a, b, c]. 133 for (int i = jsonPanels.length(); i >= 1; i--) { 134 jsonPanels.put(i, jsonPanels.get(i - 1)); 135 } 136 // And this inserts [d, a, b, c]. 137 jsonPanels.put(0, jsonPanelConfig); 138 } 139 140 // Maybe add the new panel to the back of the array. 141 if ((isPhone && positionOnPhones == Position.BACK) || 142 (isTablet && positionOnTablets == Position.BACK)) { 143 jsonPanels.put(jsonPanelConfig); 144 } 145 } 146 147 /** 148 * Updates the panels to combine the History and Sync panels into the (Combined) History panel. 149 * 150 * Tries to replace the History panel with the Combined History panel if visible, or falls back to 151 * replacing the Sync panel if it's visible. That way, we minimize panel reordering during a migration. 152 * @param context Android context 153 * @param jsonPanels array of original JSON panels 154 * @return new array of updated JSON panels 155 * @throws JSONException 156 */ combineHistoryAndSyncPanels(Context context, JSONArray jsonPanels)157 private static JSONArray combineHistoryAndSyncPanels(Context context, JSONArray jsonPanels) throws JSONException { 158 EnumSet<PanelConfig.Flags> historyFlags = null; 159 EnumSet<PanelConfig.Flags> syncFlags = null; 160 161 int historyIndex = -1; 162 int syncIndex = -1; 163 164 // Determine state and location of History and Sync panels. 165 for (int i = 0; i < jsonPanels.length(); i++) { 166 JSONObject panelObj = jsonPanels.getJSONObject(i); 167 final PanelConfig panelConfig = new PanelConfig(panelObj); 168 final PanelType type = panelConfig.getType(); 169 if (type == PanelType.DEPRECATED_HISTORY) { 170 historyIndex = i; 171 historyFlags = panelConfig.getFlags(); 172 } else if (type == PanelType.DEPRECATED_REMOTE_TABS) { 173 syncIndex = i; 174 syncFlags = panelConfig.getFlags(); 175 } else if (type == PanelType.COMBINED_HISTORY) { 176 // Partial landing of bug 1220928 combined the History and Sync panels of users who didn't 177 // have home panel customizations (including new users), thus they don't this migration. 178 return jsonPanels; 179 } 180 } 181 182 if (historyIndex == -1 || syncIndex == -1) { 183 throw new IllegalArgumentException("Expected both History and Sync panels to be present prior to Combined History."); 184 } 185 186 PanelConfig newPanel; 187 int replaceIndex; 188 int removeIndex; 189 190 if (historyFlags.contains(PanelConfig.Flags.DISABLED_PANEL) && !syncFlags.contains(PanelConfig.Flags.DISABLED_PANEL)) { 191 // Replace the Sync panel if it's visible and the History panel is disabled. 192 replaceIndex = syncIndex; 193 removeIndex = historyIndex; 194 newPanel = createBuiltinPanelConfig(context, PanelType.COMBINED_HISTORY, syncFlags); 195 } else { 196 // Otherwise, just replace the History panel. 197 replaceIndex = historyIndex; 198 removeIndex = syncIndex; 199 newPanel = createBuiltinPanelConfig(context, PanelType.COMBINED_HISTORY, historyFlags); 200 } 201 202 // Copy the array with updated panel and removed panel. 203 final JSONArray newArray = new JSONArray(); 204 for (int i = 0; i < jsonPanels.length(); i++) { 205 if (i == replaceIndex) { 206 newArray.put(newPanel.toJSON()); 207 } else if (i == removeIndex) { 208 continue; 209 } else { 210 newArray.put(jsonPanels.get(i)); 211 } 212 } 213 214 return newArray; 215 } 216 217 /** 218 * Iterate over all homepanels to verify that there is at least one default panel. If there is 219 * no default panel, set History as the default panel. (This is only relevant for two botched 220 * migrations where the history panel should have been made the default panel, but wasn't.) 221 */ ensureDefaultPanelForV5orV8(Context context, JSONArray jsonPanels)222 private static void ensureDefaultPanelForV5orV8(Context context, JSONArray jsonPanels) throws JSONException { 223 int historyIndex = -1; 224 225 // If all panels are disabled, there is no default panel - this is the only valid state 226 // that has no default. We can use this flag to track whether any visible panels have been 227 // found. 228 boolean enabledPanelsFound = false; 229 230 for (int i = 0; i < jsonPanels.length(); i++) { 231 final PanelConfig panelConfig = new PanelConfig(jsonPanels.getJSONObject(i)); 232 if (panelConfig.isDefault()) { 233 return; 234 } 235 236 if (!panelConfig.isDisabled()) { 237 enabledPanelsFound = true; 238 } 239 240 if (panelConfig.getType() == PanelType.COMBINED_HISTORY) { 241 historyIndex = i; 242 } 243 } 244 245 if (!enabledPanelsFound) { 246 // No panels are enabled, hence there can be no default (see noEnabledPanelsFound declaration 247 // for more information). 248 return; 249 } 250 251 // Make the History panel default. We can't modify existing PanelConfigs, so make a new one. 252 final PanelConfig historyPanelConfig = createBuiltinPanelConfig(context, PanelType.COMBINED_HISTORY, EnumSet.of(PanelConfig.Flags.DEFAULT_PANEL)); 253 jsonPanels.put(historyIndex, historyPanelConfig.toJSON()); 254 } 255 256 /** 257 * Removes a panel from the home panel config. 258 * If the removed panel was set as the default home panel, we provide a replacement for it. 259 * 260 * @param context Android context 261 * @param jsonPanels array of original JSON panels 262 * @param panelToRemove The home panel to be removed. 263 * @param replacementPanel The panel which will replace it if the removed panel 264 * was the default home panel. 265 * @param alwaysUnhide If true, the replacement panel will always be unhidden, 266 * otherwise only if we turn it into the new default panel. 267 * @return new array of updated JSON panels 268 * @throws JSONException 269 */ removePanel(Context context, JSONArray jsonPanels, PanelType panelToRemove, PanelType replacementPanel, boolean alwaysUnhide)270 private static JSONArray removePanel(Context context, JSONArray jsonPanels, 271 PanelType panelToRemove, PanelType replacementPanel, boolean alwaysUnhide) throws JSONException { 272 boolean wasDefault = false; 273 boolean wasDisabled = false; 274 int replacementPanelIndex = -1; 275 boolean replacementWasDefault = false; 276 277 // JSONArrary doesn't provide remove() for API < 19, therefore we need to manually copy all 278 // the items we don't want deleted into a new array. 279 final JSONArray newJSONPanels = new JSONArray(); 280 281 for (int i = 0; i < jsonPanels.length(); i++) { 282 final JSONObject panelJSON = jsonPanels.getJSONObject(i); 283 final PanelConfig panelConfig = new PanelConfig(panelJSON); 284 285 if (panelConfig.getType() == panelToRemove) { 286 // If this panel was the default we'll need to assign a new default: 287 wasDefault = panelConfig.isDefault(); 288 wasDisabled = panelConfig.isDisabled(); 289 } else { 290 if (panelConfig.getType() == replacementPanel) { 291 replacementPanelIndex = newJSONPanels.length(); 292 if (panelConfig.isDefault()) { 293 replacementWasDefault = true; 294 } 295 } 296 297 newJSONPanels.put(panelJSON); 298 } 299 } 300 301 // Unless alwaysUnhide is true, we make the replacement panel visible only if it is going 302 // to be the new default panel, since a hidden default panel doesn't make sense. 303 // This is to allow preserving the behaviour of the original reading list migration function. 304 if ((wasDefault || alwaysUnhide) && !wasDisabled) { 305 final JSONObject replacementPanelConfig; 306 if (wasDefault) { 307 // If the removed panel was the default, the replacement has to be made the new default 308 replacementPanelConfig = createBuiltinPanelConfig(context, replacementPanel, EnumSet.of(PanelConfig.Flags.DEFAULT_PANEL)).toJSON(); 309 } else { 310 final EnumSet<HomeConfig.PanelConfig.Flags> flags; 311 if (replacementWasDefault) { 312 // However if the replacement panel was already default, we need to preserve it's default status 313 // (By rewriting the PanelConfig, we lose all existing flags, so we need to make sure desired 314 // flags are retained - in this case there's only DEFAULT_PANEL, which is mutually 315 // exclusive with the DISABLE_PANEL case). 316 flags = EnumSet.of(PanelConfig.Flags.DEFAULT_PANEL); 317 } else { 318 flags = EnumSet.noneOf(PanelConfig.Flags.class); 319 } 320 321 // The panel is visible since we don't set Flags.DISABLED_PANEL. 322 replacementPanelConfig = createBuiltinPanelConfig(context, replacementPanel, flags).toJSON(); 323 } 324 325 if (replacementPanelIndex != -1) { 326 newJSONPanels.put(replacementPanelIndex, replacementPanelConfig); 327 } else { 328 newJSONPanels.put(replacementPanelConfig); 329 } 330 } 331 332 return newJSONPanels; 333 } 334 335 /** 336 * Checks to see if the reading list panel already exists. 337 * 338 * @param jsonPanels JSONArray array representing the curent set of panel configs. 339 * 340 * @return boolean Whether or not the reading list panel exists. 341 */ readingListPanelExists(JSONArray jsonPanels)342 private static boolean readingListPanelExists(JSONArray jsonPanels) { 343 final int count = jsonPanels.length(); 344 for (int i = 0; i < count; i++) { 345 try { 346 final JSONObject jsonPanelConfig = jsonPanels.getJSONObject(i); 347 final PanelConfig panelConfig = new PanelConfig(jsonPanelConfig); 348 if (panelConfig.getType() == PanelType.DEPRECATED_READING_LIST) { 349 return true; 350 } 351 } catch (Exception e) { 352 // It's okay to ignore this exception, since an invalid reading list 353 // panel config is equivalent to no reading list panel. 354 Log.e(LOGTAG, "Exception loading PanelConfig from JSON", e); 355 } 356 } 357 return false; 358 } 359 360 @CheckResult migratePrefsFromVersionToVersion(final Context context, final int currentVersion, final int newVersion, final JSONArray jsonPanelsIn, final SharedPreferences.Editor prefsEditor)361 static synchronized JSONArray migratePrefsFromVersionToVersion(final Context context, final int currentVersion, final int newVersion, 362 final JSONArray jsonPanelsIn, final SharedPreferences.Editor prefsEditor) throws JSONException { 363 364 JSONArray jsonPanels = jsonPanelsIn; 365 366 for (int v = currentVersion + 1; v <= newVersion; v++) { 367 Log.d(LOGTAG, "Migrating to version = " + v); 368 369 switch (v) { 370 case 1: 371 // Add "Recent Tabs" panel. 372 addBuiltinPanelConfig(context, jsonPanels, 373 PanelType.DEPRECATED_RECENT_TABS, Position.FRONT, Position.BACK); 374 375 // Remove the old pref key. 376 prefsEditor.remove(PREFS_CONFIG_KEY_OLD); 377 break; 378 379 case 2: 380 // Add "Remote Tabs"/"Synced Tabs" panel. 381 addBuiltinPanelConfig(context, jsonPanels, 382 PanelType.DEPRECATED_REMOTE_TABS, Position.FRONT, Position.BACK); 383 break; 384 385 case 3: 386 // Add the "Reading List" panel if it does not exist. At one time, 387 // the Reading List panel was shown only to devices that were not 388 // considered "low memory". Now, we expose the panel to all devices. 389 // This migration should only occur for "low memory" devices. 390 // Note: This will not agree with the default configuration, which 391 // has DEPRECATED_REMOTE_TABS after DEPRECATED_READING_LIST on some devices. 392 if (!readingListPanelExists(jsonPanels)) { 393 addBuiltinPanelConfig(context, jsonPanels, 394 PanelType.DEPRECATED_READING_LIST, Position.BACK, Position.BACK); 395 } 396 break; 397 398 case 4: 399 // Combine the History and Sync panels. In order to minimize an unexpected reordering 400 // of panels, we try to replace the History panel if it's visible, and fall back to 401 // the Sync panel if that's visible. 402 jsonPanels = combineHistoryAndSyncPanels(context, jsonPanels); 403 break; 404 405 case 5: 406 // This is the fix for bug 1264136 where we lost track of the default panel during some migrations. 407 ensureDefaultPanelForV5orV8(context, jsonPanels); 408 break; 409 410 case 6: 411 jsonPanels = removePanel(context, jsonPanels, 412 PanelType.DEPRECATED_READING_LIST, PanelType.BOOKMARKS, false); 413 break; 414 415 case 7: 416 jsonPanels = removePanel(context, jsonPanels, 417 PanelType.DEPRECATED_RECENT_TABS, PanelType.COMBINED_HISTORY, true); 418 break; 419 420 case 8: 421 // Similar to "case 5" above, this time 1304777 - once again we lost track 422 // of the history panel 423 ensureDefaultPanelForV5orV8(context, jsonPanels); 424 break; 425 } 426 } 427 428 return jsonPanels; 429 } 430 431 /** 432 * Migrates JSON config data storage. 433 * 434 * @param context Context used to get shared preferences and create built-in panel. 435 * @param jsonString String currently stored in preferences. 436 * 437 * @return JSONArray array representing new set of panel configs. 438 */ maybePerformMigration(Context context, String jsonString)439 private static synchronized JSONArray maybePerformMigration(Context context, String jsonString) throws JSONException { 440 // If the migration is already done, we're at the current version. 441 if (sMigrationDone) { 442 final JSONObject json = new JSONObject(jsonString); 443 return json.getJSONArray(JSON_KEY_PANELS); 444 } 445 446 // Make sure we only do this version check once. 447 sMigrationDone = true; 448 449 JSONArray jsonPanels; 450 final int version; 451 452 final SharedPreferences prefs = GeckoSharedPrefs.forProfile(context); 453 if (prefs.contains(PREFS_CONFIG_KEY_OLD)) { 454 // Our original implementation did not contain versioning, so this is implicitly version 0. 455 jsonPanels = new JSONArray(jsonString); 456 version = 0; 457 } else { 458 final JSONObject json = new JSONObject(jsonString); 459 jsonPanels = json.getJSONArray(JSON_KEY_PANELS); 460 version = json.getInt(JSON_KEY_VERSION); 461 } 462 463 if (version == VERSION) { 464 return jsonPanels; 465 } 466 467 Log.d(LOGTAG, "Performing migration"); 468 469 final SharedPreferences.Editor prefsEditor = prefs.edit(); 470 471 jsonPanels = migratePrefsFromVersionToVersion(context, version, VERSION, jsonPanels, prefsEditor); 472 473 // Save the new panel config and the new version number. 474 final JSONObject newJson = new JSONObject(); 475 newJson.put(JSON_KEY_PANELS, jsonPanels); 476 newJson.put(JSON_KEY_VERSION, VERSION); 477 478 prefsEditor.putString(PREFS_CONFIG_KEY, newJson.toString()); 479 prefsEditor.apply(); 480 481 return jsonPanels; 482 } 483 loadConfigFromString(String jsonString)484 private State loadConfigFromString(String jsonString) { 485 final JSONArray jsonPanelConfigs; 486 try { 487 jsonPanelConfigs = maybePerformMigration(mContext, jsonString); 488 updatePrefsFromConfig(jsonPanelConfigs); 489 } catch (JSONException e) { 490 Log.e(LOGTAG, "Error loading the list of home panels from JSON prefs", e); 491 492 // Fallback to default config 493 return loadDefaultConfig(); 494 } 495 496 final ArrayList<PanelConfig> panelConfigs = new ArrayList<PanelConfig>(); 497 498 final int count = jsonPanelConfigs.length(); 499 for (int i = 0; i < count; i++) { 500 try { 501 final JSONObject jsonPanelConfig = jsonPanelConfigs.getJSONObject(i); 502 final PanelConfig panelConfig = new PanelConfig(jsonPanelConfig); 503 panelConfigs.add(panelConfig); 504 } catch (Exception e) { 505 Log.e(LOGTAG, "Exception loading PanelConfig from JSON", e); 506 } 507 } 508 509 return new State(panelConfigs, false); 510 } 511 512 @Override load()513 public State load() { 514 final SharedPreferences prefs = getSharedPreferences(); 515 516 final String key = (prefs.contains(PREFS_CONFIG_KEY_OLD) ? PREFS_CONFIG_KEY_OLD : PREFS_CONFIG_KEY); 517 final String jsonString = prefs.getString(key, null); 518 519 final State configState; 520 if (TextUtils.isEmpty(jsonString)) { 521 configState = loadDefaultConfig(); 522 } else { 523 configState = loadConfigFromString(jsonString); 524 } 525 526 return configState; 527 } 528 529 @Override save(State configState)530 public void save(State configState) { 531 final SharedPreferences prefs = getSharedPreferences(); 532 final SharedPreferences.Editor editor = prefs.edit(); 533 534 // No need to save the state to disk if it represents the default 535 // HomeConfig configuration. Simply force all existing HomeConfigLoader 536 // instances to refresh their contents. 537 if (!configState.isDefault()) { 538 final JSONArray jsonPanelConfigs = new JSONArray(); 539 540 for (PanelConfig panelConfig : configState) { 541 try { 542 final JSONObject jsonPanelConfig = panelConfig.toJSON(); 543 jsonPanelConfigs.put(jsonPanelConfig); 544 } catch (Exception e) { 545 Log.e(LOGTAG, "Exception converting PanelConfig to JSON", e); 546 } 547 } 548 549 try { 550 final JSONObject json = new JSONObject(); 551 json.put(JSON_KEY_PANELS, jsonPanelConfigs); 552 json.put(JSON_KEY_VERSION, VERSION); 553 554 editor.putString(PREFS_CONFIG_KEY, json.toString()); 555 } catch (JSONException e) { 556 Log.e(LOGTAG, "Exception saving PanelConfig state", e); 557 } 558 } 559 560 editor.putString(PREFS_LOCALE_KEY, Locale.getDefault().toString()); 561 editor.apply(); 562 563 // Trigger reload listeners on all live backend instances 564 sendReloadBroadcast(); 565 } 566 567 @Override getLocale()568 public String getLocale() { 569 final SharedPreferences prefs = getSharedPreferences(); 570 571 String locale = prefs.getString(PREFS_LOCALE_KEY, null); 572 if (locale == null) { 573 // Initialize config with the current locale 574 final String currentLocale = Locale.getDefault().toString(); 575 576 final SharedPreferences.Editor editor = prefs.edit(); 577 editor.putString(PREFS_LOCALE_KEY, currentLocale); 578 editor.apply(); 579 580 // If the user has saved HomeConfig before, return null this 581 // one time to trigger a refresh and ensure we use the 582 // correct locale for the saved state. For more context, 583 // see HomePanelsManager.onLocaleReady(). 584 if (!prefs.contains(PREFS_CONFIG_KEY)) { 585 locale = currentLocale; 586 } 587 } 588 589 return locale; 590 } 591 592 @Override setOnReloadListener(OnReloadListener listener)593 public void setOnReloadListener(OnReloadListener listener) { 594 if (mReloadListener != null) { 595 unregisterReloadReceiver(); 596 mReloadBroadcastReceiver = null; 597 } 598 599 mReloadListener = listener; 600 601 if (mReloadListener != null) { 602 mReloadBroadcastReceiver = new ReloadBroadcastReceiver(); 603 registerReloadReceiver(); 604 } 605 } 606 607 /** 608 * Update prefs that depend on home panels state. 609 * 610 * This includes the prefs that keep track of whether bookmarks or history are enabled, which are 611 * used to control the visibility of the corresponding menu items. 612 */ updatePrefsFromConfig(JSONArray panelsArray)613 private void updatePrefsFromConfig(JSONArray panelsArray) { 614 final SharedPreferences prefs = GeckoSharedPrefs.forProfile(mContext); 615 if (!prefs.contains(HomeConfig.PREF_KEY_BOOKMARKS_PANEL_ENABLED) 616 || !prefs.contains(HomeConfig.PREF_KEY_HISTORY_PANEL_ENABLED)) { 617 618 final String bookmarkType = PanelType.BOOKMARKS.toString(); 619 final String historyType = PanelType.COMBINED_HISTORY.toString(); 620 try { 621 for (int i = 0; i < panelsArray.length(); i++) { 622 final JSONObject panelObj = panelsArray.getJSONObject(i); 623 final String panelType = panelObj.optString(PanelConfig.JSON_KEY_TYPE, null); 624 if (panelType == null) { 625 break; 626 } 627 final boolean isDisabled = panelObj.optBoolean(PanelConfig.JSON_KEY_DISABLED, false); 628 if (bookmarkType.equals(panelType)) { 629 prefs.edit().putBoolean(HomeConfig.PREF_KEY_BOOKMARKS_PANEL_ENABLED, !isDisabled).apply(); 630 } else if (historyType.equals(panelType)) { 631 prefs.edit().putBoolean(HomeConfig.PREF_KEY_HISTORY_PANEL_ENABLED, !isDisabled).apply(); 632 } 633 } 634 } catch (JSONException e) { 635 Log.e(LOGTAG, "Error fetching panel from config to update prefs"); 636 } 637 } 638 } 639 640 sendReloadBroadcast()641 private void sendReloadBroadcast() { 642 final LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(mContext); 643 final Intent reloadIntent = new Intent(RELOAD_BROADCAST); 644 lbm.sendBroadcast(reloadIntent); 645 } 646 registerReloadReceiver()647 private void registerReloadReceiver() { 648 final LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(mContext); 649 lbm.registerReceiver(mReloadBroadcastReceiver, new IntentFilter(RELOAD_BROADCAST)); 650 } 651 unregisterReloadReceiver()652 private void unregisterReloadReceiver() { 653 final LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(mContext); 654 lbm.unregisterReceiver(mReloadBroadcastReceiver); 655 } 656 657 private class ReloadBroadcastReceiver extends BroadcastReceiver { 658 @Override onReceive(Context context, Intent intent)659 public void onReceive(Context context, Intent intent) { 660 mReloadListener.onReload(); 661 } 662 } 663 } 664