Settings.kt 37.1 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.FeatureFlags
30
import org.mozilla.fenix.R
31
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
32
import org.mozilla.fenix.components.metrics.MozillaProductDetector
33
import org.mozilla.fenix.components.settings.counterPreference
34
import org.mozilla.fenix.components.settings.featureFlagPreference
35
import org.mozilla.fenix.components.toolbar.ToolbarPosition
36
import org.mozilla.fenix.ext.components
37
import org.mozilla.fenix.ext.getPreferenceKey
38
import org.mozilla.fenix.settings.PhoneFeature
39
import org.mozilla.fenix.settings.deletebrowsingdata.DeleteBrowsingDataOnQuitType
40
41
import org.mozilla.fenix.settings.logins.SavedLoginsSortingStrategyMenu
import org.mozilla.fenix.settings.logins.SortingStrategy
42
import org.mozilla.fenix.settings.registerOnSharedPreferenceChangeListener
43
import java.security.InvalidParameterException
44

45
46
private const val AUTOPLAY_USER_SETTING = "AUTOPLAY_USER_SETTING"

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

54
    companion object {
55
        const val topSitesMaxCount = 16
56
        const val FENIX_PREFERENCES = "fenix_preferences"
57

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

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

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

75
76
77
78
79
80
        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
81
82
            BLOCKED_INT -> Action.BLOCKED
            ASK_TO_ALLOW_INT -> Action.ASK_TO_ALLOW
83
84
85
86
87
88
89
            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
90
91
            // Users from older versions may have saved invalid values. Migrate them to BLOCKED
            ASK_TO_ALLOW_INT -> AutoplayAction.BLOCKED
92
            else -> throw InvalidParameterException("$this is not a valid SitePermissionsRules.AutoplayAction")
93
        }
94
95
    }

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

100
    override val preferences: SharedPreferences =
101
102
        appContext.getSharedPreferences(FENIX_PREFERENCES, MODE_PRIVATE)

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

108
109
110
111
112
113
114
115
116
117
    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
    )

118
119
120
121
122
123
124
125
    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

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

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

136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
    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 = ""
    )

151
152
    var openLinksInAPrivateTab by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_open_links_in_a_private_tab),
Alex Catarineu's avatar
Alex Catarineu committed
153
        default = true
154
155
    )

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

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

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

Alex Catarineu's avatar
Alex Catarineu committed
171
172
173
174
175
    var spoofEnglish by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_spoof_english),
        default = false
    )

176
177
    // If any of the prefs have been modified, quit displaying the fenix moved tip
    fun shouldDisplayFenixMovingTip(): Boolean =
178
179
180
181
182
183
184
185
186
187
188
189
        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
                )
190

191
192
193
194
    var defaultSearchEngineName by stringPreference(
        appContext.getPreferenceKey(R.string.pref_key_search_engine),
        default = ""
    )
195

196
197
198
199
200
201
202
203
204
205
    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
    )

206
207
208
209
210
    var showCollectionsPlaceholderOnHome by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_show_collections_placeholder_home),
        default = true
    )

211
    val isCrashReportingEnabled: Boolean
Denys M's avatar
Denys M committed
212
        get() = isCrashReportEnabledInBuild &&
213
214
215
216
                preferences.getBoolean(
                    appContext.getPreferenceKey(R.string.pref_key_crash_reporter),
                    true
                )
217

218
219
220
221
    val isRemoteDebuggingEnabled by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_remote_debugging),
        default = false
    )
222

223
224
    val isTelemetryEnabled by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_telemetry),
225
        default = BuildConfig.DATA_COLLECTION_DISABLED == false
226
    )
227

228
229
    val isMarketingTelemetryEnabled by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_marketing_telemetry),
230
        default = BuildConfig.DATA_COLLECTION_DISABLED == false
231
232
    )

jhugman's avatar
jhugman committed
233
    var isExperimentationEnabled by booleanPreference(
234
        appContext.getPreferenceKey(R.string.pref_key_experimentation),
235
        default = BuildConfig.DATA_COLLECTION_DISABLED == false
236
237
    )

238
    private var trackingProtectionOnboardingShownThisSession = false
239
    var isOverrideTPPopupsForPerformanceTest = false
240

241
242
    val shouldShowTrackingProtectionCfr: Boolean
        get() = !isOverrideTPPopupsForPerformanceTest && canShowCfr &&
