Settings.kt 34.7 KB
Newer Older
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
3
4
5
 * 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.fenix.utils
6

7
import android.accessibilityservice.AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES
8
import android.app.Application
9
import android.content.Context
10
import android.content.Context.MODE_PRIVATE
11
import android.content.SharedPreferences
12
13
import android.content.pm.ShortcutManager
import android.os.Build
14
import android.view.accessibility.AccessibilityManager
Denys M's avatar
Denys M committed
15
16
import androidx.annotation.VisibleForTesting
import androidx.annotation.VisibleForTesting.PRIVATE
17
import androidx.lifecycle.LifecycleOwner
18
import mozilla.components.feature.sitepermissions.SitePermissionsRules
Colin Lee's avatar
Colin Lee committed
19
import mozilla.components.feature.sitepermissions.SitePermissionsRules.Action
20
import mozilla.components.feature.sitepermissions.SitePermissionsRules.AutoplayAction
21
22
23
24
import mozilla.components.support.ktx.android.content.PreferencesHolder
import mozilla.components.support.ktx.android.content.booleanPreference
import mozilla.components.support.ktx.android.content.floatPreference
import mozilla.components.support.ktx.android.content.intPreference
25
import mozilla.components.support.ktx.android.content.longPreference
26
import mozilla.components.support.ktx.android.content.stringPreference
27
import org.mozilla.fenix.BuildConfig
28
import org.mozilla.fenix.Config
29
import org.mozilla.fenix.R
30
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
31
import org.mozilla.fenix.components.metrics.MozillaProductDetector
32
import org.mozilla.fenix.components.settings.counterPreference
33
import org.mozilla.fenix.components.toolbar.ToolbarPosition
34
import org.mozilla.fenix.ext.components
35
import org.mozilla.fenix.ext.getPreferenceKey
36
import org.mozilla.fenix.settings.PhoneFeature
37
import org.mozilla.fenix.settings.deletebrowsingdata.DeleteBrowsingDataOnQuitType
38
39
import org.mozilla.fenix.settings.logins.SavedLoginsSortingStrategyMenu
import org.mozilla.fenix.settings.logins.SortingStrategy
40
import org.mozilla.fenix.settings.registerOnSharedPreferenceChangeListener
41
import java.security.InvalidParameterException
42

43
44
private const val AUTOPLAY_USER_SETTING = "AUTOPLAY_USER_SETTING"

45
46
/**
 * A simple wrapper for SharedPreferences that makes reading preference a little bit easier.
47
 * @param appContext Reference to application context.
48
 */
49
@Suppress("LargeClass", "TooManyFunctions")
50
51
class Settings(private val appContext: Context) : PreferencesHolder {

52
    companion object {
53
        const val topSitesMaxCount = 16
54
        const val FENIX_PREFERENCES = "fenix_preferences"
55

56
57
        private const val BLOCKED_INT = 0
        private const val ASK_TO_ALLOW_INT = 1
58
        private const val ALLOWED_INT = 2
59
60
        private const val CFR_COUNT_CONDITION_FOCUS_INSTALLED = 1
        private const val CFR_COUNT_CONDITION_FOCUS_NOT_INSTALLED = 3
61

62
        const val ONE_DAY_MS = 60 * 60 * 24 * 1000L
63
        const val THREE_DAYS_MS = 3 * ONE_DAY_MS
64
65
66
        const val ONE_WEEK_MS = 60 * 60 * 24 * 7 * 1000L
        const val ONE_MONTH_MS = (60 * 60 * 24 * 365 * 1000L) / 12

67
        private fun Action.toInt() = when (this) {
Colin Lee's avatar
Colin Lee committed
68
69
            Action.BLOCKED -> BLOCKED_INT
            Action.ASK_TO_ALLOW -> ASK_TO_ALLOW_INT
70
            Action.ALLOWED -> ALLOWED_INT
71
72
        }

73
74
75
76
77
78
        private fun AutoplayAction.toInt() = when (this) {
            AutoplayAction.BLOCKED -> BLOCKED_INT
            AutoplayAction.ALLOWED -> ALLOWED_INT
        }

        private fun Int.toAction() = when (this) {
Colin Lee's avatar
Colin Lee committed
79
80
            BLOCKED_INT -> Action.BLOCKED
            ASK_TO_ALLOW_INT -> Action.ASK_TO_ALLOW
81
82
83
84
85
86
87
            ALLOWED_INT -> Action.ALLOWED
            else -> throw InvalidParameterException("$this is not a valid SitePermissionsRules.Action")
        }

        private fun Int.toAutoplayAction() = when (this) {
            BLOCKED_INT -> AutoplayAction.BLOCKED
            ALLOWED_INT -> AutoplayAction.ALLOWED
88
89
            // Users from older versions may have saved invalid values. Migrate them to BLOCKED
            ASK_TO_ALLOW_INT -> AutoplayAction.BLOCKED
90
            else -> throw InvalidParameterException("$this is not a valid SitePermissionsRules.AutoplayAction")
91
        }
92
93
    }

94
95
96
    @VisibleForTesting
    internal val isCrashReportEnabledInBuild: Boolean =
        BuildConfig.CRASH_REPORTING && Config.channel.isReleased
97

98
    override val preferences: SharedPreferences =
99
100
        appContext.getSharedPreferences(FENIX_PREFERENCES, MODE_PRIVATE)

101
    var showTopFrecentSites by booleanPreference(
102
        appContext.getPreferenceKey(R.string.pref_key_enable_top_frecent_sites),
103
        default = true
104
105
    )

106
107
108
109
110
111
112
113
114
115
    var numberOfAppLaunches by intPreference(
        appContext.getPreferenceKey(R.string.pref_key_times_app_opened),
        default = 0
    )

