Ask for consent before starting update checks

NewPipe is contacting its servers without asking for the users' consent. This is categorized as "tracking" by F-Droid (see https://github.com/TeamNewPipe/NewPipe/discussions/10785).

This commit disables checking for udpates by default and adds a dialog asking for the user's consent to automatically check for updates if the app version is eligible for them. After upgrading to a version containing this commit the user is asked directly on the first app start. On fresh installs however, showing it on the first app start contributes to a bad onboarding an welcoming experience. Therefore, the dialog is shown at the second app start.

Co-authored-by: Stypox <stypox@pm.me>
This commit is contained in:
Tobi 2024-03-27 21:27:20 +01:00 committed by TobiGr
parent 1d3a69a29f
commit a3bbbf03b4
8 changed files with 70 additions and 16 deletions

View File

@ -60,6 +60,8 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins;
public class App extends Application { public class App extends Application {
public static final String PACKAGE_NAME = BuildConfig.APPLICATION_ID; public static final String PACKAGE_NAME = BuildConfig.APPLICATION_ID;
private static final String TAG = App.class.toString(); private static final String TAG = App.class.toString();
private boolean isFirstRun = false;
private static App app; private static App app;
@NonNull @NonNull
@ -85,7 +87,13 @@ public class App extends Application {
return; return;
} }
// Initialize settings first because others inits can use its values // check if the last used preference version is set
// to determine whether this is the first app run
final int lastUsedPrefVersion = PreferenceManager.getDefaultSharedPreferences(this)
.getInt(getString(R.string.last_used_preferences_version), -1);
isFirstRun = lastUsedPrefVersion == -1;
// Initialize settings first because other initializations can use its values
NewPipeSettings.initSettings(this); NewPipeSettings.initSettings(this);
NewPipe.init(getDownloader(), NewPipe.init(getDownloader(),
@ -255,4 +263,7 @@ public class App extends Application {
return false; return false;
} }
public boolean isFirstRun() {
return isFirstRun;
}
} }

View File

@ -79,6 +79,7 @@ import org.schabi.newpipe.player.Player;
import org.schabi.newpipe.player.event.OnKeyDownListener; import org.schabi.newpipe.player.event.OnKeyDownListener;
import org.schabi.newpipe.player.helper.PlayerHolder; import org.schabi.newpipe.player.helper.PlayerHolder;
import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.settings.UpdateSettingsFragment;
import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.KioskTranslator; import org.schabi.newpipe.util.KioskTranslator;
@ -86,6 +87,7 @@ import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PeertubeHelper; import org.schabi.newpipe.util.PeertubeHelper;
import org.schabi.newpipe.util.PermissionHelper; import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ReleaseVersionUtil;
import org.schabi.newpipe.util.SerializedCache; import org.schabi.newpipe.util.SerializedCache;
import org.schabi.newpipe.util.ServiceHelper; import org.schabi.newpipe.util.ServiceHelper;
import org.schabi.newpipe.util.StateSaver; import org.schabi.newpipe.util.StateSaver;
@ -167,6 +169,11 @@ public class MainActivity extends AppCompatActivity {
// if this is enabled by the user. // if this is enabled by the user.
NotificationWorker.initialize(this); NotificationWorker.initialize(this);
} }
if (!UpdateSettingsFragment.wasUserAskedForConsent(this)
&& ReleaseVersionUtil.INSTANCE.isReleaseApk()
&& !App.getApp().isFirstRun()) {
UpdateSettingsFragment.askForConsentToUpdateChecks(this);
}
} }
@Override @Override

View File

