Search in sources :

Example 1 with StorageAccessException

use of com.ichi2.anki.exception.StorageAccessException in project AnkiChinaAndroid by ankichinateam.

the class AnkiDroidApp method onCreate.

/**
 * On application creation.
 */
@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(activityLifecycleCallbacks);
    if (sInstance != null) {
        Timber.i("onCreate() called multiple times");
        // 5887 - fix crash.
        if (sInstance.getResources() == null) {
            Timber.w("Skipping re-initialisation - no resources. Maybe uninstalling app?");
            return;
        }
    }
    sInstance = this;
    // Get preferences
    SharedPreferences preferences = getSharedPrefs(this);
    Consts.LOGIN_SERVER = preferences.getInt(Consts.KEY_ANKI_ACCOUNT_SERVER, 0);
    // Setup logging and crash reporting
    acraCoreConfigBuilder = new CoreConfigurationBuilder(this);
    if (BuildConfig.DEBUG) {
        // Enable verbose error logging and do method tracing to put the Class name as log tag
        Timber.plant(new DebugTree());
        setDebugACRAConfig(preferences);
    } else {
        Timber.plant(new ProductionCrashReportingTree());
        setProductionACRAConfig(preferences);
    }
    Timber.tag(TAG);
    Timber.d("Startup - Application Start");
    // Analytics falls back to a sensible default if this is not set.
    if (ACRA.isACRASenderServiceProcess() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        try {
            WebViewDebugging.setDataDirectorySuffix("acra");
        } catch (Exception e) {
            Timber.w(e, "Failed to set WebView data directory");
        }
    }
    // analytics after ACRA, they both install UncaughtExceptionHandlers but Analytics chains while ACRA does not
    UsageAnalytics.initialize(this);
    if (BuildConfig.DEBUG) {
        UsageAnalytics.setDryRun(true);
    }
    // Stop after analytics and logging are initialised.
    if (ACRA.isACRASenderServiceProcess()) {
        Timber.d("Skipping AnkiDroidApp.onCreate from ACRA sender process");
        return;
    }
    if (AdaptionUtil.isUserATestClient()) {
        UIUtils.showThemedToast(this.getApplicationContext(), getString(R.string.user_is_a_robot), false);
    }
    CardBrowserContextMenu.ensureConsistentStateWithSharedPreferences(this);
    AnkiCardContextMenu.ensureConsistentStateWithSharedPreferences(this);
    NotificationChannels.setup(getApplicationContext());
    // Configure WebView to allow file scheme pages to access cookies.
    CookieManager.setAcceptFileSchemeCookies(true);
    // Prepare Cookies to be synchronized between RAM and permanent storage.
    CompatHelper.getCompat().prepareWebViewCookies(this.getApplicationContext());
    // Set good default values for swipe detection
    final ViewConfiguration vc = ViewConfiguration.get(this);
    DEFAULT_SWIPE_MIN_DISTANCE = vc.getScaledPagingTouchSlop();
    DEFAULT_SWIPE_THRESHOLD_VELOCITY = vc.getScaledMinimumFlingVelocity();
    // Forget the last deck that was used in the CardBrowser
    CardBrowser.clearLastDeckId();
    // Create the AnkiDroid directory if missing. Send exception report if inaccessible.
    if (Permissions.hasStorageAccessPermission(this)) {
        try {
            String dir = CollectionHelper.getCurrentAnkiDroidDirectory(this);
            CollectionHelper.initializeAnkiDroidDirectory(dir);
        } catch (StorageAccessException e) {
            Timber.e(e, "Could not initialize AnkiDroid directory");
            String defaultDir = CollectionHelper.getDefaultAnkiDroidDirectory();
            if (isSdCardMounted() && CollectionHelper.getCurrentAnkiDroidDirectory(this).equals(defaultDir)) {
                // Don't send report if the user is using a custom directory as SD cards trip up here a lot
                sendExceptionReport(e, "AnkiDroidApp.onCreate");
            }
        }
    }
    Timber.i("AnkiDroidApp: Starting Services");
    new BootService().onReceive(this, new Intent(this, BootService.class));
    // Register BroadcastReceiver NotificationService
    NotificationService ns = new NotificationService();
    LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
    lbm.registerReceiver(ns, new IntentFilter(NotificationService.INTENT_ACTION));
}
Also used : IntentFilter(android.content.IntentFilter) SharedPreferences(android.content.SharedPreferences) StorageAccessException(com.ichi2.anki.exception.StorageAccessException) Intent(android.content.Intent) NotificationService(com.ichi2.anki.services.NotificationService) LocalBroadcastManager(androidx.localbroadcastmanager.content.LocalBroadcastManager) ManuallyReportedException(com.ichi2.anki.exception.ManuallyReportedException) StorageAccessException(com.ichi2.anki.exception.StorageAccessException) CoreConfigurationBuilder(org.acra.config.CoreConfigurationBuilder) DebugTree(timber.log.Timber.DebugTree) ViewConfiguration(android.view.ViewConfiguration) BootService(com.ichi2.anki.services.BootService)

Example 2 with StorageAccessException

use of com.ichi2.anki.exception.StorageAccessException in project Anki-Android by ankidroid.

the class AnkiDroidApp method onCreate.

/**
 * On application creation.
 */
@Override
public void onCreate() {
    super.onCreate();
    if (sInstance != null) {
        Timber.i("onCreate() called multiple times");
        // 5887 - fix crash.
        if (sInstance.getResources() == null) {
            Timber.w("Skipping re-initialisation - no resources. Maybe uninstalling app?");
            return;
        }
    }
    sInstance = this;
    // Get preferences
    SharedPreferences preferences = getSharedPrefs(this);
    // Setup logging and crash reporting
    mAcraCoreConfigBuilder = new CoreConfigurationBuilder(this);
    if (BuildConfig.DEBUG) {
        // Enable verbose error logging and do method tracing to put the Class name as log tag
        Timber.plant(new DebugTree());
        setDebugACRAConfig(preferences);
        List<ReferenceMatcher> referenceMatchers = new ArrayList<>();
        // Add known memory leaks to 'referenceMatchers'
        matchKnownMemoryLeaks(referenceMatchers);
        // AppWatcher manual install if not already installed
        if (!AppWatcher.INSTANCE.isInstalled()) {
            AppWatcher.INSTANCE.manualInstall(this);
        }
        // Show 'Leaks' app launcher. It has been removed by default via constants.xml.
        LeakCanary.INSTANCE.showLeakDisplayActivityLauncherIcon(true);
    } else {
        Timber.plant(new ProductionCrashReportingTree());
        setProductionACRAConfig(preferences);
        disableLeakCanary();
    }
    Timber.tag(TAG);
    Timber.d("Startup - Application Start");
    // Analytics falls back to a sensible default if this is not set.
    if (ACRA.isACRASenderServiceProcess() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        try {
            WebViewDebugging.setDataDirectorySuffix("acra");
        } catch (Exception e) {
            Timber.w(e, "Failed to set WebView data directory");
        }
    }
    // analytics after ACRA, they both install UncaughtExceptionHandlers but Analytics chains while ACRA does not
    UsageAnalytics.initialize(this);
    if (BuildConfig.DEBUG) {
        UsageAnalytics.setDryRun(true);
    }
    // Stop after analytics and logging are initialised.
    if (ACRA.isACRASenderServiceProcess()) {
        Timber.d("Skipping AnkiDroidApp.onCreate from ACRA sender process");
        return;
    }
    if (AdaptionUtil.isUserATestClient()) {
        UIUtils.showThemedToast(this.getApplicationContext(), getString(R.string.user_is_a_robot), false);
    }
    // make default HTML / JS debugging true for debug build and disable for unit/android tests
    if (BuildConfig.DEBUG && !AdaptionUtil.isRunningAsUnitTest()) {
        preferences.edit().putBoolean("html_javascript_debugging", true).apply();
    }
    CardBrowserContextMenu.ensureConsistentStateWithSharedPreferences(this);
    AnkiCardContextMenu.ensureConsistentStateWithSharedPreferences(this);
    NotificationChannels.setup(getApplicationContext());
    // Configure WebView to allow file scheme pages to access cookies.
    if (!acceptFileSchemeCookies()) {
        return;
    }
    // Forget the last deck that was used in the CardBrowser
    CardBrowser.clearLastDeckId();
    // Create the AnkiDroid directory if missing. Send exception report if inaccessible.
    if (Permissions.hasStorageAccessPermission(this)) {
        try {
            String dir = CollectionHelper.getCurrentAnkiDroidDirectory(this);
            CollectionHelper.initializeAnkiDroidDirectory(dir);
        } catch (StorageAccessException e) {
            Timber.e(e, "Could not initialize AnkiDroid directory");
            String defaultDir = CollectionHelper.getDefaultAnkiDroidDirectory(this);
            if (isSdCardMounted() && CollectionHelper.getCurrentAnkiDroidDirectory(this).equals(defaultDir)) {
                // Don't send report if the user is using a custom directory as SD cards trip up here a lot
                sendExceptionReport(e, "AnkiDroidApp.onCreate");
            }
        }
    }
    Timber.i("AnkiDroidApp: Starting Services");
    new BootService().onReceive(this, new Intent(this, BootService.class));
    // Register BroadcastReceiver NotificationService
    NotificationService ns = new NotificationService();
    LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
    lbm.registerReceiver(ns, new IntentFilter(NotificationService.INTENT_ACTION));
}
Also used : IntentFilter(android.content.IntentFilter) ReferenceMatcher(shark.ReferenceMatcher) SharedPreferences(android.content.SharedPreferences) ArrayList(java.util.ArrayList) StorageAccessException(com.ichi2.anki.exception.StorageAccessException) Intent(android.content.Intent) NotificationService(com.ichi2.anki.services.NotificationService) LocalBroadcastManager(androidx.localbroadcastmanager.content.LocalBroadcastManager) ManuallyReportedException(com.ichi2.anki.exception.ManuallyReportedException) StorageAccessException(com.ichi2.anki.exception.StorageAccessException) CoreConfigurationBuilder(org.acra.config.CoreConfigurationBuilder) DebugTree(timber.log.Timber.DebugTree) BootService(com.ichi2.anki.services.BootService)

Example 3 with StorageAccessException

use of com.ichi2.anki.exception.StorageAccessException in project Anki-Android by ankidroid.

the class CollectionHelper method initializeAnkiDroidDirectory.

/**
 * Create the AnkiDroid directory if it doesn't exist and add a .nomedia file to it if needed.
 *
 * The AnkiDroid directory is a user preference stored under {@link #PREF_DECK_PATH}, and a sensible
 * default is chosen if the preference hasn't been created yet (i.e., on the first run).
 *
 * The presence of a .nomedia file indicates to media scanners that the directory must be
 * excluded from their search. We need to include this to avoid media scanners including
 * media files from the collection.media directory. The .nomedia file works at the directory
 * level, so placing it in the AnkiDroid directory will ensure media scanners will also exclude
 * the collection.media sub-directory.
 *
 * @param path  Directory to initialize
 * @throws StorageAccessException If no write access to directory
 */
public static synchronized void initializeAnkiDroidDirectory(String path) throws StorageAccessException {
    // Create specified directory if it doesn't exit
    File dir = new File(path);
    if (!dir.exists() && !dir.mkdirs()) {
        throw new StorageAccessException("Failed to create AnkiDroid directory " + path);
    }
    if (!dir.canWrite()) {
        throw new StorageAccessException("No write access to AnkiDroid directory " + path);
    }
    // Add a .nomedia file to it if it doesn't exist
    File nomedia = new File(dir, ".nomedia");
    if (!nomedia.exists()) {
        try {
            nomedia.createNewFile();
        } catch (IOException e) {
            throw new StorageAccessException("Failed to create .nomedia file", e);
        }
    }
}
Also used : StorageAccessException(com.ichi2.anki.exception.StorageAccessException) IOException(java.io.IOException) File(java.io.File)

Example 4 with StorageAccessException

use of com.ichi2.anki.exception.StorageAccessException in project AnkiChinaAndroid by ankichinateam.

the class Preferences method initSubscreen.