    var lastReviewPromptTimeInMillis by longPreference(
        appContext.getPreferenceKey(R.string.pref_key_last_review_prompt_shown_time),
        default = 0L
    )

116
117
118
119
120
121
122
123
    var lastCfrShownTimeInMillis by longPreference(
        appContext.getPreferenceKey(R.string.pref_key_last_cfr_shown_time),
        default = 0L
    )

    val canShowCfr: Boolean
        get() = (System.currentTimeMillis() - lastCfrShownTimeInMillis) > THREE_DAYS_MS

124
125
126
127
128
    var forceEnableZoom by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_accessibility_force_enable_zoom),
        default = false
    )

129
130
131
132
133
    var adjustCampaignId by stringPreference(
        appContext.getPreferenceKey(R.string.pref_key_adjust_campaign),
        default = ""
    )

134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
    var adjustNetwork by stringPreference(
        appContext.getPreferenceKey(R.string.pref_key_adjust_network),
        default = ""
    )

    var adjustAdGroup by stringPreference(
        appContext.getPreferenceKey(R.string.pref_key_adjust_adgroup),
        default = ""
    )

    var adjustCreative by stringPreference(
        appContext.getPreferenceKey(R.string.pref_key_adjust_creative),
        default = ""
    )

149
150
    var openLinksInAPrivateTab by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_open_links_in_a_private_tab),
151
152
153
        default = false
    )

154
155
    var allowScreenshotsInPrivateMode by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_allow_screenshots_in_private_mode),
156
        default = false
157
158
    )

ekager's avatar
ekager committed
159
160
161
162
163
    var shouldDisplayMasterPasswordMigrationTip by booleanPreference(
        appContext.getString(R.string.pref_key_master_password_tip),
        true
    )

164
165
166
167
168
    var shouldReturnToBrowser by booleanPreference(
        appContext.getString(R.string.pref_key_return_to_browser),
        false
    )

169
170
    // If any of the prefs have been modified, quit displaying the fenix moved tip
    fun shouldDisplayFenixMovingTip(): Boolean =
171
172
173
174
175
176
177
178
179
180
181
182
        preferences.getBoolean(
            appContext.getString(R.string.pref_key_migrating_from_fenix_nightly_tip),
            true
        ) &&
                preferences.getBoolean(
                    appContext.getString(R.string.pref_key_migrating_from_firefox_nightly_tip),
                    true
                ) &&
                preferences.getBoolean(
                    appContext.getString(R.string.pref_key_migrating_from_fenix_tip),
                    true
                )
183

184
185
186
187
    var defaultSearchEngineName by stringPreference(
        appContext.getPreferenceKey(R.string.pref_key_search_engine),
        default = ""
    )
188

189
190
191
192
193
194
195
196
197
198
    var openInAppOpened by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_open_in_app_opened),
        default = false
    )

    var installPwaOpened by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_install_pwa_opened),
        default = false
    )

199
200
201
202
203
    var showCollectionsPlaceholderOnHome by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_show_collections_placeholder_home),
        default = true
    )

204
    val isCrashReportingEnabled: Boolean
Denys M's avatar
Denys M committed
205
        get() = isCrashReportEnabledInBuild &&
206
207
208
209
                preferences.getBoolean(
                    appContext.getPreferenceKey(R.string.pref_key_crash_reporter),
                    true
                )
210

211
212
213
214
    val isRemoteDebuggingEnabled by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_remote_debugging),
        default = false
    )
215

216
217
218
219
    val isTelemetryEnabled by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_telemetry),
        default = true
    )