243
                (trackingProtectionOnboardingCount.underMaxCount() &&
244
                        !trackingProtectionOnboardingShownThisSession)
245

246
247
    var showSecretDebugMenuThisSession = false

248
    val shouldShowSecurityPinWarningSync: Boolean
249
        get() = loginsSecureWarningSyncCount.underMaxCount()
250
251

    val shouldShowSecurityPinWarning: Boolean
252
        get() = loginsSecureWarningCount.underMaxCount()
253

254
255
256
257
258
    var shouldShowPrivacyPopWindow by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_privacy_pop_window),
        default = true
    )

259
260
261
262
    var shouldUseLightTheme by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_light_theme),
        default = false
    )
263

264
265
266
267
268
    var shouldUseAutoSize by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_accessibility_auto_size),
        default = true
    )

269
270
271
272
    var fontSizeFactor by floatPreference(
        appContext.getPreferenceKey(R.string.pref_key_accessibility_font_scale),
        default = 1f
    )
273

274
275
276
277
278
279
280
281
282
283
    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
    )

284
285
286
287
288
    val shouldShowSyncedTabsSuggestions by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_search_synced_tabs),
        default = true
    )

289
290
    val shouldShowClipboardSuggestions by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_show_clipboard_suggestions),
291
292
        default = true
    )
Emily Kager's avatar
Emily Kager committed
293

294
    val shouldShowSearchShortcuts by booleanPreference(
295
        appContext.getPreferenceKey(R.string.pref_key_show_search_engine_shortcuts),
296
        default = false
297
298
    )

299
300
301
302
303
304
305
306
307
308
    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
    )

309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
    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
    )

329
330
331
332
333
334
    var tabsTrayRewrite by featureFlagPreference(
        appContext.getPreferenceKey(R.string.pref_key_new_tabs_tray),
        default = false,
        featureFlag = FeatureFlags.tabsTrayRewrite
    )

335
336
337
338
    fun getTabTimeout(): Long = when {
        closeTabsAfterOneDay -> ONE_DAY_MS
        closeTabsAfterOneWeek -> ONE_WEEK_MS
        closeTabsAfterOneMonth -> ONE_MONTH_MS
339
        else -> Long.MAX_VALUE
340
341
    }

342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
    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
        }
    }

367
368
    fun getTabTimeoutString(): String = when {
        closeTabsAfterOneDay -> {
369
            appContext.getString(R.string.close_tabs_after_one_day_summary)
370
371
        }
        closeTabsAfterOneWeek -> {
372
            appContext.getString(R.string.close_tabs_after_one_week_summary)
373
374
        }
        closeTabsAfterOneMonth -> {
375
            appContext.getString(R.string.close_tabs_after_one_month_summary)
376
377
        }
        else -> {
378
            appContext.getString(R.string.close_tabs_manually_summary)
379
380
381
        }
    }

382
383
384
385
    val shouldUseDarkTheme by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_dark_theme),
        default = false
    )
386

387
388
389
390
    var shouldFollowDeviceTheme by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_follow_device_theme),
        default = false
    )
391

392
393
    var shouldUseTrackingProtection by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_tracking_protection),
394
        default = false
395
    )
396

397
398
399
400
    /**
     * Caches the last known "is default browser" state when the app was paused.
     * For an up to do date state use `isDefaultBrowser` instead.
     */
401
    var wasDefaultBrowserOnLastResume by booleanPreference(
402
403
404
405
        appContext.getPreferenceKey(R.string.pref_key_default_browser),
        default = isDefaultBrowser()
    )

406
407
408
409
410
    fun isDefaultBrowser(): Boolean {
        val browsers = BrowsersCache.all(appContext)
        return browsers.isDefaultBrowser
    }

411
412
413
414
    val shouldUseAutoBatteryTheme by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_auto_battery_theme),
        default = false
    )
Emily Kager's avatar
Emily Kager committed
415

416
417
418
419
420
    val useStandardTrackingProtection by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_tracking_protection_standard_option),
        true
    )

421
    val useStrictTrackingProtection by booleanPreference(
422
        appContext.getPreferenceKey(R.string.pref_key_tracking_protection_strict_default),
423
        false
424
425
    )

mcarare's avatar
mcarare committed
426
427
428
429
430
    val useCustomTrackingProtection by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_tracking_protection_custom_option),
        false
    )