private void initSubscreen(String action, PreferenceContext listener) {
    android.preference.PreferenceScreen screen;
    switch(action) {
        case "com.ichi2.anki.prefs.general":
            listener.addPreferencesFromResource(R.xml.preferences_general);
            screen = listener.getPreferenceScreen();
            if (AdaptionUtil.isRestrictedLearningDevice()) {
                android.preference.CheckBoxPreference mCheckBoxPref_Vibrate = (android.preference.CheckBoxPreference) screen.findPreference("widgetVibrate");
                android.preference.CheckBoxPreference mCheckBoxPref_Blink = (android.preference.CheckBoxPreference) screen.findPreference("widgetBlink");
                android.preference.PreferenceCategory mCategory = (android.preference.PreferenceCategory) screen.findPreference("category_general_notification_pref");
                mCategory.removePreference(mCheckBoxPref_Vibrate);
                mCategory.removePreference(mCheckBoxPref_Blink);
            }
            // Build languages
            initializeLanguageDialog(screen);
            break;
        case "com.ichi2.anki.prefs.reviewing":
            listener.addPreferencesFromResource(R.xml.preferences_reviewing);
            screen = listener.getPreferenceScreen();
            // Show error toast if the user tries to disable answer button without gestures on
            // android.preference.ListPreference fullscreenPreference = (android.preference.ListPreference)
            // screen.findPreference("fullscreenMode");
            // fullscreenPreference.setOnPreferenceChangeListener((preference, newValue) -> {
            // SharedPreferences prefs = AnkiDroidApp.getSharedPrefs(Preferences.this);
            // if (prefs.getBoolean("gestures", false) || !"2".equals(newValue)) {
            // return true;
            // } else {
            // Toast.makeText(getApplicationContext(),
            // R.string.full_screen_error_gestures, Toast.LENGTH_LONG).show();
            // return false;
            // }
            // });
            // Custom buttons options
            android.preference.Preference customButtonsPreference = screen.findPreference("custom_buttons_link");
            customButtonsPreference.setOnPreferenceClickListener(preference -> {
                Intent i = getPreferenceSubscreenIntent(Preferences.this, "com.ichi2.anki.prefs.custom_buttons");
                startActivity(i);
                return true;
            });
            break;
        case "com.ichi2.anki.prefs.appearance":
            listener.addPreferencesFromResource(R.xml.preferences_appearance);
            screen = listener.getPreferenceScreen();
            backgroundImage = (android.preference.CheckBoxPreference) screen.findPreference("deckPickerBackground");
            backgroundImage.setOnPreferenceClickListener(preference -> {
                if (backgroundImage.isChecked()) {
                    try {
                        Intent galleryIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                        startActivityForResult(galleryIntent, RESULT_LOAD_IMG);
                        backgroundImage.setChecked(true);
                    } catch (Exception ex) {
                        Timber.e("%s", ex.getLocalizedMessage());
                    }
                } else {
                    backgroundImage.setChecked(false);
                    String currentAnkiDroidDirectory = CollectionHelper.getCurrentAnkiDroidDirectory(this);
                    File imgFile = new File(currentAnkiDroidDirectory, "DeckPickerBackground.png");
                    if (imgFile.exists()) {
                        if (imgFile.delete()) {
                            UIUtils.showThemedToast(this, getString(R.string.background_image_removed), false);
                        } else {
                            UIUtils.showThemedToast(this, getString(R.string.error_deleting_image), false);
                        }
                    } else {
                        UIUtils.showThemedToast(this, getString(R.string.background_image_removed), false);
                    }
                }
                return true;
            });
            initializeCustomFontsDialog(screen);
            break;
        case "com.ichi2.anki.prefs.gestures":
            listener.addPreferencesFromResource(R.xml.preferences_gestures);
            break;
        case "com.ichi2.anki.prefs.custom_controller_buttons":
            getSupportActionBar().setTitle(R.string.pref_cat_controller);
            listener.addPreferencesFromResource(R.xml.preferences_custom_controller_buttons);
            break;
        case "com.ichi2.anki.prefs.custom_buttons":
            getSupportActionBar().setTitle(R.string.custom_buttons);
            listener.addPreferencesFromResource(R.xml.preferences_custom_buttons);
            screen = listener.getPreferenceScreen();
            // Reset toolbar button customizations
            android.preference.Preference reset_custom_buttons = screen.findPreference("reset_custom_buttons");
            reset_custom_buttons.setOnPreferenceClickListener(preference -> {
                SharedPreferences.Editor edit = AnkiDroidApp.getSharedPrefs(getBaseContext()).edit();
                edit.remove("customButtonUndo");
                edit.remove("customButtonScheduleCard");
                edit.remove("customButtonMarkCard");
                edit.remove("customButtonEditCard");
                edit.remove("customButtonAddCard");
                edit.remove("customButtonReplay");
                edit.remove("customButtonSelectTts");
                edit.remove("customButtonDeckOptions");
                edit.remove("customButtonBury");
                edit.remove("customButtonSuspend");
                edit.remove("customButtonFlag");
                edit.remove("customButtonDelete");
                edit.remove("customButtonClearWhiteboard");
                edit.remove("customButtonShowHideWhiteboard");
                edit.apply();
                // TODO: Should reload the preferences screen on completion
                return true;
            });
            break;
        case "com.ichi2.anki.prefs.advanced":
            listener.addPreferencesFromResource(R.xml.preferences_advanced);
            screen = listener.getPreferenceScreen();
            // Check that input is valid before committing change in the collection path
            android.preference.EditTextPreference collectionPathPreference = (android.preference.EditTextPreference) screen.findPreference("deckPath");
            collectionPathPreference.setOnPreferenceChangeListener((preference, newValue) -> {
                final String newPath = (String) newValue;
                try {
                    CollectionHelper.initializeAnkiDroidDirectory(newPath);
                    return true;
                } catch (StorageAccessException e) {
                    Timber.e(e, "Could not initialize directory: %s", newPath);
                    Toast.makeText(getApplicationContext(), R.string.dialog_collection_path_not_dir, Toast.LENGTH_LONG).show();
                    return false;
                }
            });
            // Custom sync server option
            // android.preference.Preference customSyncServerPreference = screen.findPreference("custom_sync_server_link");
            // customSyncServerPreference.setOnPreferenceClickListener(preference -> {
            // Intent i = getPreferenceSubscreenIntent(Preferences.this,
            // "com.ichi2.anki.prefs.custom_sync_server");
            // startActivity(i);
            // return true;
            // });
            // Advanced statistics option
            android.preference.Preference advancedStatisticsPreference = screen.findPreference("advanced_statistics_link");
            advancedStatisticsPreference.setOnPreferenceClickListener(preference -> {
                Intent i = getPreferenceSubscreenIntent(Preferences.this, "com.ichi2.anki.prefs.advanced_statistics");
                startActivity(i);
                return true;
            });
            setupContextMenuPreference(screen, CardBrowserContextMenu.CARD_BROWSER_CONTEXT_MENU_PREF_KEY, R.string.card_browser_context_menu);
            setupContextMenuPreference(screen, AnkiCardContextMenu.ANKI_CARD_CONTEXT_MENU_PREF_KEY, R.string.context_menu_anki_card_label);
            // Make it possible to test crash reporting, but only for DEBUG builds
            if (BuildConfig.DEBUG && !AdaptionUtil.isUserATestClient()) {
                Timber.i("Debug mode, allowing for test crashes");
                android.preference.Preference triggerTestCrashPreference = new android.preference.Preference(this);
                triggerTestCrashPreference.setKey("trigger_crash_preference");
                triggerTestCrashPreference.setTitle("Trigger test crash");
                triggerTestCrashPreference.setSummary("Touch here for an immediate test crash");
                triggerTestCrashPreference.setOnPreferenceClickListener(preference -> {
                    Timber.w("Crash triggered on purpose from advanced preferences in debug mode");
                    throw new RuntimeException("This is a test crash");
                });
                screen.addPreference(triggerTestCrashPreference);
            }
            // Make it possible to test analytics, but only for DEBUG builds
            if (BuildConfig.DEBUG) {
                Timber.i("Debug mode, allowing for dynamic analytics config");
                android.preference.Preference analyticsDebugMode = new android.preference.Preference(this);
                analyticsDebugMode.setKey("analytics_debug_preference");
                analyticsDebugMode.setTitle("Switch Analytics to dev mode");
                analyticsDebugMode.setSummary("Touch here to use Analytics dev tag and 100% sample rate");
                analyticsDebugMode.setOnPreferenceClickListener(preference -> {
                    UsageAnalytics.setDevMode();
                    return true;
                });
                screen.addPreference(analyticsDebugMode);
            }
            if (BuildConfig.DEBUG) {
                Timber.i("Debug mode, allowing database lock preference");
                android.preference.Preference lockDbPreference = new android.preference.Preference(this);
                lockDbPreference.setKey("debug_lock_database");
                lockDbPreference.setTitle("Lock Database");
                lockDbPreference.setSummary("Touch here to lock the database (all threads block in-process, exception if using second process)");
                lockDbPreference.setOnPreferenceClickListener(preference -> {
                    DatabaseLock.engage(this);
                    return true;
                });
                screen.addPreference(lockDbPreference);
            }
            // Force full sync option
            ConfirmationPreference fullSyncPreference = (ConfirmationPreference) screen.findPreference("force_full_sync");
            fullSyncPreference.setDialogMessage(R.string.force_full_sync_summary);
            fullSyncPreference.setDialogTitle(R.string.force_full_sync_title);
            fullSyncPreference.setOkHandler(() -> {
                if (getCol() == null) {
                    Toast.makeText(getApplicationContext(), R.string.directory_inaccessible, Toast.LENGTH_LONG).show();
                    return;
                }
                getCol().modSchemaNoCheck();
                getCol().setMod();
                Toast.makeText(getApplicationContext(), android.R.string.ok, Toast.LENGTH_SHORT).show();
            });
            // Workaround preferences
            removeUnnecessaryAdvancedPrefs(screen);
            addThirdPartyAppsListener(screen);
            break;
        case "com.ichi2.anki.prefs.custom_sync_server":
            getSupportActionBar().setTitle(R.string.custom_sync_server_title);
            listener.addPreferencesFromResource(R.xml.preferences_custom_sync_server);
            screen = listener.getPreferenceScreen();
            android.preference.Preference syncUrlPreference = screen.findPreference("syncBaseUrl");
            android.preference.Preference mSyncUrlPreference = screen.findPreference("syncMediaUrl");
            syncUrlPreference.setOnPreferenceChangeListener((android.preference.Preference preference, Object newValue) -> {
                String newUrl = newValue.toString();
                if (!URLUtil.isValidUrl(newUrl)) {
                    new AlertDialog.Builder(this).setTitle(R.string.custom_sync_server_base_url_invalid).setPositiveButton(R.string.dialog_ok, null).show();
                    return false;
                }
                return true;
            });
            mSyncUrlPreference.setOnPreferenceChangeListener((android.preference.Preference preference, Object newValue) -> {
                String newUrl = newValue.toString();
                if (!URLUtil.isValidUrl(newUrl)) {
                    new AlertDialog.Builder(this).setTitle(R.string.custom_sync_server_media_url_invalid).setPositiveButton(R.string.dialog_ok, null).show();
                    return false;
                }
                return true;
            });
            break;
        case "com.ichi2.anki.prefs.advanced_statistics":
            getSupportActionBar().setTitle(R.string.advanced_statistics_title);
            listener.addPreferencesFromResource(R.xml.preferences_advanced_statistics);
            break;
    }
}
Also used : AlertDialog(android.app.AlertDialog) SharedPreferences(android.content.SharedPreferences) PendingIntent(android.app.PendingIntent) Intent(android.content.Intent) StorageAccessException(com.ichi2.anki.exception.StorageAccessException) ActivityNotFoundException(android.content.ActivityNotFoundException) ConfirmModSchemaException(com.ichi2.anki.exception.ConfirmModSchemaException) StorageAccessException(com.ichi2.anki.exception.StorageAccessException) BadTokenException(android.view.WindowManager.BadTokenException) NumberRangePreference(com.ichi2.preferences.NumberRangePreference) ConfirmationPreference(com.ichi2.ui.ConfirmationPreference) SeekBarPreference(com.ichi2.ui.SeekBarPreference) JSONObject(com.ichi2.utils.JSONObject) File(java.io.File) ConfirmationPreference(com.ichi2.ui.ConfirmationPreference)

Aggregations

StorageAccessException (com.ichi2.anki.exception.StorageAccessException)4 Intent (android.content.Intent)3 SharedPreferences (android.content.SharedPreferences)3 IntentFilter (android.content.IntentFilter)2 LocalBroadcastManager (androidx.localbroadcastmanager.content.LocalBroadcastManager)2 ManuallyReportedException (com.ichi2.anki.exception.ManuallyReportedException)2 BootService (com.ichi2.anki.services.BootService)2 NotificationService (com.ichi2.anki.services.NotificationService)2 File (java.io.File)2 CoreConfigurationBuilder (org.acra.config.CoreConfigurationBuilder)2 DebugTree (timber.log.Timber.DebugTree)2 AlertDialog (android.app.AlertDialog)1 PendingIntent (android.app.PendingIntent)1 ActivityNotFoundException (android.content.ActivityNotFoundException)1 ViewConfiguration (android.view.ViewConfiguration)1 BadTokenException (android.view.WindowManager.BadTokenException)1 ConfirmModSchemaException (com.ichi2.anki.exception.ConfirmModSchemaException)1 NumberRangePreference (com.ichi2.preferences.NumberRangePreference)1 ConfirmationPreference (com.ichi2.ui.ConfirmationPreference)1 SeekBarPreference (com.ichi2.ui.SeekBarPreference)1