2 * Copyright © 2018-2022 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.content.SharedPreferences;
25 import android.database.Cursor;
26 import android.database.MatrixCursor;
27 import android.os.Bundle;
28 import android.preference.PreferenceManager;
29 import android.view.View;
30 import android.view.WindowManager;
31 import android.widget.AdapterView;
32 import android.widget.ArrayAdapter;
33 import android.widget.ListView;
34 import android.widget.ResourceCursorAdapter;
35 import android.widget.Spinner;
36 import android.widget.TextView;
38 import androidx.annotation.NonNull;
39 import androidx.appcompat.app.ActionBar;
40 import androidx.appcompat.app.AppCompatActivity;
41 import androidx.appcompat.widget.Toolbar;
42 import androidx.fragment.app.DialogFragment;
44 import com.stoutner.privacybrowser.R;
45 import com.stoutner.privacybrowser.adapters.RequestsArrayAdapter;
46 import com.stoutner.privacybrowser.dialogs.ViewRequestDialog;
47 import com.stoutner.privacybrowser.helpers.BlocklistHelper;
49 import java.util.ArrayList;
50 import java.util.List;
52 public class RequestsActivity extends AppCompatActivity implements ViewRequestDialog.ViewRequestListener {
53 // The resource requests are populated by `MainWebViewActivity` before `RequestsActivity` is launched.
54 public static List<String[]> resourceRequests;
56 // Initialize the class constants.
57 private final String LISTVIEW_POSITION = "listview_position";
59 // Define the class views.
60 private ListView requestsListView;
63 public void onCreate(Bundle savedInstanceState) {
64 // Get a handle for the shared preferences.
65 SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
67 // Get the preferences.
68 boolean allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false);
69 boolean bottomAppBar = sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false);
71 // Disable screenshots if not allowed.
72 if (!allowScreenshots) {
73 getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
77 setTheme(R.style.PrivacyBrowser);
79 // Run the default commands.
80 super.onCreate(savedInstanceState);
82 // Get the launching intent
83 Intent intent = getIntent();
85 // Get the status of the third-party blocklist.
86 boolean blockAllThirdPartyRequests = intent.getBooleanExtra("block_all_third_party_requests", false);
88 // Set the content view.
90 setContentView(R.layout.requests_bottom_appbar);
92 setContentView(R.layout.requests_top_appbar);
95 // Use the AndroidX toolbar until the minimum API is >= 21.
96 Toolbar toolbar = findViewById(R.id.requests_toolbar);
97 setSupportActionBar(toolbar);
99 // Get a handle for the app bar and the list view.
100 ActionBar appBar = getSupportActionBar();
101 requestsListView = findViewById(R.id.requests_listview);
103 // Remove the incorrect lint warning that `appBar` might be null.
104 assert appBar != null;
106 // Display the spinner and the back arrow in the app bar.
107 appBar.setCustomView(R.layout.spinner);
108 appBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_HOME_AS_UP);
110 // Initialize the resource array lists. A list is needed for all the resource requests, or the activity can crash if `MainWebViewActivity.resourceRequests` is modified after the activity loads.
111 List<String[]> allResourceRequests = new ArrayList<>();
112 List<String[]> defaultResourceRequests = new ArrayList<>();
113 List<String[]> allowedResourceRequests = new ArrayList<>();
114 List<String[]> thirdPartyResourceRequests = new ArrayList<>();
115 List<String[]> blockedResourceRequests = new ArrayList<>();
117 // Populate the resource array lists.
118 for (String[] request : resourceRequests) {
119 switch (request[BlocklistHelper.REQUEST_DISPOSITION]) {
120 case BlocklistHelper.REQUEST_DEFAULT:
121 // Add the request to the list of all requests.
122 allResourceRequests.add(request);
124 // Add the request to the list of default requests.
125 defaultResourceRequests.add(request);
128 case BlocklistHelper.REQUEST_ALLOWED:
129 // Add the request to the list of all requests.
130 allResourceRequests.add(request);
132 // Add the request to the list of allowed requests.
133 allowedResourceRequests.add(request);
136 case BlocklistHelper.REQUEST_THIRD_PARTY:
137 // Add the request to the list of all requests.
138 allResourceRequests.add(request);
140 // Add the request to the list of third-party requests.
141 thirdPartyResourceRequests.add(request);
144 case BlocklistHelper.REQUEST_BLOCKED:
145 // Add the request to the list of all requests.
146 allResourceRequests.add(request);
148 // Add the request to the list of blocked requests.
149 blockedResourceRequests.add(request);
154 // Setup a matrix cursor for the resource lists.
155 MatrixCursor spinnerCursor = new MatrixCursor(new String[]{"_id", "Requests"});
156 spinnerCursor.addRow(new Object[]{0, getString(R.string.all) + " - " + allResourceRequests.size()});
157 spinnerCursor.addRow(new Object[]{1, getString(R.string.default_label) + " - " + defaultResourceRequests.size()});
158 spinnerCursor.addRow(new Object[]{2, getString(R.string.allowed_plural) + " - " + allowedResourceRequests.size()});
159 if (blockAllThirdPartyRequests) {
160 spinnerCursor.addRow(new Object[]{3, getString(R.string.third_party_plural) + " - " + thirdPartyResourceRequests.size()});
162 spinnerCursor.addRow(new Object[]{4, getString(R.string.blocked_plural) + " - " + blockedResourceRequests.size()});
164 // Create a resource cursor adapter for the spinner.
165 ResourceCursorAdapter spinnerCursorAdapter = new ResourceCursorAdapter(this, R.layout.requests_appbar_spinner_item, spinnerCursor, 0) {
167 public void bindView(View view, Context context, Cursor cursor) {
168 // Get a handle for the spinner item text view.
169 TextView spinnerItemTextView = view.findViewById(R.id.spinner_item_textview);
171 // Set the text view to display the resource list.
172 spinnerItemTextView.setText(cursor.getString(1));
176 // Set the resource cursor adapter drop down view resource.
177 spinnerCursorAdapter.setDropDownViewResource(R.layout.requests_appbar_spinner_dropdown_item);
179 // Get a handle for the app bar spinner and set the adapter.
180 Spinner appBarSpinner = findViewById(R.id.spinner);
181 appBarSpinner.setAdapter(spinnerCursorAdapter);
183 // Get a handle for the context.
184 Context context = this;
186 // Handle clicks on the spinner dropdown.
187 appBarSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
189 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
191 case 0: // All requests.
192 // Get an adapter for all the request.
193 ArrayAdapter<String[]> allResourceRequestsArrayAdapter = new RequestsArrayAdapter(context, allResourceRequests);
195 // Display the adapter in the list view.
196 requestsListView.setAdapter(allResourceRequestsArrayAdapter);
199 case 1: // Default requests.
200 // Get an adapter for the default requests.
201 ArrayAdapter<String[]> defaultResourceRequestsArrayAdapter = new RequestsArrayAdapter(context, defaultResourceRequests);
203 // Display the adapter in the list view.
204 requestsListView.setAdapter(defaultResourceRequestsArrayAdapter);
207 case 2: // Allowed requests.
208 // Get an adapter for the allowed requests.
209 ArrayAdapter<String[]> allowedResourceRequestsArrayAdapter = new RequestsArrayAdapter(context, allowedResourceRequests);
211 // Display the adapter in the list view.
212 requestsListView.setAdapter(allowedResourceRequestsArrayAdapter);
215 case 3: // Third-party requests.
216 // Get an adapter for the third-party requests.
217 ArrayAdapter<String[]> thirdPartyResourceRequestsArrayAdapter = new RequestsArrayAdapter(context, thirdPartyResourceRequests);
219 //Display the adapter in the list view.
220 requestsListView.setAdapter(thirdPartyResourceRequestsArrayAdapter);
223 case 4: // Blocked requests.
224 // Get an adapter fo the blocked requests.
225 ArrayAdapter<String[]> blockedResourceRequestsArrayAdapter = new RequestsArrayAdapter(context, blockedResourceRequests);
227 // Display the adapter in the list view.
228 requestsListView.setAdapter(blockedResourceRequestsArrayAdapter);
234 public void onNothingSelected(AdapterView<?> parent) {
239 // Create an array adapter with the list of the resource requests.
240 ArrayAdapter<String[]> resourceRequestsArrayAdapter = new RequestsArrayAdapter(context, allResourceRequests);
242 // Populate the list view with the resource requests adapter.
243 requestsListView.setAdapter(resourceRequestsArrayAdapter);
245 // Listen for taps on entries in the list view.
246 requestsListView.setOnItemClickListener((AdapterView<?> parent, View view, int position, long id) -> {
247 // Display the view request dialog. The list view is 0 based, so the position must be incremented by 1.
248 launchViewRequestDialog(position + 1);
251 // Check to see if the activity has been restarted.
252 if (savedInstanceState != null) {
253 // Scroll to the saved position.
254 requestsListView.post(() -> requestsListView.setSelection(savedInstanceState.getInt(LISTVIEW_POSITION)));
259 public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
260 // Run the default commands.
261 super.onSaveInstanceState(savedInstanceState);
263 // Get the listview position.
264 int listViewPosition = requestsListView.getFirstVisiblePosition();
266 // Store the listview position in the bundle.
267 savedInstanceState.putInt(LISTVIEW_POSITION, listViewPosition);
271 public void onPrevious(int currentId) {
272 // Show the previous dialog.
273 launchViewRequestDialog(currentId -1);
277 public void onNext(int currentId) {
278 // Show the next dialog.
279 launchViewRequestDialog(currentId + 1);
282 private void launchViewRequestDialog(int id) {
283 // Determine if this is the last request in the list.
284 boolean isLastRequest = (id == requestsListView.getCount());
286 // Get the string array for the selected resource request. The resource requests list view is zero based.
287 String[] selectedRequestStringArray = (String[]) requestsListView.getItemAtPosition(id - 1);
289 // Remove the warning that `selectedRequest` might be null.
290 assert selectedRequestStringArray != null;
292 // Show the request detail dialog.
293 DialogFragment viewRequestDialogFragment = ViewRequestDialog.request(id, isLastRequest, selectedRequestStringArray);
294 viewRequestDialogFragment.show(getSupportFragmentManager(), getString(R.string.request_details));