431
432
433
434
435
436
437
438
439
440
    @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()
441
        appContext.components.let {
442
443
444
445
446
447
448
            val policy = it.core.trackingProtectionPolicyFactory
                .createTrackingProtectionPolicy()
            it.useCases.settingsUseCases.updateTrackingProtection.invoke(policy)
            it.useCases.sessionUseCases.reload.invoke()
        }
    }

mcarare's avatar
mcarare committed
449
450
451
452
    val blockCookiesInCustomTrackingProtection by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_tracking_protection_custom_cookies),
        true
    )
453

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

    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),
466
        "all"
mcarare's avatar
mcarare committed
467
468
469
470
471
472
473
474
475
476
477
478
    )

    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
    )

479
480
481
482
483
    val blockRedirectTrackersInCustomTrackingProtection by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_tracking_protection_redirect_trackers),
        true
    )

484
485
486
487
488
489
490
    /**
     * Prefer to use a fixed top toolbar when:
     * - a talkback service is enabled or
     * - switch access is enabled.
     *
     * This is automatically inferred based on the current system status. Not a setting in our app.
     */
491
492
    val shouldUseFixedTopToolbar: Boolean
        get() {
493
            return touchExplorationIsEnabled || switchServiceIsEnabled
494
495
        }

Alex Catarineu's avatar
Alex Catarineu committed
496
497
498
499
500
501
    val shouldDisableNormalMode by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_disable_normal_mode),
        true
    )

    var lastKnownMode: BrowsingMode = BrowsingMode.Private
502
503
504
        get() {
            val lastKnownModeWasPrivate = preferences.getBoolean(
                appContext.getPreferenceKey(R.string.pref_key_last_known_mode_private),
Alex Catarineu's avatar
Alex Catarineu committed
505
                shouldDisableNormalMode
506
507
508
509
510
511
512
513
514
515
516
517
518
            )

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

            preferences.edit()
                .putBoolean(
519
520
521
                    appContext.getPreferenceKey(R.string.pref_key_last_known_mode_private),
                    lastKnownModeWasPrivate
                )
522
523
524
525
526
                .apply()

            field = value
        }

527
528
529
530
531
    var shouldDeleteBrowsingDataOnQuit by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_delete_browsing_data_on_quit),
        default = false
    )

532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
    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
    )

557
558
559
560
561
    var deleteDownloads by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_delete_downloads_now),
        default = true
    )

562
563
    var shouldUseBottomToolbar by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_toolbar_bottom),
564
565
        // Default accessibility users to top toolbar
        default = !touchExplorationIsEnabled && !switchServiceIsEnabled
566
567
    )

568
569
570
    val toolbarPosition: ToolbarPosition
        get() = if (shouldUseBottomToolbar) ToolbarPosition.BOTTOM else ToolbarPosition.TOP

571
572
573
574
    /**
     * 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
     */
575
    val switchServiceIsEnabled: Boolean
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
        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
        }

591
    val touchExplorationIsEnabled: Boolean
592
593
594
595
596
597
        get() {
            val accessibilityManager =
                appContext.getSystemService(Context.ACCESSIBILITY_SERVICE) as? AccessibilityManager
            return accessibilityManager?.isTouchExplorationEnabled ?: false
        }

598
    val accessibilityServicesEnabled: Boolean
599
600
601
        get() {
            return touchExplorationIsEnabled || switchServiceIsEnabled
        }
602

603
604
    fun getDeleteDataOnQuit(type: DeleteBrowsingDataOnQuitType): Boolean =
        preferences.getBoolean(type.getPreferenceKey(appContext), false)
605

606
607
    fun setDeleteDataOnQuit(type: DeleteBrowsingDataOnQuitType, value: Boolean) {
        preferences.edit().putBoolean(type.getPreferenceKey(appContext), value).apply()
608
609
    }

610
611
612
    fun shouldDeleteAnyDataOnQuit() =
        DeleteBrowsingDataOnQuitType.values().any { getDeleteDataOnQuit(it) }

613
614
615
616
617
618
619
620
621
622
    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()

623
    @VisibleForTesting(otherwise = PRIVATE)
624
    internal val loginsSecureWarningSyncCount = counterPreference(
625
        appContext.getPreferenceKey(R.string.pref_key_logins_secure_warning_sync),
626
        maxCount = 1
627
628
629
    )

    @VisibleForTesting(otherwise = PRIVATE)