220

221
222
223
224
225
    val isMarketingTelemetryEnabled by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_marketing_telemetry),
        default = true
    )

jhugman's avatar
jhugman committed
226
    var isExperimentationEnabled by booleanPreference(
227
228
229
230
        appContext.getPreferenceKey(R.string.pref_key_experimentation),
        default = true
    )

231
    private var trackingProtectionOnboardingShownThisSession = false
232
    var isOverrideTPPopupsForPerformanceTest = false
233

234
235
    val shouldShowTrackingProtectionCfr: Boolean
        get() = !isOverrideTPPopupsForPerformanceTest && canShowCfr &&
236
                (trackingProtectionOnboardingCount.underMaxCount() &&
237
                        !trackingProtectionOnboardingShownThisSession)
238

239
240
    var showSecretDebugMenuThisSession = false

241
    val shouldShowSecurityPinWarningSync: Boolean
242
        get() = loginsSecureWarningSyncCount.underMaxCount()
243
244

    val shouldShowSecurityPinWarning: Boolean
245
        get() = loginsSecureWarningCount.underMaxCount()
246

247
248
249
250
251
    var shouldShowPrivacyPopWindow by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_privacy_pop_window),
        default = true
    )

252
253
254
255
    var shouldUseLightTheme by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_light_theme),
        default = false
    )
256

257
258
259
260
261
    var shouldUseAutoSize by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_accessibility_auto_size),
        default = true
    )

262
263
264
265
    var fontSizeFactor by floatPreference(
        appContext.getPreferenceKey(R.string.pref_key_accessibility_font_scale),
        default = 1f
    )
266

267
268
269
270
271
272
273
274
275
276
    val shouldShowHistorySuggestions by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_search_browsing_history),
        default = true
    )

    val shouldShowBookmarkSuggestions by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_search_bookmarks),
        default = true
    )

277
278
279
280
281
    val shouldShowSyncedTabsSuggestions by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_search_synced_tabs),
        default = true
    )

282
283
    val shouldShowClipboardSuggestions by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_show_clipboard_suggestions),
284
285
        default = true
    )
Emily Kager's avatar
Emily Kager committed
286

287
    val shouldShowSearchShortcuts by booleanPreference(
288
        appContext.getPreferenceKey(R.string.pref_key_show_search_engine_shortcuts),
289
        default = false
290
291
    )

292
293
294
295
296
297
298
299
300
301
    var listTabView by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_tab_view_list),
        default = true
    )

    var gridTabView by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_tab_view_grid),
        default = false
    )

302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
    var manuallyCloseTabs by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_close_tabs_manually),
        default = true
    )

    var closeTabsAfterOneDay by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_close_tabs_after_one_day),
        default = false
    )

    var closeTabsAfterOneWeek by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_close_tabs_after_one_week),
        default = false
    )

    var closeTabsAfterOneMonth by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_close_tabs_after_one_month),
        default = false
    )

    fun getTabTimeout(): Long = when {
        closeTabsAfterOneDay -> ONE_DAY_MS
        closeTabsAfterOneWeek -> ONE_WEEK_MS
        closeTabsAfterOneMonth -> ONE_MONTH_MS
326
        else -> Long.MAX_VALUE
327
328
    }

329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
    enum class TabView {
        GRID, LIST
    }

    fun getTabViewPingString() = if (gridTabView) TabView.GRID.name else TabView.LIST.name

    enum class TabTimout {
        ONE_DAY, ONE_WEEK, ONE_MONTH, MANUAL
    }

    fun getTabTimeoutPingString(): String = when {
        closeTabsAfterOneDay -> {
            TabTimout.ONE_DAY.name
        }
        closeTabsAfterOneWeek -> {
            TabTimout.ONE_WEEK.name
        }
        closeTabsAfterOneMonth -> {
            TabTimout.ONE_MONTH.name
        }
        else -> {
            TabTimout.MANUAL.name
        }
    }

354
355
    fun getTabTimeoutString(): String = when {
        closeTabsAfterOneDay -> {
356
            appContext.getString(R.string.close_tabs_after_one_day_summary)
357
358
        }
        closeTabsAfterOneWeek -> {
359
            appContext.getString(R.string.close_tabs_after_one_week_summary)
360
361
        }
        closeTabsAfterOneMonth -> {
362
            appContext.getString(R.string.close_tabs_after_one_month_summary)
363
364
        }
        else -> {
365
            appContext.getString(R.string.close_tabs_manually_summary)
366
367
368
        }
    }

369
370
371
372
    val shouldUseDarkTheme by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_dark_theme),
        default = false
    )
373

