2 * Copyright 2016-2023 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
6 * Privacy Browser Android is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * Privacy Browser Android is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Privacy Browser Android. If not, see <http://www.gnu.org/licenses/>.
20 package com.stoutner.privacybrowser.activities
22 import android.content.Context
23 import android.content.Intent
24 import android.database.Cursor
25 import android.graphics.Bitmap
26 import android.graphics.BitmapFactory
27 import android.graphics.Typeface
28 import android.graphics.drawable.BitmapDrawable
29 import android.os.Bundle
30 import android.util.SparseBooleanArray
31 import android.view.ActionMode
32 import android.view.Menu
33 import android.view.MenuItem
34 import android.view.View
35 import android.view.ViewGroup
36 import android.view.Window
37 import android.view.WindowManager
38 import android.widget.AbsListView.MultiChoiceModeListener
39 import android.widget.AdapterView
40 import android.widget.EditText
41 import android.widget.ImageView
42 import android.widget.ListView
43 import android.widget.RadioButton
44 import android.widget.TextView
46 import androidx.activity.OnBackPressedCallback
47 import androidx.appcompat.app.ActionBar
48 import androidx.appcompat.app.AppCompatActivity
49 import androidx.appcompat.widget.Toolbar
50 import androidx.cursoradapter.widget.CursorAdapter
51 import androidx.fragment.app.DialogFragment
52 import androidx.preference.PreferenceManager
54 import com.google.android.material.floatingactionbutton.FloatingActionButton
55 import com.google.android.material.snackbar.Snackbar
57 import com.stoutner.privacybrowser.R
58 import com.stoutner.privacybrowser.dialogs.CreateBookmarkDialog
59 import com.stoutner.privacybrowser.dialogs.CreateBookmarkFolderDialog
60 import com.stoutner.privacybrowser.dialogs.EditBookmarkDialog
61 import com.stoutner.privacybrowser.dialogs.EditBookmarkFolderDialog
62 import com.stoutner.privacybrowser.dialogs.MoveToFolderDialog
63 import com.stoutner.privacybrowser.helpers.BOOKMARK_NAME
64 import com.stoutner.privacybrowser.helpers.DISPLAY_ORDER
65 import com.stoutner.privacybrowser.helpers.FAVORITE_ICON
66 import com.stoutner.privacybrowser.helpers.FOLDER_ID
67 import com.stoutner.privacybrowser.helpers.ID
68 import com.stoutner.privacybrowser.helpers.IS_FOLDER
69 import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper
71 import java.io.ByteArrayOutputStream
72 import java.util.function.Consumer
74 // Define the public constants.
75 const val CURRENT_FAVORITE_ICON_BYTE_ARRAY = "A"
76 const val CURRENT_FOLDER_ID = "B"
77 const val CURRENT_TITLE = "C"
79 // Define the private constants.
80 private const val CHECKED_BOOKMARKS_ARRAY_LIST = "D"
82 class BookmarksActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener, EditBookmarkDialog.EditBookmarkListener,
83 EditBookmarkFolderDialog.EditBookmarkFolderListener, MoveToFolderDialog.MoveToFolderListener {
86 // Define the public static variables, which are accessed from the bookmarks database view activity.
87 var currentFolderId: Long = 0
88 var restartFromBookmarksDatabaseViewActivity = false
91 // Define the class variables.
92 private var bookmarksDeletedSnackbar: Snackbar? = null
93 private var closeActivityAfterDismissingSnackbar = false
94 private var contextualActionMode: ActionMode? = null
96 // Declare the class variables.
97 private lateinit var appBar: ActionBar
98 private lateinit var bookmarksCursor: Cursor
99 private lateinit var bookmarksCursorAdapter: CursorAdapter
100 private lateinit var bookmarksDatabaseHelper: BookmarksDatabaseHelper
101 private lateinit var bookmarksListView: ListView
102 private lateinit var currentFavoriteIconByteArray: ByteArray
103 private lateinit var moveBookmarkDownMenuItem: MenuItem
104 private lateinit var moveBookmarkUpMenuItem: MenuItem
105 private lateinit var moveToFolderMenuItem: MenuItem
107 override fun onCreate(savedInstanceState: Bundle?) {
108 // Get a handle for the shared preferences.
109 val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
111 // Get the preferences.
112 val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
113 val bottomAppBar = sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false)
115 // Disable screenshots if not allowed.
116 if (!allowScreenshots) {
117 window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
120 // Run the default commands.
121 super.onCreate(savedInstanceState)
123 // Get the intent that launched the activity.
124 val launchingIntent = intent
126 // Populate the variables from the launching intent.
127 currentFolderId = launchingIntent.getLongExtra(CURRENT_FOLDER_ID, HOME_FOLDER_ID)
128 val currentTitle = launchingIntent.getStringExtra(CURRENT_TITLE)!!
129 val currentUrl = launchingIntent.getStringExtra(CURRENT_URL)!!
130 currentFavoriteIconByteArray = launchingIntent.getByteArrayExtra(CURRENT_FAVORITE_ICON_BYTE_ARRAY)!!
132 // Convert the favorite icon byte array to a bitmap.
133 val currentFavoriteIconBitmap = BitmapFactory.decodeByteArray(currentFavoriteIconByteArray, 0, currentFavoriteIconByteArray.size)
135 // Set the content according to the app bar position.
137 // Set the content view.
138 setContentView(R.layout.bookmarks_bottom_appbar)
140 // `Window.FEATURE_ACTION_MODE_OVERLAY` makes the contextual action mode cover the support action bar. It must be requested before the content is set.
141 supportRequestWindowFeature(Window.FEATURE_ACTION_MODE_OVERLAY)
143 // Set the content view.
144 setContentView(R.layout.bookmarks_top_appbar)
147 // Get handles for the views.
148 val toolbar = findViewById<Toolbar>(R.id.bookmarks_toolbar)
149 bookmarksListView = findViewById(R.id.bookmarks_listview)
150 val createBookmarkFolderFab = findViewById<FloatingActionButton>(R.id.create_bookmark_folder_fab)
151 val createBookmarkFab = findViewById<FloatingActionButton>(R.id.create_bookmark_fab)
153 // Set the support action bar.
154 setSupportActionBar(toolbar)
156 // Get a handle for the app bar.
157 appBar = supportActionBar!!
159 // Display the home arrow on the app bar.
160 appBar.setDisplayHomeAsUpEnabled(true)
162 // Control what the system back command does.
163 val onBackPressedCallback: OnBackPressedCallback = object : OnBackPressedCallback(true) {
164 override fun handleOnBackPressed() {
165 // Prepare to finish the activity.
170 // Register the on back pressed callback.
171 onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
173 // Initialize the database helper.
174 bookmarksDatabaseHelper = BookmarksDatabaseHelper(this)
176 // Set a listener so that tapping a list item edits the bookmark or opens a folder.
177 bookmarksListView.onItemClickListener = AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, _: Int, id: Long ->
178 // Convert the id from long to int to match the format of the bookmarks database.
179 val databaseId = id.toInt()
181 // Get the bookmark cursor for this ID.
182 val bookmarkCursor = bookmarksDatabaseHelper.getBookmark(databaseId)
184 // Move the cursor to the first entry.
185 bookmarkCursor.moveToFirst()
187 // Act upon the bookmark according to the type.
188 if (bookmarkCursor.getInt(bookmarkCursor.getColumnIndexOrThrow(IS_FOLDER)) == 1) { // The selected bookmark is a folder.
189 // Update the current folder ID.
190 currentFolderId = bookmarkCursor.getLong(bookmarkCursor.getColumnIndexOrThrow(FOLDER_ID))
192 // Load the new folder.
194 } else { // The selected bookmark is not a folder.
195 // Instantiate the edit bookmark dialog.
196 val editBookmarkDialog = EditBookmarkDialog.editBookmark(databaseId, currentFavoriteIconBitmap)
199 editBookmarkDialog.show(supportFragmentManager, resources.getString(R.string.edit_bookmark))
203 bookmarkCursor.close()
206 // Handle long-presses on the list view.
207 bookmarksListView.setMultiChoiceModeListener(object : MultiChoiceModeListener {
208 // Define the object variables.
209 private var deletingBookmarks = false
211 // Declare the object variables.
212 private lateinit var editBookmarkMenuItem: MenuItem
213 private lateinit var deleteBookmarksMenuItem: MenuItem
214 private lateinit var selectAllBookmarksMenuItem: MenuItem
216 override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
217 // Inflate the menu for the contextual app bar.
218 menuInflater.inflate(R.menu.bookmarks_context_menu, menu)
221 if (currentFolderId == HOME_FOLDER_ID) { // The current folder is the home folder.
222 mode.setTitle(R.string.bookmarks)
223 } else { // Use the current folder name as the title.
224 mode.title = bookmarksDatabaseHelper.getFolderName(currentFolderId)
227 // Get handles for menu items that need to be selectively disabled.
228 moveBookmarkUpMenuItem = menu.findItem(R.id.move_bookmark_up)
229 moveBookmarkDownMenuItem = menu.findItem(R.id.move_bookmark_down)
230 moveToFolderMenuItem = menu.findItem(R.id.move_to_folder)
231 editBookmarkMenuItem = menu.findItem(R.id.edit_bookmark)
232 deleteBookmarksMenuItem = menu.findItem(R.id.delete_bookmark)
233 selectAllBookmarksMenuItem = menu.findItem(R.id.context_menu_select_all_bookmarks)
235 // Disable the delete bookmarks menu item if a delete is pending.
236 deleteBookmarksMenuItem.isEnabled = !deletingBookmarks
238 // Store a handle for the contextual action bar so it can be closed programatically.
239 contextualActionMode = mode
245 override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
246 // Display the move to folder menu item if at least one other folder exists.
247 moveToFolderMenuItem.isVisible = bookmarksDatabaseHelper.hasFoldersExceptDatabaseId(bookmarksListView.checkedItemIds)
253 override fun onItemCheckedStateChanged(mode: ActionMode, position: Int, id: Long, checked: Boolean) {
254 // Get the number of selected bookmarks.
255 val numberOfSelectedBookmarks = bookmarksListView.checkedItemCount
257 // Only process commands if at least one bookmark is selected. Otherwise, a context menu with 0 selected bookmarks is briefly displayed.
258 if (numberOfSelectedBookmarks > 0) {
259 // Adjust the action mode and the menu according to the number of selected bookmarks.
260 if (numberOfSelectedBookmarks == 1) { // One bookmark is selected.
261 // Show the applicable menu items.
262 moveBookmarkUpMenuItem.isVisible = true
263 moveBookmarkDownMenuItem.isVisible = true
264 editBookmarkMenuItem.isVisible = true
266 // Update the enabled status of the move icons.
268 } else { // More than one bookmark is selected.
269 // Hide non-applicable `MenuItems`.
270 moveBookmarkUpMenuItem.isVisible = false
271 moveBookmarkDownMenuItem.isVisible = false
272 editBookmarkMenuItem.isVisible = false
275 // Display the move to folder menu item if at least one other folder exists.
276 moveToFolderMenuItem.isVisible = bookmarksDatabaseHelper.hasFoldersExceptDatabaseId(bookmarksListView.checkedItemIds)
278 // List the number of selected bookmarks in the subtitle.
279 mode.subtitle = getString(R.string.selected, numberOfSelectedBookmarks)
281 // Show the select all menu item if all the bookmarks are not selected.
282 selectAllBookmarksMenuItem.isVisible = (numberOfSelectedBookmarks != bookmarksListView.count)
286 override fun onActionItemClicked(actionMode: ActionMode, menuItem: MenuItem): Boolean {
287 // Declare the variables.
288 val selectedBookmarkNewPosition: Int
289 val selectedBookmarksPositionsSparseBooleanArray: SparseBooleanArray
291 // Initialize the selected bookmark position.
292 var selectedBookmarkPosition = 0
294 // Get the menu item ID.
295 val menuItemId = menuItem.itemId
297 // Run the commands according to the selected action item.
298 if (menuItemId == R.id.move_bookmark_up) { // Move the bookmark up.
299 // Get the array of checked bookmark positions.
300 selectedBookmarksPositionsSparseBooleanArray = bookmarksListView.checkedItemPositions
302 // 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`.
303 for (i in 0 until selectedBookmarksPositionsSparseBooleanArray.size()) {
304 // Check to see if the value for the bookmark is true, meaning it is currently selected.
305 if (selectedBookmarksPositionsSparseBooleanArray.valueAt(i)) {
306 // Only one bookmark should have a value of `true` when move bookmark up is enabled.
307 selectedBookmarkPosition = selectedBookmarksPositionsSparseBooleanArray.keyAt(i)
311 // Calculate the new position of the selected bookmark.
312 selectedBookmarkNewPosition = selectedBookmarkPosition - 1
314 // Iterate through the bookmarks.
315 for (i in 0 until bookmarksListView.count) {
316 // Get the database ID for the current bookmark.
317 val currentBookmarkDatabaseId = bookmarksListView.getItemIdAtPosition(i).toInt()
319 // Update the display order for the current bookmark.
320 if (i == selectedBookmarkPosition) { // The current bookmark is the selected bookmark.
321 // Move the current bookmark up one.
322 bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i - 1)
323 } else if ((i + 1) == selectedBookmarkPosition) { // The current bookmark is immediately above the selected bookmark.
324 // Move the current bookmark down one.
325 bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i + 1)
326 } else { // The current bookmark is not changing positions.
327 // Move the bookmarks cursor to the current bookmark position.
328 bookmarksCursor.moveToPosition(i)
330 // Update the display order only if it is not correct in the database. This fixes problems where the display order somehow got out of sync.
331 if (bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(DISPLAY_ORDER)) != i)
332 bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i)
336 // Update the bookmarks cursor with the current contents of the bookmarks database.
337 bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
339 // Update the list view.
340 bookmarksCursorAdapter.changeCursor(bookmarksCursor)
342 // Scroll to the new bookmark position.
343 scrollBookmarks(selectedBookmarkNewPosition)
345 // Update the enabled status of the move icons.
347 } else if (menuItemId == R.id.move_bookmark_down) { // Move the bookmark down.
348 // Get the array of checked bookmark positions.
349 selectedBookmarksPositionsSparseBooleanArray = bookmarksListView.checkedItemPositions
351 // 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`.
352 for (i in 0 until selectedBookmarksPositionsSparseBooleanArray.size()) {
353 // Check to see if the value for the bookmark is true, meaning it is currently selected.
354 if (selectedBookmarksPositionsSparseBooleanArray.valueAt(i)) {
355 // Only one bookmark should have a value of `true` when move bookmark down is enabled.
356 selectedBookmarkPosition = selectedBookmarksPositionsSparseBooleanArray.keyAt(i)
360 // Calculate the new position of the selected bookmark.
361 selectedBookmarkNewPosition = selectedBookmarkPosition + 1
363 // Iterate through the bookmarks.
364 for (i in 0 until bookmarksListView.count) {
365 // Get the database ID for the current bookmark.
366 val currentBookmarkDatabaseId = bookmarksListView.getItemIdAtPosition(i).toInt()
368 // Update the display order for the current bookmark.
369 if (i == selectedBookmarkPosition) { // The current bookmark is the selected bookmark.
370 // Move the current bookmark down one.
371 bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i + 1)
372 } else if (i - 1 == selectedBookmarkPosition) { // The current bookmark is immediately below the selected bookmark.
373 // Move the bookmark below the selected bookmark up one.
374 bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i - 1)
375 } else { // The current bookmark is not changing positions.
376 // Move the bookmarks cursor to the current bookmark position.
377 bookmarksCursor.moveToPosition(i)
379 // Update the display order only if it is not correct in the database. This fixes problems where the display order somehow got out of sync.
380 if (bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(DISPLAY_ORDER)) != i) {
381 bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i)
386 // Update the bookmarks cursor with the current contents of the bookmarks database.
387 bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
389 // Update the list view.
390 bookmarksCursorAdapter.changeCursor(bookmarksCursor)
392 // Scroll to the new bookmark position.
393 scrollBookmarks(selectedBookmarkNewPosition)
395 // Update the enabled status of the move icons.
397 } else if (menuItemId == R.id.move_to_folder) { // Move to folder.
398 // Instantiate the move to folder alert dialog.
399 val moveToFolderDialog = MoveToFolderDialog.moveBookmarks(currentFolderId, bookmarksListView.checkedItemIds)
401 // Show the move to folder alert dialog.
402 moveToFolderDialog.show(supportFragmentManager, resources.getString(R.string.move_to_folder))
403 } else if (menuItemId == R.id.edit_bookmark) {
404 // Get the array of checked bookmark positions.
405 selectedBookmarksPositionsSparseBooleanArray = bookmarksListView.checkedItemPositions
407 // 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`.
408 for (i in 0 until selectedBookmarksPositionsSparseBooleanArray.size()) {
409 // Check to see if the value for the bookmark is true, meaning it is currently selected.
410 if (selectedBookmarksPositionsSparseBooleanArray.valueAt(i)) {
411 // Only one bookmark should have a value of `true` when move edit bookmark is enabled.
412 selectedBookmarkPosition = selectedBookmarksPositionsSparseBooleanArray.keyAt(i)
416 // Move the cursor to the selected position.
417 bookmarksCursor.moveToPosition(selectedBookmarkPosition)
419 // Get the selected bookmark database ID.
420 val databaseId = bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(ID))
422 // Show the edit bookmark or edit bookmark folder dialog.
423 if (bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(IS_FOLDER)) == 1) { // A folder is selected.
424 // Instantiate the edit bookmark folder dialog.
425 val editFolderDialog = EditBookmarkFolderDialog.editFolder(databaseId, currentFavoriteIconBitmap)
428 editFolderDialog.show(supportFragmentManager, resources.getString(R.string.edit_folder))
429 } else { // A bookmark is selected.
430 // Instantiate the edit bookmark dialog.
431 val editBookmarkDialog = EditBookmarkDialog.editBookmark(databaseId, currentFavoriteIconBitmap)
434 editBookmarkDialog.show(supportFragmentManager, resources.getString(R.string.edit_bookmark))
436 } else if (menuItemId == R.id.delete_bookmark) { // Delete bookmark.
437 // Set the deleting bookmarks flag, which prevents the delete menu item from being enabled until the current process finishes.
438 deletingBookmarks = true
440 // Get an array of the selected row IDs.
441 val selectedBookmarksIdsLongArray = bookmarksListView.checkedItemIds
443 // Initialize a variable to count the number of bookmarks to delete.
444 var numberOfBookmarksToDelete = 0
446 // Count the number of bookmarks to delete.
447 for (databaseIdLong in selectedBookmarksIdsLongArray) {
448 // Convert the database ID long to an int.
449 val databaseIdInt = databaseIdLong.toInt()
451 // Count the contents of the folder if the selected bookmark is a folder.
452 if (bookmarksDatabaseHelper.isFolder(databaseIdInt)) {
453 // Add the bookmarks from the folder to the running total.
454 numberOfBookmarksToDelete += countBookmarkFolderContents(databaseIdInt)
457 // Increment the count of the number of bookmarks to delete.
458 numberOfBookmarksToDelete++
461 // 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.
462 selectedBookmarksPositionsSparseBooleanArray = bookmarksListView.checkedItemPositions.clone()
464 // Update the bookmarks cursor with the current contents of the bookmarks database except for the specified database IDs.
465 bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrderExcept(selectedBookmarksIdsLongArray, currentFolderId)
467 // Update the list view.
468 bookmarksCursorAdapter.changeCursor(bookmarksCursor)
470 // Create a Snackbar with the number of deleted bookmarks.
471 bookmarksDeletedSnackbar = Snackbar.make(findViewById(R.id.bookmarks_coordinatorlayout), getString(R.string.bookmarks_deleted, numberOfBookmarksToDelete), Snackbar.LENGTH_LONG)
472 .setAction(R.string.undo) { } // Do nothing because everything will be handled by `onDismissed()` below.
473 .addCallback(object : Snackbar.Callback() {
474 override fun onDismissed(snackbar: Snackbar, event: Int) {
475 if (event == DISMISS_EVENT_ACTION) { // The user pushed the undo button.
476 // Update the bookmarks cursor with the current contents of the bookmarks database, including the "deleted" bookmarks.
477 bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
479 // Update the list view.
480 bookmarksCursorAdapter.changeCursor(bookmarksCursor)
482 // Re-select the previously selected bookmarks.
483 for (i in 0 until selectedBookmarksPositionsSparseBooleanArray.size())
484 bookmarksListView.setItemChecked(selectedBookmarksPositionsSparseBooleanArray.keyAt(i), true)
485 } else { // The snackbar was dismissed without the undo button being pushed.
486 // Delete each selected bookmark.
487 for (databaseIdLong in selectedBookmarksIdsLongArray) {
488 // Convert the database long ID to an int.
489 val databaseIdInt = databaseIdLong.toInt()
491 // Delete the contents of the folder if the selected bookmark is a folder.
492 if (bookmarksDatabaseHelper.isFolder(databaseIdInt))
493 deleteBookmarkFolderContents(databaseIdInt)
495 // Delete the selected bookmark.
496 bookmarksDatabaseHelper.deleteBookmark(databaseIdInt)
499 // Update the display order.
500 for (i in 0 until bookmarksListView.count) {
501 // Get the database ID for the current bookmark.
502 val currentBookmarkDatabaseId = bookmarksListView.getItemIdAtPosition(i).toInt()
504 // Move bookmarks cursor to the current bookmark position.
505 bookmarksCursor.moveToPosition(i)
507 // Update the display order only if it is not correct in the database.
508 if (bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(DISPLAY_ORDER)) != i)
509 bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i)
513 // Reset the deleting bookmarks flag.
514 deletingBookmarks = false
516 // Enable the delete bookmarks menu item.
517 deleteBookmarksMenuItem.isEnabled = true
519 // Close the activity if back has been pressed.
520 if (closeActivityAfterDismissingSnackbar) {
521 // Close the bookmarks drawer and reload the bookmarks list view when returning to the main WebView activity.
522 MainWebViewActivity.restartFromBookmarksActivity = true
524 // Finish the activity.
530 // Show the Snackbar.
531 bookmarksDeletedSnackbar!!.show()
532 } else if (menuItemId == R.id.context_menu_select_all_bookmarks) { // Select all.
533 // Get the total number of bookmarks.
534 val numberOfBookmarks = bookmarksListView.count
537 for (i in 0 until numberOfBookmarks) {
538 bookmarksListView.setItemChecked(i, true)
542 // Consume the click.
546 override fun onDestroyActionMode(mode: ActionMode) {
551 // Set the create new bookmark folder FAB to display the alert dialog.
552 createBookmarkFolderFab.setOnClickListener {
553 // Create a create bookmark folder dialog.
554 val createBookmarkFolderDialog = CreateBookmarkFolderDialog.createBookmarkFolder(currentFavoriteIconBitmap)
556 // Show the create bookmark folder dialog.
557 createBookmarkFolderDialog.show(supportFragmentManager, getString(R.string.create_folder))
560 // Set the create new bookmark FAB to display the alert dialog.
561 createBookmarkFab.setOnClickListener {
562 // Instantiate the create bookmark dialog.
563 val createBookmarkDialog = CreateBookmarkDialog.createBookmark(currentUrl, currentTitle, currentFavoriteIconBitmap)
565 // Display the create bookmark dialog.
566 createBookmarkDialog.show(supportFragmentManager, resources.getString(R.string.create_bookmark))
569 // Restore the state if the app has been restarted.
570 if (savedInstanceState != null) {
571 // Restore the current folder.
572 currentFolderId = savedInstanceState.getLong(CURRENT_FOLDER_ID, HOME_FOLDER_ID)
574 // Update the bookmarks list view after it has loaded.
575 bookmarksListView.post {
576 // Get the checked bookmarks array list.
577 val checkedBookmarksArrayList = savedInstanceState.getIntegerArrayList(CHECKED_BOOKMARKS_ARRAY_LIST)!!
579 // Check each previously checked bookmark in the list view.
580 checkedBookmarksArrayList.forEach(Consumer { position: Int -> bookmarksListView.setItemChecked(position, true) })
584 // Load the current folder.
588 public override fun onRestart() {
589 // Run the default commands.
592 // Update the list view if returning from the bookmarks database view activity.
593 if (restartFromBookmarksDatabaseViewActivity) {
594 // Load the current folder in the list view.
597 // Reset the restart from bookmarks database view activity flag.
598 restartFromBookmarksDatabaseViewActivity = false
602 public override fun onSaveInstanceState(savedInstanceState: Bundle) {
603 // Run the default commands.
604 super.onSaveInstanceState(savedInstanceState)
606 // Get the sparse boolean array of the checked items.
607 val checkedBookmarksSparseBooleanArray = bookmarksListView.checkedItemPositions
609 // Create a checked items array list.
610 val checkedBookmarksArrayList = ArrayList<Int>()
612 // Add each checked bookmark position to the array list.
613 for (i in 0 until checkedBookmarksSparseBooleanArray.size()) {
614 // Check to see if the bookmark is currently checked. Bookmarks that have previously been checked but currently aren't will be populated in the sparse boolean array, but will return false.
615 if (checkedBookmarksSparseBooleanArray.valueAt(i)) {
616 // Add the bookmark position to the checked bookmarks array list.
617 checkedBookmarksArrayList.add(checkedBookmarksSparseBooleanArray.keyAt(i))
621 // Store the variables in the saved instance state.
622 savedInstanceState.putLong(CURRENT_FOLDER_ID, currentFolderId)
623 savedInstanceState.putIntegerArrayList(CHECKED_BOOKMARKS_ARRAY_LIST, checkedBookmarksArrayList)
626 override fun onCreateOptionsMenu(menu: Menu): Boolean {
628 menuInflater.inflate(R.menu.bookmarks_options_menu, menu)
634 override fun onOptionsItemSelected(menuItem: MenuItem): Boolean {
635 // Get a handle for the menu item ID.
636 val menuItemId = menuItem.itemId
638 // Run the command according to the selected option.
639 if (menuItemId == android.R.id.home) { // Home. The home arrow is identified as `android.R.id.home`, not just `R.id.home`.
640 if (currentFolderId == HOME_FOLDER_ID) { // The current folder is the home folder.
641 // Prepare to finish the activity.
643 } else { // Currently in a subfolder.
644 // Set the former parent folder as the current folder.
645 currentFolderId = bookmarksDatabaseHelper.getParentFolderId(currentFolderId)
647 // Load the new current folder.
650 } else if (menuItemId == R.id.options_menu_select_all_bookmarks) { // Select all.
651 // Get the total number of bookmarks.
652 val numberOfBookmarks = bookmarksListView.count
655 for (i in 0 until numberOfBookmarks) {
656 bookmarksListView.setItemChecked(i, true)
658 } else if (menuItemId == R.id.bookmarks_database_view) {
659 // Close the contextual action bar if it is displayed. This can happen if the bottom app bar is enabled.
660 contextualActionMode?.finish()
662 // Create an intent to launch the bookmarks database view activity.
663 val bookmarksDatabaseViewIntent = Intent(this, BookmarksDatabaseViewActivity::class.java)
665 // Include the favorite icon byte array to the intent.
666 bookmarksDatabaseViewIntent.putExtra(CURRENT_FAVORITE_ICON_BYTE_ARRAY, currentFavoriteIconByteArray)
669 startActivity(bookmarksDatabaseViewIntent)
674 override fun createBookmark(dialogFragment: DialogFragment, favoriteIconBitmap: Bitmap) {
675 // Get the alert dialog from the fragment.
676 val dialog = dialogFragment.dialog!!
678 // Get the views from the dialog fragment.
679 val createBookmarkNameEditText = dialog.findViewById<EditText>(R.id.create_bookmark_name_edittext)
680 val createBookmarkUrlEditText = dialog.findViewById<EditText>(R.id.create_bookmark_url_edittext)
682 // Extract the strings from the edit texts.
683 val bookmarkNameString = createBookmarkNameEditText.text.toString()
684 val bookmarkUrlString = createBookmarkUrlEditText.text.toString()
686 // Create a favorite icon byte array output stream.
687 val favoriteIconByteArrayOutputStream = ByteArrayOutputStream()
689 // Convert the favorite icon bitmap to a byte array. `0` is for lossless compression (the only option for a PNG).
690 favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream)
692 // Convert the favorite icon byte array stream to a byte array.
693 val favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray()
695 // Display the new bookmark below the current items in the (0 indexed) list.
696 val newBookmarkDisplayOrder = bookmarksListView.count
698 // Create the bookmark.
699 bookmarksDatabaseHelper.createBookmark(bookmarkNameString, bookmarkUrlString, currentFolderId, newBookmarkDisplayOrder, favoriteIconByteArray)
701 // Update the bookmarks cursor with the current contents of this folder.
702 bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
704 // Update the list view.
705 bookmarksCursorAdapter.changeCursor(bookmarksCursor)
707 // Scroll to the new bookmark.
708 bookmarksListView.setSelection(newBookmarkDisplayOrder)
711 override fun createBookmarkFolder(dialogFragment: DialogFragment, favoriteIconBitmap: Bitmap) {
712 // Get the dialog from the dialog fragment.
713 val dialog = dialogFragment.dialog!!
715 // Get handles for the views in the dialog fragment.
716 val folderNameEditText = dialog.findViewById<EditText>(R.id.folder_name_edittext)
717 val defaultIconRadioButton = dialog.findViewById<RadioButton>(R.id.default_icon_radiobutton)
718 val defaultIconImageView = dialog.findViewById<ImageView>(R.id.default_icon_imageview)
720 // Get new folder name string.
721 val folderNameString = folderNameEditText.text.toString()
723 // Set the folder icon bitmap according to the dialog.
724 val folderIconBitmap = if (defaultIconRadioButton.isChecked) { // Use the default folder icon.
725 // Get the default folder icon drawable.
726 val folderIconDrawable = defaultIconImageView.drawable
728 // Convert the folder icon drawable to a bitmap drawable.
729 val folderIconBitmapDrawable = folderIconDrawable as BitmapDrawable
731 // Convert the folder icon bitmap drawable to a bitmap.
732 folderIconBitmapDrawable.bitmap
733 } else { // Use the WebView favorite icon.
734 // Copy the favorite icon bitmap to the folder icon bitmap.
738 // Create a folder icon byte array output stream.
739 val folderIconByteArrayOutputStream = ByteArrayOutputStream()
741 // Convert the folder icon bitmap to a byte array. `0` is for lossless compression (the only option for a PNG).
742 folderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, folderIconByteArrayOutputStream)
744 // Convert the folder icon byte array stream to a byte array.
745 val folderIconByteArray = folderIconByteArrayOutputStream.toByteArray()
747 // Move all the bookmarks down one in the display order.
748 for (i in 0 until bookmarksListView.count) {
749 val databaseId = bookmarksListView.getItemIdAtPosition(i).toInt()
750 bookmarksDatabaseHelper.updateDisplayOrder(databaseId, i + 1)
753 // Create the folder, which will be placed at the top of the list view.
754 bookmarksDatabaseHelper.createFolder(folderNameString, currentFolderId, folderIconByteArray)
756 // Update the bookmarks cursor with the contents of the current folder.
757 bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
759 // Update the list view.
760 bookmarksCursorAdapter.changeCursor(bookmarksCursor)
762 // Scroll to the new folder.
763 bookmarksListView.setSelection(0)
766 override fun onSaveBookmark(dialogFragment: DialogFragment, selectedBookmarkDatabaseId: Int, favoriteIconBitmap: Bitmap) {
767 // Get the dialog from the dialog fragment.
768 val dialog = dialogFragment.dialog!!
770 // Get handles for the views from the dialog fragment.
771 val bookmarkNameEditText = dialog.findViewById<EditText>(R.id.bookmark_name_edittext)
772 val bookmarkUrlEditText = dialog.findViewById<EditText>(R.id.bookmark_url_edittext)
773 val currentIconRadioButton = dialog.findViewById<RadioButton>(R.id.current_icon_radiobutton)
775 // Get the bookmark strings.
776 val bookmarkNameString = bookmarkNameEditText.text.toString()
777 val bookmarkUrlString = bookmarkUrlEditText.text.toString()
779 // Update the bookmark.
780 if (currentIconRadioButton.isChecked) { // Update the bookmark without changing the favorite icon.
781 bookmarksDatabaseHelper.updateBookmark(selectedBookmarkDatabaseId, bookmarkNameString, bookmarkUrlString)
782 } else { // Update the bookmark using the WebView favorite icon.
783 // Create a favorite icon byte array output stream.
784 val newFavoriteIconByteArrayOutputStream = ByteArrayOutputStream()
786 // Convert the favorite icon bitmap to a byte array. `0` is for lossless compression (the only option for a PNG).
787 favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, newFavoriteIconByteArrayOutputStream)
789 // Convert the favorite icon byte array stream to a byte array.
790 val newFavoriteIconByteArray = newFavoriteIconByteArrayOutputStream.toByteArray()
792 // Update the bookmark and the favorite icon.
793 bookmarksDatabaseHelper.updateBookmark(selectedBookmarkDatabaseId, bookmarkNameString, bookmarkUrlString, newFavoriteIconByteArray)
796 // Close the contextual action bar if it is displayed.
797 contextualActionMode?.finish()
799 // Update the bookmarks cursor with the contents of the current folder.
800 bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
802 // Update the list view.
803 bookmarksCursorAdapter.changeCursor(bookmarksCursor)
806 override fun onSaveBookmarkFolder(dialogFragment: DialogFragment, selectedFolderDatabaseId: Int, favoriteIconBitmap: Bitmap) {
807 // Get the dialog from the dialog fragment.
808 val dialog = dialogFragment.dialog!!
810 // Get handles for the views from the dialog fragment.
811 val currentFolderIconRadioButton = dialog.findViewById<RadioButton>(R.id.current_icon_radiobutton)
812 val defaultFolderIconRadioButton = dialog.findViewById<RadioButton>(R.id.default_icon_radiobutton)
813 val defaultFolderIconImageView = dialog.findViewById<ImageView>(R.id.default_icon_imageview)
814 val editFolderNameEditText = dialog.findViewById<EditText>(R.id.folder_name_edittext)
816 // Get the new folder name.
817 val newFolderName = editFolderNameEditText.text.toString()
819 // Check if the favorite icon has changed.
820 if (currentFolderIconRadioButton.isChecked) { // Only the name has changed.
821 // Update the name in the database.
822 bookmarksDatabaseHelper.updateFolder(selectedFolderDatabaseId, newFolderName)
823 } else { // The icon has changed.
824 // Populate the new folder icon bitmap.
825 val folderIconBitmap: Bitmap = if (defaultFolderIconRadioButton.isChecked) {
826 // Get the default folder icon drawable.
827 val folderIconDrawable = defaultFolderIconImageView.drawable
829 // Convert the folder icon drawable to a bitmap drawable.
830 val folderIconBitmapDrawable = folderIconDrawable as BitmapDrawable
832 // Convert the folder icon bitmap drawable to a bitmap.
833 folderIconBitmapDrawable.bitmap
834 } else { // Use the WebView favorite icon.
835 // Copy the favorite icon bitmap to the folder icon bitmap.
839 // Create a folder icon byte array output stream.
840 val newFolderIconByteArrayOutputStream = ByteArrayOutputStream()
842 // Convert the folder icon bitmap to a byte array. `0` is for lossless compression (the only option for a PNG).
843 folderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, newFolderIconByteArrayOutputStream)
845 // Convert the folder icon byte array stream to a byte array.
846 val newFolderIconByteArray = newFolderIconByteArrayOutputStream.toByteArray()
848 // Update the database.
849 bookmarksDatabaseHelper.updateFolder(selectedFolderDatabaseId, newFolderName, newFolderIconByteArray)
852 // Update the bookmarks cursor with the current contents of this folder.
853 bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
855 // Update the list view.
856 bookmarksCursorAdapter.changeCursor(bookmarksCursor)
858 // Close the contextual action mode.
859 contextualActionMode!!.finish()
862 override fun onMoveToFolder(dialogFragment: DialogFragment) {
863 // Get the dialog from the dialog fragment.
864 val dialog = dialogFragment.dialog!!
866 // Get a handle for the folder list view from the dialog.
867 val folderListView = dialog.findViewById<ListView>(R.id.move_to_folder_listview)
869 // Store a long array of the selected folders.
870 val newFolderLongArray = folderListView.checkedItemIds
872 // Get the new folder database ID. Only one folder will be selected so it will be the first one.
873 val newFolderDatabaseId = newFolderLongArray[0].toInt()
875 // Set the new folder name.
876 val newFolderId = if (newFolderDatabaseId == HOME_FOLDER_DATABASE_ID)
877 // The new folder is the home folder.
880 // Get the new folder name from the database.
881 bookmarksDatabaseHelper.getFolderId(newFolderDatabaseId)
883 // Get a long array with the the database ID of the selected bookmarks.
884 val selectedBookmarksLongArray = bookmarksListView.checkedItemIds
886 // Move each of the selected bookmarks to the new folder.
887 for (databaseIdLong in selectedBookmarksLongArray) {
888 // Convert the database long ID to an int for each selected bookmark.
889 val databaseIdInt = databaseIdLong.toInt()
891 // Move the selected bookmark to the new folder.
892 bookmarksDatabaseHelper.moveToFolder(databaseIdInt, newFolderId)
895 // Update the bookmarks cursor with the current contents of this folder.
896 bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
898 // Update the list view.
899 bookmarksCursorAdapter.changeCursor(bookmarksCursor)
901 // Close the contextual app bar.
902 contextualActionMode!!.finish()
905 private fun countBookmarkFolderContents(folderDatabaseId: Int): Int {
906 // Get the folder ID.
907 val folderId = bookmarksDatabaseHelper.getFolderId(folderDatabaseId)
909 // Get the contents of the folder.
910 val folderCursor = bookmarksDatabaseHelper.getBookmarkIds(folderId)
912 // Initialize the bookmark counter.
913 var bookmarkCounter = 0
915 // Count each of the bookmarks in the folder.
916 for (i in 0 until folderCursor.count) {
917 // Move the folder cursor to the current row.
918 folderCursor.moveToPosition(i)
920 // Get the database ID of the item.
921 val itemDatabaseId = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(ID))
923 // If this is a folder, recursively count the contents first.
924 if (bookmarksDatabaseHelper.isFolder(itemDatabaseId))
925 bookmarkCounter += countBookmarkFolderContents(itemDatabaseId)
927 // Add the bookmark to the running total.
931 // Return the bookmark counter.
932 return bookmarkCounter
935 private fun deleteBookmarkFolderContents(folderDatabaseId: Int) {
936 // Get the folder ID.
937 val folderId = bookmarksDatabaseHelper.getFolderId(folderDatabaseId)
939 // Get the contents of the folder.
940 val folderCursor = bookmarksDatabaseHelper.getBookmarkIds(folderId)
942 // Delete each of the bookmarks in the folder.
943 for (i in 0 until folderCursor.count) {
944 // Move the folder cursor to the current row.
945 folderCursor.moveToPosition(i)
947 // Get the database ID of the item.
948 val itemDatabaseId = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(ID))
950 // If this is a folder, recursively delete the contents first.
951 if (bookmarksDatabaseHelper.isFolder(itemDatabaseId))
952 deleteBookmarkFolderContents(itemDatabaseId)
954 // Delete the bookmark.
955 bookmarksDatabaseHelper.deleteBookmark(itemDatabaseId)
959 private fun prepareFinish() {
960 // Check to see if a snackbar is currently displayed. If so, it must be closed before exiting so that a pending delete is completed before reloading the list view in the bookmarks drawer.
961 if (bookmarksDeletedSnackbar != null && bookmarksDeletedSnackbar!!.isShown) { // Close the bookmarks deleted snackbar before going home.
962 // Set the close flag.
963 closeActivityAfterDismissingSnackbar = true
965 // Dismiss the snackbar.
966 bookmarksDeletedSnackbar!!.dismiss()
967 } else { // Go home immediately.
968 // Update the bookmarks folder for the bookmarks drawer in the main WebView activity.
969 MainWebViewActivity.currentBookmarksFolderId = currentFolderId
971 // Close the bookmarks drawer and reload the bookmarks list view when returning to the main WebView activity.
972 MainWebViewActivity.restartFromBookmarksActivity = true
974 // Exit the bookmarks activity.
979 private fun updateMoveIcons() {
980 // Get a long array of the selected bookmarks.
981 val selectedBookmarksLongArray = bookmarksListView.checkedItemIds
983 // Get the database IDs for the first, last, and selected bookmarks.
984 val firstBookmarkDatabaseId = bookmarksListView.getItemIdAtPosition(0).toInt()
985 val lastBookmarkDatabaseId = bookmarksListView.getItemIdAtPosition(bookmarksListView.count - 1).toInt() // The bookmarks list view is 0 indexed.
986 val selectedBookmarkDatabaseId = selectedBookmarksLongArray[0].toInt()
988 // Update the move bookmark up menu item.
989 if (selectedBookmarkDatabaseId == firstBookmarkDatabaseId) { // The selected bookmark is in the first position.
990 // Disable the move bookmark up menu item.
991 moveBookmarkUpMenuItem.isEnabled = false
994 moveBookmarkUpMenuItem.setIcon(R.drawable.move_up_disabled)
995 } else { // The selected bookmark is not in the first position.
996 // Enable the move bookmark up menu item.
997 moveBookmarkUpMenuItem.isEnabled = true
999 // Set the icon according to the theme.
1000 moveBookmarkUpMenuItem.setIcon(R.drawable.move_up_enabled)
1003 // Update the move bookmark down menu item.
1004 if (selectedBookmarkDatabaseId == lastBookmarkDatabaseId) { // The selected bookmark is in the last position.
1005 // Disable the move bookmark down menu item.
1006 moveBookmarkDownMenuItem.isEnabled = false
1009 moveBookmarkDownMenuItem.setIcon(R.drawable.move_down_disabled)
1010 } else { // The selected bookmark is not in the last position.
1011 // Enable the move bookmark down menu item.
1012 moveBookmarkDownMenuItem.isEnabled = true
1015 moveBookmarkDownMenuItem.setIcon(R.drawable.move_down_enabled)
1019 private fun scrollBookmarks(selectedBookmarkPosition: Int) {
1020 // Get the first and last visible bookmark positions.
1021 val firstVisibleBookmarkPosition = bookmarksListView.firstVisiblePosition
1022 val lastVisibleBookmarkPosition = bookmarksListView.lastVisiblePosition
1024 // Calculate the number of bookmarks per screen.
1025 val numberOfBookmarksPerScreen = lastVisibleBookmarkPosition - firstVisibleBookmarkPosition
1027 // Scroll with the moved bookmark if necessary.
1028 if (selectedBookmarkPosition <= firstVisibleBookmarkPosition) { // The selected bookmark position is at or above the top of the screen.
1029 // Scroll to the selected bookmark position.
1030 bookmarksListView.setSelection(selectedBookmarkPosition)
1031 } else if (selectedBookmarkPosition >= lastVisibleBookmarkPosition - 1) { // The selected bookmark is at or below the bottom of the screen.
1032 // The `-1` handles partial bookmarks displayed at the bottom of the list view. This command scrolls to display the selected bookmark at the bottom of the screen.
1033 // `+1` assures that the entire bookmark will be displayed in situations where only a partial bookmark fits at the bottom of the list view.
1034 bookmarksListView.setSelection(selectedBookmarkPosition - numberOfBookmarksPerScreen + 1)
1038 private fun loadFolder() {
1039 // Update the bookmarks cursor with the contents of the bookmarks database for the current folder.
1040 bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderId)
1042 // Setup a cursor adapter.
1043 bookmarksCursorAdapter = object : CursorAdapter(this, bookmarksCursor, false) {
1044 override fun newView(context: Context, cursor: Cursor, parent: ViewGroup): View {
1045 // Inflate the individual item layout.
1046 return layoutInflater.inflate(R.layout.bookmarks_activity_item_linearlayout, parent, false)
1049 override fun bindView(view: View, context: Context, cursor: Cursor) {
1050 // Get handles for the views.
1051 val bookmarkFavoriteIconImageView = view.findViewById<ImageView>(R.id.bookmark_favorite_icon)
1052 val bookmarkNameTextView = view.findViewById<TextView>(R.id.bookmark_name)
1054 // Get the favorite icon byte array from the cursor.
1055 val favoriteIconByteArray = cursor.getBlob(cursor.getColumnIndexOrThrow(FAVORITE_ICON))
1057 // Convert the byte array to a bitmap beginning at the first byte and ending at the last.
1058 val favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.size)
1060 // Display the bitmap in the bookmark favorite icon image view.
1061 bookmarkFavoriteIconImageView.setImageBitmap(favoriteIconBitmap)
1063 // Get the bookmark name from the cursor.
1064 val bookmarkNameString = cursor.getString(cursor.getColumnIndexOrThrow(BOOKMARK_NAME))
1066 // Display the bookmark name.
1067 bookmarkNameTextView.text = bookmarkNameString
1069 // Make the font bold for folders.
1070 if (cursor.getInt(cursor.getColumnIndexOrThrow(IS_FOLDER)) == 1)
1071 bookmarkNameTextView.typeface = Typeface.DEFAULT_BOLD
1072 else // Reset the font to default for normal bookmarks.
1073 bookmarkNameTextView.typeface = Typeface.DEFAULT
1077 // Populate the list view with the adapter.
1078 bookmarksListView.adapter = bookmarksCursorAdapter
1080 // Set the app bar title.
1081 if (currentFolderId == HOME_FOLDER_ID) // The home folder is the current folder.
1082 appBar.setTitle(R.string.bookmarks)
1084 appBar.title = bookmarksDatabaseHelper.getFolderName(currentFolderId)
1087 public override fun onDestroy() {
1088 // Close the bookmarks cursor and database.
1089 bookmarksCursor.close()
1090 bookmarksDatabaseHelper.close()
1092 // Run the default commands.