From: Soren Stoutner Date: Thu, 30 Jun 2016 05:02:37 +0000 (-0700) Subject: Initial bookmarks implementation. Fix for Custom User Agent not working. https... X-Git-Tag: v1.8~7 X-Git-Url: https://gitweb.stoutner.com/?a=commitdiff_plain;h=9c582d802e641b2b6d27271310fc16898020b470;p=PrivacyBrowserAndroid.git Initial bookmarks implementation. Fix for Custom User Agent not working. https://redmine.stoutner.com/issues/40 --- diff --git a/.idea/dictionaries/soren.xml b/.idea/dictionaries/soren.xml index 2fcbda73..f0434fc3 100644 --- a/.idea/dictionaries/soren.xml +++ b/.idea/dictionaries/soren.xml @@ -7,24 +7,32 @@ appbarlayout aren bebdb + bookmarkname + bookmarkurl buildapi buildversion chromeversion commitdiff coordinatorlayout + displayorder eadd + edittext exynos + favoriteicon imbedded imbedding intl + isfolder khtml konqueror linearlayout + listview logins mozilla nojs orbot panopticlick + parentfolder redmine referer relativelayout @@ -36,6 +44,7 @@ tablayout techrepublic textview + uids webkay webkitversion whatismyip diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8bc6746e..246393e9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -69,12 +69,13 @@ + @@ -91,12 +92,23 @@ android:persistableMode="persistNever" > + + + + diff --git a/app/src/main/assets/about_license.html b/app/src/main/assets/about_license.html index 094ec166..6e4dc0a8 100644 --- a/app/src/main/assets/about_license.html +++ b/app/src/main/assets/about_license.html @@ -63,16 +63,20 @@

ic_arrow_forward.

-

ic_file_download.

+

ic_bookmark_border.

-

ic_import_contacts.

+

ic_file_download.

ic_settings.

+

ic_import_contacts.

+

ic_info_outline.

ic_exit_to_app.

+

ic_add.

+

GNU General Public License

diff --git a/app/src/main/assets/guide_clear_and_exit.html b/app/src/main/assets/guide_clear_and_exit.html index 7f297296..9d5c1f34 100644 --- a/app/src/main/assets/guide_clear_and_exit.html +++ b/app/src/main/assets/guide_clear_and_exit.html @@ -38,6 +38,7 @@ -

A full list of planned features and bug reports is available on redmine.stoutner.com.

+

A full list of planned features and bug reports is available at redmine.stoutner.com.

