Convert LogcatActivity to Kotlin. https://redmine.stoutner.com/issues/770
[PrivacyBrowser.git] / app / src / main / java / com / stoutner / privacybrowser / dialogs / UrlHistoryDialog.kt
1 /*
2  * Copyright © 2016-2021 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
5  *
6  * Privacy Browser 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.
10  *
11  * Privacy Browser 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.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 package com.stoutner.privacybrowser.dialogs
21
22 import android.annotation.SuppressLint
23 import android.app.Dialog
24 import android.content.Context
25 import android.content.DialogInterface
26 import android.graphics.drawable.BitmapDrawable
27 import android.os.Bundle
28 import android.view.View
29 import android.view.WindowManager
30 import android.widget.AdapterView
31 import android.widget.AdapterView.OnItemClickListener
32 import android.widget.ListView
33 import android.widget.TextView
34
35 import androidx.appcompat.app.AlertDialog
36 import androidx.core.content.ContextCompat
37 import androidx.fragment.app.DialogFragment
38 import androidx.preference.PreferenceManager
39
40 import com.stoutner.privacybrowser.R
41 import com.stoutner.privacybrowser.activities.MainWebViewActivity
42 import com.stoutner.privacybrowser.adapters.HistoryArrayAdapter
43 import com.stoutner.privacybrowser.dataclasses.History
44 import com.stoutner.privacybrowser.views.NestedScrollWebView
45
46 // Define the class constants.
47 private const val WEBVIEW_FRAGMENT_ID = "webview_fragment_id"
48
49 class UrlHistoryDialog : DialogFragment() {
50     // Declare the class variables.
51     private lateinit var navigateHistoryListener: NavigateHistoryListener
52
53     // The public interface is used to send information back to the parent activity.
54     interface NavigateHistoryListener {
55         fun navigateHistory(url: String, steps: Int)
56     }
57
58     override fun onAttach(context: Context) {
59         // Run the default commands.
60         super.onAttach(context)
61
62         // Get a handle for the listener from the launching context.
63         navigateHistoryListener = context as NavigateHistoryListener
64     }
65
66     companion object {
67         // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin.
68         @JvmStatic
69         fun loadBackForwardList(webViewFragmentId: Long): UrlHistoryDialog {
70             // Create an arguments bundle.
71             val argumentsBundle = Bundle()
72
73             // Store the WebView fragment ID in the bundle.
74             argumentsBundle.putLong(WEBVIEW_FRAGMENT_ID, webViewFragmentId)
75
76             // Create a new instance of the URL history dialog.
77             val urlHistoryDialog = UrlHistoryDialog()
78
79             // Add the arguments bundle to the new dialog.
80             urlHistoryDialog.arguments = argumentsBundle
81
82             // Return the new dialog.
83             return urlHistoryDialog
84         }
85     }
86
87     // `@SuppressLint("InflateParams")` removes the warning about using null as the parent view group when inflating the alert dialog.
88     @SuppressLint("InflateParams")
89     override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
90         // Get the WebView fragment ID from the arguments.
91         val webViewFragmentId = requireArguments().getLong(WEBVIEW_FRAGMENT_ID)
92
93         // Get the current position of this WebView fragment.
94         val webViewPosition = MainWebViewActivity.webViewPagerAdapter.getPositionForId(webViewFragmentId)
95
96         // Get the WebView tab fragment.
97         val webViewTabFragment = MainWebViewActivity.webViewPagerAdapter.getPageFragment(webViewPosition)
98
99         // Get the fragment view.
100         val fragmentView = webViewTabFragment.requireView()
101
102         // Get a handle for the current nested scroll WebView.
103         val nestedScrollWebView: NestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview)
104
105         // Get the web back forward list from the nested scroll WebView.
106         val webBackForwardList = nestedScrollWebView.copyBackForwardList()
107
108         // Store the current page index.
109         val currentPageIndex = webBackForwardList.currentIndex
110
111         // Get the default favorite icon drawable.  `ContextCompat` must be used until the minimum API >= 21.
112         val defaultFavoriteIconDrawable = ContextCompat.getDrawable(requireContext(), R.drawable.world)
113
114         // Convert the default favorite icon drawable to a bitmap drawable.
115         val defaultFavoriteIconBitmapDrawable = (defaultFavoriteIconDrawable as BitmapDrawable)
116
117         // Extract a bitmap from the default favorite icon bitmap drawable.
118         val defaultFavoriteIcon = defaultFavoriteIconBitmapDrawable.bitmap
119
120         // Create a history array list.
121         val historyArrayList = ArrayList<History>()
122
123         // Populate the history array list, descending from the end of the list so that the newest entries are at the top.  `-1` is needed because the history array list is zero-based.
124         for (i in webBackForwardList.size - 1 downTo 0) {
125             // Store the favorite icon bitmap.
126             val favoriteIconBitmap = if (webBackForwardList.getItemAtIndex(i).favicon == null) {
127                 // If the web back forward list does not have a favorite icon, use Privacy Browser's default world icon.
128                 defaultFavoriteIcon
129             } else {  // Use the icon from the web back forward list.
130                 webBackForwardList.getItemAtIndex(i).favicon
131             }
132
133             // Store the favorite icon and the URL in history entry.
134             val historyEntry = History(favoriteIconBitmap!!, webBackForwardList.getItemAtIndex(i).url)
135
136             // Add this history entry to the history array list.
137             historyArrayList.add(historyEntry)
138         }
139
140         // Subtract the original current page ID from the array size because the order of the array is reversed so that the newest entries are at the top.  `-1` is needed because the array is zero-based.
141         val currentPageId = webBackForwardList.size - 1 - currentPageIndex
142
143         // Use an alert dialog builder to create the alert dialog.
144         val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog)
145
146         // Set the title.
147         dialogBuilder.setTitle(R.string.history)
148
149         // Set the view.  The parent view is `null` because it will be assigned by the alert dialog.
150         dialogBuilder.setView(layoutInflater.inflate(R.layout.url_history_dialog, null))
151
152         // Setup the clear history button listener.
153         dialogBuilder.setNegativeButton(R.string.clear_history) { _: DialogInterface, _: Int ->
154             // Clear the history.
155             nestedScrollWebView.clearHistory()
156         }
157
158         // Set the close button listener.  Using `null` as the listener closes the dialog without doing anything else.
159         dialogBuilder.setPositiveButton(R.string.close, null)
160
161         // Create an alert dialog from the alert dialog builder.
162         val alertDialog = dialogBuilder.create()
163
164         // Get a handle for the shared preferences.
165         val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
166
167         // Get the screenshot preference.
168         val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
169
170         // Disable screenshots if not allowed.
171         if (!allowScreenshots) {
172             // Disable screenshots.
173             alertDialog.window!!.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
174         }
175
176         //The alert dialog must be shown before the contents can be modified.
177         alertDialog.show()
178
179         // Instantiate a history array adapter.
180         val historyArrayAdapter = HistoryArrayAdapter(context, historyArrayList, currentPageId)
181
182         // Get a handle for the list view.
183         val listView = alertDialog.findViewById<ListView>(R.id.history_listview)!!
184
185         // Set the list view adapter.
186         listView.adapter = historyArrayAdapter
187
188         // Listen for clicks on entries in the list view.
189         listView.onItemClickListener = OnItemClickListener { _: AdapterView<*>?, view: View, _: Int, id: Long ->
190             // Convert the long ID to an int.
191             val itemId = id.toInt()
192
193             // Only consume the click if it is not on the current page ID.
194             if (itemId != currentPageId) {
195                 // Get a handle for the URL text view.
196                 val urlTextView = view.findViewById<TextView>(R.id.history_url_textview)
197
198                 // Get the URL.
199                 val url = urlTextView.text.toString()
200
201                 // Invoke the navigate history listener in the calling activity.  These commands cannot be run here because they need access to `applyDomainSettings()`.
202                 navigateHistoryListener.navigateHistory(url, currentPageId - itemId)
203
204                 // Dismiss the alert dialog.
205                 alertDialog.dismiss()
206             }
207         }
208
209         // Return the alert dialog.
210         return alertDialog
211     }
212 }