2 * Copyright © 2019-2021 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
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.
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.
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/>.
20 package com.stoutner.privacybrowser.dialogs
22 import android.Manifest
23 import android.annotation.SuppressLint
24 import android.app.Dialog
25 import android.content.Context
26 import android.content.DialogInterface
27 import android.content.Intent
28 import android.content.pm.PackageManager
29 import android.content.res.Configuration
30 import android.os.Bundle
31 import android.text.Editable
32 import android.text.TextWatcher
33 import android.view.View
34 import android.view.WindowManager
35 import android.widget.Button
36 import android.widget.EditText
37 import android.widget.TextView
39 import androidx.appcompat.app.AlertDialog
40 import androidx.core.content.ContextCompat
41 import androidx.fragment.app.DialogFragment
42 import androidx.preference.PreferenceManager
44 import com.stoutner.privacybrowser.R
45 import com.stoutner.privacybrowser.activities.MainWebViewActivity
46 import com.stoutner.privacybrowser.helpers.DownloadLocationHelper
50 class OpenDialog : DialogFragment() {
51 // Define the open listener.
52 private lateinit var openListener: OpenListener
54 // The public interface is used to send information back to the parent activity.
55 interface OpenListener {
56 fun onOpen(dialogFragment: DialogFragment)
59 override fun onAttach(context: Context) {
60 // Run the default commands.
61 super.onAttach(context)
63 // Get a handle for the open listener from the launching context.
64 openListener = context as OpenListener
67 // `@SuppressLint("InflateParams")` removes the warning about using null as the parent view group when inflating the alert dialog.
68 @SuppressLint("InflateParams")
69 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
70 // Use an alert dialog builder to create the alert dialog.
71 val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog)
73 // Get the current theme status.
74 val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
76 // Set the icon according to the theme.
77 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
78 dialogBuilder.setIcon(R.drawable.proxy_enabled_day)
80 dialogBuilder.setIcon(R.drawable.proxy_enabled_night)
84 dialogBuilder.setTitle(R.string.open)
86 // Set the view. The parent view is null because it will be assigned by the alert dialog.
87 dialogBuilder.setView(requireActivity().layoutInflater.inflate(R.layout.open_dialog, null))
89 // Set the cancel button listener. Using `null` as the listener closes the dialog without doing anything else.
90 dialogBuilder.setNegativeButton(R.string.cancel, null)
92 // Set the open button listener.
93 dialogBuilder.setPositiveButton(R.string.open) { _: DialogInterface?, _: Int ->
94 // Return the dialog fragment to the parent activity.
95 openListener.onOpen(this)
98 // Create an alert dialog from the builder.
99 val alertDialog = dialogBuilder.create()
101 // Get a handle for the shared preferences.
102 val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
104 // Get the screenshot preference.
105 val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
107 // Disable screenshots if not allowed.
108 if (!allowScreenshots) {
109 alertDialog.window!!.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
112 // The alert dialog must be shown before items in the layout can be modified.
115 // Get handles for the layout items.
116 val fileNameEditText = alertDialog.findViewById<EditText>(R.id.file_name_edittext)!!
117 val browseButton = alertDialog.findViewById<Button>(R.id.browse_button)!!
118 val fileDoesNotExistTextView = alertDialog.findViewById<TextView>(R.id.file_does_not_exist_textview)!!
119 val storagePermissionTextView = alertDialog.findViewById<TextView>(R.id.storage_permission_textview)!!
120 val openButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
122 // Update the status of the open button when the file name changes.
123 fileNameEditText.addTextChangedListener(object : TextWatcher {
124 override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
128 override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
132 override fun afterTextChanged(editable: Editable) {
133 // Get the current file name.
134 val fileNameString = fileNameEditText.text.toString()
136 // Convert the file name string to a file.
137 val file = File(fileNameString)
139 // Check to see if the file exists.
140 if (file.exists()) { // The file exists.
141 // Hide the notification that the file does not exist.
142 fileDoesNotExistTextView.visibility = View.GONE
144 // Enable the open button.
145 openButton.isEnabled = true
146 } else { // The file does not exist.
147 // Show the notification that the file does not exist.
148 fileDoesNotExistTextView.visibility = View.VISIBLE
150 // Disable the open button.
151 openButton.isEnabled = false
156 // Instantiate the download location helper.
157 val downloadLocationHelper = DownloadLocationHelper()
159 // Get the default file path.
160 val defaultFilePath = downloadLocationHelper.getDownloadLocation(context) + "/"
162 // Display the default file path.
163 fileNameEditText.setText(defaultFilePath)
165 // Move the cursor to the end of the default file path.
166 fileNameEditText.setSelection(defaultFilePath.length)
168 // Hide the storage permission text view if the permission has already been granted.
169 if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
170 storagePermissionTextView.visibility = View.GONE
173 // Handle clicks on the browse button.
174 browseButton.setOnClickListener {
175 // Create the file picker intent.
176 val browseIntent = Intent(Intent.ACTION_OPEN_DOCUMENT)
178 // Set the intent MIME type to include all files so that everything is visible.
179 browseIntent.type = "*/*"
181 // Start the file picker. This must be started under `activity` to that the request code is returned correctly.
182 requireActivity().startActivityForResult(browseIntent, MainWebViewActivity.BROWSE_OPEN_REQUEST_CODE)
185 // Return the alert dialog.