\ No newline at end of file diff --git a/app/src/main/assets/images/advertising_id.png b/app/src/main/assets/images/advertising_id.png new file mode 100644 index 00000000..8ebf58ee Binary files /dev/null and b/app/src/main/assets/images/advertising_id.png differ diff --git a/app/src/main/assets/images/ic_bookmark_border.png b/app/src/main/assets/images/ic_bookmark_border.png new file mode 100644 index 00000000..ddc4b3c4 Binary files /dev/null and b/app/src/main/assets/images/ic_bookmark_border.png differ diff --git a/app/src/main/assets/images/panopticlick.png b/app/src/main/assets/images/panopticlick.png new file mode 100644 index 00000000..ba9cd5d2 Binary files /dev/null and b/app/src/main/assets/images/panopticlick.png differ diff --git a/app/src/main/assets/images/user_agent.png b/app/src/main/assets/images/user_agent.png new file mode 100644 index 00000000..50ac8dc2 Binary files /dev/null and b/app/src/main/assets/images/user_agent.png differ diff --git a/app/src/main/assets/images/webkay.png b/app/src/main/assets/images/webkay.png new file mode 100644 index 00000000..09a1b5e7 Binary files /dev/null and b/app/src/main/assets/images/webkay.png differ diff --git a/app/src/main/java/com/stoutner/privacybrowser/AboutActivity.java b/app/src/main/java/com/stoutner/privacybrowser/AboutActivity.java index 95b5fb33..62978aac 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/AboutActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/AboutActivity.java @@ -36,8 +36,8 @@ public class AboutActivity extends AppCompatActivity { setContentView(R.layout.about_coordinatorlayout); // We need to use the SupportActionBar from android.support.v7.app.ActionBar until the minimum API is >= 21. - Toolbar supportAppBar = (Toolbar) findViewById(R.id.about_toolbar); - setSupportActionBar(supportAppBar); + Toolbar aboutAppBar = (Toolbar) findViewById(R.id.about_toolbar); + setSupportActionBar(aboutAppBar); // Display the home arrow on supportAppBar. final ActionBar appBar = getSupportActionBar(); diff --git a/app/src/main/java/com/stoutner/privacybrowser/BookmarksActivity.java b/app/src/main/java/com/stoutner/privacybrowser/BookmarksActivity.java new file mode 100644 index 00000000..6d081991 --- /dev/null +++ b/app/src/main/java/com/stoutner/privacybrowser/BookmarksActivity.java @@ -0,0 +1,157 @@ +/** + * Copyright 2016 Soren Stoutner . + * + * This file is part of Privacy Browser . + * + * Privacy Browser 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 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. If not, see . + */ + +package com.stoutner.privacybrowser; + +import android.app.Activity; +import android.app.DialogFragment; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.v4.app.NavUtils; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.widget.AdapterView; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.SimpleCursorAdapter; + +import java.io.ByteArrayOutputStream; + +public class BookmarksActivity extends AppCompatActivity implements CreateBookmark.CreateBookmarkListener { + private BookmarksDatabaseHandler bookmarksDatabaseHandler; + private ListView bookmarksListView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.bookmarks_coordinatorlayout); + + // We need to use the SupportActionBar from android.support.v7.app.ActionBar until the minimum API is >= 21. + Toolbar bookmarksAppBar = (Toolbar) findViewById(R.id.bookmarks_toolbar); + setSupportActionBar(bookmarksAppBar); + + // Display the home arrow on supportAppBar. + final ActionBar appBar = getSupportActionBar(); + assert appBar != null;// This assert removes the incorrect warning in Android Studio on the following line that appBar might be null. + appBar.setDisplayHomeAsUpEnabled(true); + + // Initialize the database handler and the ListView. + bookmarksDatabaseHandler = new BookmarksDatabaseHandler(this, null, null, 0); + bookmarksListView = (ListView) findViewById(R.id.bookmarks_listview); + + // Display the bookmarks in the ListView. + updateBookmarksListView(); + + // Set a listener so that tapping a list item loads the URL. We need to store the activity in a variable so that we can return to the parent activity after loading the URL. + final Activity bookmarksActivity = this; + bookmarksListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + // Convert the id from long to int to match the format of the bookmarks database. + int databaseID = (int) id; + + // Get the bookmark URL and assign it to formattedUrlString. + MainWebViewActivity.formattedUrlString = bookmarksDatabaseHandler.getBookmarkURL(databaseID); + + // Load formattedUrlString and return to the main activity. + MainWebViewActivity.mainWebView.loadUrl(MainWebViewActivity.formattedUrlString); + NavUtils.navigateUpFromSameTask(bookmarksActivity); + } + }); + + // Set a FloatingActionButton for creating new bookmarks. + FloatingActionButton createBookmarkFAB = (FloatingActionButton) findViewById(R.id.create_bookmark_fab); + assert createBookmarkFAB != null; + createBookmarkFAB.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + // Show the CreateBookmark AlertDialog and name the instance "@string/create_bookmark". + DialogFragment createBookmarkDialog = new CreateBookmark(); + createBookmarkDialog.show(getFragmentManager(), "@string/create_bookmark"); + } + }); + } + + @Override + public void onCreateBookmarkCancel(DialogFragment createBookmarkDialogFragment) { + // Do nothing because the user selected "Cancel". + } + + @Override + public void onCreateBookmarkCreate(DialogFragment createBookmarkDialogFragment) { + // Get the EditTexts from the DialogFragment and extract the strings. + EditText createBookmarkNameEditText = (EditText) createBookmarkDialogFragment.getDialog().findViewById(R.id.create_bookmark_name_edittext); + String bookmarkNameString = createBookmarkNameEditText.getText().toString(); + EditText createBookmarkURLEditText = (EditText) createBookmarkDialogFragment.getDialog().findViewById(R.id.create_bookmark_url_edittext); + String bookmarkURLString = createBookmarkURLEditText.getText().toString(); + + // Convert the favoriteIcon Bitmap to a byte array. + ByteArrayOutputStream favoriteIconByteArrayOutputStream = new ByteArrayOutputStream(); + MainWebViewActivity.favoriteIcon.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream); + byte[] favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray(); + + // Create the bookmark. + bookmarksDatabaseHandler.createBookmark(bookmarkNameString, bookmarkURLString, favoriteIconByteArray); + + // Refresh the ListView. + updateBookmarksListView(); + } + + private void updateBookmarksListView() { + // Get a Cursor with the current contents of the bookmarks database. + final Cursor bookmarksCursor = bookmarksDatabaseHandler.getBookmarksCursor(); + + // The last argument is 0 because no special behavior is required. + SimpleCursorAdapter bookmarksAdapter = new SimpleCursorAdapter(this, + R.layout.bookmarks_item_linearlayout, + bookmarksCursor, + new String[] { BookmarksDatabaseHandler.FAVORITEICON, BookmarksDatabaseHandler.BOOKMARK_NAME }, + new int[] { R.id.bookmark_favorite_icon, R.id.bookmark_name }, + 0); + + // Override the handling of R.id.bookmark_favorite_icon to load an image instead of a string. + bookmarksAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() { + public boolean setViewValue(View view, Cursor cursor, int columnIndex) { + if (view.getId() == R.id.bookmark_favorite_icon) { + // Get the byte array from the database. + byte[] favoriteIconByteArray = cursor.getBlob(columnIndex); + + // Convert the byte array to a Bitmap beginning at the first byte and ending at the last. + Bitmap favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.length); + + // Set the favoriteIconBitmap. + ImageView bookmarkFavoriteIcon = (ImageView) view; + bookmarkFavoriteIcon.setImageBitmap(favoriteIconBitmap); + return true; + } else { // Process the rest of the bookmarksAdapter with default settings. + return false; + } + } + }); + + // Update the ListView. + bookmarksListView.setAdapter(bookmarksAdapter); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/stoutner/privacybrowser/BookmarksDatabaseHandler.java b/app/src/main/java/com/stoutner/privacybrowser/BookmarksDatabaseHandler.java new file mode 100644 index 00000000..45344d3b --- /dev/null +++ b/app/src/main/java/com/stoutner/privacybrowser/BookmarksDatabaseHandler.java @@ -0,0 +1,120 @@ +/** + * Copyright 2016 Soren Stoutner . + * + * This file is part of Privacy Browser . + * + * Privacy Browser 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 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. If not, see . + */ + +package com.stoutner.privacybrowser; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; + +public class BookmarksDatabaseHandler extends SQLiteOpenHelper { + private static final int SCHEMA_VERSION = 1; + private static final String BOOKMARKS_DATABASE = "bookmarks.db"; + private static final String BOOKMARKS_TABLE = "bookmarks"; + + public static final String ID = "_id"; + public static final String DISPLAYORDER = "displayorder"; + public static final String BOOKMARK_NAME = "bookmarkname"; + public static final String BOOKMARK_URL = "bookmarkurl"; + public static final String PARENTFOLDER = "parentfolder"; + public static final String ISFOLDER = "isfolder"; + public static final String FAVORITEICON = "favoriteicon"; + + public BookmarksDatabaseHandler(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { + super(context, BOOKMARKS_DATABASE, factory, SCHEMA_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase bookmarksDatabase) { + // Create the database if it doesn't exist. + String CREATE_BOOKMARKS_TABLE = "CREATE TABLE " + BOOKMARKS_TABLE + " (" + + ID + " integer primary key, " + + DISPLAYORDER + " integer, " + + BOOKMARK_NAME + " text, " + + BOOKMARK_URL + " text, " + + PARENTFOLDER + " text, " + + ISFOLDER + " boolean, " + + FAVORITEICON + " blob);"; + + bookmarksDatabase.execSQL(CREATE_BOOKMARKS_TABLE); + } + + @Override + public void onUpgrade(SQLiteDatabase bookmarksDatabase, int oldVersion, int newVersion) { + // Code for upgrading the database will be added here when the schema version > 1. + } + + public void createBookmark(String bookmarkName, String bookmarkURL, byte[] favoriteIcon) { + ContentValues bookmarkContentValues = new ContentValues(); + + // ID is created automatically. + bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName); + bookmarkContentValues.put(BOOKMARK_URL, bookmarkURL); + bookmarkContentValues.put(PARENTFOLDER, ""); + bookmarkContentValues.put(ISFOLDER, false); + bookmarkContentValues.put(FAVORITEICON, favoriteIcon); + + // Get a writable database handle. + SQLiteDatabase bookmarksDatabase = this.getWritableDatabase(); + + // The second argument is "null", which makes it so that completely null rows cannot be created. Not a problem in our case. + bookmarksDatabase.insert(BOOKMARKS_TABLE, null, bookmarkContentValues); + + // Close the database handle. + bookmarksDatabase.close(); + } + + public Cursor getBookmarksCursor() { + // Get a readable database handle. + SQLiteDatabase bookmarksDatabase = this.getReadableDatabase(); + + // Get everything in the BOOKMARKS_TABLE. + String GET_ALL_BOOKMARKS = "Select * FROM " + BOOKMARKS_TABLE; + + // Return the results as a Cursor. The second argument is "null" because there are no selectionArgs. + // We can't close the Cursor because we need to use it in the parent activity. + return bookmarksDatabase.rawQuery(GET_ALL_BOOKMARKS, null); + } + + public String getBookmarkURL(int databaseID) { + // Get a readable database handle. + SQLiteDatabase bookmarksDatabase = this.getReadableDatabase(); + + // Get the row for the selected databaseID. + String GET_BOOKMARK_URL = "Select * FROM " + BOOKMARKS_TABLE + + " WHERE " + ID + " = " + databaseID; + + // Save the results as Cursor and move it to the first (only) row. The second argument is "null" because there are no selectionArgs. + Cursor bookmarksCursor = bookmarksDatabase.rawQuery(GET_BOOKMARK_URL, null); + bookmarksCursor.moveToFirst(); + + // Get the int that identifies the "BOOKMARK_URL" column and save the string as bookmarkURL. + int urlColumnInt = bookmarksCursor.getColumnIndex(BOOKMARK_URL); + String bookmarkURLString = bookmarksCursor.getString(urlColumnInt); + + // Close the Cursor and the database handle. + bookmarksCursor.close(); + bookmarksDatabase.close(); + + // Return the bookmarkURLString. + return bookmarkURLString; + } +} diff --git a/app/src/main/java/com/stoutner/privacybrowser/CreateBookmark.java b/app/src/main/java/com/stoutner/privacybrowser/CreateBookmark.java new file mode 100644 index 00000000..f844ca34 --- /dev/null +++ b/app/src/main/java/com/stoutner/privacybrowser/CreateBookmark.java @@ -0,0 +1,150 @@ +/** + * Copyright 2016 Soren Stoutner . + * + * This file is part of Privacy Browser . + * + * Privacy Browser 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 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. If not, see . + */ + +package com.stoutner.privacybrowser; + +import android.app.Activity; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.DialogInterface; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.support.annotation.NonNull; +// If we don't use android.support.v7.app.AlertDialog instead of android.app.AlertDialog then the dialog will be covered by the keyboard. +import android.support.v7.app.AlertDialog; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager; +import android.widget.EditText; + +public class CreateBookmark extends DialogFragment { + // The public interface is used to send information back to the parent activity. + public interface CreateBookmarkListener { + void onCreateBookmarkCancel(DialogFragment createBookmarkDialogFragment); + + void onCreateBookmarkCreate(DialogFragment createBookmarkDialogFragment); + } + + // createBookmarkListener is used in onAttach() and onCreateDialog() + private CreateBookmarkListener createBookmarkListener; + + // Check to make sure the parent activity implements the listener. + public void onAttach(Activity parentActivity) { + super.onAttach(parentActivity); + try { + createBookmarkListener = (CreateBookmarkListener) parentActivity; + } catch(ClassCastException exception) { + throw new ClassCastException(parentActivity.toString() + " must implement CreateBookmarkListener."); + } + } + + // onCreateDialog requires @NonNull. + @Override + @NonNull + public Dialog onCreateDialog(Bundle savedInstanceState) { + // Create a drawable version of the favorite icon. + Drawable favoriteIconDrawable = new BitmapDrawable(getResources(), MainWebViewActivity.favoriteIcon); + + // Get the activity's layout inflater. + LayoutInflater customDialogInflater = getActivity().getLayoutInflater(); + + // Use AlertDialog.Builder to create the AlertDialog. The style formats the color of the button text. + AlertDialog.Builder createBookmarkDialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowser_AlertDialog); + createBookmarkDialogBuilder.setTitle(R.string.create_bookmark); + createBookmarkDialogBuilder.setIcon(favoriteIconDrawable); + // The parent view is "null" because it will be assigned by AlertDialog. + createBookmarkDialogBuilder.setView(customDialogInflater.inflate(R.layout.create_bookmark_dialog, null)); + + // Set an onClick listener on the negative button. + createBookmarkDialogBuilder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + createBookmarkListener.onCreateBookmarkCancel(CreateBookmark.this); + } + }); + + // Set an onClick listener on the positive button. + createBookmarkDialogBuilder.setPositiveButton(R.string.create, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + createBookmarkListener.onCreateBookmarkCreate(CreateBookmark.this); + } + }); + + + // Create an AlertDialog from the AlertDialog.Builder. + final AlertDialog createBookmarkDialog = createBookmarkDialogBuilder.create(); + + // Show the keyboard when the Dialog is displayed on the screen. + createBookmarkDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); + + // We need to show the AlertDialog before we can call setOnKeyListener() below. + createBookmarkDialog.show(); + + // Allow the "enter" key on the keyboard to create the bookmark from "create_bookmark_name_edittext". + EditText createBookmarkNameEditText = (EditText) createBookmarkDialog.findViewById(R.id.create_bookmark_name_edittext); + assert createBookmarkNameEditText != null; // Remove the warning below that createBookmarkNameEditText might be null. + createBookmarkNameEditText.setOnKeyListener(new View.OnKeyListener() { + public boolean onKey(View v, int keyCode, KeyEvent event) { + // If the event is a key-down on the "enter" button, select the PositiveButton "Create". + if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { + // Trigger the createBookmarkListener. + createBookmarkListener.onCreateBookmarkCreate(CreateBookmark.this); + + // Manually dismiss the AlertDialog. + createBookmarkDialog.dismiss(); + + // Consume the event. + return true; + } else { // If any other key was pressed, do not consume the event. + return false; + } + } + }); + + // Set the formattedUrlString as the initial text of "create_bookmark_url_edittext". + EditText createBookmarkUrlEditText = (EditText) createBookmarkDialog.findViewById(R.id.create_bookmark_url_edittext); + assert createBookmarkUrlEditText != null;// Remove the warning below that createBookmarkUrlEditText might be null. + createBookmarkUrlEditText.setText(MainWebViewActivity.formattedUrlString); + + // Allow the "enter" key on the keyboard to create the bookmark from "create_bookmark_url_edittext". + createBookmarkUrlEditText.setOnKeyListener(new View.OnKeyListener() { + public boolean onKey(View v, int keyCode, KeyEvent event) { + // If the event is a key-down on the "enter" button, select the PositiveButton "Create". + if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { + // Trigger the create listener. + createBookmarkListener.onCreateBookmarkCreate(CreateBookmark.this); + + // Manually dismiss the AlertDialog. + createBookmarkDialog.dismiss(); + + // Consume the event. + return true; + } else { // If any other key was pressed, do not consume the event. + return false; + } + } + }); + + // onCreateDialog requires the return of an AlertDialog + return createBookmarkDialog; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/stoutner/privacybrowser/CreateHomeScreenShortcut.java b/app/src/main/java/com/stoutner/privacybrowser/CreateHomeScreenShortcut.java index 9808d407..f4f4034e 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/CreateHomeScreenShortcut.java +++ b/app/src/main/java/com/stoutner/privacybrowser/CreateHomeScreenShortcut.java @@ -1,5 +1,5 @@ /** - * Copyright 2015 Soren Stoutner . + * Copyright 2015-2016 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -21,37 +21,38 @@ package com.stoutner.privacybrowser; import android.app.Activity; import android.app.Dialog; +import android.app.DialogFragment; import android.content.DialogInterface; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.support.annotation.NonNull; -import android.support.v4.app.DialogFragment; +// If we don't use android.support.v7.app.AlertDialog instead of android.app.AlertDialog then the dialog will be covered by the keyboard. import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatDialogFragment; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; import android.widget.EditText; -public class CreateHomeScreenShortcut extends AppCompatDialogFragment { - // The public interface is used to send information back to the activity that called CreateHomeScreenShortcut. +public class CreateHomeScreenShortcut extends DialogFragment { + // The public interface is used to send information back to the parent activity. public interface CreateHomeScreenSchortcutListener { - void onCreateHomeScreenShortcutCancel(DialogFragment dialog); + void onCreateHomeScreenShortcutCancel(DialogFragment dialogFragment); - void onCreateHomeScreenShortcutCreate(DialogFragment dialog); + void onCreateHomeScreenShortcutCreate(DialogFragment dialogFragment); } - private CreateHomeScreenSchortcutListener buttonListener; + //createHomeScreenShortcutListener is used in onAttach and and onCreateDialog. + private CreateHomeScreenSchortcutListener createHomeScreenShortcutListener; - // Check to make sure that the activity that called CreateHomeScreenShortcut implements both listeners. - public void onAttach(Activity activity) { - super.onAttach(activity); + // Check to make sure that the parent activity implements the listener. + public void onAttach(Activity parentActivity) { + super.onAttach(parentActivity); try { - buttonListener = (CreateHomeScreenSchortcutListener) activity; - } catch (ClassCastException e) { - throw new ClassCastException(activity.toString() + " must implement CreateHomeScreenShortcutListener."); + createHomeScreenShortcutListener = (CreateHomeScreenSchortcutListener) parentActivity; + } catch(ClassCastException exception) { + throw new ClassCastException(parentActivity.toString() + " must implement CreateHomeScreenShortcutListener."); } } @@ -62,57 +63,64 @@ public class CreateHomeScreenShortcut extends AppCompatDialogFragment { // Create a drawable version of the favorite icon. Drawable favoriteIconDrawable = new BitmapDrawable(getResources(), MainWebViewActivity.favoriteIcon); - // Use AlertDialog.Builder to create the AlertDialog - AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity()); + // Get the activity's layout inflater. LayoutInflater customDialogInflater = getActivity().getLayoutInflater(); - alertDialogBuilder.setTitle(R.string.shortcut_name); - alertDialogBuilder.setIcon(favoriteIconDrawable); - alertDialogBuilder.setView(customDialogInflater.inflate(R.layout.create_home_screen_shortcut_dialog, null)); - alertDialogBuilder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + + // Use AlertDialog.Builder to create the AlertDialog. The style formats the color of the button text. + AlertDialog.Builder createHomeScreenShorcutDialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowser_AlertDialog); + createHomeScreenShorcutDialogBuilder.setTitle(R.string.create_shortcut); + createHomeScreenShorcutDialogBuilder.setIcon(favoriteIconDrawable); + // The parent view is "null" because it will be assigned by AlertDialog. + createHomeScreenShorcutDialogBuilder.setView(customDialogInflater.inflate(R.layout.create_home_screen_shortcut_dialog, null)); + + // Set an onClick listener on the negative button. + createHomeScreenShorcutDialogBuilder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - buttonListener.onCreateHomeScreenShortcutCancel(CreateHomeScreenShortcut.this); + createHomeScreenShortcutListener.onCreateHomeScreenShortcutCancel(CreateHomeScreenShortcut.this); } }); - alertDialogBuilder.setPositiveButton(R.string.create, new DialogInterface.OnClickListener() { + + // Set an onClick listener on the positive button. + createHomeScreenShorcutDialogBuilder.setPositiveButton(R.string.create, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - buttonListener.onCreateHomeScreenShortcutCreate(CreateHomeScreenShortcut.this); + createHomeScreenShortcutListener.onCreateHomeScreenShortcutCreate(CreateHomeScreenShortcut.this); } }); - // Assign the alertDialogBuilder to an AlertDialog. - final AlertDialog alertDialog = alertDialogBuilder.create(); - // Show the keyboard when the dialog is displayed on the screen. - alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); + // Create an AlertDialog from the AlertDialogBuilder. + final AlertDialog createHomeScreenShortcutAlertDialog = createHomeScreenShorcutDialogBuilder.create(); - // We need to show alertDialog before we can setOnKeyListener below. - alertDialog.show(); + // Show the keyboard when the Dialog is displayed on the screen. + createHomeScreenShortcutAlertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); - EditText shortcutNameEditText = (EditText) alertDialog.findViewById(R.id.shortcutNameEditText); + // We need to show the AlertDialog before we can call setOnKeyListener() below. + createHomeScreenShortcutAlertDialog.show(); // Allow the "enter" key on the keyboard to create the shortcut. + EditText shortcutNameEditText = (EditText) createHomeScreenShortcutAlertDialog.findViewById(R.id.shortcut_name_edittext); + assert shortcutNameEditText != null; // Remove the warning below that shortcutNameEditText might be null. shortcutNameEditText.setOnKeyListener(new View.OnKeyListener() { public boolean onKey(View v, int keyCode, KeyEvent event) { - // If the event is a key-down event on the "enter" button, select the PositiveButton "Create". + // If the event is a key-down on the "enter" button, select the PositiveButton "Create". if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { // Trigger the create listener. - buttonListener.onCreateHomeScreenShortcutCreate(CreateHomeScreenShortcut.this); + createHomeScreenShortcutListener.onCreateHomeScreenShortcutCreate(CreateHomeScreenShortcut.this); - // Manually dismiss alertDialog. - alertDialog.dismiss(); + // Manually dismiss the AlertDialog. + createHomeScreenShortcutAlertDialog.dismiss(); // Consume the event. return true; - } else { - // If any other key was pressed, do not consume the event. + } else { // If any other key was pressed, do not consume the event. return false; } } }); // onCreateDialog requires the return of an AlertDialog. - return alertDialog; + return createHomeScreenShortcutAlertDialog; } } \ No newline at end of file diff --git a/app/src/main/java/com/stoutner/privacybrowser/GuideActivity.java b/app/src/main/java/com/stoutner/privacybrowser/GuideActivity.java index e54ac6a5..df8f812e 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/GuideActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/GuideActivity.java @@ -36,8 +36,8 @@ public class GuideActivity extends AppCompatActivity { setContentView(R.layout.guide_coordinatorlayout); // We need to use the SupportActionBar from android.support.v7.app.ActionBar until the minimum API is >= 21. - Toolbar supportAppBar = (Toolbar) findViewById(R.id.guide_toolbar); - setSupportActionBar(supportAppBar); + Toolbar guideAppBar = (Toolbar) findViewById(R.id.guide_toolbar); + setSupportActionBar(guideAppBar); // Display the home arrow on supportAppBar. final ActionBar appBar = getSupportActionBar(); diff --git a/app/src/main/java/com/stoutner/privacybrowser/MainWebViewActivity.java b/app/src/main/java/com/stoutner/privacybrowser/MainWebViewActivity.java index 66fe512e..c83b8d6b 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/MainWebViewActivity.java @@ -21,6 +21,7 @@ package com.stoutner.privacybrowser; import android.annotation.SuppressLint; import android.app.Activity; +import android.app.DialogFragment; import android.app.DownloadManager; import android.content.Intent; import android.content.SharedPreferences; @@ -32,14 +33,12 @@ import android.os.Bundle; import android.preference.PreferenceManager; import android.support.design.widget.NavigationView; import android.support.design.widget.Snackbar; -import android.support.v4.app.DialogFragment; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.AppCompatActivity; -import android.support.v7.app.AppCompatDialogFragment; import android.support.v7.widget.Toolbar; import android.util.Patterns; import android.view.KeyEvent; @@ -66,46 +65,70 @@ import java.net.URLEncoder; // We need to use AppCompatActivity from android.support.v7.app.AppCompatActivity to have access to the SupportActionBar until the minimum API is >= 21. public class MainWebViewActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, CreateHomeScreenShortcut.CreateHomeScreenSchortcutListener { - // favoriteIcon is public static so it can be accessed from CreateHomeScreenShortcut. + // favoriteIcon is public static so it can be accessed from CreateHomeScreenShortcut and BookmarksActivity. + // It is also used in onCreate() and onCreateHomeScreenShortcutCreate(). public static Bitmap favoriteIcon; - // mainWebView is public static so it can be accessed from SettingsFragment. It is also used in onCreate(), onOptionsItemSelected(), onNavigationItemSelected(), and loadUrlFromTextBox(). + + // mainWebView is public static so it can be accessed from SettingsFragment. + // It is also used in onCreate(), onOptionsItemSelected(), onNavigationItemSelected(), and loadUrlFromTextBox(). public static WebView mainWebView; + // formattedUrlString is public static so it can be accessed from BookmarksActivity. + // It is also used in onCreate(), onOptionsItemSelected(), onCreateHomeScreenShortcutCreate(), and loadUrlFromTextBox(). + public static String formattedUrlString; + // mainMenu is public static so it can be accessed from SettingsFragment. It is also used in onCreateOptionsMenu() and onOptionsItemSelected(). public static Menu mainMenu; + // cookieManager is public static so it can be accessed from SettingsFragment. It is also used in onCreate(), onOptionsItemSelected(), and onNavigationItemSelected(). public static CookieManager cookieManager; - // javaScriptEnabled is public static so it can be accessed from SettingsFragment. It is also used in onCreate(), onCreateOptionsMenu(), onOptionsItemSelected(), and loadUrlFromTextBox(). + + // javaScriptEnabled is public static so it can be accessed from SettingsFragment. + // It is also used in onCreate(), onCreateOptionsMenu(), onOptionsItemSelected(), and loadUrlFromTextBox(). public static boolean javaScriptEnabled; - // firstPartyCookiesEnabled is public static so it can be accessed from SettingsFragment. It is also used in onCreate(), onCreateOptionsMenu(), onPrepareOptionsMenu(), and onOptionsItemSelected(). + + // firstPartyCookiesEnabled is public static so it can be accessed from SettingsFragment. + // It is also used in onCreate(), onCreateOptionsMenu(), onPrepareOptionsMenu(), and onOptionsItemSelected(). public static boolean firstPartyCookiesEnabled; + // thirdPartyCookiesEnabled is used in onCreate(), onCreateOptionsMenu(), onPrepareOptionsMenu(), and onOptionsItemSelected(). public static boolean thirdPartyCookiesEnabled; + // domStorageEnabled is public static so it can be accessed from SettingsFragment. It is also used in onCreate(), onCreateOptionsMenu(), and onOptionsItemSelected(). public static boolean domStorageEnabled; + // saveFormDataEnabled is public static so it can be accessed from SettingsFragment. It is also used in onCreate(), onCreateOptionsMenu(), and onOptionsItemSelected(). public static boolean saveFormDataEnabled; + // javaScriptDisabledSearchURL is public static so it can be accessed from SettingsFragment. It is also used in onCreate() and loadURLFromTextBox(). public static String javaScriptDisabledSearchURL; + // javaScriptEnabledSearchURL is public static so it can be accessed from SettingsFragment. It is also used in onCreate() and loadURLFromTextBox(). public static String javaScriptEnabledSearchURL; + // homepage is public static so it can be accessed from SettingsFragment. It is also used in onCreate() and onOptionsItemSelected(). public static String homepage; + // swipeToRefresh is public static so it can be accessed from SettingsFragment. It is also used in onCreate(). public static SwipeRefreshLayout swipeToRefresh; + // swipeToRefreshEnabled is public static so it can be accessed from SettingsFragment. It is also used in onCreate(). public static boolean swipeToRefreshEnabled; + + // drawerToggle is used in onCreate(), onPostCreate(), onConfigurationChanged(), onNewIntent(), and onNavigationItemSelected(). private ActionBarDrawerToggle drawerToggle; + // drawerLayout is used in onCreate(), onNewIntent(), and onBackPressed(). private DrawerLayout drawerLayout; - // formattedUrlString is used in onCreate(), onOptionsItemSelected(), onCreateHomeScreenShortcutCreate(), and loadUrlFromTextBox(). - private String formattedUrlString; + // privacyIcon is used in onCreateOptionsMenu() and updatePrivacyIcon(). private MenuItem privacyIcon; + // urlTextBox is used in onCreate(), onOptionsItemSelected(), and loadUrlFromTextBox(). private EditText urlTextBox; + // adView is used in onCreate() and onConfigurationChanged(). private View adView; @@ -604,9 +627,9 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation return true; case R.id.addToHomescreen: - // Show the CreateHomeScreenShortcut AlertDialog and name this instance createShortcut. - AppCompatDialogFragment shortcutDialog = new CreateHomeScreenShortcut(); - shortcutDialog.show(getSupportFragmentManager(), "createShortcut"); + // Show the CreateHomeScreenShortcut AlertDialog and name this instance "@string/create_shortcut". + DialogFragment createHomeScreenShortcutDialogFragment = new CreateHomeScreenShortcut(); + createHomeScreenShortcutDialogFragment.show(getFragmentManager(), "@string/create_shortcut"); //Everything else will be handled by CreateHomeScreenShortcut and the associated listeners below. return true; @@ -644,6 +667,12 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation } break; + case R.id.bookmarks: + // Launch BookmarksActivity. + Intent bookmarksIntent = new Intent(this, BookmarksActivity.class); + startActivity(bookmarksIntent); + break; + case R.id.downloads: // Launch the system Download Manager. Intent downloadManagerIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS); @@ -654,18 +683,18 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation startActivity(downloadManagerIntent); break; - case R.id.guide: - // Launch GuideActivity. - Intent guideIntent = new Intent(this, GuideActivity.class); - startActivity(guideIntent); - break; - case R.id.settings: // Launch SettingsActivity. Intent settingsIntent = new Intent(this, SettingsActivity.class); startActivity(settingsIntent); break; + case R.id.guide: + // Launch GuideActivity. + Intent guideIntent = new Intent(this, GuideActivity.class); + startActivity(guideIntent); + break; + case R.id.about: // Launch AboutActivity. Intent aboutIntent = new Intent(this, AboutActivity.class); @@ -684,6 +713,10 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation WebStorage domStorage = WebStorage.getInstance(); domStorage.deleteAllData(); + // Clear form data. + WebViewDatabase formData = WebViewDatabase.getInstance(this); + formData.clearFormData(); + // Clear cache. The argument of "true" includes disk files. mainWebView.clearCache(true); @@ -730,14 +763,14 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation } @Override - public void onCreateHomeScreenShortcutCancel(DialogFragment dialog) { + public void onCreateHomeScreenShortcutCancel(DialogFragment dialogFragment) { // Do nothing because the user selected "Cancel". } @Override - public void onCreateHomeScreenShortcutCreate(DialogFragment dialog) { + public void onCreateHomeScreenShortcutCreate(DialogFragment dialogFragment) { // Get shortcutNameEditText from the alert dialog. - EditText shortcutNameEditText = (EditText) dialog.getDialog().findViewById(R.id.shortcutNameEditText); + EditText shortcutNameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.shortcut_name_edittext); // Create the bookmark shortcut based on formattedUrlString. Intent bookmarkShortcut = new Intent(); diff --git a/app/src/main/java/com/stoutner/privacybrowser/SettingsFragment.java b/app/src/main/java/com/stoutner/privacybrowser/SettingsFragment.java index 2afa9e78..cd6a81e1 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/SettingsFragment.java +++ b/app/src/main/java/com/stoutner/privacybrowser/SettingsFragment.java @@ -235,7 +235,7 @@ public class SettingsFragment extends PreferenceFragment { customUserAgent.setSummary(sharedPreferences.getString("custom_user_agent", "PrivacyBrowser/1.0")); // Update mainWebView's user agent. The default is "PrivacyBrowser/1.0". - MainWebViewActivity.mainWebView.getSettings().setUserAgentString(sharedPreferences.getString("user_agent", "PrivacyBrowser/1.0")); + MainWebViewActivity.mainWebView.getSettings().setUserAgentString(sharedPreferences.getString("custom_user_agent", "PrivacyBrowser/1.0")); break; case "javascript_disabled_search": diff --git a/app/src/main/res/drawable/add.xml b/app/src/main/res/drawable/add.xml new file mode 100644 index 00000000..92e17d17 --- /dev/null +++ b/app/src/main/res/drawable/add.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/app/src/main/res/drawable/bookmarks.xml b/app/src/main/res/drawable/bookmarks.xml new file mode 100644 index 00000000..76762491 --- /dev/null +++ b/app/src/main/res/drawable/bookmarks.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/app/src/main/res/layout/about_coordinatorlayout.xml b/app/src/main/res/layout/about_coordinatorlayout.xml index 4ec9d4d1..46fe2497 100644 --- a/app/src/main/res/layout/about_coordinatorlayout.xml +++ b/app/src/main/res/layout/about_coordinatorlayout.xml @@ -31,8 +31,8 @@ @@ -46,8 +46,8 @@ diff --git a/app/src/main/res/layout/bookmarks_coordinatorlayout.xml b/app/src/main/res/layout/bookmarks_coordinatorlayout.xml new file mode 100644 index 00000000..152e3b3a --- /dev/null +++ b/app/src/main/res/layout/bookmarks_coordinatorlayout.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/bookmarks_item_linearlayout.xml b/app/src/main/res/layout/bookmarks_item_linearlayout.xml new file mode 100644 index 00000000..c1a8f21f --- /dev/null +++ b/app/src/main/res/layout/bookmarks_item_linearlayout.xml @@ -0,0 +1,45 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/create_bookmark_dialog.xml b/app/src/main/res/layout/create_bookmark_dialog.xml new file mode 100644 index 00000000..35a5e020 --- /dev/null +++ b/app/src/main/res/layout/create_bookmark_dialog.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/create_home_screen_shortcut_dialog.xml b/app/src/main/res/layout/create_home_screen_shortcut_dialog.xml index 6cf50f00..32d2eb4d 100644 --- a/app/src/main/res/layout/create_home_screen_shortcut_dialog.xml +++ b/app/src/main/res/layout/create_home_screen_shortcut_dialog.xml @@ -1,7 +1,7 @@ - - - + diff --git a/app/src/main/res/menu/menu_navigation.xml b/app/src/main/res/menu/menu_navigation.xml index 6b2761a8..756013c3 100644 --- a/app/src/main/res/menu/menu_navigation.xml +++ b/app/src/main/res/menu/menu_navigation.xml @@ -42,37 +42,46 @@ + + + + + android:orderInCategory="80" /> + android:id="@+id/navigationGroup3" > + android:orderInCategory="90" /> \ No newline at end of file diff --git a/app/src/main/res/values-v19/styles.xml b/app/src/main/res/values-v19/styles.xml index d039bcbd..ea2d867a 100644 --- a/app/src/main/res/values-v19/styles.xml +++ b/app/src/main/res/values-v19/styles.xml @@ -28,7 +28,7 @@ - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index eabe4beb..77428aee 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -42,9 +42,10 @@ Home Back Forward + Bookmarks Downloads - Guide Settings + Guide About Clear and Exit @@ -62,10 +63,16 @@ Refresh + Create shortcut Shortcut name Cancel Create + + Create bookmark + Bookmark name + Bookmark URL + Privacy Browser Guide Overview diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 371d0134..36b66785 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -26,7 +26,7 @@ + \ No newline at end of file