374
375
376
377
    var shouldFollowDeviceTheme by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_follow_device_theme),
        default = false
    )
378

379
380
381
382
    var shouldUseTrackingProtection by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_tracking_protection),
        default = true
    )
383

384
385
386
387
    /**
     * Caches the last known "is default browser" state when the app was paused.
     * For an up to do date state use `isDefaultBrowser` instead.
     */
388
    var wasDefaultBrowserOnLastResume by booleanPreference(
389
390
391
392
        appContext.getPreferenceKey(R.string.pref_key_default_browser),
        default = isDefaultBrowser()
    )

393
394
395
396
397
    fun isDefaultBrowser(): Boolean {
        val browsers = BrowsersCache.all(appContext)
        return browsers.isDefaultBrowser
    }

398
399
400
401
    val shouldUseAutoBatteryTheme by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_auto_battery_theme),
        default = false
    )
Emily Kager's avatar
Emily Kager committed
402

403
404
405
406
407
    val useStandardTrackingProtection by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_tracking_protection_standard_option),
        true
    )

408
    val useStrictTrackingProtection by booleanPreference(
409
        appContext.getPreferenceKey(R.string.pref_key_tracking_protection_strict_default),
410
        false
411
412
    )

mcarare's avatar
mcarare committed
413
414
415
416
417
    val useCustomTrackingProtection by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_tracking_protection_custom_option),
        false
    )

418
419
420
421
422
423
424
425
426
427
    @VisibleForTesting(otherwise = PRIVATE)
    fun setStrictETP() {
        preferences.edit().putBoolean(
            appContext.getPreferenceKey(R.string.pref_key_tracking_protection_strict_default),
            true
        ).apply()
        preferences.edit().putBoolean(
            appContext.getPreferenceKey(R.string.pref_key_tracking_protection_standard_option),
            false
        ).apply()
428
        appContext.components.let {
429
430
431
432
433
434
435
            val policy = it.core.trackingProtectionPolicyFactory
                .createTrackingProtectionPolicy()
            it.useCases.settingsUseCases.updateTrackingProtection.invoke(policy)
            it.useCases.sessionUseCases.reload.invoke()
        }
    }

mcarare's avatar
mcarare committed
436
437
438
439
    val blockCookiesInCustomTrackingProtection by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_tracking_protection_custom_cookies),
        true
    )
440

mcarare's avatar
mcarare committed
441
442
    val blockCookiesSelectionInCustomTrackingProtection by stringPreference(
        appContext.getPreferenceKey(R.string.pref_key_tracking_protection_custom_cookies_select),
443
        "social"
mcarare's avatar
mcarare committed
444
445
446
447
448
449
450
451
452
    )

    val blockTrackingContentInCustomTrackingProtection by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_tracking_protection_custom_tracking_content),
        true
    )

    val blockTrackingContentSelectionInCustomTrackingProtection by stringPreference(
        appContext.getPreferenceKey(R.string.pref_key_tracking_protection_custom_tracking_content_select),
453
        "all"
mcarare's avatar
mcarare committed
454
455
456
457
458
459
460
461
462
463
464
465
    )

    val blockCryptominersInCustomTrackingProtection by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_tracking_protection_custom_cryptominers),
        true
    )

    val blockFingerprintersInCustomTrackingProtection by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_tracking_protection_custom_fingerprinters),
        true
    )

466
467
468
469
470
    val blockRedirectTrackersInCustomTrackingProtection by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_tracking_protection_redirect_trackers),
        true
    )

471
472
    val shouldUseFixedTopToolbar: Boolean
        get() {
473
            return touchExplorationIsEnabled || switchServiceIsEnabled
474
475
        }

476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
    var lastKnownMode: BrowsingMode = BrowsingMode.Normal
        get() {
            val lastKnownModeWasPrivate = preferences.getBoolean(
                appContext.getPreferenceKey(R.string.pref_key_last_known_mode_private),
                false
            )

            return if (lastKnownModeWasPrivate) {
                BrowsingMode.Private
            } else {
                BrowsingMode.Normal
            }
        }
        set(value) {
            val lastKnownModeWasPrivate = (value == BrowsingMode.Private)

            preferences.edit()
                .putBoolean(
494
495
496
                    appContext.getPreferenceKey(R.string.pref_key_last_known_mode_private),
                    lastKnownModeWasPrivate
                )
497
498
499
500
501
                .apply()

            field = value
        }

502
503
504
505
506
    var shouldDeleteBrowsingDataOnQuit by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_delete_browsing_data_on_quit),
        default = false
    )