630
    internal val loginsSecureWarningCount = counterPreference(
631
        appContext.getPreferenceKey(R.string.pref_key_logins_secure_warning),
632
        maxCount = 1
633
634
    )

635
    fun incrementShowLoginsSecureWarningCount() = loginsSecureWarningCount.increment()
636

637
    fun incrementShowLoginsSecureWarningSyncCount() = loginsSecureWarningSyncCount.increment()
638

639
    val shouldShowSearchSuggestions by booleanPreference(
640
641
642
        appContext.getPreferenceKey(R.string.pref_key_show_search_suggestions),
        default = true
    )
643

644
645
646
647
648
    val shouldAutocompleteInAwesomebar by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_enable_autocomplete_urls),
        default = true
    )

649
    var defaultTopSitesAdded by booleanPreference(
ekager's avatar
ekager committed
650
651
652
653
        appContext.getPreferenceKey(R.string.default_top_sites_added),
        default = false
    )

654
    var shouldShowSearchSuggestionsInPrivate by booleanPreference(
655
656
657
658
        appContext.getPreferenceKey(R.string.pref_key_show_search_suggestions_in_private),
        default = false
    )

659
660
661
662
663
    var showSearchSuggestionsInPrivateOnboardingFinished by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_show_search_suggestions_in_private_onboarding),
        default = false
    )

664
    fun incrementVisitedInstallableCount() = pwaInstallableVisitCount.increment()
665
666

    @VisibleForTesting(otherwise = PRIVATE)
667
    internal val pwaInstallableVisitCount = counterPreference(
668
        appContext.getPreferenceKey(R.string.pref_key_install_pwa_visits),
669
        maxCount = 3
670
671
672
    )

    private val userNeedsToVisitInstallableSites: Boolean
673
        get() = pwaInstallableVisitCount.underMaxCount()
674

675
    val shouldShowPwaCfr: Boolean
676
        get() {
677
            if (!canShowCfr) return false
678
679
680
            // We only want to show this on the 3rd time a user visits a site
            if (userNeedsToVisitInstallableSites) return false

681
            // ShortcutManager::pinnedShortcuts is only available on Oreo+
682
            if (!userKnowsAboutPwas && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
683
684
                val manager = appContext.getSystemService(ShortcutManager::class.java)
                val alreadyHavePwaInstalled = manager != null && manager.pinnedShortcuts.size > 0
685

686
                // Users know about PWAs onboarding if they already have PWAs installed.
687
                userKnowsAboutPwas = alreadyHavePwaInstalled
688
            }
689
            // Show dialog only if user does not know abut PWAs
690
            return !userKnowsAboutPwas
691
        }
692

693
    var userKnowsAboutPwas by booleanPreference(
694
        appContext.getPreferenceKey(R.string.pref_key_user_knows_about_pwa),
695
        default = true
696
697
    )

698
699
700
701
702
    var shouldShowOpenInAppBanner by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_should_show_open_in_app_banner),
        default = true
    )

703
704
705
    val shouldShowOpenInAppCfr: Boolean
        get() = canShowCfr && shouldShowOpenInAppBanner

706
707
708
709
710
    var shouldShowAutoCloseTabsBanner by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_should_show_auto_close_tabs_banner),
        default = true
    )

711
712
713
714
715
    var shouldShowGridViewBanner by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_should_show_grid_view_banner),
        default = true
    )

716
    @VisibleForTesting(otherwise = PRIVATE)
717
    internal val trackingProtectionOnboardingCount = counterPreference(
718
        appContext.getPreferenceKey(R.string.pref_key_tracking_protection_onboarding),
719
        maxCount = 1
720
721
722
723
    )

    fun incrementTrackingProtectionOnboardingCount() {
        trackingProtectionOnboardingShownThisSession = true
724
        trackingProtectionOnboardingCount.increment()
725
726
    }

Colin Lee's avatar
Colin Lee committed
727
728
729
730
    fun getSitePermissionsPhoneFeatureAction(
        feature: PhoneFeature,
        default: Action = Action.ASK_TO_ALLOW
    ) =
731
732
        preferences.getInt(feature.getPreferenceKey(appContext), default.toInt()).toAction()

