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 static final String PACKAGE_NAME = BuildConfig.APPLICATION_ID;
private static final String TAG = App.class.toString();
private boolean isFirstRun = false;
private static App app;
@NonNull
@ -85,7 +87,13 @@ public class App extends Application {
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);
NewPipe.init(getDownloader(),
@ -255,4 +263,7 @@ public class App extends Application {
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.helper.PlayerHolder;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.settings.UpdateSettingsFragment;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.DeviceUtils;
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.PeertubeHelper;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ReleaseVersionUtil;
import org.schabi.newpipe.util.SerializedCache;
import org.schabi.newpipe.util.ServiceHelper;
import org.schabi.newpipe.util.StateSaver;
@ -167,6 +169,11 @@ public class MainActivity extends AppCompatActivity {
// if this is enabled by the user.
NotificationWorker.initialize(this);
}
if (!UpdateSettingsFragment.wasUserAskedForConsent(this)
&& ReleaseVersionUtil.INSTANCE.isReleaseApk()
&& !App.getApp().isFirstRun()) {
UpdateSettingsFragment.askForConsentToUpdateChecks(this);
}
}
@Override

View File

@ -11,6 +11,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.preference.PreferenceManager;
import org.schabi.newpipe.App;
import org.schabi.newpipe.R;
import org.schabi.newpipe.util.DeviceUtils;
@ -44,14 +45,8 @@ public final class NewPipeSettings {
private NewPipeSettings() { }
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
SettingMigrations.runMigrationsIfNeeded(context, isFirstRun);
SettingMigrations.runMigrationsIfNeeded(context);
// readAgain is true so that if new settings are added their default value is set
PreferenceManager.setDefaultValues(context, R.xml.main_settings, true);
@ -68,7 +63,7 @@ public final class NewPipeSettings {
saveDefaultVideoDownloadDirectory(context);
saveDefaultAudioDownloadDirectory(context);
disableMediaTunnelingIfNecessary(context, isFirstRun);
disableMediaTunnelingIfNecessary(context);
}
static void saveDefaultVideoDownloadDirectory(final Context context) {
@ -146,8 +141,7 @@ public final class NewPipeSettings {
R.string.show_remote_search_suggestions_key);
}
private static void disableMediaTunnelingIfNecessary(@NonNull final Context context,
final boolean isFirstRun) {
private static void disableMediaTunnelingIfNecessary(@NonNull final Context context) {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final String disabledTunnelingKey = context.getString(R.string.disable_media_tunneling_key);
final String disabledTunnelingAutomaticallyKey =
@ -162,7 +156,7 @@ public final class NewPipeSettings {
prefs.getInt(disabledTunnelingAutomaticallyKey, -1) == 0
&& !prefs.getBoolean(disabledTunnelingKey, false);
if (Boolean.TRUE.equals(isFirstRun)
if (App.getApp().isFirstRun()
|| (wasDeviceBlacklistUpdated && !wasMediaTunnelingEnabledByUser)) {
setMediaTunneling(context);
}

View File

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

View File

@ -1,9 +1,12 @@
package org.schabi.newpipe.settings;
import android.app.AlertDialog;
import android.content.Context;
import android.os.Bundle;
import android.widget.Toast;
import androidx.preference.Preference;
import androidx.preference.PreferenceManager;
import org.schabi.newpipe.NewVersionWorker;
import org.schabi.newpipe.R;
@ -36,4 +39,38 @@ public class UpdateSettingsFragment extends BasePreferenceFragment {
findPreference(getString(R.string.manual_update_key))
.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>
<!-- Updates -->
<string name="update_check_consent_key">update_check_consent_key</string>
<string name="update_app_key">update_app_key</string>
<string name="manual_update_key">manual_update_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="cancel">Cancel</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="mark_as_watched">Mark as watched</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_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="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_description">Manually check for new versions</string>
<!-- Minimize to exit action -->
<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>

View File

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