507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
    var deleteOpenTabs by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_delete_open_tabs_now),
        default = true
    )

    var deleteBrowsingHistory by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_delete_browsing_history_now),
        default = true
    )

    var deleteCookies by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_delete_cookies_now),
        default = true
    )

    var deleteCache by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_delete_caches_now),
        default = true
    )

    var deleteSitePermissions by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_delete_permissions_now),
        default = true
    )

532
533
534
535
536
    var deleteDownloads by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_delete_downloads_now),
        default = true
    )

537
538
    var shouldUseBottomToolbar by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_toolbar_bottom),
539
540
        // Default accessibility users to top toolbar
        default = !touchExplorationIsEnabled && !switchServiceIsEnabled
541
542
    )

543
544
545
    val toolbarPosition: ToolbarPosition
        get() = if (shouldUseBottomToolbar) ToolbarPosition.BOTTOM else ToolbarPosition.TOP

546
547
548
549
    /**
     * Check each active accessibility service to see if it can perform gestures, if any can,
     * then it is *likely* a switch service is enabled. We are assuming this to be the case based on #7486
     */
550
    val switchServiceIsEnabled: Boolean
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
        get() {
            val accessibilityManager =
                appContext.getSystemService(Context.ACCESSIBILITY_SERVICE) as? AccessibilityManager

            accessibilityManager?.getEnabledAccessibilityServiceList(0)?.let { activeServices ->
                for (service in activeServices) {
                    if (service.capabilities.and(CAPABILITY_CAN_PERFORM_GESTURES) == 1) {
                        return true
                    }
                }
            }

            return false
        }

566
    val touchExplorationIsEnabled: Boolean
567
568
569
570
571
572
        get() {
            val accessibilityManager =
                appContext.getSystemService(Context.ACCESSIBILITY_SERVICE) as? AccessibilityManager
            return accessibilityManager?.isTouchExplorationEnabled ?: false
        }

573
    val accessibilityServicesEnabled: Boolean
574
575
576
        get() {
            return touchExplorationIsEnabled || switchServiceIsEnabled
        }
577

578
579
    fun getDeleteDataOnQuit(type: DeleteBrowsingDataOnQuitType): Boolean =
        preferences.getBoolean(type.getPreferenceKey(appContext), false)
580

581
582
    fun setDeleteDataOnQuit(type: DeleteBrowsingDataOnQuitType, value: Boolean) {
        preferences.edit().putBoolean(type.getPreferenceKey(appContext), value).apply()
583
584
    }

585
586
587
    fun shouldDeleteAnyDataOnQuit() =
        DeleteBrowsingDataOnQuitType.values().any { getDeleteDataOnQuit(it) }

588
589
590
591
592
593
594
595
596
597
    val passwordsEncryptionKeyGenerated by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_encryption_key_generated),
        false
    )

    fun recordPasswordsEncryptionKeyGenerated() = preferences.edit().putBoolean(
        appContext.getPreferenceKey(R.string.pref_key_encryption_key_generated),
        true
    ).apply()

598
    @VisibleForTesting(otherwise = PRIVATE)
599
    internal val loginsSecureWarningSyncCount = counterPreference(
600
        appContext.getPreferenceKey(R.string.pref_key_logins_secure_warning_sync),
601
        maxCount = 1
602
603
604
    )

    @VisibleForTesting(otherwise = PRIVATE)
605
    internal val loginsSecureWarningCount = counterPreference(
606
        appContext.getPreferenceKey(R.string.pref_key_logins_secure_warning),
607
        maxCount = 1
608
609
    )

610
    fun incrementShowLoginsSecureWarningCount() = loginsSecureWarningCount.increment()
611

612
    fun incrementShowLoginsSecureWarningSyncCount() = loginsSecureWarningSyncCount.increment()
613

614
    val shouldShowSearchSuggestions by booleanPreference(
615
616
617
        appContext.getPreferenceKey(R.string.pref_key_show_search_suggestions),
        default = true
    )
618

619
620
621
622
623
    val shouldAutocompleteInAwesomebar by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_enable_autocomplete_urls),
        default = true
    )

624
    var defaultTopSitesAdded by booleanPreference(
ekager's avatar
ekager committed
625
626
627
628
        appContext.getPreferenceKey(R.string.default_top_sites_added),
        default = false
    )

629
    var shouldShowSearchSuggestionsInPrivate by booleanPreference(
630
631
632
633
        appContext.getPreferenceKey(R.string.pref_key_show_search_suggestions_in_private),
        default = false
    )

634
635
636
637
638
    var showSearchSuggestionsInPrivateOnboardingFinished by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_show_search_suggestions_in_private_onboarding),
        default = false
    )