733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
    /**
     * 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)

761
    private fun getSitePermissionsPhoneFeatureAutoplayAction(
762
763
764
        feature: PhoneFeature,
        default: AutoplayAction = AutoplayAction.BLOCKED
    ) = preferences.getInt(feature.getPreferenceKey(appContext), default.toInt()).toAutoplayAction()
765

766
767
    fun setSitePermissionsPhoneFeatureAction(
        feature: PhoneFeature,
Colin Lee's avatar
Colin Lee committed
768
        value: Action
769
    ) {
770
        preferences.edit().putInt(feature.getPreferenceKey(appContext), value.toInt()).apply()
771
    }
772
773
774

    fun getSitePermissionsCustomSettingsRules(): SitePermissionsRules {
        return SitePermissionsRules(
775
776
777
            notification = getSitePermissionsPhoneFeatureAction(PhoneFeature.NOTIFICATION),
            microphone = getSitePermissionsPhoneFeatureAction(PhoneFeature.MICROPHONE),
            location = getSitePermissionsPhoneFeatureAction(PhoneFeature.LOCATION),
778
779
            camera = getSitePermissionsPhoneFeatureAction(PhoneFeature.CAMERA),
            autoplayAudible = getSitePermissionsPhoneFeatureAutoplayAction(PhoneFeature.AUTOPLAY_AUDIBLE),
780
            autoplayInaudible = getSitePermissionsPhoneFeatureAutoplayAction(PhoneFeature.AUTOPLAY_INAUDIBLE),
781
782
            persistentStorage = getSitePermissionsPhoneFeatureAction(PhoneFeature.PERSISTENT_STORAGE),
            mediaKeySystemAccess = getSitePermissionsPhoneFeatureAction(PhoneFeature.MEDIA_KEY_SYSTEM_ACCESS)
783
784
785
        )
    }

786
787
788
789
790
791
792
    fun setSitePermissionSettingListener(lifecycleOwner: LifecycleOwner, listener: () -> Unit) {
        val sitePermissionKeys = listOf(
            PhoneFeature.NOTIFICATION,
            PhoneFeature.MICROPHONE,
            PhoneFeature.LOCATION,
            PhoneFeature.CAMERA,
            PhoneFeature.AUTOPLAY_AUDIBLE,
793
794
795
            PhoneFeature.AUTOPLAY_INAUDIBLE,
            PhoneFeature.PERSISTENT_STORAGE,
            PhoneFeature.MEDIA_KEY_SYSTEM_ACCESS
796
797
798
799
800
801
802
        ).map { it.getPreferenceKey(appContext) }

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

Sawyer Blatz's avatar
Sawyer Blatz committed
803
804
805
806
807
    var shouldShowVoiceSearch by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_show_voice_search),
        default = true
    )

808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
    /**
     * 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
    )

826
827
828
829
830
831
832
833
834
835
    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
    )

836
837
    var fxaSignedIn by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_fxa_signed_in),
838
        default = false
839
    )
Sawyer Blatz's avatar
Sawyer Blatz committed
840

841
842
    var fxaHasSyncedItems by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_fxa_has_synced_items),
843
        default = false
844
    )
Sawyer Blatz's avatar
Sawyer Blatz committed
845

846
847
848
849
850
    var lastPlacesStorageMaintenance by longPreference(
        appContext.getPreferenceKey(R.string.pref_key_last_maintenance),
        default = 0
    )

851
852
853
854
855
856
857
858
859
    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
860
861
862
863
        get() = 0 < preferences.getInt(
            appContext.getPreferenceKey(R.string.pref_key_search_widget_installed),
            0
        )
864

865
    fun incrementNumTimesPrivateModeOpened() = numTimesPrivateModeOpened.increment()
866

867
    var showedPrivateModeContextualFeatureRecommender by booleanPreference(
868
869
870
871
        appContext.getPreferenceKey(R.string.pref_key_showed_private_mode_cfr),
        default = false
    )

872
873
874
    private val numTimesPrivateModeOpened = counterPreference(
        appContext.getPreferenceKey(R.string.pref_key_private_mode_opened)
    )
875

876
    val shouldShowPrivateModeCfr: Boolean
877
        get() {
878
            if (!canShowCfr) return false
879
880
881
882
            val focusInstalled = MozillaProductDetector
                .getInstalledMozillaProducts(appContext as Application)
                .contains(MozillaProductDetector.MozillaProducts.FOCUS.productName)

883
            val showCondition = if (focusInstalled) {
884
                numTimesPrivateModeOpened.value >= CFR_COUNT_CONDITION_FOCUS_INSTALLED
885
            } else {
886
                numTimesPrivateModeOpened.value >= CFR_COUNT_CONDITION_FOCUS_NOT_INSTALLED
887
            }
888

Alex Catarineu's avatar
Alex Catarineu committed
889
890
            if (!shouldDisableNormalMode && showCondition &&
                !showedPrivateModeContextualFeatureRecommender) {
891
892
893
894
895
                return true
            }

            return false
        }
896
897
898
899
900

    var openLinksInExternalApp by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_open_links_in_external_app),
        default = false
    )
901
902
903
904
905
906
907
908
909
910

    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 = ""
    )
911

912
913
914
915
916
917
918
919
920
921
922
923
924
925
    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()
    }

926
    var topSitesSize by intPreference(
927
928
929
        appContext.getPreferenceKey(R.string.pref_key_top_sites_size),
        default = 0
    )
930

931
932
933
934
935
    val topSitesMaxLimit by intPreference(
        appContext.getPreferenceKey(R.string.pref_key_top_sites_max_limit),
        default = topSitesMaxCount
    )

936
937
938
939
    var openTabsCount by intPreference(
        appContext.getPreferenceKey(R.string.pref_key_open_tabs_count),
        0
    )
940

941
942
943
944
945
946
947
948
949
950
    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
    )

951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
    /**
     * Storing number of installed add-ons for telemetry purposes
     */
    var installedAddonsCount by intPreference(
        appContext.getPreferenceKey(R.string.pref_key_installed_addons_count),
        0
    )

    /**
     * Storing the list of installed add-ons for telemetry purposes
     */
    var installedAddonsList by stringPreference(
        appContext.getPreferenceKey(R.string.pref_key_installed_addons_list),
        default = ""
    )

    /**
     * Storing number of enabled add-ons for telemetry purposes
     */
    var enabledAddonsCount by intPreference(
        appContext.getPreferenceKey(R.string.pref_key_enabled_addons_count),
        0
    )

    /**
     * Storing the list of enabled add-ons for telemetry purposes
     */
    var enabledAddonsList by stringPreference(
        appContext.getPreferenceKey(R.string.pref_key_enabled_addons_list),
        default = ""
    )

