From: Soren Stoutner Date: Sat, 4 Mar 2017 03:19:59 +0000 (-0700) Subject: Partial Domain implementation. X-Git-Tag: v2.0~8 X-Git-Url: https://gitweb.stoutner.com/?a=commitdiff_plain;h=348acd7bd2c9ba9638ff87b34df4e7501214ca27;p=PrivacyBrowserAndroid.git Partial Domain implementation. --- diff --git a/app/build.gradle b/app/build.gradle index 72d7b3a1..c1a932f9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -51,14 +51,11 @@ android { applicationId "com.stoutner.privacybrowser.free" } } - - // `return void` removes the lint error: `Not all execution paths return a value`. - return void } dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') - compile 'com.android.support:design:25.1.1' + compile 'com.android.support:design:25.2.0' // Only compile `com.google.firebase:firebase-ads:9.8.0` for the free flavor. freeCompile 'com.google.firebase:firebase-ads:9.8.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d19b67eb..616c7ba6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -42,8 +42,7 @@ android:fullBackupContent="false" android:supportsRtl="true" > - + @@ -117,15 +116,13 @@ android:persistableMode="persistNever" tools:ignore="UnusedAttribute" /> - diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/Bookmarks.java b/app/src/main/java/com/stoutner/privacybrowser/activities/Bookmarks.java index 27a9afe8..03ee0d17 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/Bookmarks.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/Bookmarks.java @@ -1,5 +1,5 @@ -/** - * Copyright 2016 Soren Stoutner . +/* + * Copyright 2016-2017 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -61,9 +61,8 @@ import com.stoutner.privacybrowser.dialogs.CreateBookmarkFolder; import java.io.ByteArrayOutputStream; -public class Bookmarks extends AppCompatActivity implements CreateBookmark.CreateBookmarkListener, - CreateBookmarkFolder.CreateBookmarkFolderListener, EditBookmark.EditBookmarkListener, - EditBookmarkFolder.EditBookmarkFolderListener, MoveToFolder.MoveToFolderListener { +public class Bookmarks extends AppCompatActivity implements CreateBookmark.CreateBookmarkListener, CreateBookmarkFolder.CreateBookmarkFolderListener, EditBookmark.EditBookmarkListener, EditBookmarkFolder.EditBookmarkFolderListener, + MoveToFolder.MoveToFolderListener { // `bookmarksDatabaseHelper` is public static so it can be accessed from `EditBookmark` and `MoveToFolder`. It is also used in `onCreate()`, // `onCreateBookmarkCreate()`, `updateBookmarksListView()`, and `updateBookmarksListViewExcept()`. @@ -111,12 +110,12 @@ public class Bookmarks extends AppCompatActivity implements CreateBookmark.Creat appBar.setDisplayHomeAsUpEnabled(true); - // Initialize the database handler and the ListView. `this` specifies the context. The two `null`s do not specify the database name or a `CursorFactory`. - // The `0` is to specify a database version, but that is set instead using a constant in `BookmarksDatabaseHelper`. + // Initialize the database handler and the `ListView`. `this` specifies the context. The two `nulls` do not specify the database name or a `CursorFactory`. + // The `0` specifies a database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`. bookmarksDatabaseHelper = new BookmarksDatabaseHelper(this, null, null, 0); bookmarksListView = (ListView) findViewById(R.id.bookmarks_listview); - // Set currentFolder to the home folder, which is null in the database. + // Set currentFolder to the home folder, which is `""` in the database. currentFolder = ""; // Display the bookmarks in the ListView. @@ -130,7 +129,7 @@ public class Bookmarks extends AppCompatActivity implements CreateBookmark.Creat // Convert the id from long to int to match the format of the bookmarks database. int databaseID = (int) id; - // Get the bookmark `Cursor` and move it to the first row. + // Get the bookmark `Cursor` for this ID and move it to the first row. Cursor bookmarkCursor = bookmarksDatabaseHelper.getBookmarkCursor(databaseID); bookmarkCursor.moveToFirst(); @@ -572,10 +571,12 @@ public class Bookmarks extends AppCompatActivity implements CreateBookmark.Creat @Override public void onCreateBookmark(AppCompatDialogFragment dialogFragment) { - // Get the `EditText`s from the `createBookmarkDialogFragment` and extract the strings. + // Get the `EditTexts` from the `dialogFragment`. EditText createBookmarkNameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.create_bookmark_name_edittext); - String bookmarkNameString = createBookmarkNameEditText.getText().toString(); EditText createBookmarkUrlEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.create_bookmark_url_edittext); + + // Extract the strings from the `EditTexts`. + String bookmarkNameString = createBookmarkNameEditText.getText().toString(); String bookmarkUrlString = createBookmarkUrlEditText.getText().toString(); // Convert the favoriteIcon Bitmap to a byte array. @@ -590,7 +591,7 @@ public class Bookmarks extends AppCompatActivity implements CreateBookmark.Creat // Create the bookmark. bookmarksDatabaseHelper.createBookmark(bookmarkNameString, bookmarkUrlString, newBookmarkDisplayOrder, currentFolder, favoriteIconByteArray); - // Refresh the ListView. `setSelection` scrolls to the bottom of the list. + // Refresh the `ListView`. `setSelection` scrolls to the bottom of the list. updateBookmarksListView(currentFolder); bookmarksListView.setSelection(newBookmarkDisplayOrder); } @@ -782,7 +783,7 @@ public class Bookmarks extends AppCompatActivity implements CreateBookmark.Creat // Get a `Cursor` with the current contents of the bookmarks database. bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksCursorByDisplayOrder(folderName); - // Setup `bookmarksCursorAdapter` with `this` context. `false` disables autoRequery. + // Setup `bookmarksCursorAdapter` with `this` context. `false` disables `autoRequery`. CursorAdapter bookmarksCursorAdapter = new CursorAdapter(this, bookmarksCursor, false) { @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { @@ -868,7 +869,7 @@ public class Bookmarks extends AppCompatActivity implements CreateBookmark.Creat } }; - // Update the ListView. + // Update the `ListView`. bookmarksListView.setAdapter(bookmarksCursorAdapter); } diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseView.java b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseView.java index 5a3ccf07..44f23bfc 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseView.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseView.java @@ -1,5 +1,5 @@ -/** - * Copyright 2016 Soren Stoutner . +/* + * Copyright 2016-2017 Soren Stoutner . * * This file is part of Privacy Browser . * diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsList.java b/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsList.java index 3cf13f26..fb994f41 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsList.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsList.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2017 Soren Stoutner . * * This file is part of Privacy Browser . @@ -19,12 +19,33 @@ package com.stoutner.privacybrowser.activities; +import android.content.Context; +import android.database.Cursor; import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; +import android.support.v7.app.AppCompatDialogFragment; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.CursorAdapter; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.TextView; import com.stoutner.privacybrowser.R; +import com.stoutner.privacybrowser.dialogs.AddDomain; +import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper; + +public class DomainsList extends AppCompatActivity implements AddDomain.AddDomainListener { + // `domainsDatabaseHelper` is used in `onCreate()`, `onAddDomain()`, and `updateDomainsRecyclerView()`. + private static DomainsDatabaseHelper domainsDatabaseHelper; + + // `domainsRecyclerView` is used in `onCreate()` and `updateDomainsListView()`. + private ListView domainsListView; -public class DomainsList extends AppCompatActivity { private boolean twoPaneMode; @Override @@ -32,5 +53,88 @@ public class DomainsList extends AppCompatActivity { super.onCreate(savedInstanceState); setContentView(R.layout.domains_list_coordinatorlayout); + // We need to use the `SupportActionBar` from `android.support.v7.app.ActionBar` until the minimum API is >= 21. + final Toolbar bookmarksAppBar = (Toolbar) findViewById(R.id.domains_toolbar); + setSupportActionBar(bookmarksAppBar); + + // Display the home arrow on `SupportActionBar`. + 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. `this` specifies the context. The two `nulls` do not specify the database name or a `CursorFactory`. + // The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`. + domainsDatabaseHelper = new DomainsDatabaseHelper(this, null, null, 0); + + // Determine if we are in two pane mode. `domains_list_framelayout` is only populated if two panes are present. + twoPaneMode = ((findViewById(R.id.domains_list_framelayout)) != null); + + // Initialize `domainsListView`. + domainsListView = (ListView) findViewById(R.id.domains_listview); + + domainsListView.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 domains database. + int databaseId = (int) id; + + // Get the database `Cursor` for this ID and move it to the first row. + Cursor domainCursor = domainsDatabaseHelper.getCursorForId(databaseId); + domainCursor.moveToFirst(); + + // If the + } + }); + + FloatingActionButton addDomainFAB = (FloatingActionButton) findViewById(R.id.add_domain_fab); + addDomainFAB.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + // Show the `AddDomain` `AlertDialog` and name the instance `@string/add_domain`. + AppCompatDialogFragment addDomainDialog = new AddDomain(); + addDomainDialog.show(getSupportFragmentManager(), getResources().getString(R.string.add_domain)); + } + }); + + // Load the `ListView`. + updateDomainsListView(); + } + + @Override + public void onAddDomain(AppCompatDialogFragment dialogFragment) { + // Get the `domainNameEditText` from `dialogFragment` and extract the string. + EditText domainNameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.domain_name_edittext); + String domainNameString = domainNameEditText.getText().toString(); + + // Create the domain. + domainsDatabaseHelper.addDomain(domainNameString); + + // Refresh the `ListView`. + updateDomainsListView(); + } + + private void updateDomainsListView() { + // Get a `Cursor` with the current contents of the domains database. + Cursor domainsCursor = domainsDatabaseHelper.getCursorOrderedByDomain(); + + // Setup `domainsCursorAdapter` with `this` context. `false` disables `autoRequery`. + CursorAdapter domainsCursorAdapter = new CursorAdapter(this, domainsCursor, false) { + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + // Inflate the individual item layout. `false` does not attach it to the root. + return getLayoutInflater().inflate(R.layout.domain_name_linearlayout, parent, false); + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + // Set the domain name. + String domainNameString = cursor.getString(cursor.getColumnIndex(DomainsDatabaseHelper.DOMAIN)); + TextView domainNameTextView = (TextView) view.findViewById(R.id.domain_name_textview); + domainNameTextView.setText(domainNameString); + } + }; + + // Update the `RecyclerView`. + domainsListView.setAdapter(domainsCursorAdapter); } } \ No newline at end of file diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebView.java b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebView.java index 19397117..e6e5304a 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebView.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebView.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2015-2017 Soren Stoutner . * * Download cookie code contributed 2017 Hendrik Knackstedt. Copyright assigned to Soren Stoutner . diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/AddDomain.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/AddDomain.java new file mode 100644 index 00000000..c176633a --- /dev/null +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/AddDomain.java @@ -0,0 +1,120 @@ +/* + * Copyright 2017 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.dialogs; + +import android.annotation.SuppressLint; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +// We have to use `AppCompatDialogFragment` instead of `DialogFragment` or an error is produced on API <= 22. +import android.support.annotation.NonNull; +import android.support.v7.app.AppCompatDialogFragment; +import android.view.KeyEvent; +import android.view.View; +import android.view.WindowManager; +import android.widget.EditText; + +import com.stoutner.privacybrowser.R; + +public class AddDomain extends AppCompatDialogFragment { + // The public interface is used to send information back to the parent activity. + public interface AddDomainListener { + void onAddDomain(AppCompatDialogFragment dialogFragment); + } + + // `addDomainListener` is used in `onAttach()` and `onCreateDialog()`. + private AddDomainListener addDomainListener; + + + public void onAttach(Context context) { + super.onAttach(context); + + // Get a handle for `AddDomainListener` from `context`. + try { + addDomainListener = (AddDomainListener) context; + } catch(ClassCastException exception) { + throw new ClassCastException(context.toString() + " must implement `AddDomainListener`."); + } + } + + // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`. + @SuppressLint("InflateParams") + @Override + @NonNull + public Dialog onCreateDialog(Bundle savedInstanceState) { + // Use `AlertDialog.Builder` to create the `AlertDialog`. The style formats the color of the button text. + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.LightAlertDialog); + dialogBuilder.setTitle(R.string.add_domain); + // The parent view is `null` because it will be assigned by the `AlertDialog`. + dialogBuilder.setView(getActivity().getLayoutInflater().inflate(R.layout.add_domain_dialog, null)); + + // Set an `onClick()` listener for the negative button. + dialogBuilder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // Do nothing. The `AlertDialog` will close automatically. + } + }); + + // Set an `onClick()` listener for the positive button. + dialogBuilder.setPositiveButton(R.string.add, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // Return the `DialogFragment` to the parent activity on add. + addDomainListener.onAddDomain(AddDomain.this); + } + }); + + // Create an `AlertDialog` from the `AlertDialog.Builder`. + final AlertDialog alertDialog = dialogBuilder.create(); + + // Remove the warning below the `setSoftInputMode` might produce `java.lang.NullPointerException`. + assert alertDialog.getWindow() != null; + + // Show the keyboard when the `AlertDialog` is displayed on the screen. + alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); + + // We need to show the `AlertDialog` before w3e can call `setOnKeyListener()` below. + alertDialog.show(); + + // Allow the `enter` key on the keyboard to create the domain from `add_domain_edittext`. + EditText addDomainEditText = (EditText) alertDialog.findViewById(R.id.domain_name_edittext); + addDomainEditText.setOnKeyListener(new View.OnKeyListener() { + public boolean onKey(View view, int keyCode, KeyEvent event) { + // If the event is a key-down on the `enter` key, select the `PositiveButton` `Add`. + if ((keyCode == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN)) { + // Trigger `addDomainListener` and return the `DialogFragment` to the parent activity. + addDomainListener.onAddDomain(AddDomain.this); + // Manually dismiss the `AlertDialog`. + alertDialog.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 alertDialog; + } +} diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmark.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmark.java index 77c58607..5ac074b2 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmark.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmark.java @@ -1,5 +1,5 @@ -/** - * Copyright 2016 Soren Stoutner . +/* + * Copyright 2016-2017 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -20,6 +20,7 @@ package com.stoutner.privacybrowser.dialogs; import android.annotation.SuppressLint; +import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; @@ -27,9 +28,7 @@ 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; -// We have to use `AppCompatDialogFragment` instead of `DialogFragment` or an error is produced on API <=22. +// We have to use `AppCompatDialogFragment` instead of `DialogFragment` or an error is produced on API <= 22. import android.support.v7.app.AppCompatDialogFragment; import android.view.KeyEvent; import android.view.View; @@ -56,7 +55,7 @@ public class CreateBookmark extends AppCompatDialogFragment { try { createBookmarkListener = (CreateBookmarkListener) context; } catch(ClassCastException exception) { - throw new ClassCastException(context.toString() + " must implement CreateBookmarkListener."); + throw new ClassCastException(context.toString() + " must implement `CreateBookmarkListener`."); } } @@ -99,7 +98,7 @@ public class CreateBookmark extends AppCompatDialogFragment { // Remove the warning below that `setSoftInputMode` might produce `java.lang.NullPointerException`. assert alertDialog.getWindow() != null; - // Show the keyboard when the `Dialog` is displayed on the screen. + // Show the keyboard when the `AlertDialog` is displayed on the screen. alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); // We need to show the `AlertDialog` before we can call `setOnKeyListener()` below. @@ -109,9 +108,9 @@ public class CreateBookmark extends AppCompatDialogFragment { EditText createBookmarkNameEditText = (EditText) alertDialog.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)) { + public boolean onKey(View view, int keyCode, KeyEvent event) { + // If the event is a key-down on the `enter` key, select the `PositiveButton` `Create`. + if ((keyCode == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN)) { // Trigger `createBookmarkListener` and return the `DialogFragment` to the parent activity. createBookmarkListener.onCreateBookmark(CreateBookmark.this); // Manually dismiss the `AlertDialog`. @@ -132,8 +131,8 @@ public class CreateBookmark extends AppCompatDialogFragment { // 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)) { + // If the event is a key-down on the "enter" key, select the PositiveButton "Create". + if ((keyCode == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN)) { // Trigger `createBookmarkListener` and return the DialogFragment to the parent activity. createBookmarkListener.onCreateBookmark(CreateBookmark.this); // Manually dismiss the `AlertDialog`. diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkFolder.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkFolder.java index e3afd11b..606915c5 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkFolder.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkFolder.java @@ -1,5 +1,5 @@ -/** - * Copyright 2016 Soren Stoutner . +/* + * Copyright 2016-2017 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -20,13 +20,12 @@ package com.stoutner.privacybrowser.dialogs; import android.annotation.SuppressLint; +import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; 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; // We have to use `AppCompatDialogFragment` instead of `DialogFragment` or an error is produced on API <=22. import android.support.v7.app.AppCompatDialogFragment; import android.view.KeyEvent; diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateHomeScreenShortcut.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateHomeScreenShortcut.java index d55f7ff7..50a3aef5 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateHomeScreenShortcut.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateHomeScreenShortcut.java @@ -1,5 +1,5 @@ -/** - * Copyright 2015-2016 Soren Stoutner . +/* + * Copyright 2015-2017 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -20,6 +20,7 @@ package com.stoutner.privacybrowser.dialogs; import android.annotation.SuppressLint; +import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; @@ -27,8 +28,6 @@ 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; // We have to use `AppCompatDialogFragment` instead of `DialogFragment` or an error is produced on API <=22. import android.support.v7.app.AppCompatDialogFragment; import android.view.KeyEvent; diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/DownloadFile.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/DownloadFile.java index fe2048af..e4c2156f 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/DownloadFile.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/DownloadFile.java @@ -1,5 +1,5 @@ -/** - * Copyright 2016 Soren Stoutner . +/* + * Copyright 2016-2017 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -20,14 +20,13 @@ package com.stoutner.privacybrowser.dialogs; import android.annotation.SuppressLint; +import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.net.Uri; import android.os.Bundle; import android.support.annotation.NonNull; -// `android.support.v7.app.AlertDialog` uses more of the horizontal screen real estate versus `android.app.AlertDialog's` smaller width. -import android.support.v7.app.AlertDialog; // We have to use `AppCompatDialogFragment` instead of `DialogFragment` or an error is produced on API <=22. import android.support.v7.app.AppCompatDialogFragment; import android.view.KeyEvent; diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/DownloadImage.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/DownloadImage.java index ceb0d624..8de68987 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/DownloadImage.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/DownloadImage.java @@ -1,5 +1,5 @@ -/** - * Copyright 2016 Soren Stoutner . +/* + * Copyright 2016-2017 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -20,13 +20,14 @@ package com.stoutner.privacybrowser.dialogs; import android.annotation.SuppressLint; +import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.net.Uri; import android.os.Bundle; import android.support.annotation.NonNull; -import android.support.v7.app.AlertDialog; +// We have to use `AppCompatDialogFragment` instead of `DialogFragment` or an error is produced on API <= 22. import android.support.v7.app.AppCompatDialogFragment; import android.view.KeyEvent; import android.view.LayoutInflater; diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmark.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmark.java index a02a4deb..b502ca88 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmark.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmark.java @@ -1,5 +1,5 @@ -/** - * Copyright 2016 Soren Stoutner . +/* + * Copyright 2016-2017 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -20,6 +20,7 @@ package com.stoutner.privacybrowser.dialogs; import android.annotation.SuppressLint; +import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; @@ -27,9 +28,7 @@ import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; -// 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.annotation.NonNull; -import android.support.v7.app.AlertDialog; // We have to use `AppCompatDialogFragment` instead of `DialogFragment` or an error is produced on API <=22. import android.support.v7.app.AppCompatDialogFragment; import android.view.KeyEvent; diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolder.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolder.java index 74bd8903..77726ebe 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolder.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolder.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2016 Soren Stoutner . * * This file is part of Privacy Browser . @@ -20,6 +20,7 @@ package com.stoutner.privacybrowser.dialogs; import android.annotation.SuppressLint; +import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; @@ -28,8 +29,6 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; 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; // We have to use `AppCompatDialogFragment` instead of `DialogFragment` or an error is produced on API <=22. import android.support.v7.app.AppCompatDialogFragment; import android.view.KeyEvent; diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolder.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolder.java index 38af5f21..217281c1 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolder.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolder.java @@ -1,5 +1,5 @@ -/** - * Copyright 2016 Soren Stoutner . +/* + * Copyright 2016-2017 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -20,6 +20,7 @@ package com.stoutner.privacybrowser.dialogs; import android.annotation.SuppressLint; +import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; @@ -33,10 +34,8 @@ 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.v4.content.ContextCompat; // We have to use `AppCompatDialogFragment` instead of `DialogFragment` or an error is produced on API <=22. -import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatDialogFragment; import android.view.View; import android.view.ViewGroup; diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/SslCertificateError.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/SslCertificateError.java index 10afe62e..917b3027 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/SslCertificateError.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/SslCertificateError.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2016-2017 Soren Stoutner . * * This file is part of Privacy Browser . @@ -28,6 +28,7 @@ import android.net.http.SslCertificate; import android.net.http.SslError; import android.os.Bundle; import android.support.annotation.NonNull; +// We have to use `AppCompatDialogFragment` instead of `DialogFragment` or an error is produced on API <= 22. import android.support.v7.app.AppCompatDialogFragment; import android.text.SpannableStringBuilder; import android.text.Spanned; diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/UrlHistory.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/UrlHistory.java index 46a7bbb5..a362b704 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/UrlHistory.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/UrlHistory.java @@ -1,5 +1,5 @@ -/** - * Copyright 2016 Soren Stoutner . +/* + * Copyright 2016-2017 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -20,6 +20,7 @@ package com.stoutner.privacybrowser.dialogs; import android.annotation.SuppressLint; +import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; @@ -28,10 +29,9 @@ import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; -// `android.support.v7.app.AlertDialog` uses more of the horizontal screen real estate versus `android.app.AlertDialog's` smaller width. import android.support.annotation.NonNull; import android.support.v4.content.ContextCompat; -import android.support.v7.app.AlertDialog; +// We have to use `AppCompatDialogFragment` instead of `DialogFragment` or an error is produced on API <= 22. `android.support.v7.app.AlertDialog` also uses more of the horizontal screen real estate versus `android.app.AlertDialog's` smaller width. import android.support.v7.app.AppCompatDialogFragment; import android.util.Base64; import android.view.LayoutInflater; diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificate.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificate.java index b675c45b..4b16ab1c 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificate.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificate.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2016-2017 Soren Stoutner . * * This file is part of Privacy Browser . diff --git a/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.java b/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.java index 051da668..ccd420e1 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.java +++ b/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.java @@ -1,5 +1,5 @@ -/** - * Copyright 2016 Soren Stoutner . +/* + * Copyright 2016-2017 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -40,14 +40,14 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { public static final String FAVORITE_ICON = "favoriteicon"; // Initialize the database. The lint warnings for the unused parameters are suppressed. - public BookmarksDatabaseHelper(Context context, @SuppressWarnings("UnusedParameters") String name, SQLiteDatabase.CursorFactory factory, @SuppressWarnings("UnusedParameters") int version) { - super(context, BOOKMARKS_DATABASE, factory, SCHEMA_VERSION); + public BookmarksDatabaseHelper(Context context, @SuppressWarnings("UnusedParameters") String name, SQLiteDatabase.CursorFactory cursorFactory, @SuppressWarnings("UnusedParameters") int version) { + super(context, BOOKMARKS_DATABASE, cursorFactory, SCHEMA_VERSION); } @Override public void onCreate(SQLiteDatabase bookmarksDatabase) { - // Create the database if it doesn't exist. - String CREATE_BOOKMARKS_TABLE = "CREATE TABLE " + BOOKMARKS_TABLE + " (" + + // Setup the SQL string to create the `bookmarks` table. + final String CREATE_BOOKMARKS_TABLE = "CREATE TABLE " + BOOKMARKS_TABLE + " (" + _ID + " integer primary key, " + DISPLAY_ORDER + " integer, " + BOOKMARK_NAME + " text, " + @@ -56,6 +56,7 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { IS_FOLDER + " boolean, " + FAVORITE_ICON + " blob);"; + // Create the `bookmarks` table if it doesn't exist. bookmarksDatabase.execSQL(CREATE_BOOKMARKS_TABLE); } @@ -65,6 +66,7 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { } public void createBookmark(String bookmarkName, String bookmarkURL, int displayOrder, String parentFolder, byte[] favoriteIcon) { + // We need to store the bookmark data in a `ContentValues`. ContentValues bookmarkContentValues = new ContentValues(); // ID is created automatically. @@ -78,7 +80,7 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { // 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. + // Insert a new row. The second argument is `null`, which makes it so that a completely null row cannot be created. bookmarksDatabase.insert(BOOKMARKS_TABLE, null, bookmarkContentValues); // Close the database handle. @@ -113,8 +115,7 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { final String GET_ONE_BOOKMARK = "Select * FROM " + BOOKMARKS_TABLE + " WHERE " + _ID + " = " + databaseId; - // 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 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_ONE_BOOKMARK, null); } @@ -219,7 +220,7 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { // Get a readable database handle. SQLiteDatabase bookmarksDatabase = this.getReadableDatabase(); - // Get everything in the BOOKMARKS_TABLE. + // Get everything in `BOOKMARKS_TABLE`. final String GET_ALL_BOOKMARKS = "Select * FROM " + BOOKMARKS_TABLE; // Return the results as a Cursor. The second argument is `null` because there are no selectionArgs. @@ -234,13 +235,12 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { // SQL escape `folderName`. folderName = DatabaseUtils.sqlEscapeString(folderName); - // Get everything in the BOOKMARKS_TABLE. + // Get everything in the `BOOKMARKS_TABLE` with `folderName` as the `PARENT_FOLDER`. final String GET_ALL_BOOKMARKS = "Select * FROM " + BOOKMARKS_TABLE + " WHERE " + PARENT_FOLDER + " = " + folderName + " ORDER BY " + DISPLAY_ORDER + " ASC"; - // 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 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); } diff --git a/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java b/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java new file mode 100644 index 00000000..1a5c066b --- /dev/null +++ b/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java @@ -0,0 +1,98 @@ +/* + * Copyright 2017 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.helpers; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; + +public class DomainsDatabaseHelper extends SQLiteOpenHelper { + private static final int SCHEMA_VERSION = 1; + private static final String DOMAINS_DATABASE = "domains.db"; + private static final String DOMAINS_TABLE = "domains"; + private static final String _ID = "_id"; + + public static final String DOMAIN = "domain"; + + + // Initialize the database. The lint warnings for the unused parameters are suppressed. + public DomainsDatabaseHelper(Context context, @SuppressWarnings("UnusedParameters") String name, SQLiteDatabase.CursorFactory cursorFactory, @SuppressWarnings("UnusedParameters") int version) { + super(context, DOMAINS_DATABASE, cursorFactory, SCHEMA_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase domainsDatabase) { + // Setup the SQL string to create the `domains` table. + final String CREATE_DOMAINS_TABLE = "CREATE TABLE " + DOMAINS_TABLE + " (" + + _ID + " integer primary key, " + + DOMAIN + " text);"; + + // Create the `domains` table if it doesn't exist. + domainsDatabase.execSQL(CREATE_DOMAINS_TABLE); + } + + @Override + public void onUpgrade(SQLiteDatabase domainsDatabase, int oldVersion, int newVersion) { + // Code for upgrading the database will be added here when the schema version > 1. + } + + public Cursor getCursorOrderedByDomain() { + // Get a readable database handle. + SQLiteDatabase domainsDatabase = this.getReadableDatabase(); + + // Get everything in `DOMAINS_TABLE` ordered by `DOMAIN`. + final String GET_CURSOR_SORTED_BY_DOMAIN = "Select * FROM " + DOMAINS_TABLE + + " ORDER BY " + DOMAIN + " ASC"; + + // 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 domainsDatabase.rawQuery(GET_CURSOR_SORTED_BY_DOMAIN, null); + } + + public Cursor getCursorForId(int databaseId) { + // Get a readable database handle. + SQLiteDatabase domainsDatabase = this.getReadableDatabase(); + + // Prepare the SQL statement to ge the `Cursor` for `databaseId`. + final String GET_CURSOR_FOR_ID = "Select * FROM " + DOMAINS_TABLE + + " WHERE " + _ID + " = " + databaseId; + + // 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 domainsDatabase.rawQuery(GET_CURSOR_FOR_ID, null); + } + + public void addDomain(String domainName) { + // We need to store the domain data in a `ContentValues`. + ContentValues domainContentValues = new ContentValues(); + + // ID is created automatically. + domainContentValues.put(DOMAIN, domainName); + + // Get a writable database handle. + SQLiteDatabase domainsDatabase = this.getWritableDatabase(); + + // Insert a new row. The second argument is `null`, which makes it so that a completely null row cannot be created. + domainsDatabase.insert(DOMAINS_TABLE, null, domainContentValues); + + // Close the database handle. + domainsDatabase.close(); + } +} diff --git a/app/src/main/res/drawable/domains_list_selector.xml b/app/src/main/res/drawable/domains_list_selector.xml new file mode 100644 index 00000000..4d726a65 --- /dev/null +++ b/app/src/main/res/drawable/domains_list_selector.xml @@ -0,0 +1,26 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-w900dp/domains_list.xml b/app/src/main/res/layout-w900dp/domains_list.xml index 5a7d407b..4d6df86c 100644 --- a/app/src/main/res/layout-w900dp/domains_list.xml +++ b/app/src/main/res/layout-w900dp/domains_list.xml @@ -21,18 +21,27 @@ + android:showDividers="middle" > - + + android:layout_width="0dp" + android:layout_weight="1" + android:divider="@color/white" + android:dividerHeight="0dp" /> + + \ No newline at end of file diff --git a/app/src/main/res/layout/add_domain_dialog.xml b/app/src/main/res/layout/add_domain_dialog.xml new file mode 100644 index 00000000..981ae13e --- /dev/null +++ b/app/src/main/res/layout/add_domain_dialog.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 index 49670020..51ec0f19 100644 --- a/app/src/main/res/layout/create_bookmark_dialog.xml +++ b/app/src/main/res/layout/create_bookmark_dialog.xml @@ -1,7 +1,7 @@ + - + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/domain_name_linearlayout.xml b/app/src/main/res/layout/domain_name_linearlayout.xml new file mode 100644 index 00000000..5a63377b --- /dev/null +++ b/app/src/main/res/layout/domain_name_linearlayout.xml @@ -0,0 +1,38 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/domains_list.xml b/app/src/main/res/layout/domains_list.xml index 3d5fb643..6864bcbc 100644 --- a/app/src/main/res/layout/domains_list.xml +++ b/app/src/main/res/layout/domains_list.xml @@ -18,7 +18,13 @@ You should have received a copy of the GNU General Public License along with Privacy Browser. If not, see . --> - + \ No newline at end of file + android:layout_width="match_parent" + android:divider="@color/white" + android:dividerHeight="0dp" /> \ No newline at end of file diff --git a/app/src/main/res/layout/domains_list_coordinatorlayout.xml b/app/src/main/res/layout/domains_list_coordinatorlayout.xml index cbc89d9b..7f870a92 100644 --- a/app/src/main/res/layout/domains_list_coordinatorlayout.xml +++ b/app/src/main/res/layout/domains_list_coordinatorlayout.xml @@ -40,17 +40,19 @@ - - + + Domains + Add Domain + Add + Domain Name Privacy Browser Guide diff --git a/build.gradle b/build.gradle index f46d6535..c6f2f500 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:2.3.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6907c5bc..92cecb13 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Aug 16 16:10:23 MST 2016 +#Thu Mar 02 15:10:19 MST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip