Translation 2016 Aaron Gerlach <aaron@gerlach.com>. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
- This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+ 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
<p><svg class="icon"><use href="../shared_images/share.svg#icon"/></svg> share.</p>
<p><svg class="icon"><use href="../shared_images/smartphone.svg#icon"/></svg> smartphone.</p>
<p><svg class="icon"><use href="../shared_images/sort.svg#icon"/></svg> sort.</p>
+ <p><svg class="icon"><use href="../shared_images/sort_by_alpha_rounded_fill0_weight400_grade0_24px.svg#icon"/></svg> sort_<wbr>by_<wbr>alpha_<wbr>rounded_<wbr>fill0_<wbr>weight400_<wbr>grade0_<wbr>24px.</p>
<p><svg class="icon"><use href="../shared_images/style.svg#icon"/></svg> style.</p>
<p><svg class="icon"><use href="../shared_images/subheader_rounded_weight400_grade0_48px.svg#icon"/></svg> subheader_<wbr>rounded_<wbr>weight400_<wbr>grade0_<wbr>48px.</p>
<p><svg class="icon"><use href="../shared_images/tab.svg#icon"/></svg> tab.</p>
<!--
Copyright 2016-2024 Soren Stoutner <soren@stoutner.com>.
- This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+ 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
<p><svg class="icon"><use href="../shared_images/share.svg#icon"/></svg> share.</p>
<p><svg class="icon"><use href="../shared_images/smartphone.svg#icon"/></svg> smartphone.</p>
<p><svg class="icon"><use href="../shared_images/sort.svg#icon"/></svg> sort.</p>
+ <p><svg class="icon"><use href="../shared_images/sort_by_alpha_rounded_fill0_weight400_grade0_24px.svg#icon"/></svg> sort_<wbr>by_<wbr>alpha_<wbr>rounded_<wbr>fill0_<wbr>weight400_<wbr>grade0_<wbr>24px.</p>
<p><svg class="icon"><use href="../shared_images/style.svg#icon"/></svg> style.</p>
<p><svg class="icon"><use href="../shared_images/subheader_rounded_weight400_grade0_48px.svg#icon"/></svg> subheader_<wbr>rounded_<wbr>weight400_<wbr>grade0_<wbr>48px.</p>
<p><svg class="icon"><use href="../shared_images/tab.svg#icon"/></svg> tab.</p>
Translation 2017-2020,2023 Jose A. León. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
- This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+ 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
<p><svg class="icon"><use href="../shared_images/share.svg#icon"/></svg> share.</p>
<p><svg class="icon"><use href="../shared_images/smartphone.svg#icon"/></svg> smartphone.</p>
<p><svg class="icon"><use href="../shared_images/sort.svg#icon"/></svg> sort.</p>
+ <p><svg class="icon"><use href="../shared_images/sort_by_alpha_rounded_fill0_weight400_grade0_24px.svg#icon"/></svg> sort_<wbr>by_<wbr>alpha_<wbr>rounded_<wbr>fill0_<wbr>weight400_<wbr>grade0_<wbr>24px.</p>
<p><svg class="icon"><use href="../shared_images/style.svg#icon"/></svg> style.</p>
<p><svg class="icon"><use href="../shared_images/subheader_rounded_weight400_grade0_48px.svg#icon"/></svg> subheader_<wbr>rounded_<wbr>weight400_<wbr>grade0_<wbr>48px.</p>
<p><svg class="icon"><use href="../shared_images/tab.svg#icon"/></svg> tab.</p>
Translation 2019-2023 Kévin L. <kevinliste@framalistes.org>. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
- This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+ 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
<p><svg class="icon"><use href="../shared_images/share.svg#icon"/></svg> share.</p>
<p><svg class="icon"><use href="../shared_images/smartphone.svg#icon"/></svg> smartphone.</p>
<p><svg class="icon"><use href="../shared_images/sort.svg#icon"/></svg> sort.</p>
+ <p><svg class="icon"><use href="../shared_images/sort_by_alpha_rounded_fill0_weight400_grade0_24px.svg#icon"/></svg> sort_<wbr>by_<wbr>alpha_<wbr>rounded_<wbr>fill0_<wbr>weight400_<wbr>grade0_<wbr>24px.</p>
<p><svg class="icon"><use href="../shared_images/style.svg#icon"/></svg> style.</p>
<p><svg class="icon"><use href="../shared_images/subheader_rounded_weight400_grade0_48px.svg#icon"/></svg> subheader_<wbr>rounded_<wbr>weight400_<wbr>grade0_<wbr>48px.</p>
<p><svg class="icon"><use href="../shared_images/tab.svg#icon"/></svg> tab.</p>
Translation 2017-2020, 2022-2023 Francesco Buratti. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
- This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+ 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
<p><svg class="icon"><use href="../shared_images/share.svg#icon"/></svg> share.</p>
<p><svg class="icon"><use href="../shared_images/smartphone.svg#icon"/></svg> smartphone.</p>
<p><svg class="icon"><use href="../shared_images/sort.svg#icon"/></svg> sort.</p>
+ <p><svg class="icon"><use href="../shared_images/sort_by_alpha_rounded_fill0_weight400_grade0_24px.svg#icon"/></svg> sort_<wbr>by_<wbr>alpha_<wbr>rounded_<wbr>fill0_<wbr>weight400_<wbr>grade0_<wbr>24px.</p>
<p><svg class="icon"><use href="../shared_images/style.svg#icon"/></svg> style.</p>
<p><svg class="icon"><use href="../shared_images/subheader_rounded_weight400_grade0_48px.svg#icon"/></svg> subheader_<wbr>rounded_<wbr>weight400_<wbr>grade0_<wbr>48px.</p>
<p><svg class="icon"><use href="../shared_images/tab.svg#icon"/></svg> tab.</p>
Translation 2021-2022 Thiago Nazareno Conceição Silva de Jesus <mochileiro2006-trilhas@yahoo.com.br>. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
- This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+ 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
<p><svg class="icon"><use href="../shared_images/share.svg#icon"/></svg> share.</p>
<p><svg class="icon"><use href="../shared_images/smartphone.svg#icon"/></svg> smartphone.</p>
<p><svg class="icon"><use href="../shared_images/sort.svg#icon"/></svg> sort.</p>
+ <p><svg class="icon"><use href="../shared_images/sort_by_alpha_rounded_fill0_weight400_grade0_24px.svg#icon"/></svg> sort_<wbr>by_<wbr>alpha_<wbr>rounded_<wbr>fill0_<wbr>weight400_<wbr>grade0_<wbr>24px.</p>
<p><svg class="icon"><use href="../shared_images/style.svg#icon"/></svg> style.</p>
<p><svg class="icon"><use href="../shared_images/subheader_rounded_weight400_grade0_48px.svg#icon"/></svg> subheader_<wbr>rounded_<wbr>weight400_<wbr>grade0_<wbr>48px.</p>
<p><svg class="icon"><use href="../shared_images/tab.svg#icon"/></svg> tab.</p>
<!--
Copyright 2016-2024 Soren Stoutner <soren@stoutner.com>.
- This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+ 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
<p><svg class="icon"><use href="../shared_images/share.svg#icon"/></svg> share.</p>
<p><svg class="icon"><use href="../shared_images/smartphone.svg#icon"/></svg> smartphone.</p>
<p><svg class="icon"><use href="../shared_images/sort.svg#icon"/></svg> sort.</p>
+ <p><svg class="icon"><use href="../shared_images/sort_by_alpha_rounded_fill0_weight400_grade0_24px.svg#icon"/></svg> sort_<wbr>by_<wbr>alpha_<wbr>rounded_<wbr>fill0_<wbr>weight400_<wbr>grade0_<wbr>24px.</p>
<p><svg class="icon"><use href="../shared_images/style.svg#icon"/></svg> style.</p>
<p><svg class="icon"><use href="../shared_images/subheader_rounded_weight400_grade0_48px.svg#icon"/></svg> subheader_<wbr>rounded_<wbr>weight400_<wbr>grade0_<wbr>48px.</p>
<p><svg class="icon"><use href="../shared_images/tab.svg#icon"/></svg> tab.</p>
<!--
Copyright 2023 Soren Stoutner <soren@stoutner.com>.
- This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+ This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android/>.
- It is a modified version of `map`, which is part of the Android Material icon set and is released under the Apache License 2.0.
+ It is a modified version of `map`, which is part of the Android Material icon set and is released under the Apache License 2.0 <https://fonts.google.com/icons>.
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
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
- Copyright 2020,2022 Soren Stoutner <soren@stoutner.com>.
+ Copyright 2020, 2022 Soren Stoutner <soren@stoutner.com>.
- This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+ This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android/>.
- It is a modified version of `question_answer`, which is part of the Android Material icon set and is released under the Apache License 2.0.
+ It is a modified version of `question_answer`, which is part of the Android Material icon set and is released under the Apache License 2.0 <https://fonts.google.com/icons>.
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
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+
+<!--
+ Copyright 2024 Soren Stoutner <soren@stoutner.com>.
+
+ This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android/>.
+
+ It is a modified version of `sort_by_alpha`, which is part of the Android Material icon set and is released under the Apache License 2.0 <https://fonts.google.com/icons>.
+
+ 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/>. -->
+
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 -960 960 960"
+ id="icon">
+
+ <path
+ d="m196-376-23 70q-4 11-14 18.5t-22 7.5q-20 0-32.5-16.5T100-333l120-321q5-12 15-19t23-7h30q13 0 23 7t15 19l121 323q7 19-4.5 35T411-280q-12 0-22-7.5T375-306l-25-70H196Zm24-68h104l-48-150h-6l-50 150Zm418 92h166q15 0 25.5 10.5T840-316q0 15-10.5 25.5T804-280H572q-10 0-17-7t-7-17v-38q0-7 2-13.5t7-11.5l193-241H592q-15 0-25.5-10.5T556-644q0-15 10.5-25.5T592-680h222q10 0 17 7t7 17v38q0 7-2 13.5t-7 11.5L638-352ZM384-760q-7 0-9.5-6t2.5-11l89-89q6-6 14-6t14 6l89 89q5 5 2.5 11t-9.5 6H384Zm82 666-89-89q-5-5-2.5-11t9.5-6h192q7 0 9.5 6t-2.5 11l-89 89q-6 6-14 6t-14-6Z" />
+</svg>
<!--
Copyright 2016-2024 Soren Stoutner <soren@stoutner.com>.
- This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+ 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
<p><svg class="icon"><use href="../shared_images/share.svg#icon"/></svg> share.</p>
<p><svg class="icon"><use href="../shared_images/smartphone.svg#icon"/></svg> smartphone.</p>
<p><svg class="icon"><use href="../shared_images/sort.svg#icon"/></svg> sort.</p>
+ <p><svg class="icon"><use href="../shared_images/sort_by_alpha_rounded_fill0_weight400_grade0_24px.svg#icon"/></svg> sort_<wbr>by_<wbr>alpha_<wbr>rounded_<wbr>fill0_<wbr>weight400_<wbr>grade0_<wbr>24px.</p>
<p><svg class="icon"><use href="../shared_images/style.svg#icon"/></svg> style.</p>
<p><svg class="icon"><use href="../shared_images/subheader_rounded_weight400_grade0_48px.svg#icon"/></svg> subheader_<wbr>rounded_<wbr>weight400_<wbr>grade0_<wbr>48px.</p>
<p><svg class="icon"><use href="../shared_images/tab.svg#icon"/></svg> tab.</p>
Translation 2023 Xin. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
- This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+ 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
<p><svg class="icon"><use href="../shared_images/share.svg#icon"/></svg> share.</p>
<p><svg class="icon"><use href="../shared_images/smartphone.svg#icon"/></svg> smartphone.</p>
<p><svg class="icon"><use href="../shared_images/sort.svg#icon"/></svg> sort.</p>
+ <p><svg class="icon"><use href="../shared_images/sort_by_alpha_rounded_fill0_weight400_grade0_24px.svg#icon"/></svg> sort_<wbr>by_<wbr>alpha_<wbr>rounded_<wbr>fill0_<wbr>weight400_<wbr>grade0_<wbr>24px.</p>
<p><svg class="icon"><use href="../shared_images/style.svg#icon"/></svg> style.</p>
<p><svg class="icon"><use href="../shared_images/subheader_rounded_weight400_grade0_48px.svg#icon"/></svg> subheader_<wbr>rounded_<wbr>weight400_<wbr>grade0_<wbr>48px.</p>
<p><svg class="icon"><use href="../shared_images/tab.svg#icon"/></svg> tab.</p>
import java.util.function.Consumer
// Define the public constants.
-const val CURRENT_FAVORITE_ICON_BYTE_ARRAY = "A"
-const val CURRENT_FOLDER_ID = "B"
-const val CURRENT_TITLE = "C"
+const val CURRENT_FAVORITE_ICON_BYTE_ARRAY = "current_favorite_icon_byte_array"
+const val CURRENT_FOLDER_ID = "current_folder_id"
+const val CURRENT_TITLE = "current_title"
// Define the private constants.
-private const val CHECKED_BOOKMARKS_ARRAY_LIST = "D"
+private const val CHECKED_BOOKMARKS_ARRAY_LIST = "checked_bookmarks_array_list"
class BookmarksActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener, EditBookmarkDialog.EditBookmarkListener,
EditBookmarkFolderDialog.EditBookmarkFolderListener, MoveToFolderDialog.MoveToFolderListener {
private var checkingManyBookmarks = false
private var closeActivityAfterDismissingSnackbar = false
private var contextualActionMode: ActionMode? = null
+ private var sortBookmarksAlphabetically = false
// Declare the class variables.
private lateinit var appBar: ActionBar
// Get the preferences.
val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
val bottomAppBar = sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false)
+ sortBookmarksAlphabetically = sharedPreferences.getBoolean(getString(R.string.sort_bookmarks_alphabetically_key), false)
// Disable screenshots if not allowed.
if (!allowScreenshots) {
deleteBookmarksMenuItem = menu.findItem(R.id.delete_bookmark)
selectAllBookmarksMenuItem = menu.findItem(R.id.context_menu_select_all_bookmarks)
+ // Hide the move up and down menu items if bookmarks are sorted alphabetically.
+ if (sortBookmarksAlphabetically) {
+ moveBookmarkUpMenuItem.isVisible = false
+ moveBookmarkDownMenuItem.isVisible = false
+ }
+
// Disable the delete bookmarks menu item if a delete is pending.
deleteBookmarksMenuItem.isEnabled = !deletingBookmarks
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
+ // Update the move menu items if the bookmarks are not sorted alphabetically.
+ if (!sortBookmarksAlphabetically) {
+ moveBookmarkUpMenuItem.isVisible = true
+ moveBookmarkDownMenuItem.isVisible = true
+ }
+
+ // Show the edit bookmark menu item.
editBookmarkMenuItem.isVisible = true
- // Update the enabled status of the move icons.
- updateMoveIcons()
+ // Update the enabled status of the move icons if the bookmarks are not sorted alphabetically.
+ if (!sortBookmarksAlphabetically)
+ updateMoveIcons()
} else { // More than one bookmark is selected.
- // Hide non-applicable `MenuItems`.
- moveBookmarkUpMenuItem.isVisible = false
- moveBookmarkDownMenuItem.isVisible = false
+ // Update the move menu items if the bookmarks are not sorted alphabetically.
+ if (!sortBookmarksAlphabetically) {
+ moveBookmarkUpMenuItem.isVisible = false
+ moveBookmarkDownMenuItem.isVisible = false
+ }
+
+ // Hide the edit bookmark menu item.
editBookmarkMenuItem.isVisible = false
}
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(checkedBookmarksIdsLongArray, currentFolderId)
+ bookmarksCursor = if (sortBookmarksAlphabetically)
+ bookmarksDatabaseHelper.getBookmarksSortedAlphabeticallyExcept(checkedBookmarksIdsLongArray, currentFolderId)
+ else
+ bookmarksDatabaseHelper.getBookmarksByDisplayOrderExcept(checkedBookmarksIdsLongArray, currentFolderId)
// Update the list view.
bookmarksCursorAdapter.changeCursor(bookmarksCursor)
override fun onDismissed(snackbar: Snackbar, event: Int) {
if (event == DISMISS_EVENT_ACTION) { // The user pushed the undo button.
// Update the bookmarks cursor with the current contents of the bookmarks database, including the "deleted" bookmarks.
- bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
+ bookmarksCursor = if (sortBookmarksAlphabetically)
+ bookmarksDatabaseHelper.getBookmarksSortedAlphabetically(currentFolderId)
+ else
+ bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
// Update the list view.
bookmarksCursorAdapter.changeCursor(bookmarksCursor)
bookmarksDatabaseHelper.deleteBookmark(databaseIdInt)
}
- // Update the display order.
- for (i in 0 until bookmarksListView.count) {
- // Get the database ID for the current bookmark.
- val currentBookmarkDatabaseId = bookmarksListView.getItemIdAtPosition(i).toInt()
-
- // Move bookmarks cursor to the current bookmark position.
- bookmarksCursor.moveToPosition(i)
-
- // Update the display order only if it is not correct in the database.
- if (bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(DISPLAY_ORDER)) != i)
- bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i)
- }
+ // Recalculate the display order of the current folder.
+ bookmarksDatabaseHelper.recalculateFolderContentsDisplayOrder(currentFolderId)
}
// Reset the deleting bookmarks flag.
bookmarksDatabaseHelper.createBookmark(bookmarkNameString, bookmarkUrlString, currentFolderId, newBookmarkDisplayOrder, favoriteIconByteArray)
// Update the bookmarks cursor with the current contents of this folder.
- bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
+ bookmarksCursor = if (sortBookmarksAlphabetically)
+ bookmarksDatabaseHelper.getBookmarksSortedAlphabetically(currentFolderId)
+ else
+ bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
// Update the list view.
bookmarksCursorAdapter.changeCursor(bookmarksCursor)
bookmarksDatabaseHelper.createFolder(folderNameString, currentFolderId, displayOrder = 0, folderIconByteArray)
// Update the bookmarks cursor with the contents of the current folder.
- bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
+ bookmarksCursor = if (sortBookmarksAlphabetically)
+ bookmarksDatabaseHelper.getBookmarksSortedAlphabetically(currentFolderId)
+ else
+ bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
// Update the list view.
bookmarksCursorAdapter.changeCursor(bookmarksCursor)
contextualActionMode?.finish()
// Update the bookmarks cursor with the contents of the current folder.
- bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
+ bookmarksCursor = if (sortBookmarksAlphabetically)
+ bookmarksDatabaseHelper.getBookmarksSortedAlphabetically(currentFolderId)
+ else
+ bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
// Update the list view.
bookmarksCursorAdapter.changeCursor(bookmarksCursor)
}
// Update the bookmarks cursor with the current contents of this folder.
- bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
+ bookmarksCursor = if (sortBookmarksAlphabetically)
+ bookmarksDatabaseHelper.getBookmarksSortedAlphabetically(currentFolderId)
+ else
+ bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
// Update the list view.
bookmarksCursorAdapter.changeCursor(bookmarksCursor)
bookmarksDatabaseHelper.moveToFolder(databaseIdInt, newFolderId)
}
+ // Recalculate the display order of the current folder.
+ bookmarksDatabaseHelper.recalculateFolderContentsDisplayOrder(currentFolderId)
+
// Update the bookmarks cursor with the current contents of this folder.
- bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
+ bookmarksCursor = if (sortBookmarksAlphabetically)
+ bookmarksDatabaseHelper.getBookmarksSortedAlphabetically(currentFolderId)
+ else
+ bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
// Update the list view.
bookmarksCursorAdapter.changeCursor(bookmarksCursor)
private fun loadFolder() {
// Update the bookmarks cursor with the contents of the bookmarks database for the current folder.
- bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
+ bookmarksCursor = if (sortBookmarksAlphabetically)
+ bookmarksDatabaseHelper.getBookmarksSortedAlphabetically(currentFolderId)
+ else
+ bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
// Setup a cursor adapter.
bookmarksCursorAdapter = object : CursorAdapter(this, bookmarksCursor, false) {
private var savedStateArrayList: ArrayList<Bundle>? = null
private var savedTabPosition = 0
private var scrollAppBar = false
+ private var sortBookmarksAlphabetically = false
private var ultraPrivacy: ArrayList<List<Array<String>>>? = null
private var waitingForProxy = false
hideAppBar = sharedPreferences.getBoolean(getString(R.string.hide_app_bar_key), true)
val downloadProvider = sharedPreferences.getString(getString(R.string.download_provider_key), getString(R.string.download_provider_default_value))!!
scrollAppBar = sharedPreferences.getBoolean(getString(R.string.scroll_app_bar_key), false)
+ sortBookmarksAlphabetically = sharedPreferences.getBoolean(getString(R.string.sort_bookmarks_alphabetically_key), false)
// Determine if downloading should be handled by an external app.
downloadWithExternalApp = (downloadProvider == downloadProviderEntryValuesStringArray[2])
@Suppress("DEPRECATION")
rootFrameLayout.systemUiVisibility = 0
}
+
+ // Load the bookmarks folder.
+ loadBookmarksFolder()
}
// `reloadWebsite` is used if returning from the Domains activity. Otherwise JavaScript might not function correctly if it is newly enabled.
bookmarksDatabaseHelper!!.createBookmark(bookmarkNameString, bookmarkUrlString, currentBookmarksFolderId, newBookmarkDisplayOrder, favoriteIconByteArray)
// Update the bookmarks cursor with the current contents of this folder.
- bookmarksCursor = bookmarksDatabaseHelper!!.getBookmarksByDisplayOrder(currentBookmarksFolderId)
+ bookmarksCursor = if (sortBookmarksAlphabetically)
+ bookmarksDatabaseHelper!!.getBookmarksSortedAlphabetically(currentBookmarksFolderId)
+ else
+ bookmarksDatabaseHelper!!.getBookmarksByDisplayOrder(currentBookmarksFolderId)
// Update the list view.
bookmarksCursorAdapter.changeCursor(bookmarksCursor)
bookmarksDatabaseHelper!!.createFolder(folderNameString, currentBookmarksFolderId, displayOrder = 0, folderIconByteArray)
// Update the bookmarks cursor with the current contents of this folder.
- bookmarksCursor = bookmarksDatabaseHelper!!.getBookmarksByDisplayOrder(currentBookmarksFolderId)
+ bookmarksCursor = if (sortBookmarksAlphabetically)
+ bookmarksDatabaseHelper!!.getBookmarksSortedAlphabetically(currentBookmarksFolderId)
+ else
+ bookmarksDatabaseHelper!!.getBookmarksByDisplayOrder(currentBookmarksFolderId)
// Update the list view.
bookmarksCursorAdapter.changeCursor(bookmarksCursor)
drawerLayout.setDrawerTitle(GravityCompat.START, getString(R.string.navigation_drawer))
drawerLayout.setDrawerTitle(GravityCompat.END, getString(R.string.bookmarks))
- // Load the bookmarks folder.
- loadBookmarksFolder()
-
// Handle clicks on bookmarks.
bookmarksListView.onItemClickListener = AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, _: Int, id: Long ->
// Convert the id from long to int to match the format of the bookmarks database.
private fun loadBookmarksFolder() {
// Update the bookmarks cursor with the contents of the bookmarks database for the current folder.
- bookmarksCursor = bookmarksDatabaseHelper!!.getBookmarksByDisplayOrder(currentBookmarksFolderId)
+ bookmarksCursor = if (sortBookmarksAlphabetically)
+ bookmarksDatabaseHelper!!.getBookmarksSortedAlphabetically(currentBookmarksFolderId)
+ else
+ bookmarksDatabaseHelper!!.getBookmarksByDisplayOrder(currentBookmarksFolderId)
// Populate the bookmarks cursor adapter.
bookmarksCursorAdapter = object : CursorAdapter(this, bookmarksCursor, false) {
/*
* Copyright 2016-2022 Soren Stoutner <soren@stoutner.com>.
*
- * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+ * 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
private lateinit var searchCustomURLPreference: Preference
private lateinit var searchPreference: Preference
private lateinit var sharedPreferenceChangeListener: OnSharedPreferenceChangeListener
+ private lateinit var sortBookmarksAlphabeticallyPreference: Preference
private lateinit var swipeToRefreshPreference: Preference
private lateinit var trackingQueriesPreference: Preference
private lateinit var translatedUserAgentNamesArray: Array<String>
scrollAppBarPreference = findPreference(getString(R.string.scroll_app_bar_key))!!
bottomAppBarPreference = findPreference(getString(R.string.bottom_app_bar_key))!!
displayAdditionalAppBarIconsPreference = findPreference(getString(R.string.display_additional_app_bar_icons_key))!!
+ sortBookmarksAlphabeticallyPreference = findPreference(getString(R.string.sort_bookmarks_alphabetically_key))!!
appThemePreference = findPreference(getString(R.string.app_theme_key))!!
webViewThemePreference = findPreference(getString(R.string.webview_theme_key))!!
wideViewportPreference = findPreference(getString(R.string.wide_viewport_key))!!
else
displayAdditionalAppBarIconsPreference.setIcon(R.drawable.more_disabled)
+ // Set the sort bookmarks alphabetically icon.
+ if (sharedPreferences.getBoolean(getString(R.string.sort_bookmarks_alphabetically_key), false))
+ sortBookmarksAlphabeticallyPreference.setIcon(R.drawable.sort_by_alpha_enabled)
+ else
+ sortBookmarksAlphabeticallyPreference.setIcon(R.drawable.sort_by_alpha_disabled)
+
// Set the WebView theme icon.
if (webViewThemePreference.isEnabled) { // The WebView theme preference is enabled.
when (webViewThemeEntryNumber) {
restartPrivacyBrowser()
}
+ getString(R.string.sort_bookmarks_alphabetically_key) -> {
+ // Update the icon.
+ if (sharedPreferences.getBoolean(getString(R.string.sort_bookmarks_alphabetically_key), false))
+ sortBookmarksAlphabeticallyPreference.setIcon(R.drawable.sort_by_alpha_enabled)
+ else
+ sortBookmarksAlphabeticallyPreference.setIcon(R.drawable.sort_by_alpha_disabled)
+ }
+
getString(R.string.app_theme_key) -> {
// Get the app theme entry number that matches the current app theme.
val appThemeEntryNumber: Int = when (sharedPreferences.getString(getString(R.string.app_theme_key), getString(R.string.app_theme_default_value))) {
/*
- * Copyright 2016-2023 Soren Stoutner <soren@stoutner.com>.
+ * Copyright 2016-2024 Soren Stoutner <soren@stoutner.com>.
*
- * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+ * 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
idsNotToGetStringBuilder.append(databaseIdLong)
}
- // Return a cursor with all the bookmarks in the specified folder except for those database IDs specified ordered by display order.
- // The cursor cannot be closed because it will be used in the parent activity.
+ // Return a cursor with all the bookmarks in the specified folder except for those database IDs specified ordered by display order. The cursor cannot be closed because it will be used in the parent activity.
return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER_ID = $parentFolderId AND $ID NOT IN ($idsNotToGetStringBuilder) ORDER BY $DISPLAY_ORDER ASC", null)
}
return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER_ID = $parentFolderId AND $ID NOT IN ($idsNotToGetStringBuilder)", null)
}
+ fun getBookmarksSortedAlphabetically(parentFolderId: Long): Cursor {
+ // Get a readable database handle.
+ val bookmarksDatabase = this.readableDatabase
+
+ // Get the folders sorted alphabetically.
+ val foldersCursor = bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER_ID = $parentFolderId AND $IS_FOLDER = 1 ORDER BY $BOOKMARK_NAME ASC", null)
+
+ // Get the bookmarks sorted alphabetically.
+ val bookmarksCursor = bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER_ID = $parentFolderId AND $IS_FOLDER = 0 ORDER BY $BOOKMARK_NAME ASC", null)
+
+ // Return the merged cursors.
+ return MergeCursor(arrayOf(foldersCursor, bookmarksCursor))
+ }
+
+ fun getBookmarksSortedAlphabeticallyExcept(exceptIdLongArray: LongArray, parentFolderId: Long): Cursor {
+ // Get a readable database handle.
+ val bookmarksDatabase = this.readableDatabase
+
+ // Prepare a string builder to contain the comma-separated list of IDs not to get.
+ val idsNotToGetStringBuilder = StringBuilder()
+
+ // Extract the array of IDs not to get to the string builder.
+ for (databaseIdLong in exceptIdLongArray) {
+ // Check to see if there is already a number in the builder.
+ if (idsNotToGetStringBuilder.isNotEmpty()) {
+ // This is not the first number, so place a `,` before the new number.
+ idsNotToGetStringBuilder.append(",")
+ }
+
+ // Add the new number to the builder.
+ idsNotToGetStringBuilder.append(databaseIdLong)
+ }
+
+ // Get the folders sorted alphabetically except for those database IDs specified, ordered by name.
+ val foldersCursor = bookmarksDatabase.rawQuery(
+ "SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER_ID = $parentFolderId AND $IS_FOLDER = 1 AND $ID NOT IN ($idsNotToGetStringBuilder) ORDER BY $BOOKMARK_NAME ASC", null)
+
+ // Get the bookmarks sorted alphabetically except for those database IDs specified, ordered by name.
+ val bookmarksCursor = bookmarksDatabase.rawQuery(
+ "SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER_ID = $parentFolderId AND $IS_FOLDER = 0 AND $ID NOT IN ($idsNotToGetStringBuilder) ORDER BY $BOOKMARK_NAME ASC", null)
+
+ // Return the merged cursors.
+ return MergeCursor(arrayOf(foldersCursor, bookmarksCursor))
+ }
+
fun getFolderBookmarks(parentFolderId: Long): Cursor {
// Get a readable database handle.
val bookmarksDatabase = this.readableDatabase
bookmarksDatabase.close()
}
+ fun recalculateFolderContentsDisplayOrder(folderId: Long) {
+ // Get a readable database.
+ val bookmarksDatabase = this.readableDatabase
+
+ // Get a cursor with the current content of the folder.
+ val folderContentsCursor = bookmarksDatabase.rawQuery("SELECT $ID, $DISPLAY_ORDER FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER_ID = $folderId ORDER BY $DISPLAY_ORDER ASC", null)
+
+ // Get the count of the folder contents.
+ val folderContentsCount = folderContentsCursor.count
+
+ // Get the query columns.
+ val databaseIdColumnInt = folderContentsCursor.getColumnIndexOrThrow(ID)
+ val displayOrderColumnInt = folderContentsCursor.getColumnIndexOrThrow(DISPLAY_ORDER)
+
+ // Move to the first entry.
+ folderContentsCursor.moveToFirst()
+
+ // Update the display order if it isn't currently correct.
+ for (i in 0 until folderContentsCount) {
+ // Use the current value for `i` as the display order if it isn't currently so.
+ if (i != folderContentsCursor.getInt(displayOrderColumnInt))
+ updateDisplayOrder(folderContentsCursor.getInt(databaseIdColumnInt), i)
+
+ // Move to the next entry.
+ folderContentsCursor.moveToNext()
+ }
+
+ // Close the cursor.
+ folderContentsCursor.close()
+ }
+
// Update the bookmark name and URL.
fun updateBookmark(databaseId: Int, bookmarkName: String, bookmarkUrl: String) {
// Initialize a content values.
/*
* Copyright 2018-2024 Soren Stoutner <soren@stoutner.com>.
*
- * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+ * 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
import java.util.Date
// Define the public constants.
-const val IMPORT_EXPORT_SCHEMA_VERSION = 19
-const val EXPORT_SUCCESSFUL = "A"
-const val IMPORT_SUCCESSFUL = "B"
+const val IMPORT_EXPORT_SCHEMA_VERSION = 20
+const val EXPORT_SUCCESSFUL = "export_successful"
+const val IMPORT_SUCCESSFUL = "import_successful"
// Define the private class constants.
private const val ALLOW_SCREENSHOTS = "allow_screenshots"
private const val SEARCH = "search"
private const val SEARCH_CUSTOM_URL = "search_custom_url"
private const val SCROLL_APP_BAR = "scroll_app_bar"
+private const val SORT_BOOKMARKS_ALPHABETICALLY = "sort_bookmarks_alphabetically"
private const val PREFERENCES_SWIPE_TO_REFRESH = "swipe_to_refresh"
private const val TRACKING_QUERIES = "tracking_queries"
private const val ULTRAPRIVACY = "ultraprivacy"
// This upgrade removed `enableformdata` from the Domains table and `save_form_data` and `clear_form_data` from the Preferences table.
// There is no need to delete the columns as they will simply be ignored by the import.
+ // Upgrade from schema version 19, first used in Privacy Browser 3.18, to schema version 20, first used in Privacy Browser 3.19.
+ if (importDatabaseVersion < 20) {
+ // Create the new sort bookmarks alphabetically column.
+ importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $SORT_BOOKMARKS_ALPHABETICALLY BOOLEAN")
+
+ // Get the current sort bookmarks alphabetically column.
+ val sortBookmarksAlphabetically = sharedPreferences.getBoolean(SORT_BOOKMARKS_ALPHABETICALLY, false)
+
+ // Populate the preferences table with the current sort bookmarks alphabetically value.
+ // This can switch to using the variables directly once the minimum API >= 30. <https://www.sqlite.org/datatype3.html#boolean_datatype>
+ // <https://developer.android.com/reference/android/database/sqlite/package-summary>
+ if (sortBookmarksAlphabetically)
+ importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $SORT_BOOKMARKS_ALPHABETICALLY = 1")
+ else
+ importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $SORT_BOOKMARKS_ALPHABETICALLY = 0")
+ }
+
/* End of database upgrade logic. */
.putBoolean(SCROLL_APP_BAR, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(SCROLL_APP_BAR)) == 1)
.putBoolean(BOTTOM_APP_BAR, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(BOTTOM_APP_BAR)) == 1)
.putBoolean(DISPLAY_ADDITIONAL_APP_BAR_ICONS, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(DISPLAY_ADDITIONAL_APP_BAR_ICONS)) == 1)
+ .putBoolean(SORT_BOOKMARKS_ALPHABETICALLY, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(SORT_BOOKMARKS_ALPHABETICALLY)) == 1)
.putString(APP_THEME, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(APP_THEME)))
.putString(WEBVIEW_THEME, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(WEBVIEW_THEME)))
.putBoolean(WIDE_VIEWPORT, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(WIDE_VIEWPORT)) == 1)
"$SCROLL_APP_BAR BOOLEAN, " +
"$BOTTOM_APP_BAR BOOLEAN, " +
"$DISPLAY_ADDITIONAL_APP_BAR_ICONS BOOLEAN, " +
+ "$SORT_BOOKMARKS_ALPHABETICALLY BOOLEAN, " +
"$APP_THEME TEXT, " +
"$WEBVIEW_THEME TEXT, " +
"$WIDE_VIEWPORT BOOLEAN, " +
preferencesContentValues.put(SCROLL_APP_BAR, sharedPreferences.getBoolean(SCROLL_APP_BAR, true))
preferencesContentValues.put(BOTTOM_APP_BAR, sharedPreferences.getBoolean(BOTTOM_APP_BAR, false))
preferencesContentValues.put(DISPLAY_ADDITIONAL_APP_BAR_ICONS, sharedPreferences.getBoolean(DISPLAY_ADDITIONAL_APP_BAR_ICONS, false))
+ preferencesContentValues.put(SORT_BOOKMARKS_ALPHABETICALLY, sharedPreferences.getBoolean(SORT_BOOKMARKS_ALPHABETICALLY, false))
preferencesContentValues.put(APP_THEME, sharedPreferences.getString(APP_THEME, context.getString(R.string.app_theme_default_value)))
preferencesContentValues.put(WEBVIEW_THEME, sharedPreferences.getString(WEBVIEW_THEME, context.getString(R.string.webview_theme_default_value)))
preferencesContentValues.put(WIDE_VIEWPORT, sharedPreferences.getBoolean(WIDE_VIEWPORT, true))
-<!-- This file comes from the Android Material icon set, where it is called `more`. It is released under the Apache License 2.0. -->
+<!-- This file comes from the Android Material icon set, where it is called `more`. It is released under the Apache License 2.0 <https://fonts.google.com/icons>. -->
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
-<!-- This file comes from the Android Material icon set, where it is called `more`. It is released under the Apache License 2.0. -->
+<!-- This file comes from the Android Material icon set, where it is called `more`. It is released under the Apache License 2.0 <https://fonts.google.com/icons>. -->
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
-<!-- This file comes from the Android Material icon set, where it is called `vertical_align_bottom`. It is released under the Apache License 2.0. -->
+<!-- This file comes from the Android Material icon set, where it is called `vertical_align_bottom`. It is released under the Apache License 2.0 <https://fonts.google.com/icons>. -->
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
-<!-- This file comes from the Android Material icon set, where it is called `vertical_align_bottom`. It is released under the Apache License 2.0. -->
+<!-- This file comes from the Android Material icon set, where it is called `vertical_align_bottom`. It is released under the Apache License 2.0 <https://fonts.google.com/icons>. -->
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
<!--
Copyright 2017,2022 Soren Stoutner <soren@stoutner.com>.
- This file is derived from elements of `folder` and `exit_to_app`, which are part of the Android Material icon set. They are released under the Apache License 2.0.
+ This file is derived from elements of `folder` and `exit_to_app`, which are part of the Android Material icon set. They are released under the Apache License 2.0 <https://fonts.google.com/icons>.
This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
-<!-- This file comes from the Android Material icon set, where it is called `vertical_align_top`. It is released under the Apache License 2.0. -->
+<!-- This file comes from the Android Material icon set, where it is called `vertical_align_top`. It is released under the Apache License 2.0 <https://fonts.google.com/icons>. -->
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
-<!-- This file comes from the Android Material icon set, where it is called `vertical_align_top`. It is released under the Apache License 2.0. -->
+<!-- This file comes from the Android Material icon set, where it is called `vertical_align_top`. It is released under the Apache License 2.0 <https://fonts.google.com/icons>. -->
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
--- /dev/null
+<!-- This file comes from the Android Material icon set, where it is called `sort_by_alpha_rounded_fill0_weight400_grade0_24px`. It is released under the Apache License 2.0 <https://fonts.google.com/icons>. -->
+
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="960"
+ android:viewportWidth="960" >
+
+ <path
+ android:fillColor="@color/disabled_icon"
+ android:pathData="m196,584 l-23,70q-4,11 -14,18.5t-22,7.5q-20,0 -32.5,-16.5T100,627l120,-321q5,-12 15,-19t23,-7h30q13,0 23,7t15,19l121,323q7,19 -4.5,35T411,680q-12,0 -22,-7.5T375,654l-25,-70L196,584ZM220,516h104l-48,-150h-6l-50,150ZM638,608h166q15,0 25.5,10.5T840,644q0,15 -10.5,25.5T804,680L572,680q-10,0 -17,-7t-7,-17v-38q0,-7 2,-13.5t7,-11.5l193,-241L592,352q-15,0 -25.5,-10.5T556,316q0,-15 10.5,-25.5T592,280h222q10,0 17,7t7,17v38q0,7 -2,13.5t-7,11.5L638,608ZM384,200q-7,0 -9.5,-6t2.5,-11l89,-89q6,-6 14,-6t14,6l89,89q5,5 2.5,11t-9.5,6L384,200ZM466,866 L377,777q-5,-5 -2.5,-11t9.5,-6h192q7,0 9.5,6t-2.5,11l-89,89q-6,6 -14,6t-14,-6Z" />
+</vector>
--- /dev/null
+<!-- This file comes from the Android Material icon set, where it is called `sort_by_alpha_rounded_fill0_weight400_grade0_24px`. It is released under the Apache License 2.0 <https://fonts.google.com/icons>. -->
+
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="960"
+ android:viewportWidth="960" >
+
+ <path
+ android:fillColor="@color/blue_icon"
+ android:pathData="m196,584 l-23,70q-4,11 -14,18.5t-22,7.5q-20,0 -32.5,-16.5T100,627l120,-321q5,-12 15,-19t23,-7h30q13,0 23,7t15,19l121,323q7,19 -4.5,35T411,680q-12,0 -22,-7.5T375,654l-25,-70L196,584ZM220,516h104l-48,-150h-6l-50,150ZM638,608h166q15,0 25.5,10.5T840,644q0,15 -10.5,25.5T804,680L572,680q-10,0 -17,-7t-7,-17v-38q0,-7 2,-13.5t7,-11.5l193,-241L592,352q-15,0 -25.5,-10.5T556,316q0,-15 10.5,-25.5T592,280h222q10,0 17,7t7,17v38q0,7 -2,13.5t-7,11.5L638,608ZM384,200q-7,0 -9.5,-6t2.5,-11l89,-89q6,-6 14,-6t14,6l89,89q5,5 2.5,11t-9.5,6L384,200ZM466,866 L377,777q-5,-5 -2.5,-11t9.5,-6h192q7,0 9.5,6t-2.5,11l-89,89q-6,6 -14,6t-14,-6Z" />
+</vector>
<string name="display_additional_app_bar_icons">Display additional app bar icons</string>
<string name="display_additional_app_bar_icons_summary">Display icons in the app bar for refreshing the WebView and, if there is room, for opening the bookmarks drawer and toggling cookies.
Changing this setting will restart Privacy Browser.</string>
+ <string name="sort_bookmarks_alphabetically">Sort bookmarks alphabetically</string>
+ <string name="sort_bookmarks_alphabetically_summary">Sort the bookmarks alphabetically, with the folders displayed above the bookmarks. This disables manual ordering of bookmarks.</string>
<string name="app_theme">App theme</string>
<string-array name="app_theme_entries">
<item>System default</item>
<string name="scroll_app_bar_key" translatable="false">scroll_app_bar</string>
<string name="search_custom_url_key" translatable="false">search_custom_url</string>
<string name="search_key" translatable="false">search</string>
+ <string name="sort_bookmarks_alphabetically_key" translatable="false">sort_bookmarks_alphabetically</string>
<string name="swipe_to_refresh_key" translatable="false">swipe_to_refresh</string>
<string name="tracking_queries_key" translatable="false">tracking_queries</string>
<string name="ultralist_key" translatable="false">ultralist</string>
app:summary="@string/display_additional_app_bar_icons_summary"
app:defaultValue="false" />
+ <SwitchPreferenceCompat
+ app:key="@string/sort_bookmarks_alphabetically_key"
+ app:title="@string/sort_bookmarks_alphabetically"
+ app:summary="@string/sort_bookmarks_alphabetically_summary"
+ app:defaultValue="false" />
+
<ListPreference
app:key="@string/app_theme_key"
app:title="@string/app_theme"