983
984
    private var savedLoginsSortingStrategyString by stringPreference(
        appContext.getPreferenceKey(R.string.pref_key_saved_logins_sorting_strategy),
985
        default = SavedLoginsSortingStrategyMenu.Item.AlphabeticallySort.strategyString
986
987
988
    )

    val savedLoginsMenuHighlightedItem: SavedLoginsSortingStrategyMenu.Item
989
        get() = SavedLoginsSortingStrategyMenu.Item.fromString(savedLoginsSortingStrategyString)
990
991
992

    var savedLoginsSortingStrategy: SortingStrategy
        get() {
993
994
995
996
            return when (savedLoginsMenuHighlightedItem) {
                SavedLoginsSortingStrategyMenu.Item.AlphabeticallySort ->
                    SortingStrategy.Alphabetically(appContext.components.publicSuffixList)
                SavedLoginsSortingStrategyMenu.Item.LastUsedSort -> SortingStrategy.LastUsed
997
998
999
1000
            }
        }
        set(value) {
            savedLoginsSortingStrategyString = when (value) {
1001
1002
1003
1004
                is SortingStrategy.Alphabetically ->
                    SavedLoginsSortingStrategyMenu.Item.AlphabeticallySort.strategyString
                is SortingStrategy.LastUsed ->
                    SavedLoginsSortingStrategyMenu.Item.LastUsedSort.strategyString
1005
1006
            }
        }
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021

    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
    )
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033

    var creditCardsFeature by featureFlagPreference(
        appContext.getPreferenceKey(R.string.pref_key_show_credit_cards_feature),
        default = false,
        featureFlag = FeatureFlags.creditCardsFeature
    )

    var addressFeature by featureFlagPreference(
        appContext.getPreferenceKey(R.string.pref_key_show_address_feature),
        default = false,
        featureFlag = FeatureFlags.addressesFeature
    )
Alex Catarineu's avatar
Alex Catarineu committed
1034
1035
1036
1037
1038

    var noscriptInstalled by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_noscript_installed),
        default = false
    )
1039
}