639
    fun incrementVisitedInstallableCount() = pwaInstallableVisitCount.increment()
640
641

    @VisibleForTesting(otherwise = PRIVATE)
642
    internal val pwaInstallableVisitCount = counterPreference(
643
        appContext.getPreferenceKey(R.string.pref_key_install_pwa_visits),
644
        maxCount = 3
645
646
647
    )

    private val userNeedsToVisitInstallableSites: Boolean
648
        get() = pwaInstallableVisitCount.underMaxCount()
649

650
    val shouldShowPwaCfr: Boolean
651
        get() {
652
            if (!canShowCfr) return false
653
654
655
            // We only want to show this on the 3rd time a user visits a site
            if (userNeedsToVisitInstallableSites) return false

656
            // ShortcutManager::pinnedShortcuts is only available on Oreo+
657
            if (!userKnowsAboutPwas && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
658
659
                val manager = appContext.getSystemService(ShortcutManager::class.java)
                val alreadyHavePwaInstalled = manager != null && manager.pinnedShortcuts.size > 0
660

661
                // Users know about PWAs onboarding if they already have PWAs installed.
662
                userKnowsAboutPwas = alreadyHavePwaInstalled
663
            }
664
            // Show dialog only if user does not know abut PWAs
665
            return !userKnowsAboutPwas
666
        }
667

668
    var userKnowsAboutPwas by booleanPreference(
669
670
671
672
        appContext.getPreferenceKey(R.string.pref_key_user_knows_about_pwa),
        default = false
    )

673
674
675
676
677
    var shouldShowOpenInAppBanner by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_should_show_open_in_app_banner),
        default = true
    )

678
679
680
    val shouldShowOpenInAppCfr: Boolean
        get() = canShowCfr && shouldShowOpenInAppBanner

681
682
683
684
685
    var shouldShowAutoCloseTabsBanner by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_should_show_auto_close_tabs_banner),
        default = true
    )

686
687
688
689
690
    var shouldShowGridViewBanner by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_should_show_grid_view_banner),
        default = true
    )

691
    @VisibleForTesting(otherwise = PRIVATE)
692
    internal val trackingProtectionOnboardingCount = counterPreference(
693
        appContext.getPreferenceKey(R.string.pref_key_tracking_protection_onboarding),
694
        maxCount = 1
695
696
697
698
    )

    fun incrementTrackingProtectionOnboardingCount() {
        trackingProtectionOnboardingShownThisSession = true
699
        trackingProtectionOnboardingCount.increment()
700
701
    }

Colin Lee's avatar
Colin Lee committed
702
703
704
705
    fun getSitePermissionsPhoneFeatureAction(
        feature: PhoneFeature,
        default: Action = Action.ASK_TO_ALLOW
    ) =
706
707
        preferences.getInt(feature.getPreferenceKey(appContext), default.toInt()).toAction()

708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
    /**
     * Saves the user selected autoplay setting.
     *
     * Under the hood, autoplay is represented by two settings, [AUTOPLAY_AUDIBLE] and
     * [AUTOPLAY_INAUDIBLE]. The user selection cannot be inferred from the combination of these
     * settings because, while on [AUTOPLAY_ALLOW_ON_WIFI], they will be indistinguishable from
     * either [AUTOPLAY_ALLOW_ALL] or [AUTOPLAY_BLOCK_ALL]. Because of this, we are forced to save
     * the user selected setting as well.
     */
    fun setAutoplayUserSetting(
        autoplaySetting: Int
    ) {
        preferences.edit().putInt(AUTOPLAY_USER_SETTING, autoplaySetting).apply()
    }

    /**
     * Gets the user selected autoplay setting.
     *
     * Under the hood, autoplay is represented by two settings, [AUTOPLAY_AUDIBLE] and
     * [AUTOPLAY_INAUDIBLE]. The user selection cannot be inferred from the combination of these
     * settings because, while on [AUTOPLAY_ALLOW_ON_WIFI], they will be indistinguishable from
     * either [AUTOPLAY_ALLOW_ALL] or [AUTOPLAY_BLOCK_ALL]. Because of this, we are forced to save
     * the user selected setting as well.
     */
    fun getAutoplayUserSetting(
        default: Int
    ) = preferences.getInt(AUTOPLAY_USER_SETTING, default)

736
    private fun getSitePermissionsPhoneFeatureAutoplayAction(
737
738
739
        feature: PhoneFeature,
        default: AutoplayAction = AutoplayAction.BLOCKED
    ) = preferences.getInt(feature.getPreferenceKey(appContext), default.toInt()).toAutoplayAction()
740

741
742
    fun setSitePermissionsPhoneFeatureAction(
        feature: PhoneFeature,
Colin Lee's avatar
Colin Lee committed
743
        value: Action
744
    ) {
745
        preferences.edit().putInt(feature.getPreferenceKey(appContext), value.toInt()).apply()
746
    }
747
748
749

    fun getSitePermissionsCustomSettingsRules(): SitePermissionsRules {
        return SitePermissionsRules(
750
751
752
            notification = getSitePermissionsPhoneFeatureAction(PhoneFeature.NOTIFICATION),
            microphone = getSitePermissionsPhoneFeatureAction(PhoneFeature.MICROPHONE),
            location = getSitePermissionsPhoneFeatureAction(PhoneFeature.LOCATION),
753
754
            camera = getSitePermissionsPhoneFeatureAction(PhoneFeature.CAMERA),
            autoplayAudible = getSitePermissionsPhoneFeatureAutoplayAction(PhoneFeature.AUTOPLAY_AUDIBLE),
755
            autoplayInaudible = getSitePermissionsPhoneFeatureAutoplayAction(PhoneFeature.AUTOPLAY_INAUDIBLE),
756
757
            persistentStorage = getSitePermissionsPhoneFeatureAction(PhoneFeature.PERSISTENT_STORAGE),
            mediaKeySystemAccess = getSitePermissionsPhoneFeatureAction(PhoneFeature.MEDIA_KEY_SYSTEM_ACCESS)
758
759
760
        )
    }

761
762
763
764
765
766
767
    fun setSitePermissionSettingListener(lifecycleOwner: LifecycleOwner, listener: () -> Unit) {
        val sitePermissionKeys = listOf(
            PhoneFeature.NOTIFICATION,
            PhoneFeature.MICROPHONE,
            PhoneFeature.LOCATION,
            PhoneFeature.CAMERA,
            PhoneFeature.AUTOPLAY_AUDIBLE,
768
769
770
            PhoneFeature.AUTOPLAY_INAUDIBLE,
            PhoneFeature.PERSISTENT_STORAGE,
            PhoneFeature.MEDIA_KEY_SYSTEM_ACCESS
771
772
773
774
775
776
777
        ).map { it.getPreferenceKey(appContext) }

        preferences.registerOnSharedPreferenceChangeListener(lifecycleOwner) { _, key ->
            if (key in sitePermissionKeys) listener.invoke()
        }
    }

Sawyer Blatz's avatar
Sawyer Blatz committed
778
779
780
781
782
    var shouldShowVoiceSearch by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_show_voice_search),
        default = true
    )

783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
    /**
     * Used in [SearchDialogFragment.kt], [SearchFragment.kt] (deprecated), and [PairFragment.kt]
     * to see if we need to check for camera permissions before using the QR code scanner.
     */
    var shouldShowCameraPermissionPrompt by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_camera_permissions_needed),
        default = true
    )

    /**
     * Sets the state of permissions that have been checked, where [false] denotes already checked
     * and [true] denotes needing to check. See [shouldShowCameraPermissionPrompt].
     */
    var setCameraPermissionNeededState by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_camera_permissions_needed),
        default = true
    )

801
802
803
804
805
806
807
808
809
810
    var shouldPromptToSaveLogins by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_save_logins),
        default = true
    )

    var shouldAutofillLogins by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_autofill_logins),
        default = true
    )

811
812
    var fxaSignedIn by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_fxa_signed_in),
813
        default = false
814
    )
Sawyer Blatz's avatar
Sawyer Blatz committed
815

816
817
    var fxaHasSyncedItems by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_fxa_has_synced_items),
818
        default = false
819
    )
Sawyer Blatz's avatar
Sawyer Blatz committed
820

821
822
823
824
825
    var lastPlacesStorageMaintenance by longPreference(
        appContext.getPreferenceKey(R.string.pref_key_last_maintenance),
        default = 0
    )

826
827
828
829
830
831
832
833
834
    fun addSearchWidgetInstalled(count: Int) {
        val key = appContext.getPreferenceKey(R.string.pref_key_search_widget_installed)
        val newValue = preferences.getInt(key, 0) + count
        preferences.edit()
            .putInt(key, newValue)
            .apply()
    }

    val searchWidgetInstalled: Boolean
835
836
837
838
        get() = 0 < preferences.getInt(
            appContext.getPreferenceKey(R.string.pref_key_search_widget_installed),
            0
        )
839

840
    fun incrementNumTimesPrivateModeOpened() = numTimesPrivateModeOpened.increment()
841

842
    var showedPrivateModeContextualFeatureRecommender by booleanPreference(
843
844
845
846
        appContext.getPreferenceKey(R.string.pref_key_showed_private_mode_cfr),
        default = false
    )

