<w>buildversion</w>
<w>buratti</w>
<w>cardview</w>
+ <w>ceecdfd</w>
<w>checkedtextview</w>
<w>chromebooks</w>
<w>chromeversion</w>
<w>commitdiff</w>
<w>coordinatorlayout</w>
<w>customuseragent</w>
+ <w>daeef</w>
<w>databaseview</w>
<w>deeplinks</w>
<w>didn</w>
// Only compile Firebase ads for the free flavor.
freeImplementation 'com.google.firebase:firebase-ads:17.1.2'
-
- // Only compile the consent library for the free flavor. It is used to comply with the GDPR in Europe.
- freeImplementation 'com.google.android.ads.consent:consent-library:1.0.6'
}
\ No newline at end of file
import android.os.Build;
import android.os.Bundle;
-import com.google.ads.consent.ConsentInformation;
-import com.google.ads.consent.ConsentStatus;
import com.stoutner.privacybrowser.R;
import com.stoutner.privacybrowser.activities.MainWebViewActivity;
+import com.stoutner.privacybrowser.helpers.AdConsentDatabaseHelper;
import com.stoutner.privacybrowser.helpers.AdHelper;
public class AdConsentDialog extends DialogFragment {
dialogBuilder.setIcon(R.drawable.block_ads_enabled_light);
}
+ // Initialize the bookmarks database helper. The `0` specifies a database version, but that is ignored and set instead using a constant in `AdConsentDatabaseHelper`.
+ // `getContext()` can be used instead of `getActivity.getApplicationContext()` on the minimum API >= 23.
+ AdConsentDatabaseHelper adConsentDatabaseHelper = new AdConsentDatabaseHelper(getActivity().getApplicationContext(), null, null, 0);
+
// Set the title.
dialogBuilder.setTitle(R.string.ad_consent);
// Set the text.
dialogBuilder.setMessage(R.string.ad_consent_text);
- // Get a handle for the consent information.
- ConsentInformation consentInformation = ConsentInformation.getInstance(getActivity().getApplicationContext());
-
// Configure the close button.
dialogBuilder.setNegativeButton(R.string.close_browser, (DialogInterface dialog, int which) -> {
- // Set the consent status to Unknown.
- consentInformation.setConsentStatus(ConsentStatus.UNKNOWN);
+ // Update the ad consent database.
+ adConsentDatabaseHelper.updateAdConsent(false);
// Close the browser. `finishAndRemoveTask` also removes Privacy Browser from the recent app list.
if (Build.VERSION.SDK_INT >= 21) {
// Configure the accept button.
dialogBuilder.setPositiveButton(R.string.accept_ads, (DialogInterface dialog, int which) -> {
- // Set the consent status to Non-Personalized.
- consentInformation.setConsentStatus(ConsentStatus.NON_PERSONALIZED);
+ // Update the ad consent database.
+ adConsentDatabaseHelper.updateAdConsent(true);
- // Indicate the user is under age, which disables personalized advertising and remarketing. https://developers.google.com/admob/android/eu-consent
- consentInformation.setTagForUnderAgeOfConsent(true);
-
- // Load an ad.
+ // Load an ad. `getContext()` can be used instead of `getActivity.getApplicationContext()` on the minimum API >= 23.
AdHelper.loadAd(getActivity().findViewById(R.id.adview), getActivity().getApplicationContext(), getString(R.string.ad_unit_id));
});
// Close Privacy Browser Free if the dialog is cancelled without selecting a button (by tapping on the background).
@Override
public void onCancel(DialogInterface dialogInterface) {
- // Set the consent status to Unknown.
- ConsentInformation.getInstance(getActivity().getApplicationContext()).setConsentStatus(ConsentStatus.UNKNOWN);
+ // Initialize the bookmarks database helper. The `0` specifies a database version, but that is ignored and set instead using a constant in `AdConsentDatabaseHelper`.
+ // `getContext()` can be used instead of `getActivity.getApplicationContext()` on the minimum API >= 23.
+ AdConsentDatabaseHelper adConsentDatabaseHelper = new AdConsentDatabaseHelper(getActivity().getApplicationContext(), null, null, 0);
+
+ // Update the ad consent database.
+ adConsentDatabaseHelper.updateAdConsent(false);
// Close the browser. `finishAndRemoveTask` also removes Privacy Browser from the recent app list.
if (Build.VERSION.SDK_INT >= 21) {
--- /dev/null
+/*
+ * Copyright © 2018 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/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 <http://www.gnu.org/licenses/>.
+ */
+
+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 AdConsentDatabaseHelper extends SQLiteOpenHelper {
+ private static final int SCHEMA_VERSION = 1;
+ private static final String AD_CONSENT_DATABASE = "ad_consent.db";
+ private static final String AD_CONSENT_TABLE = "ad_consent";
+
+ private static final String _ID = "_id";
+ private static final String AD_CONSENT = "ad_consent";
+
+ private static final String CREATE_AD_CONSENT_TABLE = "CREATE TABLE " + AD_CONSENT_TABLE + " (" +
+ _ID + " INTEGER PRIMARY KEY, " +
+ AD_CONSENT + " BOOLEAN)";
+
+ // Initialize the database. The lint warnings for the unused parameters are suppressed.
+ public AdConsentDatabaseHelper(Context context, @SuppressWarnings("UnusedParameters") String name, SQLiteDatabase.CursorFactory cursorFactory, @SuppressWarnings("UnusedParameters") int version) {
+ super(context, AD_CONSENT_DATABASE, cursorFactory, SCHEMA_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase adConsentDatabase) {
+ // Create the ad consent database.
+ adConsentDatabase.execSQL(CREATE_AD_CONSENT_TABLE);
+
+ // Create an ad consent ContentValues.
+ ContentValues adConsentContentValues = new ContentValues();
+
+ // Populate the ad consent content values.
+ adConsentContentValues.put(AD_CONSENT, false);
+
+ // Insert a new row. The second argument is `null`, which makes it so that a completely null row cannot be created.
+ adConsentDatabase.insert(AD_CONSENT_TABLE, null, adConsentContentValues);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase adConsentDatabase, int oldVersion, int newVersion) {
+ // Code for upgrading the database will be added here if the schema version ever increases above 1.
+ }
+
+ // Check to see if ad consent has been granted.
+ public boolean isGranted() {
+ // Get a readable database handle.
+ SQLiteDatabase adConsentDatabase = this.getReadableDatabase();
+
+ // Get the ad consent cursor.
+ Cursor adConsentCursor = adConsentDatabase.rawQuery("SELECT * FROM " + AD_CONSENT_TABLE, null);
+
+ // Move to the first entry.
+ adConsentCursor.moveToFirst();
+
+ // Get the ad consent boolean.
+ boolean adConsent = (adConsentCursor.getInt(adConsentCursor.getColumnIndex(AD_CONSENT)) == 1);
+
+ // Close the cursor.
+ adConsentCursor.close();
+
+ // Close the database.
+ adConsentDatabase.close();
+
+ // Return the ad consent boolean.
+ return adConsent;
+ }
+
+ // Update the ad consent.
+ public void updateAdConsent(boolean adConsent) {
+ // Get a writable database handle.
+ SQLiteDatabase adConsentDatabase = this.getWritableDatabase();
+
+ // Create an ad consent integer.
+ int adConsentInt;
+
+ // Set the ad consent integer according to the boolean.
+ if (adConsent) {
+ adConsentInt = 1;
+ } else {
+ adConsentInt = 0;
+ }
+
+ // Update the ad consent in the database.
+ adConsentDatabase.execSQL("UPDATE " + AD_CONSENT_TABLE + " SET " + AD_CONSENT + " = " + adConsentInt);
+
+ // Close the database.
+ adConsentDatabase.close();
+ }
+}
\ No newline at end of file
import android.view.View;
import android.widget.RelativeLayout;
-import com.google.ads.consent.ConsentInfoUpdateListener;
-import com.google.ads.consent.ConsentInformation;
-import com.google.ads.consent.ConsentStatus;
import com.google.ads.mediation.admob.AdMobAdapter;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdSize;
// Initialize mobile ads.
MobileAds.initialize(applicationContext, googleAppId);
- // Store the publisher ID in a string array.
- String[] publisherIds = {"pub-5962503714887045"};
-
- // Check to see if consent is needed in Europe to comply with the GDPR.
- ConsentInformation consentInformation = ConsentInformation.getInstance(applicationContext);
- consentInformation.requestConsentInfoUpdate(publisherIds, new ConsentInfoUpdateListener() {
- @Override
- public void onConsentInfoUpdated(ConsentStatus consentStatus) {
- if (consentStatus == ConsentStatus.UNKNOWN) { // The user has not yet consented to ads.
- // Display the ad consent dialog.
- DialogFragment adConsentDialogFragment = new AdConsentDialog();
- adConsentDialogFragment.show(fragmentManager, "Ad Consent");
- } else { // The user has consented to ads.
- // Indicate the user is under age, which disables personalized advertising and remarketing. https://developers.google.com/admob/android/eu-consent
- consentInformation.setTagForUnderAgeOfConsent(true);
-
- // Load an ad.
- loadAd(view, applicationContext, adUnitId);
- }
- }
-
- @Override
- public void onFailedToUpdateConsentInfo(String reason) { // The user is not in Europe.
- // Indicate the user is under age, which disables personalized advertising and remarketing. https://developers.google.com/admob/android/eu-consent
- consentInformation.setTagForUnderAgeOfConsent(true);
-
- // Load an ad.
- loadAd(view, applicationContext, adUnitId);
- }
- });
+ // Initialize the bookmarks database helper. The `0` specifies a database version, but that is ignored and set instead using a constant in `AdConsentDatabaseHelper`.
+ AdConsentDatabaseHelper adConsentDatabaseHelper = new AdConsentDatabaseHelper(applicationContext, null, null, 0);
+
+ // Check to see if consent has been granted.
+ boolean adConsentGranted = adConsentDatabaseHelper.isGranted();
+
+ // Display the ad consent dialog if needed.
+ if (!adConsentGranted) { // Ad consent has not been granted.
+ // Display the ad consent dialog.
+ DialogFragment adConsentDialogFragment = new AdConsentDialog();
+ adConsentDialogFragment.show(fragmentManager, "Ad Consent");
+ } else { // Ad consent has been granted.
+ // Load an ad.
+ loadAd(view, applicationContext, adUnitId);
+ }
// Set the initialized variable to true so this section doesn't run again.
initialized = true;
// Display the new AdView.
adViewParentLayout.addView(adView);
- // Only request non-personalized ads.
+ // Only request non-personalized ads. https://developers.google.com/ad-manager/mobile-ads-sdk/android/eu-consent#forward_consent_to_the_google_mobile_ads_sdk
Bundle adSettingsBundle = new Bundle();
adSettingsBundle.putString("npa", "1");
- // Request a new ad.
+ // Build the ad request.
AdRequest adRequest = new AdRequest.Builder().addNetworkExtrasBundle(AdMobAdapter.class, adSettingsBundle).build();
- // Pixel test ads.
- // AdRequest adRequest = new AdRequest.Builder().addTestDevice("20DAEEF7662E2238C99A509BE5D78A26").addNetworkExtrasBundle(AdMobAdapter.class, adSettingsBundle).build();
- // Pixel 2 XL test ads.
- // AdRequest adRequest = new AdRequest.Builder().addTestDevice("137D42984218CEECDFD11927BB7D6416").addNetworkExtrasBundle(AdMobAdapter.class, adSettingsBundle).build();
+
+ // Make it so.
adView.loadAd(adRequest);
}
<!-- Ad Consent. -->
<string name="ad_consent_text">Privacy Browser Free blendet einen Werbebanner unten am Bildschirm ein.
- Diese Werbungen kommen von Googles üblich genutzten Anbietern und sind anonymisiert und nicht verfolgend eingestellt. \n\nDie Standardversion von Privacy Browser beinhaltet keine Werbung.</string>
+ Diese Werbungen kommen von Googles üblich genutzten Anbietern und sind anonymisiert. \n\nDie Standardversion von Privacy Browser beinhaltet keine Werbung.</string>
<string name="close_browser">Browser schließen</string>
<string name="accept_ads">Werbung zustimmen</string>
</resources>
\ No newline at end of file
<!-- Ad Consent. -->
<string name="ad_consent_text">Navegador Privado Gratuíto muestra un anuncio de banner en la parte inferior de la pantalla.
- Estos anuncios proceden del conjunto de proveedores habituales de Google y están configurados para que no sean personalizados ni de seguimiento.
+ Estos anuncios proceden del conjunto de proveedores habituales de Google y están configurados para que no sean personalizados.
\n\nLa versión estándar de Navegador Privado no contiene anuncios.</string>
<string name="close_browser">Cerrar el navegador</string>
<string name="accept_ads">Aceptar anuncios</string>
<!-- Ad Consent. -->
<string name="ad_consent_text">Privacy Browser Free mostra un banner pubblicitario nella parte inferiore dello schermo.
- Questi annunci provengono dai provider normalmente utilizzati da Google e sono configurati in modo da non essere personalizzati e da non tracciare l\'utente.
+ Questi annunci provengono dai provider normalmente utilizzati da Google e sono configurati in modo da non essere personalizzati.
\n\nLa versione standard di Privacy Browser non contiene annunci.</string>
<string name="close_browser">Chiudi il Browser</string>
<string name="accept_ads">Accetta gli Annunci</string>
<!-- Ad Consent. -->
<string name="ad_consent_text">Privacy Browser Free отображает рекламный баннер в нижней части экрана.
- Реклама поступает от широко используемых Google поставщиков и настроена так, чтобы исключить персонализацию и возможность отслеживания.
+ Реклама поступает от широко используемых Google поставщиков и настроена так, чтобы исключить персонализацию.
\n\nСтандартная версия Privacy Browser не содержит рекламы.</string>
<string name="close_browser">Закрыть браузер</string>
<string name="accept_ads">Разрешить рекламу</string>
You should have received a copy of the GNU General Public License
along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>. -->
-<resources>
+<!-- `tools:ignore="MissingTranslation"` allows release APKs to be built if translation strings are missing. The missing strings will fall back to English. -->
+<resources
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:ignore="MissingTranslation" >
+
<!-- Providers. -->
<string name="file_provider" translatable="false">com.stoutner.privacybrowser.fileprovider.free</string>
<!-- Ad Consent. -->
<string name="ad_consent_text">Privacy Browser Free displays a banner ad on the bottom of the screen.
- These ads come from Google’s set of commonly used providers and are configured to be non-personalized and non-tracking.
+ These ads come from Google’s set of commonly used providers and are configured to be non-personalized.
\n\nThe standard version of Privacy Browser does not contain ads.</string>
<string name="close_browser">Close Browser</string>
<string name="accept_ads">Accept Ads</string>
final MenuItem navigationHistoryMenuItem = navigationMenu.getItem(3);
final MenuItem navigationRequestsMenuItem = navigationMenu.getItem(4);
- // Initialize the bookmarks database helper. `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`.
+ // Initialize the bookmarks database helper. 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);
// Initialize `currentBookmarksFolder`. `""` is the home folder in the database.
// Get a readable database handle.
SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
- // Prepare the SQL statement to get the `Cursor` for `databaseId`
- final String GET_ONE_BOOKMARK = "SELECT * FROM " + BOOKMARKS_TABLE +
+ // Prepare the SQL statement to get the cursor for the database ID.
+ 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.
// Get a readable database handle.
SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
- // Prepare the SQL statement to get the `Cursor` for the folder.
- final String GET_FOLDER = "SELECT * FROM " + BOOKMARKS_TABLE +
+ // Prepare the SQL statement to get the cursor for the folder.
+ String GET_FOLDER = "SELECT * FROM " + BOOKMARKS_TABLE +
" WHERE " + _ID + " = " + databaseId;
- // Get `folderCursor`. The second argument is `null` because there are no `selectionArgs`.
+ // Get a folder cursor.
Cursor folderCursor = bookmarksDatabase.rawQuery(GET_FOLDER, null);
- // Get `folderName`.
+ // Get the folder name.
folderCursor.moveToFirst();
String folderName = folderCursor.getString(folderCursor.getColumnIndex(BOOKMARK_NAME));
" WHERE " + IS_FOLDER + " = " + 1 +
" AND " + BOOKMARK_NAME + " = " + currentFolder;
- // The second argument is `null` because there are no `selectionArgs`.
+ // Get the bookmark cursor and move to the first entry.
Cursor bookmarkCursor = bookmarksDatabase.rawQuery(GET_PARENT_FOLDER, null);
bookmarkCursor.moveToFirst();