@ -11,6 +11,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import org.schabi.newpipe.App;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.DeviceUtils;
@ -44,14 +45,8 @@ public final class NewPipeSettings {
private NewPipeSettings() { } private NewPipeSettings() { }
public static void initSettings(final Context context) { public static void initSettings(final Context context) {
// check if the last used preference version is set
// to determine whether this is the first app run
final int lastUsedPrefVersion = PreferenceManager.getDefaultSharedPreferences(context)
.getInt(context.getString(R.string.last_used_preferences_version), -1);
final boolean isFirstRun = lastUsedPrefVersion == -1;
// first run migrations, then setDefaultValues, since the latter requires the correct types // first run migrations, then setDefaultValues, since the latter requires the correct types
SettingMigrations.runMigrationsIfNeeded(context, isFirstRun); SettingMigrations.runMigrationsIfNeeded(context);
// readAgain is true so that if new settings are added their default value is set // readAgain is true so that if new settings are added their default value is set
PreferenceManager.setDefaultValues(context, R.xml.main_settings, true); PreferenceManager.setDefaultValues(context, R.xml.main_settings, true);
@ -68,7 +63,7 @@ public final class NewPipeSettings {
saveDefaultVideoDownloadDirectory(context); saveDefaultVideoDownloadDirectory(context);
saveDefaultAudioDownloadDirectory(context); saveDefaultAudioDownloadDirectory(context);
disableMediaTunnelingIfNecessary(context, isFirstRun); disableMediaTunnelingIfNecessary(context);
} }
static void saveDefaultVideoDownloadDirectory(final Context context) { static void saveDefaultVideoDownloadDirectory(final Context context) {
@ -146,8 +141,7 @@ public final class NewPipeSettings {
R.string.show_remote_search_suggestions_key); R.string.show_remote_search_suggestions_key);
} }
private static void disableMediaTunnelingIfNecessary(@NonNull final Context context, private static void disableMediaTunnelingIfNecessary(@NonNull final Context context) {
final boolean isFirstRun) {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final String disabledTunnelingKey = context.getString(R.string.disable_media_tunneling_key); final String disabledTunnelingKey = context.getString(R.string.disable_media_tunneling_key);
final String disabledTunnelingAutomaticallyKey = final String disabledTunnelingAutomaticallyKey =
@ -162,7 +156,7 @@ public final class NewPipeSettings {
prefs.getInt(disabledTunnelingAutomaticallyKey, -1) == 0 prefs.getInt(disabledTunnelingAutomaticallyKey, -1) == 0
&& !prefs.getBoolean(disabledTunnelingKey, false); && !prefs.getBoolean(disabledTunnelingKey, false);
if (Boolean.TRUE.equals(isFirstRun) if (App.getApp().isFirstRun()
|| (wasDeviceBlacklistUpdated && !wasMediaTunnelingEnabledByUser)) { || (wasDeviceBlacklistUpdated && !wasMediaTunnelingEnabledByUser)) {
setMediaTunneling(context); setMediaTunneling(context);
} }

View File

@ -7,6 +7,7 @@ import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import org.schabi.newpipe.App;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.ErrorInfo;
import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.error.ErrorUtil;
@ -163,15 +164,14 @@ public final class SettingMigrations {
private static final int VERSION = 6; private static final int VERSION = 6;
public static void runMigrationsIfNeeded(@NonNull final Context context, public static void runMigrationsIfNeeded(@NonNull final Context context) {
final boolean isFirstRun) {
// setup migrations and check if there is something to do // setup migrations and check if there is something to do
sp = PreferenceManager.getDefaultSharedPreferences(context); sp = PreferenceManager.getDefaultSharedPreferences(context);
final String lastPrefVersionKey = context.getString(R.string.last_used_preferences_version); final String lastPrefVersionKey = context.getString(R.string.last_used_preferences_version);
final int lastPrefVersion = sp.getInt(lastPrefVersionKey, 0); final int lastPrefVersion = sp.getInt(lastPrefVersionKey, 0);
// no migration to run, already up to date // no migration to run, already up to date
if (isFirstRun) { if (App.getApp().isFirstRun()) {
sp.edit().putInt(lastPrefVersionKey, VERSION).apply(); sp.edit().putInt(lastPrefVersionKey, VERSION).apply();
return; return;
} else if (lastPrefVersion == VERSION) { } else if (lastPrefVersion == VERSION) {

View File

@ -1,9 +1,12 @@
package org.schabi.newpipe.settings; package org.schabi.newpipe.settings;
import android.app.AlertDialog;
import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.widget.Toast; import android.widget.Toast;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceManager;
import org.schabi.newpipe.NewVersionWorker; import org.schabi.newpipe.NewVersionWorker;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
@ -36,4 +39,38 @@ public class UpdateSettingsFragment extends BasePreferenceFragment {
findPreference(getString(R.string.manual_update_key)) findPreference(getString(R.string.manual_update_key))
.setOnPreferenceClickListener(manualUpdateClick); .setOnPreferenceClickListener(manualUpdateClick);
} }
public static void askForConsentToUpdateChecks(final Context context) {
new AlertDialog.Builder(context)
.setTitle(context.getString(R.string.check_for_updates))
.setMessage(context.getString(R.string.auto_update_check_description))
.setPositiveButton(context.getString(R.string.yes), (d, w) -> {
d.dismiss();
setAutoUpdateCheckEnabled(context, true);
})
.setNegativeButton(R.string.no, (d, w) -> {
d.dismiss();
// set explicitly to false, since the default is true on previous versions
setAutoUpdateCheckEnabled(context, false);
})
.show();
}
private static void setAutoUpdateCheckEnabled(final Context context, final boolean enabled) {
PreferenceManager.getDefaultSharedPreferences(context)
.edit()
.putBoolean(context.getString(R.string.update_app_key), enabled)
.putBoolean(context.getString(R.string.update_check_consent_key), true)
.apply();
}
/**
* Whether the user was asked for consent to automatically check for app updates.
* @param context
* @return true if the user was asked for consent, false otherwise
*/
public static boolean wasUserAskedForConsent(final Context context) {
return PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean(context.getString(R.string.update_check_consent_key), false);
}
} }

View File

@ -494,6 +494,7 @@
</string-array> </string-array>
<!-- Updates --> <!-- Updates -->
<string name="update_check_consent_key">update_check_consent_key</string>
<string name="update_app_key">update_app_key</string> <string name="update_app_key">update_app_key</string>
<string name="manual_update_key">manual_update_key</string> <string name="manual_update_key">manual_update_key</string>
<string name="update_pref_screen_key">update_pref_screen_key</string> <string name="update_pref_screen_key">update_pref_screen_key</string>

View File

@ -7,6 +7,8 @@
<string name="install">Install</string> <string name="install">Install</string>
<string name="cancel">Cancel</string> <string name="cancel">Cancel</string>
<string name="ok">OK</string> <string name="ok">OK</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="open_in_browser">Open in browser</string> <string name="open_in_browser">Open in browser</string>
<string name="mark_as_watched">Mark as watched</string> <string name="mark_as_watched">Mark as watched</string>
<string name="open_in_popup_mode">Open in popup mode</string> <string name="open_in_popup_mode">Open in popup mode</string>
@ -557,8 +559,10 @@
<string name="updates_setting_title">Updates</string> <string name="updates_setting_title">Updates</string>
<string name="updates_setting_description">Show a notification to prompt app update when a new version is available</string> <string name="updates_setting_description">Show a notification to prompt app update when a new version is available</string>
<string name="check_for_updates">Check for updates</string> <string name="check_for_updates">Check for updates</string>
<string name="auto_update_check_description">NewPipe can automatically check for new versions from time to time and notify you once they are available.\nDo you want to enable this?</string>
<string name="manual_update_title" translatable="false">@string/check_for_updates</string> <string name="manual_update_title" translatable="false">@string/check_for_updates</string>
<string name="manual_update_description">Manually check for new versions</string> <string name="manual_update_description">Manually check for new versions</string>
<!-- Minimize to exit action --> <!-- Minimize to exit action -->
<string name="minimize_on_exit_title">Minimize on app switch</string> <string name="minimize_on_exit_title">Minimize on app switch</string>
<string name="minimize_on_exit_summary">Action when switching to other app from main video player — %s</string> <string name="minimize_on_exit_summary">Action when switching to other app from main video player — %s</string>

View File

@ -4,7 +4,7 @@
android:title="@string/settings_category_updates_title"> android:title="@string/settings_category_updates_title">
<SwitchPreferenceCompat <SwitchPreferenceCompat
android:defaultValue="true" android:defaultValue="false"
android:key="@string/update_app_key" android:key="@string/update_app_key"
android:summary="@string/updates_setting_description" android:summary="@string/updates_setting_description"
android:title="@string/updates_setting_title" android:title="@string/updates_setting_title"