847
848
849
    private val numTimesPrivateModeOpened = counterPreference(
        appContext.getPreferenceKey(R.string.pref_key_private_mode_opened)
    )
850

851
    val shouldShowPrivateModeCfr: Boolean
852
        get() {
853
            if (!canShowCfr) return false
854
855
856
857
            val focusInstalled = MozillaProductDetector
                .getInstalledMozillaProducts(appContext as Application)
                .contains(MozillaProductDetector.MozillaProducts.FOCUS.productName)

858
            val showCondition = if (focusInstalled) {
859
                numTimesPrivateModeOpened.value >= CFR_COUNT_CONDITION_FOCUS_INSTALLED
860
            } else {
861
                numTimesPrivateModeOpened.value >= CFR_COUNT_CONDITION_FOCUS_NOT_INSTALLED
862
            }
863
864
865
866
867
868
869

            if (showCondition && !showedPrivateModeContextualFeatureRecommender) {
                return true
            }

            return false
        }
870
871
872
873
874

    var openLinksInExternalApp by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_open_links_in_external_app),
        default = false
    )
875
876
877
878
879
880
881
882
883
884

    var overrideFxAServer by stringPreference(
        appContext.getPreferenceKey(R.string.pref_key_override_fxa_server),
        default = ""
    )

    var overrideSyncTokenServer by stringPreference(
        appContext.getPreferenceKey(R.string.pref_key_override_sync_tokenserver),
        default = ""
    )
885

886
887
888
889
890
891
892
893
894
895
896
897
898
899
    var overrideAmoUser by stringPreference(
        appContext.getPreferenceKey(R.string.pref_key_override_amo_user),
        default = ""
    )

    var overrideAmoCollection by stringPreference(
        appContext.getPreferenceKey(R.string.pref_key_override_amo_collection),
        default = ""
    )

    fun amoCollectionOverrideConfigured(): Boolean {
        return overrideAmoUser.isNotEmpty() || overrideAmoCollection.isNotEmpty()
    }

900
    var topSitesSize by intPreference(
901
902
903
        appContext.getPreferenceKey(R.string.pref_key_top_sites_size),
        default = 0
    )
904

905
906
907
908
909
    val topSitesMaxLimit by intPreference(
        appContext.getPreferenceKey(R.string.pref_key_top_sites_max_limit),
        default = topSitesMaxCount
    )

910
911
912
913
    var openTabsCount by intPreference(
        appContext.getPreferenceKey(R.string.pref_key_open_tabs_count),
        0
    )
914

915
916
917
918
919
920
921
922
923
924
    var mobileBookmarksSize by intPreference(
        appContext.getPreferenceKey(R.string.pref_key_mobile_bookmarks_size),
        0
    )

    var desktopBookmarksSize by intPreference(
        appContext.getPreferenceKey(R.string.pref_key_desktop_bookmarks_size),
        0
    )

925
926
    private var savedLoginsSortingStrategyString by stringPreference(
        appContext.getPreferenceKey(R.string.pref_key_saved_logins_sorting_strategy),
927
        default = SavedLoginsSortingStrategyMenu.Item.AlphabeticallySort.strategyString
928
929
930
    )

    val savedLoginsMenuHighlightedItem: SavedLoginsSortingStrategyMenu.Item
931
        get() = SavedLoginsSortingStrategyMenu.Item.fromString(savedLoginsSortingStrategyString)
932
933
934

    var savedLoginsSortingStrategy: SortingStrategy
        get() {
935
936
937
938
            return when (savedLoginsMenuHighlightedItem) {
                SavedLoginsSortingStrategyMenu.Item.AlphabeticallySort ->
                    SortingStrategy.Alphabetically(appContext.components.publicSuffixList)
                SavedLoginsSortingStrategyMenu.Item.LastUsedSort -> SortingStrategy.LastUsed
939
940
941
942
            }
        }
        set(value) {
            savedLoginsSortingStrategyString = when (value) {
943
944
945
946
                is SortingStrategy.Alphabetically ->
                    SavedLoginsSortingStrategyMenu.Item.AlphabeticallySort.strategyString
                is SortingStrategy.LastUsed ->
                    SavedLoginsSortingStrategyMenu.Item.LastUsedSort.strategyString
947
948
            }
        }
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963

    var isPullToRefreshEnabledInBrowser by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_website_pull_to_refresh),
        default = true
    )

    var isDynamicToolbarEnabled by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_dynamic_toolbar),
        default = true
    )

    var isSwipeToolbarToSwitchTabsEnabled by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_swipe_toolbar_switch_tabs),
        default = true
    )
964
}