// Define the class variables.
private var bookmarksDeletedSnackbar: Snackbar? = null
+ private var checkingManyBookmarks = false
private var closeActivityAfterDismissingSnackbar = false
private var contextualActionMode: ActionMode? = null
return true
}
- override fun onItemCheckedStateChanged(mode: ActionMode, position: Int, id: Long, checked: Boolean) {
- // Get the number of selected bookmarks.
- val numberOfSelectedBookmarks = bookmarksListView.checkedItemCount
-
- // Only process commands if at least one bookmark is selected. Otherwise, a context menu with 0 selected bookmarks is briefly displayed.
- if (numberOfSelectedBookmarks > 0) {
- // Adjust the action mode and the menu according to the number of selected bookmarks.
- if (numberOfSelectedBookmarks == 1) { // One bookmark is selected.
- // Show the applicable menu items.
- moveBookmarkUpMenuItem.isVisible = true
- moveBookmarkDownMenuItem.isVisible = true
- editBookmarkMenuItem.isVisible = true
-
- // Update the enabled status of the move icons.
- updateMoveIcons()
- } else { // More than one bookmark is selected.
- // Hide non-applicable `MenuItems`.
- moveBookmarkUpMenuItem.isVisible = false
- moveBookmarkDownMenuItem.isVisible = false
- editBookmarkMenuItem.isVisible = false
- }
+ override fun onItemCheckedStateChanged(actionMode: ActionMode, position: Int, id: Long, checked: Boolean) {
+ // Only update the UI if not checking many bookmarks. In that case, the flag will be reset on the last bookmark so the UI is only updated once.
+ if (!checkingManyBookmarks) {
+ // Get the number of selected bookmarks.
+ val numberOfSelectedBookmarks = bookmarksListView.checkedItemCount
+
+ // Only process commands if at least one bookmark is selected. Otherwise, a context menu with 0 selected bookmarks is briefly displayed.
+ if (numberOfSelectedBookmarks > 0) {
+ // Adjust the action mode and the menu according to the number of selected bookmarks.
+ if (numberOfSelectedBookmarks == 1) { // One bookmark is selected.
+ // Show the applicable menu items.
+ moveBookmarkUpMenuItem.isVisible = true
+ moveBookmarkDownMenuItem.isVisible = true
+ editBookmarkMenuItem.isVisible = true
+
+ // Update the enabled status of the move icons.
+ updateMoveIcons()
+ } else { // More than one bookmark is selected.
+ // Hide non-applicable `MenuItems`.
+ moveBookmarkUpMenuItem.isVisible = false
+ moveBookmarkDownMenuItem.isVisible = false
+ editBookmarkMenuItem.isVisible = false
+ }
- // Display the move to folder menu item if at least one other folder exists.
- moveToFolderMenuItem.isVisible = bookmarksDatabaseHelper.hasFoldersExceptDatabaseId(bookmarksListView.checkedItemIds)
+ // Display the move to folder menu item if at least one other folder exists.
+ moveToFolderMenuItem.isVisible = bookmarksDatabaseHelper.hasFoldersExceptDatabaseId(bookmarksListView.checkedItemIds)
- // List the number of selected bookmarks in the subtitle.
- mode.subtitle = getString(R.string.selected, numberOfSelectedBookmarks)
+ // List the number of selected bookmarks in the subtitle.
+ actionMode.subtitle = getString(R.string.selected, numberOfSelectedBookmarks)
- // Show the select all menu item if all the bookmarks are not selected.
- selectAllBookmarksMenuItem.isVisible = (numberOfSelectedBookmarks != bookmarksListView.count)
+ // Show the select all menu item if all the bookmarks are not selected.
+ selectAllBookmarksMenuItem.isVisible = (numberOfSelectedBookmarks != bookmarksListView.count)
+ }
}
}
override fun onActionItemClicked(actionMode: ActionMode, menuItem: MenuItem): Boolean {
// Declare the variables.
- val selectedBookmarkNewPosition: Int
- val selectedBookmarksPositionsSparseBooleanArray: SparseBooleanArray
+ val checkedBookmarkNewPosition: Int
+ val checkedBookmarksPositionsSparseBooleanArray: SparseBooleanArray
- // Initialize the selected bookmark position.
- var selectedBookmarkPosition = 0
+ // Initialize the checked bookmark position.
+ var checkedBookmarkPosition = 0
// Get the menu item ID.
val menuItemId = menuItem.itemId
// Run the commands according to the selected action item.
if (menuItemId == R.id.move_bookmark_up) { // Move the bookmark up.
// Get the array of checked bookmark positions.
- selectedBookmarksPositionsSparseBooleanArray = bookmarksListView.checkedItemPositions
+ checkedBookmarksPositionsSparseBooleanArray = bookmarksListView.checkedItemPositions
// Get the position of the bookmark that is selected. If other bookmarks have previously been selected they will be included in the sparse boolean array with a value of `false`.
- for (i in 0 until selectedBookmarksPositionsSparseBooleanArray.size()) {
+ for (i in 0 until checkedBookmarksPositionsSparseBooleanArray.size()) {
// Check to see if the value for the bookmark is true, meaning it is currently selected.
- if (selectedBookmarksPositionsSparseBooleanArray.valueAt(i)) {
+ if (checkedBookmarksPositionsSparseBooleanArray.valueAt(i)) {
// Only one bookmark should have a value of `true` when move bookmark up is enabled.
- selectedBookmarkPosition = selectedBookmarksPositionsSparseBooleanArray.keyAt(i)
+ checkedBookmarkPosition = checkedBookmarksPositionsSparseBooleanArray.keyAt(i)
}
}
- // Calculate the new position of the selected bookmark.
- selectedBookmarkNewPosition = selectedBookmarkPosition - 1
+ // Calculate the new position of the checked bookmark.
+ checkedBookmarkNewPosition = checkedBookmarkPosition - 1
+
+ // Get the bookmarks count.
+ val bookmarksCount = bookmarksListView.count
// Iterate through the bookmarks.
- for (i in 0 until bookmarksListView.count) {
+ for (i in 0 until bookmarksCount) {
// Get the database ID for the current bookmark.
val currentBookmarkDatabaseId = bookmarksListView.getItemIdAtPosition(i).toInt()
// Update the display order for the current bookmark.
- if (i == selectedBookmarkPosition) { // The current bookmark is the selected bookmark.
+ if (i == checkedBookmarkPosition) { // The current bookmark is the selected bookmark.
// Move the current bookmark up one.
bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i - 1)
- } else if ((i + 1) == selectedBookmarkPosition) { // The current bookmark is immediately above the selected bookmark.
+ } else if ((i + 1) == checkedBookmarkPosition) { // The current bookmark is immediately above the selected bookmark.
// Move the current bookmark down one.
bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i + 1)
} else { // The current bookmark is not changing positions.
bookmarksCursorAdapter.changeCursor(bookmarksCursor)
// Scroll to the new bookmark position.
- scrollBookmarks(selectedBookmarkNewPosition)
+ scrollBookmarks(checkedBookmarkNewPosition)
// Update the enabled status of the move icons.
updateMoveIcons()
} else if (menuItemId == R.id.move_bookmark_down) { // Move the bookmark down.
// Get the array of checked bookmark positions.
- selectedBookmarksPositionsSparseBooleanArray = bookmarksListView.checkedItemPositions
+ checkedBookmarksPositionsSparseBooleanArray = bookmarksListView.checkedItemPositions
- // Get the position of the bookmark that is selected. If other bookmarks have previously been selected they will be included in the sparse boolean array with a value of `false`.
- for (i in 0 until selectedBookmarksPositionsSparseBooleanArray.size()) {
+ // Get the position of the bookmark that is selected. If other bookmarks have previously been checked they will be included in the sparse boolean array with a value of `false`.
+ for (i in 0 until checkedBookmarksPositionsSparseBooleanArray.size()) {
// Check to see if the value for the bookmark is true, meaning it is currently selected.
- if (selectedBookmarksPositionsSparseBooleanArray.valueAt(i)) {
+ if (checkedBookmarksPositionsSparseBooleanArray.valueAt(i)) {
// Only one bookmark should have a value of `true` when move bookmark down is enabled.
- selectedBookmarkPosition = selectedBookmarksPositionsSparseBooleanArray.keyAt(i)
+ checkedBookmarkPosition = checkedBookmarksPositionsSparseBooleanArray.keyAt(i)
}
}
- // Calculate the new position of the selected bookmark.
- selectedBookmarkNewPosition = selectedBookmarkPosition + 1
+ // Calculate the new position of the checked bookmark.
+ checkedBookmarkNewPosition = checkedBookmarkPosition + 1
// Iterate through the bookmarks.
for (i in 0 until bookmarksListView.count) {
val currentBookmarkDatabaseId = bookmarksListView.getItemIdAtPosition(i).toInt()
// Update the display order for the current bookmark.
- if (i == selectedBookmarkPosition) { // The current bookmark is the selected bookmark.
+ if (i == checkedBookmarkPosition) { // The current bookmark is the checked bookmark.
// Move the current bookmark down one.
bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i + 1)
- } else if (i - 1 == selectedBookmarkPosition) { // The current bookmark is immediately below the selected bookmark.
+ } else if ((i - 1) == checkedBookmarkPosition) { // The current bookmark is immediately below the checked bookmark.
// Move the bookmark below the selected bookmark up one.
bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i - 1)
} else { // The current bookmark is not changing positions.
bookmarksCursorAdapter.changeCursor(bookmarksCursor)
// Scroll to the new bookmark position.
- scrollBookmarks(selectedBookmarkNewPosition)
+ scrollBookmarks(checkedBookmarkNewPosition)
// Update the enabled status of the move icons.
updateMoveIcons()
moveToFolderDialog.show(supportFragmentManager, resources.getString(R.string.move_to_folder))
} else if (menuItemId == R.id.edit_bookmark) {
// Get the array of checked bookmark positions.
- selectedBookmarksPositionsSparseBooleanArray = bookmarksListView.checkedItemPositions
+ checkedBookmarksPositionsSparseBooleanArray = bookmarksListView.checkedItemPositions
// Get the position of the bookmark that is selected. If other bookmarks have previously been selected they will be included in the sparse boolean array with a value of `false`.
- for (i in 0 until selectedBookmarksPositionsSparseBooleanArray.size()) {
+ for (i in 0 until checkedBookmarksPositionsSparseBooleanArray.size()) {
// Check to see if the value for the bookmark is true, meaning it is currently selected.
- if (selectedBookmarksPositionsSparseBooleanArray.valueAt(i)) {
+ if (checkedBookmarksPositionsSparseBooleanArray.valueAt(i)) {
// Only one bookmark should have a value of `true` when move edit bookmark is enabled.
- selectedBookmarkPosition = selectedBookmarksPositionsSparseBooleanArray.keyAt(i)
+ checkedBookmarkPosition = checkedBookmarksPositionsSparseBooleanArray.keyAt(i)
}
}
// Move the cursor to the selected position.
- bookmarksCursor.moveToPosition(selectedBookmarkPosition)
+ bookmarksCursor.moveToPosition(checkedBookmarkPosition)
// Get the selected bookmark database ID.
val databaseId = bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(ID))
// Set the deleting bookmarks flag, which prevents the delete menu item from being enabled until the current process finishes.
deletingBookmarks = true
- // Get an array of the selected row IDs.
- val selectedBookmarksIdsLongArray = bookmarksListView.checkedItemIds
+ // Get an array of the checked row IDs.
+ val checkedBookmarksIdsLongArray = bookmarksListView.checkedItemIds
// Initialize a variable to count the number of bookmarks to delete.
var numberOfBookmarksToDelete = 0
// Count the number of bookmarks to delete.
- for (databaseIdLong in selectedBookmarksIdsLongArray) {
+ for (databaseIdLong in checkedBookmarksIdsLongArray) {
// Convert the database ID long to an int.
val databaseIdInt = databaseIdLong.toInt()
}
// Get an array of checked bookmarks. `.clone()` makes a copy that won't change if the list view is reloaded, which is needed for re-selecting the bookmarks on undelete.
- selectedBookmarksPositionsSparseBooleanArray = bookmarksListView.checkedItemPositions.clone()
+ checkedBookmarksPositionsSparseBooleanArray = bookmarksListView.checkedItemPositions.clone()
// Update the bookmarks cursor with the current contents of the bookmarks database except for the specified database IDs.
- bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrderExcept(selectedBookmarksIdsLongArray, currentFolderId)
+ bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrderExcept(checkedBookmarksIdsLongArray, currentFolderId)
// Update the list view.
bookmarksCursorAdapter.changeCursor(bookmarksCursor)
// Update the list view.
bookmarksCursorAdapter.changeCursor(bookmarksCursor)
- // Re-select the previously selected bookmarks.
- for (i in 0 until selectedBookmarksPositionsSparseBooleanArray.size())
- bookmarksListView.setItemChecked(selectedBookmarksPositionsSparseBooleanArray.keyAt(i), true)
+ // Get the number of checked bookmarks.
+ val numberOfCheckedBookmarks = checkedBookmarksPositionsSparseBooleanArray.size()
+
+ // Set the checking many bookmarks flag.
+ checkingManyBookmarks = true
+
+ // Re-check the previously checked bookmarks.
+ for (i in 0 until numberOfCheckedBookmarks) {
+ // Reset the checking many bookmarks flag on the last bookmark so the UI is updated.
+ if (i == (numberOfCheckedBookmarks - 1))
+ checkingManyBookmarks = false
+
+ // Check the bookmark.
+ bookmarksListView.setItemChecked(checkedBookmarksPositionsSparseBooleanArray.keyAt(i), true)
+ }
} else { // The snackbar was dismissed without the undo button being pushed.
// Delete each selected bookmark.
- for (databaseIdLong in selectedBookmarksIdsLongArray) {
+ for (databaseIdLong in checkedBookmarksIdsLongArray) {
// Convert the database long ID to an int.
val databaseIdInt = databaseIdLong.toInt()
// Get the total number of bookmarks.
val numberOfBookmarks = bookmarksListView.count
+ // Set the checking many bookmarks flag.
+ checkingManyBookmarks = true
+
// Select them all.
for (i in 0 until numberOfBookmarks) {
+ // Reset the checking many bookmarks flag on the last bookmark so the UI is updated.
+ if (i == (numberOfBookmarks - 1))
+ checkingManyBookmarks = false
+
+ // Check the bookmark.
bookmarksListView.setItemChecked(i, true)
}
}
// Get the total number of bookmarks.
val numberOfBookmarks = bookmarksListView.count
+ // Set the checking many bookmarks flag.
+ checkingManyBookmarks = true
+
// Select them all.
for (i in 0 until numberOfBookmarks) {
+ // Reset the checking many bookmarks flag on the last bookmark so the UI is updated.
+ if (i == (numberOfBookmarks - 1))
+ checkingManyBookmarks = false
+
+ // Check the bookmark.
bookmarksListView.setItemChecked(i, true)
}
} else if (menuItemId == R.id.bookmarks_database_view) {
// Move all the bookmarks down one in the display order.
for (i in 0 until bookmarksListView.count) {
val databaseId = bookmarksListView.getItemIdAtPosition(i).toInt()
- bookmarksDatabaseHelper.updateDisplayOrder(databaseId, i + 1)
+ bookmarksDatabaseHelper.updateDisplayOrder(databaseId, displayOrder = i + 1)
}
// Create the folder, which will be placed at the top of the list view.
- bookmarksDatabaseHelper.createFolder(folderNameString, currentFolderId, folderIconByteArray)
+ bookmarksDatabaseHelper.createFolder(folderNameString, currentFolderId, displayOrder = 0, folderIconByteArray)
// Update the bookmarks cursor with the contents of the current folder.
bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
val folderId = bookmarksDatabaseHelper.getFolderId(folderDatabaseId)
// Get the contents of the folder.
- val folderCursor = bookmarksDatabaseHelper.getBookmarkIds(folderId)
+ val folderCursor = bookmarksDatabaseHelper.getBookmarkAndFolderIds(folderId)
// Initialize the bookmark counter.
var bookmarkCounter = 0
val folderId = bookmarksDatabaseHelper.getFolderId(folderDatabaseId)
// Get the contents of the folder.
- val folderCursor = bookmarksDatabaseHelper.getBookmarkIds(folderId)
+ val folderCursor = bookmarksDatabaseHelper.getBookmarkAndFolderIds(folderId)
// Delete each of the bookmarks in the folder.
for (i in 0 until folderCursor.count) {
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.RadioButton
+import android.widget.ScrollView
import android.widget.Spinner
import android.widget.TextView
import com.stoutner.privacybrowser.helpers.EXPORT_SUCCESSFUL
import com.stoutner.privacybrowser.helpers.IMPORT_EXPORT_SCHEMA_VERSION
import com.stoutner.privacybrowser.helpers.IMPORT_SUCCESSFUL
+import com.stoutner.privacybrowser.helpers.ImportExportBookmarksHelper
import com.stoutner.privacybrowser.helpers.ImportExportDatabaseHelper
import java.io.File
// Define the saved instance state constants.
private const val ENCRYPTION_PASSWORD_TEXTINPUTLAYOUT_VISIBILITY = "A"
private const val OPEN_KEYCHAIN_REQUIRED_TEXTVIEW_VISIBILITY = "B"
-private const val FILE_LOCATION_CARD_VIEW = "C"
-private const val FILE_NAME_LINEARLAYOUT_VISIBILITY = "D"
+private const val SETTINGS_FILE_LOCATION_CARDVIEW_VISIBILITY = "C"
+private const val SETTINGS_FILE_NAME_LINEARLAYOUT_VISIBILITY = "D"
private const val OPEN_KEYCHAIN_IMPORT_INSTRUCTIONS_TEXTVIEW_VISIBILITY = "E"
-private const val IMPORT_EXPORT_BUTTON_VISIBILITY = "F"
-private const val FILE_NAME_TEXT = "G"
-private const val IMPORT_EXPORT_BUTTON_TEXT = "H"
+private const val SETTINGS_IMPORT_EXPORT_BUTTON_VISIBILITY = "F"
+private const val SETTINGS_FILE_NAME_TEXT = "G"
+private const val SETTINGS_IMPORT_EXPORT_BUTTON_TEXT = "H"
+private const val BOOKMARKS_FILE_NAME_LINEARLAYOUT_VISIBILITY = "I"
+private const val BOOKMARKS_IMPORT_EXPORT_BUTTON_VISIBILITY = "J"
+private const val BOOKMARKS_FILE_NAME_TEXT = "K"
+private const val BOOKMARKS_IMPORT_EXPORT_BUTTON_TEXT = "L"
class ImportExportActivity : AppCompatActivity() {
// Define the class views.
+ private lateinit var scrollView: ScrollView
private lateinit var encryptionSpinner: Spinner
private lateinit var encryptionPasswordTextInputLayout: TextInputLayout
private lateinit var encryptionPasswordEditText: EditText
private lateinit var openKeychainRequiredTextView: TextView
- private lateinit var fileLocationCardView: CardView
- private lateinit var importRadioButton: RadioButton
- private lateinit var fileNameLinearLayout: LinearLayout
- private lateinit var fileNameEditText: EditText
+ private lateinit var settingsFileLocationCardView: CardView
+ private lateinit var settingsImportRadioButton: RadioButton
+ private lateinit var settingsFileNameLinearLayout: LinearLayout
+ private lateinit var settingsFileNameEditText: EditText
private lateinit var openKeychainImportInstructionsTextView: TextView
- private lateinit var importExportButton: Button
+ private lateinit var settingsImportExportButton: Button
+ private lateinit var bookmarksImportRadioButton: RadioButton
+ private lateinit var bookmarksFileNameLinearLayout: LinearLayout
+ private lateinit var bookmarksFileNameEditText: EditText
+ private lateinit var bookmarksImportExportButton: Button
// Define the class variables.
private lateinit var fileProviderDirectory: File
private lateinit var temporaryPgpEncryptedImportFile: File
private lateinit var temporaryPreEncryptedExportFile: File
- // Define the browse for import activity result launcher. It must be defined before `onCreate()` is run or the app will crash.
- private val browseForImportActivityResultLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { fileUri: Uri? ->
+ // Define the result launchers. They must be defined before `onCreate()` is run or the app will crash.
+ private val settingsBrowseForImportActivityResultLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { fileUri: Uri? ->
// Only do something if the user didn't press back from the file picker.
if (fileUri != null) {
// Get the file name string from the URI.
val fileNameString = fileUri.toString()
- // Set the file name name text.
- fileNameEditText.setText(fileNameString)
+ // Set the settings file name text.
+ settingsFileNameEditText.setText(fileNameString)
// Move the cursor to the end of the file name edit text.
- fileNameEditText.setSelection(fileNameString.length)
+ settingsFileNameEditText.setSelection(fileNameString.length)
}
}
- private val browseForExportActivityResultLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument("*/*")) { fileUri: Uri? ->
+ private val settingsBrowseForExportActivityResultLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument("*/*")) { fileUri: Uri? ->
// Only do something if the user didn't press back from the file picker.
if (fileUri != null) {
// Get the file name string from the URI.
val fileNameString = fileUri.toString()
- // Set the file name name text.
- fileNameEditText.setText(fileNameString)
+ // Set the settings file name text.
+ settingsFileNameEditText.setText(fileNameString)
// Move the cursor to the end of the file name edit text.
- fileNameEditText.setSelection(fileNameString.length)
+ settingsFileNameEditText.setSelection(fileNameString.length)
+ }
+ }
+
+ private val bookmarksBrowseForImportActivityResultLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { fileUri: Uri? ->
+ // Only do something if the user didn't press back from the file picker.
+ if (fileUri != null) {
+ // Get the file name string from the URI.
+ val fileNameString = fileUri.toString()
+
+ // Set the bookmarks file name text.
+ bookmarksFileNameEditText.setText(fileNameString)
+
+ // Move the cursor to the end of the file name edit text.
+ bookmarksFileNameEditText.setSelection(fileNameString.length)
+
+ // Scroll to the bottom.
+ scrollView.post {
+ scrollView.scrollY = scrollView.height
+ }
+ }
+ }
+
+ private val bookmarksBrowseForExportActivityResultLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument("*/*")) { fileUri: Uri? ->
+ // Only do something if the user didn't press back from the file picker.
+ if (fileUri != null) {
+ // Get the file name string from the URI.
+ val fileNameString = fileUri.toString()
+
+ // Set the bookmarks file name text.
+ bookmarksFileNameEditText.setText(fileNameString)
+
+ // Move the cursor to the end of the file name edit text.
+ bookmarksFileNameEditText.setSelection(fileNameString.length)
+
+ // Scroll to the bottom.
+ scrollView.post {
+ scrollView.scrollY = scrollView.height
+ }
}
}
}
// Get handles for the views.
+ scrollView = findViewById(R.id.scrollview)
encryptionSpinner = findViewById(R.id.encryption_spinner)
encryptionPasswordTextInputLayout = findViewById(R.id.encryption_password_textinputlayout)
encryptionPasswordEditText = findViewById(R.id.encryption_password_edittext)
openKeychainRequiredTextView = findViewById(R.id.openkeychain_required_textview)
- fileLocationCardView = findViewById(R.id.file_location_cardview)
- importRadioButton = findViewById(R.id.import_radiobutton)
- val exportRadioButton = findViewById<RadioButton>(R.id.export_radiobutton)
- fileNameLinearLayout = findViewById(R.id.file_name_linearlayout)
- fileNameEditText = findViewById(R.id.file_name_edittext)
+ settingsFileLocationCardView = findViewById(R.id.settings_file_location_cardview)
+ settingsImportRadioButton = findViewById(R.id.settings_import_radiobutton)
+ val settingsExportRadioButton = findViewById<RadioButton>(R.id.settings_export_radiobutton)
+ settingsFileNameLinearLayout = findViewById(R.id.settings_file_name_linearlayout)
+ settingsFileNameEditText = findViewById(R.id.settings_file_name_edittext)
openKeychainImportInstructionsTextView = findViewById(R.id.openkeychain_import_instructions_textview)
- importExportButton = findViewById(R.id.import_export_button)
+ settingsImportExportButton = findViewById(R.id.settings_import_export_button)
+ bookmarksImportRadioButton = findViewById(R.id.bookmarks_import_radiobutton)
+ bookmarksFileNameLinearLayout = findViewById(R.id.bookmarks_file_name_linearlayout)
+ bookmarksFileNameEditText = findViewById(R.id.bookmarks_file_name_edittext)
+ bookmarksImportExportButton = findViewById(R.id.bookmarks_import_export_button)
// Create an array adapter for the spinner.
val encryptionArrayAdapter = ArrayAdapter.createFromResource(this, R.array.encryption_type, R.layout.spinner_item)
// Update the UI when the spinner changes.
encryptionSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
- override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) {
+ override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
when (position) {
NO_ENCRYPTION -> {
// Hide the unneeded layout items.
openKeychainImportInstructionsTextView.visibility = View.GONE
// Show the file location card.
- fileLocationCardView.visibility = View.VISIBLE
+ settingsFileLocationCardView.visibility = View.VISIBLE
// Show the file name linear layout if either import or export is checked.
- if (importRadioButton.isChecked || exportRadioButton.isChecked)
- fileNameLinearLayout.visibility = View.VISIBLE
+ if (settingsImportRadioButton.isChecked || settingsExportRadioButton.isChecked)
+ settingsFileNameLinearLayout.visibility = View.VISIBLE
// Reset the text of the import button, which may have been changed to `Decrypt`.
- if (importRadioButton.isChecked)
- importExportButton.setText(R.string.import_button)
+ if (settingsImportRadioButton.isChecked)
+ settingsImportExportButton.setText(R.string.import_button)
// Clear the file name edit text.
- fileNameEditText.text.clear()
+ settingsFileNameEditText.text.clear()
// Disable the import/export button.
- importExportButton.isEnabled = false
+ settingsImportExportButton.isEnabled = false
}
PASSWORD_ENCRYPTION -> {
encryptionPasswordTextInputLayout.visibility = View.VISIBLE
// Show the file location card.
- fileLocationCardView.visibility = View.VISIBLE
+ settingsFileLocationCardView.visibility = View.VISIBLE
// Show the file name linear layout if either import or export is checked.
- if (importRadioButton.isChecked || exportRadioButton.isChecked)
- fileNameLinearLayout.visibility = View.VISIBLE
+ if (settingsImportRadioButton.isChecked || settingsExportRadioButton.isChecked)
+ settingsFileNameLinearLayout.visibility = View.VISIBLE
// Reset the text of the import button, which may have been changed to `Decrypt`.
- if (importRadioButton.isChecked)
- importExportButton.setText(R.string.import_button)
+ if (settingsImportRadioButton.isChecked)
+ settingsImportExportButton.setText(R.string.import_button)
// Clear the file name edit text.
- fileNameEditText.text.clear()
+ settingsFileNameEditText.text.clear()
// Disable the import/export button.
- importExportButton.isEnabled = false
+ settingsImportExportButton.isEnabled = false
}
OPENPGP_ENCRYPTION -> {
// Updated items based on the installation status of OpenKeychain.
if (openKeychainInstalled) { // OpenKeychain is installed.
// Show the file location card.
- fileLocationCardView.visibility = View.VISIBLE
+ settingsFileLocationCardView.visibility = View.VISIBLE
// Update the layout based on the checked radio button.
- if (importRadioButton.isChecked) {
+ if (settingsImportRadioButton.isChecked) {
// Show the file name linear layout and the OpenKeychain import instructions.
- fileNameLinearLayout.visibility = View.VISIBLE
+ settingsFileNameLinearLayout.visibility = View.VISIBLE
openKeychainImportInstructionsTextView.visibility = View.VISIBLE
// Set the text of the import button to be `Decrypt`.
- importExportButton.setText(R.string.decrypt)
+ settingsImportExportButton.setText(R.string.decrypt)
// Clear the file name edit text.
- fileNameEditText.text.clear()
+ settingsFileNameEditText.text.clear()
// Disable the import/export button.
- importExportButton.isEnabled = false
- } else if (exportRadioButton.isChecked) {
+ settingsImportExportButton.isEnabled = false
+ } else if (settingsExportRadioButton.isChecked) {
// Hide the file name linear layout and the OpenKeychain import instructions.
- fileNameLinearLayout.visibility = View.GONE
+ settingsFileNameLinearLayout.visibility = View.GONE
openKeychainImportInstructionsTextView.visibility = View.GONE
// Enable the export button.
- importExportButton.isEnabled = true
+ settingsImportExportButton.isEnabled = true
}
} else { // OpenKeychain is not installed.
// Show the OpenPGP required layout item.
openKeychainRequiredTextView.visibility = View.VISIBLE
// Hide the file location card.
- fileLocationCardView.visibility = View.GONE
+ settingsFileLocationCardView.visibility = View.GONE
}
}
}
override fun afterTextChanged(s: Editable) {
// Enable the import/export button if both the file string and the password are populated.
- importExportButton.isEnabled = fileNameEditText.text.toString().isNotEmpty() && encryptionPasswordEditText.text.toString().isNotEmpty()
+ settingsImportExportButton.isEnabled = settingsFileNameEditText.text.toString().isNotEmpty() && encryptionPasswordEditText.text.toString().isNotEmpty()
}
})
- // Update the UI when the file name edit text changes.
- fileNameEditText.addTextChangedListener(object : TextWatcher {
+ // Update the UI when the settings file name edit text changes.
+ settingsFileNameEditText.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
// Do nothing.
}
override fun afterTextChanged(s: Editable) {
// Adjust the UI according to the encryption spinner position.
if (encryptionSpinner.selectedItemPosition == PASSWORD_ENCRYPTION) {
- // Enable the import/export button if both the file name and the password are populated.
- importExportButton.isEnabled = fileNameEditText.text.toString().isNotEmpty() && encryptionPasswordEditText.text.toString().isNotEmpty()
+ // Enable the settings import/export button if both the file name and the password are populated.
+ settingsImportExportButton.isEnabled = settingsFileNameEditText.text.toString().isNotEmpty() && encryptionPasswordEditText.text.toString().isNotEmpty()
} else {
- // Enable the export button if the file name is populated.
- importExportButton.isEnabled = fileNameEditText.text.toString().isNotEmpty()
+ // Enable the settings import/export button if the file name is populated.
+ settingsImportExportButton.isEnabled = settingsFileNameEditText.text.toString().isNotEmpty()
}
}
})
+ // Update the UI when the bookmarks file name edit text changes.
+ bookmarksFileNameEditText.addTextChangedListener(object : TextWatcher {
+ override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
+ // Do nothing.
+ }
+
+ override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
+ // Do nothing.
+ }
+
+ override fun afterTextChanged(s: Editable) {
+ // Enable the bookmarks import/export button if the file name is populated.
+ bookmarksImportExportButton.isEnabled = bookmarksFileNameEditText.text.toString().isNotEmpty()
+ }
+ })
+
// Check to see if the activity has been restarted.
if (savedInstanceState == null) { // The app has not been restarted.
// Initially hide the unneeded views.
encryptionPasswordTextInputLayout.visibility = View.GONE
openKeychainRequiredTextView.visibility = View.GONE
- fileNameLinearLayout.visibility = View.GONE
+ settingsFileNameLinearLayout.visibility = View.GONE
openKeychainImportInstructionsTextView.visibility = View.GONE
- importExportButton.visibility = View.GONE
+ settingsImportExportButton.visibility = View.GONE
+ bookmarksFileNameLinearLayout.visibility = View.GONE
+ bookmarksImportExportButton.visibility = View.GONE
} else { // The app has been restarted.
// Restore the visibility of the views.
encryptionPasswordTextInputLayout.visibility = savedInstanceState.getInt(ENCRYPTION_PASSWORD_TEXTINPUTLAYOUT_VISIBILITY)
openKeychainRequiredTextView.visibility = savedInstanceState.getInt(OPEN_KEYCHAIN_REQUIRED_TEXTVIEW_VISIBILITY)
- fileLocationCardView.visibility = savedInstanceState.getInt(FILE_LOCATION_CARD_VIEW)
- fileNameLinearLayout.visibility = savedInstanceState.getInt(FILE_NAME_LINEARLAYOUT_VISIBILITY)
+ settingsFileLocationCardView.visibility = savedInstanceState.getInt(SETTINGS_FILE_LOCATION_CARDVIEW_VISIBILITY)
+ settingsFileNameLinearLayout.visibility = savedInstanceState.getInt(SETTINGS_FILE_NAME_LINEARLAYOUT_VISIBILITY)
openKeychainImportInstructionsTextView.visibility = savedInstanceState.getInt(OPEN_KEYCHAIN_IMPORT_INSTRUCTIONS_TEXTVIEW_VISIBILITY)
- importExportButton.visibility = savedInstanceState.getInt(IMPORT_EXPORT_BUTTON_VISIBILITY)
+ settingsImportExportButton.visibility = savedInstanceState.getInt(SETTINGS_IMPORT_EXPORT_BUTTON_VISIBILITY)
+ bookmarksFileNameLinearLayout.visibility = savedInstanceState.getInt(BOOKMARKS_FILE_NAME_LINEARLAYOUT_VISIBILITY)
+ bookmarksImportExportButton.visibility = savedInstanceState.getInt(BOOKMARKS_IMPORT_EXPORT_BUTTON_VISIBILITY)
// Restore the text.
- fileNameEditText.post { fileNameEditText.setText(savedInstanceState.getString(FILE_NAME_TEXT)) }
- importExportButton.text = savedInstanceState.getString(IMPORT_EXPORT_BUTTON_TEXT)
+ settingsFileNameEditText.post { settingsFileNameEditText.setText(savedInstanceState.getString(SETTINGS_FILE_NAME_TEXT)) }
+ settingsImportExportButton.text = savedInstanceState.getString(SETTINGS_IMPORT_EXPORT_BUTTON_TEXT)
+ bookmarksFileNameEditText.post { bookmarksFileNameEditText.setText(savedInstanceState.getString(BOOKMARKS_FILE_NAME_TEXT)) }
+ bookmarksImportExportButton.text = savedInstanceState.getString(BOOKMARKS_IMPORT_EXPORT_BUTTON_TEXT)
}
}
// Save the visibility of the views.
savedInstanceState.putInt(ENCRYPTION_PASSWORD_TEXTINPUTLAYOUT_VISIBILITY, encryptionPasswordTextInputLayout.visibility)
savedInstanceState.putInt(OPEN_KEYCHAIN_REQUIRED_TEXTVIEW_VISIBILITY, openKeychainRequiredTextView.visibility)
- savedInstanceState.putInt(FILE_LOCATION_CARD_VIEW, fileLocationCardView.visibility)
- savedInstanceState.putInt(FILE_NAME_LINEARLAYOUT_VISIBILITY, fileNameLinearLayout.visibility)
+ savedInstanceState.putInt(SETTINGS_FILE_LOCATION_CARDVIEW_VISIBILITY, settingsFileLocationCardView.visibility)
+ savedInstanceState.putInt(SETTINGS_FILE_NAME_LINEARLAYOUT_VISIBILITY, settingsFileNameLinearLayout.visibility)
savedInstanceState.putInt(OPEN_KEYCHAIN_IMPORT_INSTRUCTIONS_TEXTVIEW_VISIBILITY, openKeychainImportInstructionsTextView.visibility)
- savedInstanceState.putInt(IMPORT_EXPORT_BUTTON_VISIBILITY, importExportButton.visibility)
+ savedInstanceState.putInt(SETTINGS_IMPORT_EXPORT_BUTTON_VISIBILITY, settingsImportExportButton.visibility)
+ savedInstanceState.putInt(BOOKMARKS_FILE_NAME_LINEARLAYOUT_VISIBILITY, bookmarksFileNameLinearLayout.visibility)
+ savedInstanceState.putInt(BOOKMARKS_IMPORT_EXPORT_BUTTON_VISIBILITY, bookmarksImportExportButton.visibility)
// Save the text.
- savedInstanceState.putString(FILE_NAME_TEXT, fileNameEditText.text.toString())
- savedInstanceState.putString(IMPORT_EXPORT_BUTTON_TEXT, importExportButton.text.toString())
+ savedInstanceState.putString(SETTINGS_FILE_NAME_TEXT, settingsFileNameEditText.text.toString())
+ savedInstanceState.putString(SETTINGS_IMPORT_EXPORT_BUTTON_TEXT, settingsImportExportButton.text.toString())
+ savedInstanceState.putString(BOOKMARKS_FILE_NAME_TEXT, bookmarksFileNameEditText.text.toString())
+ savedInstanceState.putString(BOOKMARKS_IMPORT_EXPORT_BUTTON_VISIBILITY, bookmarksImportExportButton.text.toString())
+ }
+
+ fun onClickBookmarksRadioButton(view: View) {
+ // Check to see if import or export was selected.
+ if (view.id == R.id.bookmarks_import_radiobutton) { // The bookmarks import radio button was selected.
+ // Set the text on the bookmarks import/export button to be `Import`.
+ bookmarksImportExportButton.setText(R.string.import_button)
+ } else { // The bookmarks export radio button was selected.
+ // Set the text on the bookmarks import/export button to be `Export`.
+ bookmarksImportExportButton.setText(R.string.export)
+ }
+
+ // Display the bookmarks views.
+ bookmarksFileNameLinearLayout.visibility = View.VISIBLE
+ bookmarksImportExportButton.visibility = View.VISIBLE
+
+ // Clear the bookmarks file name edit text.
+ bookmarksFileNameEditText.text.clear()
+
+ // Disable the bookmarks import/export button.
+ bookmarksImportExportButton.isEnabled = false
+
+ // Scroll to the bottom of the screen.
+ scrollView.post {
+ scrollView.scrollY = scrollView.height
+ }
}
- fun onClickRadioButton(view: View) {
+ fun onClickSettingsRadioButton(view: View) {
// Check to see if import or export was selected.
- if (view.id == R.id.import_radiobutton) { // The import radio button is selected.
+ if (view.id == R.id.settings_import_radiobutton) { // The settings import radio button was selected.
// Check to see if OpenPGP encryption is selected.
if (encryptionSpinner.selectedItemPosition == OPENPGP_ENCRYPTION) { // OpenPGP encryption selected.
// Show the OpenKeychain import instructions.
openKeychainImportInstructionsTextView.visibility = View.VISIBLE
- // Set the text on the import/export button to be `Decrypt`.
- importExportButton.setText(R.string.decrypt)
+ // Set the text on the settings import/export button to be `Decrypt`.
+ settingsImportExportButton.setText(R.string.decrypt)
} else { // OpenPGP encryption not selected.
// Hide the OpenKeychain import instructions.
openKeychainImportInstructionsTextView.visibility = View.GONE
- // Set the text on the import/export button to be `Import`.
- importExportButton.setText(R.string.import_button)
+ // Set the text on the settings import/export button to be `Import`.
+ settingsImportExportButton.setText(R.string.import_button)
}
- // Display the file name views.
- fileNameLinearLayout.visibility = View.VISIBLE
- importExportButton.visibility = View.VISIBLE
+ // Display the views.
+ settingsFileNameLinearLayout.visibility = View.VISIBLE
+ settingsImportExportButton.visibility = View.VISIBLE
- // Clear the file name edit text.
- fileNameEditText.text.clear()
+ // Clear the settings file name edit text.
+ settingsFileNameEditText.text.clear()
- // Disable the import/export button.
- importExportButton.isEnabled = false
- } else { // The export radio button is selected.
+ // Disable the settings import/export button.
+ settingsImportExportButton.isEnabled = false
+ } else { // The settings export radio button was selected.
// Hide the OpenKeychain import instructions.
openKeychainImportInstructionsTextView.visibility = View.GONE
- // Set the text on the import/export button to be `Export`.
- importExportButton.setText(R.string.export)
+ // Set the text on the settings import/export button to be `Export`.
+ settingsImportExportButton.setText(R.string.export)
- // Show the import/export button.
- importExportButton.visibility = View.VISIBLE
+ // Show the settings import/export button.
+ settingsImportExportButton.visibility = View.VISIBLE
// Check to see if OpenPGP encryption is selected.
if (encryptionSpinner.selectedItemPosition == OPENPGP_ENCRYPTION) { // OpenPGP encryption is selected.
- // Hide the file name views.
- fileNameLinearLayout.visibility = View.GONE
+ // Hide the settings file name views.
+ settingsFileNameLinearLayout.visibility = View.GONE
- // Enable the export button.
- importExportButton.isEnabled = true
+ // Enable the settings export button.
+ settingsImportExportButton.isEnabled = true
} else { // OpenPGP encryption is not selected.
- // Show the file name view.
- fileNameLinearLayout.visibility = View.VISIBLE
+ // Show the settings file name view.
+ settingsFileNameLinearLayout.visibility = View.VISIBLE
- // Clear the file name edit text.
- fileNameEditText.text.clear()
+ // Clear the settings file name edit text.
+ settingsFileNameEditText.text.clear()
- // Disable the import/export button.
- importExportButton.isEnabled = false
+ // Disable the settings import/export button.
+ settingsImportExportButton.isEnabled = false
}
}
}
- fun browse(@Suppress("UNUSED_PARAMETER") view: View) {
+ fun settingsBrowse(@Suppress("UNUSED_PARAMETER") view: View) {
// Check to see if import or export is selected.
- if (importRadioButton.isChecked) { // Import is selected.
+ if (settingsImportRadioButton.isChecked) { // Import is selected.
// Open the file picker.
- browseForImportActivityResultLauncher.launch("*/*")
+ settingsBrowseForImportActivityResultLauncher.launch("*/*")
} else { // Export is selected
// Open the file picker with the export name according to the encryption type.
if (encryptionSpinner.selectedItemPosition == NO_ENCRYPTION) // No encryption is selected.
- browseForExportActivityResultLauncher.launch(getString(R.string.privacy_browser_settings_pbs, BuildConfig.VERSION_NAME, IMPORT_EXPORT_SCHEMA_VERSION))
+ settingsBrowseForExportActivityResultLauncher.launch(getString(R.string.privacy_browser_settings_pbs, BuildConfig.VERSION_NAME, IMPORT_EXPORT_SCHEMA_VERSION))
else // Password encryption is selected.
- browseForExportActivityResultLauncher.launch(getString(R.string.privacy_browser_settings_pbs_aes, BuildConfig.VERSION_NAME, IMPORT_EXPORT_SCHEMA_VERSION))
+ settingsBrowseForExportActivityResultLauncher.launch(getString(R.string.privacy_browser_settings_pbs_aes, BuildConfig.VERSION_NAME, IMPORT_EXPORT_SCHEMA_VERSION))
+ }
+ }
+
+ fun bookmarksBrowse(@Suppress("UNUSED_PARAMETER") view: View) {
+ // Check to see if import or export is selected.
+ if (bookmarksImportRadioButton.isChecked) { // Import is selected.
+ // Open the file picker.
+ bookmarksBrowseForImportActivityResultLauncher.launch("*/*")
+ } else { // Export is selected.
+ // Open the file picker.
+ bookmarksBrowseForExportActivityResultLauncher.launch(getString(R.string.privacy_browser_bookmarks_html))
+ }
+ }
+
+ fun importExportBookmarks(@Suppress("UNUSED_PARAMETER") view: View) {
+ // Instantiate the import/export bookmarks helper.
+ val importExportBookmarksHelper = ImportExportBookmarksHelper()
+
+ // Get the file name string.
+ val fileNameString = bookmarksFileNameEditText.text.toString()
+
+ // Check to see if import or export is selected.
+ if (bookmarksImportRadioButton.isChecked) { // Import is selected.
+ // Import the bookmarks.
+ importExportBookmarksHelper.importBookmarks(fileNameString, context = this, scrollView)
+
+ // Repopulate the bookmarks in the main WebView activity.
+ MainWebViewActivity.restartFromBookmarksActivity = true
+ } else { // Export is selected.
+ // Export the bookmarks.
+ importExportBookmarksHelper.exportBookmarks(fileNameString, context = this, scrollView)
}
}
- fun importExport(@Suppress("UNUSED_PARAMETER") view: View) {
- // Instantiate the import export database helper.
+ fun importExportSettings(@Suppress("UNUSED_PARAMETER") view: View) {
+ // Instantiate the import/export database helper.
val importExportDatabaseHelper = ImportExportDatabaseHelper()
// Check to see if import or export is selected.
- if (importRadioButton.isChecked) { // Import is selected.
+ if (settingsImportRadioButton.isChecked) { // Import is selected.
// Initialize the import status string
var importStatus = ""
// Get the file name string.
- val fileNameString = fileNameEditText.text.toString()
+ val fileNameString = settingsFileNameEditText.text.toString()
// Import according to the encryption type.
when (encryptionSpinner.selectedItemPosition) {
// Display a snack bar with the import error if it was unsuccessful.
if (importStatus != IMPORT_SUCCESSFUL)
- Snackbar.make(fileNameEditText, getString(R.string.import_failed, importStatus), Snackbar.LENGTH_INDEFINITE).show()
+ Snackbar.make(settingsFileNameEditText, getString(R.string.import_failed, importStatus), Snackbar.LENGTH_INDEFINITE).show()
} else { // Export is selected.
// Export according to the encryption type.
when (encryptionSpinner.selectedItemPosition) {
NO_ENCRYPTION -> {
// Get the file name string.
- val noEncryptionFileNameString = fileNameEditText.text.toString()
+ val noEncryptionFileNameString = settingsFileNameEditText.text.toString()
try {
- // Get the export file output stream.
+ // Get the export file output stream, truncating any existing content.
// A file may be opened directly once the minimum API >= 29. <https://developer.android.com/reference/kotlin/android/content/ContentResolver#openfile>
- val exportFileOutputStream = contentResolver.openOutputStream(Uri.parse(noEncryptionFileNameString))!!
+ val exportFileOutputStream = contentResolver.openOutputStream(Uri.parse(noEncryptionFileNameString), "wt")!!
// Export the unencrypted file.
val noEncryptionExportStatus = importExportDatabaseHelper.exportUnencrypted(exportFileOutputStream, this)
// Display an export disposition snackbar.
if (noEncryptionExportStatus == EXPORT_SUCCESSFUL)
- Snackbar.make(fileNameEditText, getString(R.string.export_successful), Snackbar.LENGTH_SHORT).show()
+ Snackbar.make(settingsFileNameEditText, getString(R.string.export_successful), Snackbar.LENGTH_SHORT).show()
else
- Snackbar.make(fileNameEditText, getString(R.string.export_failed, noEncryptionExportStatus), Snackbar.LENGTH_INDEFINITE).show()
+ Snackbar.make(settingsFileNameEditText, getString(R.string.export_failed, noEncryptionExportStatus), Snackbar.LENGTH_INDEFINITE).show()
} catch (fileNotFoundException: FileNotFoundException) {
// Display a snackbar with the exception.
- Snackbar.make(fileNameEditText, getString(R.string.export_failed, fileNotFoundException), Snackbar.LENGTH_INDEFINITE).show()
+ Snackbar.make(settingsFileNameEditText, getString(R.string.export_failed, fileNotFoundException), Snackbar.LENGTH_INDEFINITE).show()
}
}
cipher.init(Cipher.ENCRYPT_MODE, secretKey, gcmParameterSpec)
// Get the file name string.
- val passwordEncryptionFileNameString = fileNameEditText.text.toString()
+ val passwordEncryptionFileNameString = settingsFileNameEditText.text.toString()
- // Get the export file output stream.
- val exportFileOutputStream = contentResolver.openOutputStream(Uri.parse(passwordEncryptionFileNameString))!!
+ // Get the export file output stream, truncating any existing content.
+ val exportFileOutputStream = contentResolver.openOutputStream(Uri.parse(passwordEncryptionFileNameString), "wt")!!
// Add the salt and the initialization vector to the export file output stream.
exportFileOutputStream.write(saltByteArray)
// Display an export disposition snackbar.
if (passwordEncryptionExportStatus == EXPORT_SUCCESSFUL)
- Snackbar.make(fileNameEditText, getString(R.string.export_successful), Snackbar.LENGTH_SHORT).show()
+ Snackbar.make(settingsFileNameEditText, getString(R.string.export_successful), Snackbar.LENGTH_SHORT).show()
else
- Snackbar.make(fileNameEditText, getString(R.string.export_failed, passwordEncryptionExportStatus), Snackbar.LENGTH_INDEFINITE).show()
+ Snackbar.make(settingsFileNameEditText, getString(R.string.export_failed, passwordEncryptionExportStatus), Snackbar.LENGTH_INDEFINITE).show()
} catch (exception: Exception) {
// Display a snackbar with the exception.
- Snackbar.make(fileNameEditText, getString(R.string.export_failed, exception), Snackbar.LENGTH_INDEFINITE).show()
+ Snackbar.make(settingsFileNameEditText, getString(R.string.export_failed, exception), Snackbar.LENGTH_INDEFINITE).show()
}
}
// Display an export error snackbar if the temporary pre-encrypted export failed.
if (openpgpEncryptionExportStatus != EXPORT_SUCCESSFUL)
- Snackbar.make(fileNameEditText, getString(R.string.export_failed, openpgpEncryptionExportStatus), Snackbar.LENGTH_INDEFINITE).show()
+ Snackbar.make(settingsFileNameEditText, getString(R.string.export_failed, openpgpEncryptionExportStatus), Snackbar.LENGTH_INDEFINITE).show()
// Create an encryption intent for OpenKeychain.
val openKeychainEncryptIntent = Intent("org.sufficientlysecure.keychain.action.ENCRYPT_DATA")
openKeychainEncryptActivityResultLauncher.launch(openKeychainEncryptIntent)
} catch (exception: Exception) {
// Display a snackbar with the exception.
- Snackbar.make(fileNameEditText, getString(R.string.export_failed, exception), Snackbar.LENGTH_INDEFINITE).show()
+ Snackbar.make(settingsFileNameEditText, getString(R.string.export_failed, exception), Snackbar.LENGTH_INDEFINITE).show()
}
}
}
// Scroll to the new tab position if moving to the new tab.
if (moveToTab)
- tabLayout.post { tabLayout.setScrollPosition(newTabPosition, 0F, false, false) }
+ tabLayout.post {
+ tabLayout.setScrollPosition(newTabPosition, 0F, false, false)
+ }
// Show the app bar if it is at the bottom of the screen and the new tab is taking focus.
if (bottomAppBar && moveToTab && appBarLayout.translationY != 0f) {
val databaseId = bookmarksListView.getItemIdAtPosition(i).toInt()
// Move the bookmark down one slot.
- bookmarksDatabaseHelper!!.updateDisplayOrder(databaseId, i + 1)
+ bookmarksDatabaseHelper!!.updateDisplayOrder(databaseId, displayOrder = i + 1)
}
// Create the folder, which will be placed at the top of the list view.
- bookmarksDatabaseHelper!!.createFolder(folderNameString, currentBookmarksFolderId, folderIconByteArray)
+ bookmarksDatabaseHelper!!.createFolder(folderNameString, currentBookmarksFolderId, displayOrder = 0, folderIconByteArray)
// Update the bookmarks cursor with the current contents of this folder.
bookmarksCursor = bookmarksDatabaseHelper!!.getBookmarksByDisplayOrder(currentBookmarksFolderId)
// Request focus for the URL text box.
urlEditText.requestFocus()
- // Create a display keyboard handler.
- val displayKeyboardHandler = Handler(Looper.getMainLooper())
-
- // Create a display keyboard runnable.
- val displayKeyboardRunnable = Runnable {
- // Display the keyboard.
+ // Display the keyboard once the tab layout has settled.
+ tabLayout.post {
inputMethodManager.showSoftInput(urlEditText, 0)
}
-
- // Display the keyboard after 100 milliseconds, which leaves enough time for the tab to transition.
- displayKeyboardHandler.postDelayed(displayKeyboardRunnable, 100)
}
}
}
urlRelativeLayout.background = AppCompatResources.getDrawable(this, R.color.transparent)
}
} catch (exception: Exception) {
- // Try again in 100 milliseconds if the WebView has not yet been populated.
+ // Try again in 50 milliseconds if the WebView has not yet been populated.
// Create a handler to set the current WebView.
val setCurrentWebViewHandler = Handler(Looper.getMainLooper())
/*
- * Copyright © 2016-2023 Soren Stoutner <soren@stoutner.com>.
+ * Copyright 2016-2023 Soren Stoutner <soren@stoutner.com>.
*
* This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
*
// Update the enabled status of the save button.
saveButton.isEnabled = iconChanged || nameChanged || urlChanged
}
-}
\ No newline at end of file
+}
return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE", null)
}
+ // Get a cursor with just the database IDs of all the bookmarks and folders. This is useful for counting the number of bookmarks imported.
+ val allBookmarkAndFolderIds: Cursor
+ get() {
+ // Get a readable database handle.
+ val bookmarksDatabase = this.readableDatabase
+
+ // Return a cursor with all the database IDs. The cursor cannot be closed because it is used in the parent activity.
+ return bookmarksDatabase.rawQuery("SELECT $ID FROM $BOOKMARKS_TABLE", null)
+ }
+
// Get a cursor for all bookmarks and folders ordered by display order.
val allBookmarksByDisplayOrder: Cursor
get() {
}
// Create a folder.
- fun createFolder(folderName: String, parentFolderId: Long, favoriteIcon: ByteArray) {
+ fun createFolder(folderName: String, parentFolderId: Long, displayOrder: Int, favoriteIcon: ByteArray): Long {
// Create a bookmark folder content values.
val bookmarkFolderContentValues = ContentValues()
+ // Generate the folder ID.
+ val folderId = generateFolderId()
+
// The ID is created automatically. Folders are always created at the top of the list.
bookmarkFolderContentValues.put(BOOKMARK_NAME, folderName)
bookmarkFolderContentValues.put(PARENT_FOLDER_ID, parentFolderId)
- bookmarkFolderContentValues.put(DISPLAY_ORDER, 0)
+ bookmarkFolderContentValues.put(DISPLAY_ORDER, displayOrder)
bookmarkFolderContentValues.put(IS_FOLDER, true)
- bookmarkFolderContentValues.put(FOLDER_ID, generateFolderId())
+ bookmarkFolderContentValues.put(FOLDER_ID, folderId)
bookmarkFolderContentValues.put(FAVORITE_ICON, favoriteIcon)
// Get a writable database handle.
// Close the database handle.
bookmarksDatabase.close()
+
+ // Return the new folder ID.
+ return folderId
}
// Delete one bookmark.
return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $ID NOT IN ($idsNotToGetStringBuilder)", null)
}
- // Get a cursor with just database ID of bookmarks and folders in the specified folder. This is useful for deleting folders with bookmarks that have favorite icons too large to fit in a cursor.
- fun getBookmarkIds(parentFolderId: Long): Cursor {
+ // Get a cursor with just the database IDs of bookmarks and folders in the specified folder. This is useful for deleting folders with bookmarks that have favorite icons too large to fit in a cursor.
+ fun getBookmarkAndFolderIds(parentFolderId: Long): Cursor {
// Get a readable database handle.
val bookmarksDatabase = this.readableDatabase
--- /dev/null
+/*
+ * Copyright 2023 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+ *
+ * Privacy Browser Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser Android. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.helpers
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.drawable.BitmapDrawable
+import android.net.Uri
+import android.util.Base64
+import android.widget.ScrollView
+
+import androidx.appcompat.content.res.AppCompatResources
+
+import com.google.android.material.snackbar.Snackbar
+import com.stoutner.privacybrowser.BuildConfig
+
+import com.stoutner.privacybrowser.R
+
+import java.io.BufferedReader
+import java.io.ByteArrayOutputStream
+import java.io.InputStreamReader
+import java.lang.Exception
+import java.nio.charset.StandardCharsets
+
+class ImportExportBookmarksHelper {
+ // Define the class variables.
+ private var bookmarksAndFolderExported = 0
+
+ fun importBookmarks(fileNameString: String, context: Context, scrollView: ScrollView) {
+ try {
+ // Get an input stream for the file name.
+ val inputStream = context.contentResolver.openInputStream(Uri.parse(fileNameString))!!
+
+ // Load the bookmarks input stream into a buffered reader.
+ val bufferedReader = BufferedReader(InputStreamReader(inputStream))
+
+ // Instantiate the bookmarks database helper.
+ val bookmarksDatabaseHelper = BookmarksDatabaseHelper(context)
+
+ // Get the default icon drawables.
+ val defaultFavoriteIconDrawable = AppCompatResources.getDrawable(context, R.drawable.world)
+ val defaultFolderIconDrawable = AppCompatResources.getDrawable(context, R.drawable.folder_blue_bitmap)
+
+ // Cast the default icon drawables to bitmap drawables.
+ val defaultFavoriteIconBitmapDrawable = (defaultFavoriteIconDrawable as BitmapDrawable?)!!
+ val defaultFolderIconBitmapDrawable = (defaultFolderIconDrawable as BitmapDrawable)
+
+ // Get the default icon bitmaps.
+ val defaultFavoriteIconBitmap = defaultFavoriteIconBitmapDrawable.bitmap
+ val defaultFolderIconBitmap = defaultFolderIconBitmapDrawable.bitmap
+
+ // Create the default icon byte array output streams.
+ val defaultFavoriteIconByteArrayOutputStream = ByteArrayOutputStream()
+ val defaultFolderIconByteArrayOutputStream = ByteArrayOutputStream()
+
+ // Convert the default icon bitmaps to byte array streams. `0` is for lossless compression (the only option for a PNG).
+ defaultFavoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, defaultFavoriteIconByteArrayOutputStream)
+ defaultFolderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, defaultFolderIconByteArrayOutputStream)
+
+ // Convert the icon byte array streams to byte array stream to byte arrays.
+ val defaultFavoriteIconByteArray = defaultFavoriteIconByteArrayOutputStream.toByteArray()
+ val defaultFolderIconByteArray = defaultFolderIconByteArrayOutputStream.toByteArray()
+
+ // Get a cursor with all the bookmarks.
+ val initialNumberOfBookmarksAndFoldersCursor = bookmarksDatabaseHelper.allBookmarkAndFolderIds
+
+ // Get an initial count of the folders and bookmarks.
+ val initialNumberOfFoldersAndBookmarks = initialNumberOfBookmarksAndFoldersCursor.count
+
+ // Close the cursor.
+ initialNumberOfBookmarksAndFoldersCursor.close()
+
+ // Get a cursor with the contents of the home folder.
+ val homeFolderContentCursor = bookmarksDatabaseHelper.getBookmarkAndFolderIds(0L)
+
+ // Initialize the variables.
+ var parentFolderId = 0L
+ var displayOrder = homeFolderContentCursor.count
+
+ // Close the cursor.
+ homeFolderContentCursor.close()
+
+ // Parse the bookmarks.
+ bufferedReader.forEachLine {
+ // Trim the string.
+ var line = it.trimStart()
+
+ // Only process interesting lines.
+ if (line.startsWith("<DT>")) { // This is a bookmark or a folder.
+ // Remove the initial `<DT>`
+ line = line.substring(4)
+
+ // Check to see if this is a bookmark or a folder.
+ if (line.contains("HREF=\"")) { // This is a bookmark.
+ // Remove the text before the bookmark name.
+ var bookmarkName = line.substring(line.indexOf(">") + 1)
+
+ // Remove the text after the bookmark name.
+ bookmarkName = bookmarkName.substring(0, bookmarkName.indexOf("<"))
+
+ // Remove the text before the bookmark URL.
+ var bookmarkUrl = line.substring(line.indexOf("HREF=\"") + 6)
+
+ // Remove the text after the bookmark URL.
+ bookmarkUrl = bookmarkUrl.substring(0, bookmarkUrl.indexOf("\""))
+
+ // Initialize the favorite icon string.
+ var favoriteIconString = ""
+
+ // Populate the favorite icon string.
+ if (line.contains("ICON=\"data:image/png;base64,")) { // The `ICON` attribute contains a Base64 encoded icon.
+ // Remove the text before the icon string.
+ favoriteIconString = line.substring(line.indexOf("ICON=\"data:image/png;base64,") + 28)
+
+ // Remove the text after the icon string.
+ favoriteIconString = favoriteIconString.substring(0, favoriteIconString.indexOf("\""))
+ } else if (line.contains("ICON_URI=\"data:image/png;base64,")) { // The `ICON_URI` attribute contains a Base64 encoded icon.
+ // Remove the text before the icon string.
+ favoriteIconString = line.substring(line.indexOf("ICON_URI=\"data:image/png;base64,") + 32)
+
+ // Remove the text after the icon string.
+ favoriteIconString = favoriteIconString.substring(0, favoriteIconString.indexOf("\""))
+ }
+
+ // Populate the favorite icon byte array.
+ val favoriteIconByteArray = if (favoriteIconString.isEmpty()) // The favorite icon string is empty. Use the default favorite icon.
+ defaultFavoriteIconByteArray
+ else // The favorite icon string is populated. Decode it to a byte array.
+ Base64.decode(favoriteIconString, Base64.DEFAULT)
+
+ // Add the bookmark.
+ bookmarksDatabaseHelper.createBookmark(bookmarkName, bookmarkUrl, parentFolderId, displayOrder, favoriteIconByteArray)
+
+ // Increment the display order.
+ ++displayOrder
+ } else { // This is a folder. The following lines will be contain in this folder until a `</DL>` is encountered.
+ // Remove the text before the folder name.
+ var folderName = line.substring(line.indexOf(">") + 1)
+
+ // Remove the text after the folder name.
+ folderName = folderName.substring(0, folderName.indexOf("<"))
+
+ // Add the folder and set it as the new parent folder ID.
+ parentFolderId = bookmarksDatabaseHelper.createFolder(folderName, parentFolderId, displayOrder, defaultFolderIconByteArray)
+
+ // Reset the display order.
+ displayOrder = 0
+ }
+ } else if (line.startsWith("</DL>")) { // This is the end of a folder's contents.
+ // Reset the parent folder id if it isn't 0.
+ if (parentFolderId != 0L)
+ parentFolderId = bookmarksDatabaseHelper.getParentFolderId(parentFolderId)
+
+ // Get a cursor with the contents of the new parent folder.
+ val folderContentCursor = bookmarksDatabaseHelper.getBookmarkAndFolderIds(parentFolderId)
+
+ // Reset the display order.
+ displayOrder = folderContentCursor.count
+
+ // Close the cursor.
+ folderContentCursor.close()
+ }
+ }
+
+ // Get a cursor with all the bookmarks.
+ val finalNumberOfBookmarksAndFoldersCursor = bookmarksDatabaseHelper.allBookmarkAndFolderIds
+
+ // Get the final count of the folders and bookmarks.
+ val finalNumberOfFoldersAndBookmarks = finalNumberOfBookmarksAndFoldersCursor.count
+
+ // Close the cursor.
+ finalNumberOfBookmarksAndFoldersCursor.close()
+
+ // Close the bookmarks database helper.
+ bookmarksDatabaseHelper.close()
+
+ // Close the input stream.
+ inputStream.close()
+
+ // Display a snackbar with the number of folders and bookmarks imported.
+ Snackbar.make(scrollView, context.getString(R.string.bookmarks_imported, finalNumberOfFoldersAndBookmarks - initialNumberOfFoldersAndBookmarks), Snackbar.LENGTH_LONG).show()
+ } catch (exception: Exception) {
+ // Display a snackbar with the error message.
+ Snackbar.make(scrollView, context.getString(R.string.error, exception), Snackbar.LENGTH_INDEFINITE).show()
+ }
+ }
+
+ fun exportBookmarks(fileNameString: String, context: Context, scrollView: ScrollView) {
+ // Create a bookmarks string builder
+ val bookmarksStringBuilder = StringBuilder()
+
+ // Populate the headers.
+ bookmarksStringBuilder.append("<!DOCTYPE NETSCAPE-Bookmark-file-1>")
+ bookmarksStringBuilder.append("\n\n")
+ bookmarksStringBuilder.append("<!-- These bookmarks were exported from Privacy Browser Android ${BuildConfig.VERSION_NAME}. -->")
+ bookmarksStringBuilder.append("\n\n")
+ bookmarksStringBuilder.append("<meta HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=UTF-8\">")
+ bookmarksStringBuilder.append("\n\n")
+
+ // Begin the bookmarks.
+ bookmarksStringBuilder.append("<DL><p>")
+ bookmarksStringBuilder.append("\n")
+
+ // Instantiate the bookmarks database helper.
+ val bookmarksDatabaseHelper = BookmarksDatabaseHelper(context)
+
+ // Initialize the indent string.
+ val indentString = " "
+
+ // Populate the bookmarks, starting with the home folder.
+ bookmarksStringBuilder.append(populateBookmarks(bookmarksDatabaseHelper, 0L, indentString))
+
+ // End the bookmarks.
+ bookmarksStringBuilder.append("</DL>")
+ bookmarksStringBuilder.append("\n")
+
+ try {
+ // Get an output stream for the file name, truncating any existing content.
+ val outputStream = context.contentResolver.openOutputStream(Uri.parse(fileNameString), "wt")!!
+
+ // Write the bookmarks string to the output stream.
+ outputStream.write(bookmarksStringBuilder.toString().toByteArray(StandardCharsets.UTF_8))
+
+ // Close the output stream.
+ outputStream.close()
+
+ // Display a snackbar with the number of folders and bookmarks exported.
+ Snackbar.make(scrollView, context.getString(R.string.bookmarks_exported, bookmarksAndFolderExported), Snackbar.LENGTH_LONG).show()
+ } catch (exception: Exception) {
+ // Display a snackbar with the error message.
+ Snackbar.make(scrollView, context.getString(R.string.error, exception), Snackbar.LENGTH_INDEFINITE).show()
+ }
+
+ // Close the bookmarks database helper.
+ bookmarksDatabaseHelper.close()
+ }
+
+ private fun populateBookmarks(bookmarksDatabaseHelper: BookmarksDatabaseHelper, folderId: Long, indentString: String): String {
+ // Create a bookmarks string builder.
+ val bookmarksStringBuilder = StringBuilder()
+
+ // Get all the bookmarks in the current folder.
+ val bookmarksCursor = bookmarksDatabaseHelper.getBookmarks(folderId)
+
+ // Process each bookmark.
+ while (bookmarksCursor.moveToNext()) {
+ if (bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(IS_FOLDER)) == 1) { // This is a folder.
+ // Export the folder.
+ bookmarksStringBuilder.append(indentString)
+ bookmarksStringBuilder.append("<DT><H3>")
+ bookmarksStringBuilder.append(bookmarksCursor.getString(bookmarksCursor.getColumnIndexOrThrow(BOOKMARK_NAME)))
+ bookmarksStringBuilder.append("</H3>")
+ bookmarksStringBuilder.append("\n")
+
+ // Begin the folder contents.
+ bookmarksStringBuilder.append(indentString)
+ bookmarksStringBuilder.append("<DL><p>")
+ bookmarksStringBuilder.append("\n")
+
+ // Populate the folder contents.
+ bookmarksStringBuilder.append(populateBookmarks(bookmarksDatabaseHelper, bookmarksCursor.getLong(bookmarksCursor.getColumnIndexOrThrow(FOLDER_ID)), " $indentString"))
+
+ // End the folder contents.
+ bookmarksStringBuilder.append(indentString)
+ bookmarksStringBuilder.append("</DL><p>")
+ bookmarksStringBuilder.append("\n")
+
+ // Increment the bookmarks and folders exported counter.
+ ++bookmarksAndFolderExported
+ } else { // This is a bookmark.
+ // Export the bookmark.
+ bookmarksStringBuilder.append(indentString)
+ bookmarksStringBuilder.append("<DT><A HREF=\"")
+ bookmarksStringBuilder.append(bookmarksCursor.getString(bookmarksCursor.getColumnIndexOrThrow(BOOKMARK_URL)))
+ bookmarksStringBuilder.append("\" ICON=\"data:image/png;base64,")
+ bookmarksStringBuilder.append(Base64.encodeToString(bookmarksCursor.getBlob(bookmarksCursor.getColumnIndexOrThrow(FAVORITE_ICON)), Base64.NO_WRAP))
+ bookmarksStringBuilder.append("\">")
+ bookmarksStringBuilder.append(bookmarksCursor.getString(bookmarksCursor.getColumnIndexOrThrow(BOOKMARK_NAME)))
+ bookmarksStringBuilder.append("</A>")
+ bookmarksStringBuilder.append("\n")
+
+ // Increment the bookmarks and folders exported counter.
+ ++bookmarksAndFolderExported
+ }
+ }
+
+ // Return the bookmarks string.
+ return bookmarksStringBuilder.toString()
+ }
+}
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright © 2018-2022 Soren Stoutner <soren@stoutner.com>.
+ Copyright 2018-2023 Soren Stoutner <soren@stoutner.com>.
This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
android:layout_height="match_parent"
android:layout_width="match_parent" >
- <!-- the linear layout with `orientation="vertical"` keeps the content above the app bar layout. `app:layout_dodgeInsetEdges="bottom"` as a child of a coordinator layout moves the view above snackbars.-->
+ <!-- The linear layout with `orientation="vertical"` keeps the content above the app bar layout. -->
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
- android:orientation="vertical"
- app:layout_dodgeInsetEdges="bottom" >
+ android:orientation="vertical" >
<ScrollView
+ android:id="@+id/scrollview"
android:layout_height="0dp"
android:layout_width="match_parent"
android:layout_weight="1" >
- <!-- Align the cards vertically. -->
+ <!-- Align the content vertically. -->
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:orientation="vertical" >
+ android:orientation="vertical"
+ android:layout_marginTop="10dp" >
+
+ <!-- Settings and Bookmarks. -->
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:gravity="center_horizontal"
+ android:text="@string/bookmarks_and_settings"
+ android:textSize="30sp"
+ android:textStyle="bold"
+ android:textColor="?android:textColorPrimary" />
+
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:gravity="center_horizontal"
+ android:layout_marginBottom="10dp"
+ android:text="@string/sqlite_database_format"
+ android:textSize="14sp" />
<!-- The encryption card. -->
<androidx.cardview.widget.CardView
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:layout_marginTop="10dp"
- android:layout_marginBottom="5dp"
android:layout_marginStart="10dp"
- android:layout_marginEnd="10dp" >
+ android:layout_marginEnd="10dp"
+ android:layout_marginBottom="5dp" >
<!-- Align the contents of the card vertically. -->
<LinearLayout
</LinearLayout>
</androidx.cardview.widget.CardView>
- <!-- The file location card. -->
+ <!-- The bookmarks and settings file location card. -->
<androidx.cardview.widget.CardView
- android:id="@+id/file_location_cardview"
+ android:id="@+id/settings_file_location_cardview"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_marginTop="5dp"
android:orientation="horizontal" >
<RadioButton
- android:id="@+id/import_radiobutton"
+ android:id="@+id/settings_import_radiobutton"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="@string/import_button"
android:layout_marginEnd="10dp"
- android:onClick="onClickRadioButton" />
+ android:onClick="onClickSettingsRadioButton" />
<RadioButton
- android:id="@+id/export_radiobutton"
+ android:id="@+id/settings_export_radiobutton"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="@string/export"
- android:onClick="onClickRadioButton" />
+ android:onClick="onClickSettingsRadioButton" />
</RadioGroup>
<!-- Align the edit text and the select file button horizontally. -->
<LinearLayout
- android:id="@+id/file_name_linearlayout"
+ android:id="@+id/settings_file_name_linearlayout"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:orientation="horizontal"
<!-- `android:inputType="textUri" disables spell check and places an `/` on the main keyboard. -->
<com.google.android.material.textfield.TextInputEditText
- android:id="@+id/file_name_edittext"
+ android:id="@+id/settings_file_name_edittext"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:hint="@string/file_name"
</com.google.android.material.textfield.TextInputLayout>
<Button
- android:id="@+id/browse_button"
+ android:id="@+id/settings_browse_button"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/browse"
- android:onClick="browse" />
+ android:onClick="settingsBrowse" />
</LinearLayout>
<!-- OpenKeychain import instructions -->
android:textAlignment="center" />
<Button
- android:id="@+id/import_export_button"
+ android:id="@+id/settings_import_export_button"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="10dp"
+ android:text="@string/import_button"
+ android:textSize="18sp"
+ android:onClick="importExportSettings"
+ app:backgroundTint="@color/button_background_selector"
+ android:textColor="@color/button_text_selector" />
+ </LinearLayout>
+ </androidx.cardview.widget.CardView>
+
+ <!-- Bookmarks. -->
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:gravity="center_horizontal"
+ android:layout_marginTop="30dp"
+ android:text="@string/bookmarks"
+ android:textSize="30sp"
+ android:textStyle="bold"
+ android:textColor="?android:textColorPrimary" />
+
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:gravity="center_horizontal"
+ android:layout_marginBottom="10dp"
+ android:text="@string/html_format"
+ android:textSize="14sp" />
+
+ <!-- The bookmarks file location card. -->
+ <androidx.cardview.widget.CardView
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginStart="10dp"
+ android:layout_marginEnd="10dp"
+ android:layout_marginBottom="20dp" >
+
+ <!-- Align the contents of the card vertically. -->
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="20dp"
+ android:layout_marginStart="10dp"
+ android:layout_marginEnd="10dp" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="6dp"
+ android:text="@string/file_location"
+ android:textSize="25sp"
+ android:textStyle="bold"
+ android:textColor="?colorAccent" />
+
+ <RadioGroup
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:orientation="horizontal" >
+
+ <RadioButton
+ android:id="@+id/bookmarks_import_radiobutton"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/import_button"
+ android:layout_marginEnd="10dp"
+ android:onClick="onClickBookmarksRadioButton" />
+
+ <RadioButton
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/export"
+ android:onClick="onClickBookmarksRadioButton" />
+ </RadioGroup>
+
+ <!-- Align the edit text and the select file button horizontally. -->
+ <LinearLayout
+ android:id="@+id/bookmarks_file_name_linearlayout"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:orientation="horizontal"
+ android:layout_marginTop="10dp">
+
+ <!-- The text input layout makes the hint float above the edit text. -->
+ <com.google.android.material.textfield.TextInputLayout
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:layout_weight="1" >
+
+ <!-- `android:inputType="textUri" disables spell check and places an `/` on the main keyboard. -->
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/bookmarks_file_name_edittext"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:hint="@string/file_name"
+ android:inputType="textMultiLine|textUri" />
+ </com.google.android.material.textfield.TextInputLayout>
+
+ <Button
+ android:id="@+id/bookmarks_browse_button"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:text="@string/browse"
+ android:onClick="bookmarksBrowse" />
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/bookmarks_import_export_button"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:text="@string/import_button"
android:textSize="18sp"
- android:onClick="importExport"
+ android:onClick="importExportBookmarks"
app:backgroundTint="@color/button_background_selector"
android:textColor="@color/button_text_selector" />
</LinearLayout>
android:layout_width="match_parent" />
</com.google.android.material.appbar.AppBarLayout>
</LinearLayout>
-</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright © 2018-2022 Soren Stoutner <soren@stoutner.com>.
+ Copyright 2018-2023 Soren Stoutner <soren@stoutner.com>.
This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
android:layout_height="match_parent"
android:layout_width="match_parent" >
- <!-- the linear layout with `orientation="vertical"` moves the content below the app bar layout. -->
+ <!-- The linear layout with `orientation="vertical"` moves the content below the app bar layout. -->
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
</com.google.android.material.appbar.AppBarLayout>
<ScrollView
+ android:id="@+id/scrollview"
android:layout_height="match_parent"
android:layout_width="match_parent" >
- <!-- Align the cards vertically. -->
+ <!-- Align the content vertically. -->
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:orientation="vertical" >
+ android:orientation="vertical"
+ android:layout_marginTop="10dp" >
+
+ <!-- Settings and Bookmarks. -->
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:gravity="center_horizontal"
+ android:text="@string/bookmarks_and_settings"
+ android:textSize="30sp"
+ android:textStyle="bold"
+ android:textColor="?android:textColorPrimary" />
+
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:gravity="center_horizontal"
+ android:layout_marginBottom="10dp"
+ android:text="@string/sqlite_database_format"
+ android:textSize="14sp" />
<!-- The encryption card. -->
<androidx.cardview.widget.CardView
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:layout_marginTop="10dp"
- android:layout_marginBottom="5dp"
android:layout_marginStart="10dp"
- android:layout_marginEnd="10dp" >
+ android:layout_marginEnd="10dp"
+ android:layout_marginBottom="5dp" >
<!-- Align the contents of the card vertically. -->
<LinearLayout
</LinearLayout>
</androidx.cardview.widget.CardView>
- <!-- The file location card. -->
+ <!-- The bookmarks and settings file location card. -->
<androidx.cardview.widget.CardView
- android:id="@+id/file_location_cardview"
+ android:id="@+id/settings_file_location_cardview"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_marginTop="5dp"
android:orientation="horizontal" >
<RadioButton
- android:id="@+id/import_radiobutton"
+ android:id="@+id/settings_import_radiobutton"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="@string/import_button"
android:layout_marginEnd="10dp"
- android:onClick="onClickRadioButton" />
+ android:onClick="onClickSettingsRadioButton" />
<RadioButton
- android:id="@+id/export_radiobutton"
+ android:id="@+id/settings_export_radiobutton"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="@string/export"
- android:onClick="onClickRadioButton" />
+ android:onClick="onClickSettingsRadioButton" />
</RadioGroup>
<!-- Align the edit text and the select file button horizontally. -->
<LinearLayout
- android:id="@+id/file_name_linearlayout"
+ android:id="@+id/settings_file_name_linearlayout"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:orientation="horizontal"
<!-- `android:inputType="textUri" disables spell check and places an `/` on the main keyboard. -->
<com.google.android.material.textfield.TextInputEditText
- android:id="@+id/file_name_edittext"
+ android:id="@+id/settings_file_name_edittext"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:hint="@string/file_name"
</com.google.android.material.textfield.TextInputLayout>
<Button
- android:id="@+id/browse_button"
+ android:id="@+id/settings_browse_button"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/browse"
- android:onClick="browse" />
+ android:onClick="settingsBrowse" />
</LinearLayout>
<!-- OpenKeychain import instructions -->
android:textAlignment="center" />
<Button
- android:id="@+id/import_export_button"
+ android:id="@+id/settings_import_export_button"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="10dp"
+ android:text="@string/import_button"
+ android:textSize="18sp"
+ android:onClick="importExportSettings"
+ app:backgroundTint="@color/button_background_selector"
+ android:textColor="@color/button_text_selector" />
+ </LinearLayout>
+ </androidx.cardview.widget.CardView>
+
+ <!-- Bookmarks. -->
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:gravity="center_horizontal"
+ android:layout_marginTop="30dp"
+ android:text="@string/bookmarks"
+ android:textSize="30sp"
+ android:textStyle="bold"
+ android:textColor="?android:textColorPrimary" />
+
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:gravity="center_horizontal"
+ android:layout_marginBottom="10dp"
+ android:text="@string/html_format"
+ android:textSize="14sp" />
+
+ <!-- The bookmarks file location card. -->
+ <androidx.cardview.widget.CardView
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginStart="10dp"
+ android:layout_marginEnd="10dp"
+ android:layout_marginBottom="20dp" >
+
+ <!-- Align the contents of the card vertically. -->
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="20dp"
+ android:layout_marginStart="10dp"
+ android:layout_marginEnd="10dp" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="6dp"
+ android:text="@string/file_location"
+ android:textSize="25sp"
+ android:textStyle="bold"
+ android:textColor="?colorAccent" />
+
+ <RadioGroup
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:orientation="horizontal" >
+
+ <RadioButton
+ android:id="@+id/bookmarks_import_radiobutton"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/import_button"
+ android:layout_marginEnd="10dp"
+ android:onClick="onClickBookmarksRadioButton" />
+
+ <RadioButton
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/export"
+ android:onClick="onClickBookmarksRadioButton" />
+ </RadioGroup>
+
+ <!-- Align the edit text and the select file button horizontally. -->
+ <LinearLayout
+ android:id="@+id/bookmarks_file_name_linearlayout"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:orientation="horizontal"
+ android:layout_marginTop="10dp">
+
+ <!-- The text input layout makes the hint float above the edit text. -->
+ <com.google.android.material.textfield.TextInputLayout
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:layout_weight="1" >
+
+ <!-- `android:inputType="textUri" disables spell check and places an `/` on the main keyboard. -->
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/bookmarks_file_name_edittext"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:hint="@string/file_name"
+ android:inputType="textMultiLine|textUri" />
+ </com.google.android.material.textfield.TextInputLayout>
+
+ <Button
+ android:id="@+id/bookmarks_browse_button"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:text="@string/browse"
+ android:onClick="bookmarksBrowse" />
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/bookmarks_import_export_button"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:text="@string/import_button"
android:textSize="18sp"
- android:onClick="importExport"
+ android:onClick="importExportBookmarks"
app:backgroundTint="@color/button_background_selector"
android:textColor="@color/button_text_selector" />
</LinearLayout>
</LinearLayout>
</ScrollView>
</LinearLayout>
-</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright © 2018-2022 Soren Stoutner <soren@stoutner.com>.
+ Copyright 2018-2023 Soren Stoutner <soren@stoutner.com>.
This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
android:layout_width="match_parent" />
</com.google.android.material.appbar.AppBarLayout>
</LinearLayout>
-</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright © 2018-2022 Soren Stoutner <soren@stoutner.com>.
+ Copyright 2018-2023 Soren Stoutner <soren@stoutner.com>.
This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
</ScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</LinearLayout>
-</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
<string name="username">Benutzername</string>
<string name="password">Passwort</string>
- <!-- MainWebViewActivity Navigation Menu. -->
+ <!-- Main Navigation Menu. -->
<string name="navigation_drawer">Navigationspanel</string>
<string name="clear_and_exit">Leeren und verlassen</string>
<string name="home">Startseite</string>
<string name="back">Zurück</string>
<string name="forward">Vorwärts</string>
+ <string name="scroll_to_bottom">Nach unten scrollen</string>
+ <string name="scroll_to_top">Nach oben scrollen</string>
<string name="history">Verlauf</string>
<string name="clear_history">Verlauf löschen</string>
<string name="open">Öffnen</string>
<string name="user_agent_firefox_on_windows">Firefox auf Windows</string>
<string name="user_agent_chrome_on_windows">Chrome auf Windows</string>
<string name="user_agent_edge_on_windows">Edge auf Windows</string>
- <string name="user_agent_internet_explorer_on_windows">Internet Explorer auf Windows</string>
<string name="user_agent_safari_on_macos">Safari auf macOS</string>
<string name="user_agent_custom">Eigener</string>
<string name="swipe_to_refresh_options_menu">Herunterziehen zum aktualisieren</string>
<string name="blocked">blockiert</string>
<string name="request_blocked">%1$d. blockiert</string>
<string name="blocked_plural">blockiert</string>
+ <string name="filterlist">Filter-Liste</string>
<string name="sublist">Unterliste</string>
+ <string name="main_allowlist">Haupt-Positiv-Liste</string>
+ <string name="final_allowlist">Finale Positiv-Liste</string>
+ <string name="domain_allowlist">Domain-Positiv-Liste</string>
+ <string name="domain_initial_allowlist">Domain-Beginn-Positiv-Liste</string>
+ <string name="domain_final_allowlist">Domain-Ende-Positiv-Liste</string>
+ <string name="third_party_allowlist">Drittanbieter-Positiv-Liste</string>
+ <string name="third_party_domain_allowlist">Drittanbieter-Domain-Positiv-Liste</string>
+ <string name="third_party_domain_initial_allowlist">Drittanbieter-Domain-Beginn-Positiv-Liste</string>
+ <string name="main_blocklist">Haupt-Negativ-Liste</string>
+ <string name="initial_blocklist">Beginn-Negativ-Liste</string>
+ <string name="final_blocklist">Finale Negativ-Liste</string>
+ <string name="domain_blocklist">Domain-Negativ-Liste</string>
+ <string name="domain_initial_blocklist">Domain-Beginn-Negativ-Liste</string>
+ <string name="domain_final_blocklist">Domain-Ende-Negativ-Liste</string>
+ <string name="domain_regular_expression_blocklist">Negativ-Liste für reguläre Ausdrücke in Domains</string>
+ <string name="third_party_blocklist">Drittanbieter-Negativ-Liste</string>
+ <string name="third_party_initial_blocklist">Drittanbieter-Beginn-Negativ-Liste</string>
+ <string name="third_party_domain_blocklist">Drittanbieter-Domain-Negativ-Liste</string>
+ <string name="third_party_domain_initial_blocklist">Drittanbieter-Domain-Beginn-Negativ-Liste</string>
+ <string name="third_party_regular_expression_blocklist">Negativ-Liste für reguläre Ausdrücke in Drittanbietern</string>
+ <string name="third_party_domain_regular_expression_blocklist">Negativ-Liste für reguläre Ausdrücke in Drittanbieter-Domains</string>
+ <string name="regular_expression_blocklist">Negativ-Liste für reguläre Ausdrücke</string>
+ <string name="filterlist_entries">Filter-Listen-Einträge</string>
+ <string name="filterlist_original_entry">Filter-Listen-Original-Eintrag</string>
<!-- Domains. -->
<string name="domains">Domains</string>
Im Incognito-Modus schließt \'Zurück\'-Button den aktive Tab (oder die App, wenn nur ein Tab geöffnet wurde).</string>
<string name="allow_screenshots">Screenshots zulassen</string>
<string name="allow_screenshots_summary">Screenshots, Bildschirmvideos und Anzeige auf unsicheren Bildschirmen zulassen. Eine Änderung dieser Einstellung startet Privacy Browser neu.</string>
+ <string name="filterlists">Filter-Listen</string>
<string name="easylist">EasyList</string>
+ <string name="easylist_summary">Haupt-Filter-Liste für Werbung.</string>
<string name="easyprivacy">EasyPrivacy</string>
+ <string name="easyprivacy_summary">Haupt-Filter-Liste für Tracker.</string>
<string name="fanboys_annoyance_list">Fanboy’s Annoyance Sperrliste</string>
+ <string name="fanboys_annoyance_list_summary">Filtert störende Popups und Links. Beinhaltet Fanboy’s Social Blocking Lists.</string>
<string name="fanboys_social_blocking_list">Fanboy’s Social Blocking Sperrliste</string>
+ <string name="fanboys_social_blocking_list_summary">Filtert Social-Media-Inhalte von Drittanbietern.</string>
<string name="ultralist">UltraList</string>
+ <string name="ultralist_summary">UltraList filtert Werbung, die von EasyList nicht gefiltert wird, da dies Internet-Seiten beeinträchtigen könnte.</string>
<string name="ultraprivacy">UltraPrivacy</string>
+ <string name="ultraprivacy_summary">UltraPrivacy filtert Tracker, die von EasyPrivacy nicht gefiltert werden, da dies Internet-Seiten beeinträchtigen könnte.</string>
<string name="block_all_third_party_requests">Alle Zugriffe auf Dritt-Anbieter-Inhalte blockieren</string>
<string name="block_all_third_party_requests_summary">Alle Zugriffe auf Dritt-Anbieter-Inhalte zu blockieren verbessert die Privatsphäre, kann jedoch Webseiten verunstalten.</string>
<string name="url_modification">URL-Bereinigung</string>
<string name="username">Usuario</string>
<string name="password">Contraseña</string>
- <!-- MainWebViewActivity Navigation Menu. -->
+ <!-- Main Navigation Menu. -->
<string name="navigation_drawer">Caja de navegación</string>
<string name="clear_and_exit">Borrar y salir</string>
<string name="home">Inicio</string>
<string name="back">Atrás</string>
<string name="forward">Adelante</string>
+ <string name="scroll_to_bottom">Desplazar hacia abajo</string>
+ <string name="scroll_to_top">Desplazar hacia arriba</string>
<string name="history">Historial</string>
<string name="clear_history">Borrar historial</string>
<string name="open">Abrir</string>
<string name="user_agent_firefox_on_windows">Firefox en Windows</string>
<string name="user_agent_chrome_on_windows">Chrome en Windows</string>
<string name="user_agent_edge_on_windows">Edge en Windows</string>
- <string name="user_agent_internet_explorer_on_windows">Internet Explorer en Windows</string>
<string name="user_agent_safari_on_macos">Safari en macOS</string>
<string name="user_agent_custom">Personalizado</string>
<string name="swipe_to_refresh_options_menu">Deslizar para actualizar</string>
<string name="username">Nom d\'utilisateur</string>
<string name="password">Mot de passe</string>
- <!-- MainWebViewActivity Navigation Menu. -->
+ <!-- Main Navigation Menu. -->
<string name="navigation_drawer">Panneau de navigation</string>
<string name="clear_and_exit">Nettoyer et quitter</string>
<string name="home">Accueil</string>
<string name="user_agent_firefox_on_windows">Firefox sous Windows</string>
<string name="user_agent_chrome_on_windows">Chrome sous Windows</string>
<string name="user_agent_edge_on_windows">Edge sous Windows</string>
- <string name="user_agent_internet_explorer_on_windows">Internet Explorer sous Windows</string>
<string name="user_agent_safari_on_macos">Safari sous macOS</string>
<string name="user_agent_custom">Personnalisé</string>
<string name="swipe_to_refresh_options_menu">Glisser pour actualiser</string>
<string name="unencrypted_website">Sito non criptato</string>
<string name="no_ssl_certificate">La comunicazione con questo sito web non è criptata. Questo consente a terze parti la possibilità di intercettare le informazioni scambiate,
di tracciare la navigazione e di inviare contenuto maligno.</string>
+ <string name="content_url">Content URL</string>
+ <string name="content_url_message">Le Content URL sono dati caricati sul proprio dispositivo da altre app.</string>
<string name="ssl_certificate">Certificato SSL</string>
<string name="close">Chiudi</string>
<string name="domain">Dominio</string>
<string name="username">Nome utente</string>
<string name="password">Password</string>
- <!-- MainWebViewActivity Navigation Menu. -->
+ <!-- Main Navigation Menu. -->
<string name="navigation_drawer">Menù di navigazione</string>
<string name="clear_and_exit">Elimina dati ed esci</string>
<string name="home">Home</string>
<string name="back">Indietro</string>
<string name="forward">Avanti</string>
+ <string name="scroll_to_bottom">Vai alla fine</string>
+ <string name="scroll_to_top">Vai all\'inizio</string>
<string name="history">Cronologia</string>
<string name="clear_history">Elimina cronologia</string>
<string name="open">Apri</string>
<string name="user_agent_firefox_on_windows">Firefox su Windows</string>
<string name="user_agent_chrome_on_windows">Chrome su Windows</string>
<string name="user_agent_edge_on_windows">Edge su Windows</string>
- <string name="user_agent_internet_explorer_on_windows">Internet Explorer su Windows</string>
<string name="user_agent_safari_on_macos">Safari su macOS</string>
<string name="user_agent_custom">Personalizzato</string>
<string name="swipe_to_refresh_options_menu">Swipe per aggiornare</string>
<string name="username">Nome do usuário</string>
<string name="password">Senha</string>
- <!-- MainWebViewActivity Navigation Menu. -->
+ <!-- Main Navigation Menu. -->
<string name="navigation_drawer">Caixa de Navegação</string>
<string name="clear_and_exit">Limpar e fechar</string>
<string name="home">Início</string>
<string name="user_agent_firefox_on_windows">Firefox para Windows</string>
<string name="user_agent_chrome_on_windows">Chrome para Windows</string>
<string name="user_agent_edge_on_windows">Edge para Windows</string>
- <string name="user_agent_internet_explorer_on_windows">Internet Explorer para Windows</string>
<string name="user_agent_safari_on_macos">Safari para macOS</string>
<string name="user_agent_custom">Personalizado</string>
<string name="swipe_to_refresh_options_menu">Deslize para atualizar</string>
<string name="username">Имя пользователя</string>
<string name="password">Пароль</string>
- <!-- MainWebViewActivity Navigation Menu. -->
+ <!-- Main Navigation Menu. -->
<string name="navigation_drawer">Навигационная панель</string>
<string name="clear_and_exit">Очистить и выйти</string>
<string name="home">Домой</string>
<string name="back">Назад</string>
<string name="forward">Вперед</string>
+ <string name="scroll_to_bottom">Прокрутить вниз</string>
+ <string name="scroll_to_top">Прокрутить вверх</string>
<string name="history">История</string>
<string name="clear_history">Очистить историю</string>
<string name="open">Открыть</string>
<string name="user_agent_firefox_on_windows">Firefox на Windows</string>
<string name="user_agent_chrome_on_windows">Chrome на Windows</string>
<string name="user_agent_edge_on_windows">Edge на Windows</string>
- <string name="user_agent_internet_explorer_on_windows">Internet Explorer на Windows</string>
<string name="user_agent_safari_on_macos">Safari на macOS</string>
<string name="user_agent_custom">Настраиваемый</string>
<string name="swipe_to_refresh_options_menu">Потянуть для обновления</string>
<string name="username">Kullanıcı Adı</string>
<string name="password">Şifre</string>
- <!-- MainWebViewActivity Navigation Menu. -->
+ <!-- Main Navigation Menu. -->
<string name="navigation_drawer">Gezinti Menüsü</string>
<string name="clear_and_exit">Temizle ve Çık</string>
<string name="home">Anasayfa</string>
<string name="user_agent_firefox_on_windows">Firefox - Windows</string>
<string name="user_agent_chrome_on_windows">Chrome - Windows</string>
<string name="user_agent_edge_on_windows">Microsoft Edge - Windows</string>
- <string name="user_agent_internet_explorer_on_windows">İnternet Explorer - Windows</string>
<string name="user_agent_safari_on_macos">Safari - macOS</string>
<string name="user_agent_custom">Özel</string>
<string name="swipe_to_refresh_options_menu">Yenilemek için kaydır</string>
<string name="username">用户名</string>
<string name="password">密码</string>
- <!-- MainWebViewActivity Navigation Menu. -->
+ <!-- Main Navigation Menu. -->
<string name="navigation_drawer">导航栏</string>
<string name="clear_and_exit">清除并退出</string>
<string name="home">主页</string>
<string name="user_agent_firefox_on_windows">Firefox on Windows</string>
<string name="user_agent_chrome_on_windows">Chrome on Windows</string>
<string name="user_agent_edge_on_windows">Edge on Windows</string>
- <string name="user_agent_internet_explorer_on_windows">Internet Explorer on Windows</string>
<string name="user_agent_safari_on_macos">Safari on macOS</string>
<string name="user_agent_custom">自定义</string>
<string name="swipe_to_refresh_options_menu">下拉刷新</string>
<string name="username">Username</string>
<string name="password">Password</string>
- <!-- MainWebViewActivity Navigation Menu. -->
+ <!-- Main Navigation Menu. -->
<string name="navigation_drawer">Navigation Drawer</string>
<string name="clear_and_exit">Clear and Exit</string>
<string name="home">Home</string>
<!-- Import/Export. Android removes double spaces, but extra spaces can be manually specified with the Unicode `\u0020` formatting.
The `%1$*` code inserts variables into the displayed text and should be preserved in translation. <https://developer.android.com/reference/kotlin/java/util/Formatter> -->
+ <string name="bookmarks_and_settings">Bookmarks and Settings</string>
+ <string name="sqlite_database_format">SQLite database format</string>
+ <string name="html_format">HTML format</string>
<string name="encryption">Encryption</string>
<string-array name="encryption_type">
<item>None</item>
<string name="decrypt">Decrypt</string>
<string name="privacy_browser_settings_pbs">Privacy Browser Android %1$s Settings - Schema %2$d.pbs</string>
<string name="privacy_browser_settings_pbs_aes">Privacy Browser Android %1$s Settings - Schema %2$d.pbs.aes</string>
+ <string name="privacy_browser_bookmarks_html">Privacy Browser Bookmarks.html</string>
<string name="export_successful">Export successful.</string>
<string name="export_failed">Export failed:\u0020 %1$s</string>
<string name="import_failed">Import failed:\u0020 %1$s</string>
+ <string name="bookmarks_imported">%1$d folders and bookmarks imported.</string>
+ <string name="bookmarks_exported">%1$d folders and bookmarks exported.</string>
<!-- Logcat. Android removes double spaces, but extra spaces can be manually specified with the Unicode `\u0020` formatting.
The `%1$s` code inserts variables into the displayed text and should be preserved in translation. <https://developer.android.com/reference/kotlin/java/util/Formatter> -->