Improve code style to be more consistent

This commit is contained in:
wb9688 2020-03-31 19:20:15 +02:00
parent 819e52cab3
commit fda5405e48
244 changed files with 10116 additions and 7222 deletions

View file

@ -4,6 +4,7 @@ import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
@ -11,48 +12,20 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.util.Constants;
public class AppearanceSettingsFragment extends BasePreferenceFragment {
private final static boolean CAPTIONING_SETTINGS_ACCESSIBLE =
private static final boolean CAPTIONING_SETTINGS_ACCESSIBLE =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
/**
* Theme that was applied when the settings was opened (or recreated after a theme change)
* Theme that was applied when the settings was opened (or recreated after a theme change).
*/
private String startThemeKey;
private String captionSettingsKey;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String themeKey = getString(R.string.theme_key);
startThemeKey = defaultPreferences.getString(themeKey, getString(R.string.default_theme_value));
findPreference(themeKey).setOnPreferenceChangeListener(themePreferenceChange);
captionSettingsKey = getString(R.string.caption_settings_key);
if (!CAPTIONING_SETTINGS_ACCESSIBLE) {
final Preference captionSettings = findPreference(captionSettingsKey);
getPreferenceScreen().removePreference(captionSettings);
}
}
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.appearance_settings);
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
if (preference.getKey().equals(captionSettingsKey) && CAPTIONING_SETTINGS_ACCESSIBLE) {
startActivity(new Intent(Settings.ACTION_CAPTIONING_SETTINGS));
}
return super.onPreferenceTreeClick(preference);
}
private final Preference.OnPreferenceChangeListener themePreferenceChange = new Preference.OnPreferenceChangeListener() {
private final Preference.OnPreferenceChangeListener themePreferenceChange
= new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
public boolean onPreferenceChange(final Preference preference, final Object newValue) {
defaultPreferences.edit().putBoolean(Constants.KEY_THEME_CHANGE, true).apply();
defaultPreferences.edit().putString(getString(R.string.theme_key), newValue.toString()).apply();
defaultPreferences.edit()
.putString(getString(R.string.theme_key), newValue.toString()).apply();
if (!newValue.equals(startThemeKey) && getActivity() != null) {
// If it's not the current theme
@ -62,4 +35,34 @@ public class AppearanceSettingsFragment extends BasePreferenceFragment {
return false;
}
};
private String captionSettingsKey;
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String themeKey = getString(R.string.theme_key);
startThemeKey = defaultPreferences
.getString(themeKey, getString(R.string.default_theme_value));
findPreference(themeKey).setOnPreferenceChangeListener(themePreferenceChange);
captionSettingsKey = getString(R.string.caption_settings_key);
if (!CAPTIONING_SETTINGS_ACCESSIBLE) {
final Preference captionSettings = findPreference(captionSettingsKey);
getPreferenceScreen().removePreference(captionSettings);
}
}
@Override
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
addPreferencesFromResource(R.xml.appearance_settings);
}
@Override
public boolean onPreferenceTreeClick(final Preference preference) {
if (preference.getKey().equals(captionSettingsKey) && CAPTIONING_SETTINGS_ACCESSIBLE) {
startActivity(new Intent(Settings.ACTION_CAPTIONING_SETTINGS));
}
return super.onPreferenceTreeClick(preference);
}
}

View file

@ -3,11 +3,12 @@ package org.schabi.newpipe.settings;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.preference.PreferenceFragmentCompat;
import android.view.View;
import org.schabi.newpipe.MainActivity;
@ -15,16 +16,16 @@ public abstract class BasePreferenceFragment extends PreferenceFragmentCompat {
protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode());
protected final boolean DEBUG = MainActivity.DEBUG;
protected SharedPreferences defaultPreferences;
SharedPreferences defaultPreferences;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
public void onCreate(@Nullable final Bundle savedInstanceState) {
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
super.onCreate(savedInstanceState);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
public void onViewCreated(final View view, @Nullable final Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setDivider(null);
updateTitle();
@ -39,7 +40,9 @@ public abstract class BasePreferenceFragment extends PreferenceFragmentCompat {
private void updateTitle() {
if (getActivity() instanceof AppCompatActivity) {
ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
if (actionBar != null) actionBar.setTitle(getPreferenceScreen().getTitle());
if (actionBar != null) {
actionBar.setTitle(getPreferenceScreen().getTitle());
}
}
}
}

View file

@ -45,16 +45,15 @@ import java.util.zip.ZipOutputStream;
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
public class ContentSettingsFragment extends BasePreferenceFragment {
private static final int REQUEST_IMPORT_PATH = 8945;
private static final int REQUEST_EXPORT_PATH = 30945;
private File databasesDir;
private File newpipe_db;
private File newpipe_db_journal;
private File newpipe_db_shm;
private File newpipe_db_wal;
private File newpipe_settings;
private File newpipeDb;
private File newpipeDbJournal;
private File newpipeDbShm;
private File newpipeDbWal;
private File newpipeSettings;
private String thumbnailLoadToggleKey;
@ -63,17 +62,20 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
private String initialLanguage;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
thumbnailLoadToggleKey = getString(R.string.download_thumbnail_key);
initialSelectedLocalization = org.schabi.newpipe.util.Localization.getPreferredLocalization(requireContext());
initialSelectedContentCountry = org.schabi.newpipe.util.Localization.getPreferredContentCountry(requireContext());
initialLanguage = PreferenceManager.getDefaultSharedPreferences(getContext()).getString("app_language_key", "en");
initialSelectedLocalization = org.schabi.newpipe.util.Localization
.getPreferredLocalization(requireContext());
initialSelectedContentCountry = org.schabi.newpipe.util.Localization
.getPreferredContentCountry(requireContext());
initialLanguage = PreferenceManager
.getDefaultSharedPreferences(getContext()).getString("app_language_key", "en");
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
public boolean onPreferenceTreeClick(final Preference preference) {
if (preference.getKey().equals(thumbnailLoadToggleKey)) {
final ImageLoader imageLoader = ImageLoader.getInstance();
imageLoader.stop();
@ -88,17 +90,17 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
}
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
String homeDir = getActivity().getApplicationInfo().dataDir;
databasesDir = new File(homeDir + "/databases");
newpipe_db = new File(homeDir + "/databases/newpipe.db");
newpipe_db_journal = new File(homeDir + "/databases/newpipe.db-journal");
newpipe_db_shm = new File(homeDir + "/databases/newpipe.db-shm");
newpipe_db_wal = new File(homeDir + "/databases/newpipe.db-wal");
newpipeDb = new File(homeDir + "/databases/newpipe.db");
newpipeDbJournal = new File(homeDir + "/databases/newpipe.db-journal");
newpipeDbShm = new File(homeDir + "/databases/newpipe.db-shm");
newpipeDbWal = new File(homeDir + "/databases/newpipe.db-wal");
newpipe_settings = new File(homeDir + "/databases/newpipe.settings");
newpipe_settings.delete();
newpipeSettings = new File(homeDir + "/databases/newpipe.settings");
newpipeSettings.delete();
addPreferencesFromResource(R.xml.content_settings);
@ -107,7 +109,8 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
Intent i = new Intent(getActivity(), FilePickerActivityHelper.class)
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_MULTIPLE, false)
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_CREATE_DIR, false)
.putExtra(FilePickerActivityHelper.EXTRA_MODE, FilePickerActivityHelper.MODE_FILE);
.putExtra(FilePickerActivityHelper.EXTRA_MODE,
FilePickerActivityHelper.MODE_FILE);
startActivityForResult(i, REQUEST_IMPORT_PATH);
return true;
});
@ -117,7 +120,8 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
Intent i = new Intent(getActivity(), FilePickerActivityHelper.class)
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_MULTIPLE, false)
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_CREATE_DIR, true)
.putExtra(FilePickerActivityHelper.EXTRA_MODE, FilePickerActivityHelper.MODE_DIR);
.putExtra(FilePickerActivityHelper.EXTRA_MODE,
FilePickerActivityHelper.MODE_DIR);
startActivityForResult(i, REQUEST_EXPORT_PATH);
return true;
});
@ -131,22 +135,29 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
.getPreferredLocalization(requireContext());
final ContentCountry selectedContentCountry = org.schabi.newpipe.util.Localization
.getPreferredContentCountry(requireContext());
final String selectedLanguage = PreferenceManager.getDefaultSharedPreferences(getContext()).getString("app_language_key", "en");
final String selectedLanguage = PreferenceManager
.getDefaultSharedPreferences(getContext()).getString("app_language_key", "en");
if (!selectedLocalization.equals(initialSelectedLocalization)
|| !selectedContentCountry.equals(initialSelectedContentCountry) || !selectedLanguage.equals(initialLanguage)) {
Toast.makeText(requireContext(), R.string.localization_changes_requires_app_restart, Toast.LENGTH_LONG).show();
|| !selectedContentCountry.equals(initialSelectedContentCountry)
|| !selectedLanguage.equals(initialLanguage)) {
Toast.makeText(requireContext(), R.string.localization_changes_requires_app_restart,
Toast.LENGTH_LONG).show();
NewPipe.setupLocalization(selectedLocalization, selectedContentCountry);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, @NonNull Intent data) {
public void onActivityResult(final int requestCode, final int resultCode,
@NonNull final Intent data) {
assureCorrectAppLanguage(getContext());
super.onActivityResult(requestCode, resultCode, data);
if (DEBUG) {
Log.d(TAG, "onActivityResult() called with: requestCode = [" + requestCode + "], resultCode = [" + resultCode + "], data = [" + data + "]");
Log.d(TAG, "onActivityResult() called with: "
+ "requestCode = [" + requestCode + "], "
+ "resultCode = [" + resultCode + "], "
+ "data = [" + data + "]");
}
if ((requestCode == REQUEST_IMPORT_PATH || requestCode == REQUEST_EXPORT_PATH)
@ -167,7 +178,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
}
}
private void exportDatabase(String path) {
private void exportDatabase(final String path) {
try {
//checkpoint before export
NewPipeDatabase.checkpoint();
@ -175,10 +186,10 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
ZipOutputStream outZip = new ZipOutputStream(
new BufferedOutputStream(
new FileOutputStream(path)));
ZipHelper.addFileToZip(outZip, newpipe_db.getPath(), "newpipe.db");
ZipHelper.addFileToZip(outZip, newpipeDb.getPath(), "newpipe.db");
saveSharedPreferencesToFile(newpipe_settings);
ZipHelper.addFileToZip(outZip, newpipe_settings.getPath(), "newpipe.settings");
saveSharedPreferencesToFile(newpipeSettings);
ZipHelper.addFileToZip(outZip, newpipeSettings.getPath(), "newpipe.settings");
outZip.close();
@ -189,7 +200,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
}
}
private void saveSharedPreferencesToFile(File dst) {
private void saveSharedPreferencesToFile(final File dst) {
ObjectOutputStream output = null;
try {
output = new ObjectOutputStream(new FileOutputStream(dst));
@ -212,7 +223,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
}
}
private void importDatabase(String filePath) {
private void importDatabase(final String filePath) {
// check if file is supported
ZipFile zipFile = null;
try {
@ -224,7 +235,8 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
} finally {
try {
zipFile.close();
} catch (Exception ignored){}
} catch (Exception ignored) {
}
}
try {
@ -233,21 +245,20 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
}
final boolean isDbFileExtracted = ZipHelper.extractFileFromZip(filePath,
newpipe_db.getPath(), "newpipe.db");
newpipeDb.getPath(), "newpipe.db");
if (isDbFileExtracted) {
newpipe_db_journal.delete();
newpipe_db_wal.delete();
newpipe_db_shm.delete();
newpipeDbJournal.delete();
newpipeDbWal.delete();
newpipeDbShm.delete();
} else {
Toast.makeText(getContext(), R.string.could_not_import_all_files, Toast.LENGTH_LONG)
.show();
}
//If settings file exist, ask if it should be imported.
if (ZipHelper.extractFileFromZip(filePath, newpipe_settings.getPath(), "newpipe.settings")) {
if (ZipHelper.extractFileFromZip(filePath, newpipeSettings.getPath(),
"newpipe.settings")) {
AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
alert.setTitle(R.string.import_settings);
@ -258,7 +269,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
});
alert.setPositiveButton(getString(R.string.finish), (dialog, which) -> {
dialog.dismiss();
loadSharedPreferences(newpipe_settings);
loadSharedPreferences(newpipeSettings);
// restart app to properly load db
System.exit(0);
});
@ -267,33 +278,34 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
// restart app to properly load db
System.exit(0);
}
} catch (Exception e) {
onError(e);
}
}
private void loadSharedPreferences(File src) {
private void loadSharedPreferences(final File src) {
ObjectInputStream input = null;
try {
input = new ObjectInputStream(new FileInputStream(src));
SharedPreferences.Editor prefEdit = PreferenceManager.getDefaultSharedPreferences(getContext()).edit();
SharedPreferences.Editor prefEdit = PreferenceManager
.getDefaultSharedPreferences(getContext()).edit();
prefEdit.clear();
Map<String, ?> entries = (Map<String, ?>) input.readObject();
for (Map.Entry<String, ?> entry : entries.entrySet()) {
Object v = entry.getValue();
String key = entry.getKey();
if (v instanceof Boolean)
if (v instanceof Boolean) {
prefEdit.putBoolean(key, (Boolean) v);
else if (v instanceof Float)
} else if (v instanceof Float) {
prefEdit.putFloat(key, (Float) v);
else if (v instanceof Integer)
} else if (v instanceof Integer) {
prefEdit.putInt(key, (Integer) v);
else if (v instanceof Long)
} else if (v instanceof Long) {
prefEdit.putLong(key, (Long) v);
else if (v instanceof String)
prefEdit.putString(key, ((String) v));
} else if (v instanceof String) {
prefEdit.putString(key, (String) v);
}
}
prefEdit.commit();
} catch (FileNotFoundException e) {
@ -317,7 +329,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
// Error
//////////////////////////////////////////////////////////////////////////*/
protected void onError(Throwable e) {
protected void onError(final Throwable e) {
final Activity activity = getActivity();
ErrorActivity.reportError(activity, e,
activity.getClass(),

View file

@ -6,7 +6,7 @@ import org.schabi.newpipe.R;
public class DebugSettingsFragment extends BasePreferenceFragment {
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
addPreferencesFromResource(R.xml.debug_settings);
}
}

View file

@ -32,13 +32,12 @@ import us.shandian.giga.io.StoredDirectoryHelper;
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
public class DownloadSettingsFragment extends BasePreferenceFragment {
public static final boolean IGNORE_RELEASE_ON_OLD_PATH = true;
private static final int REQUEST_DOWNLOAD_VIDEO_PATH = 0x1235;
private static final int REQUEST_DOWNLOAD_AUDIO_PATH = 0x1236;
public static final boolean IGNORE_RELEASE_ON_OLD_PATH = true;
private String DOWNLOAD_PATH_VIDEO_PREFERENCE;
private String DOWNLOAD_PATH_AUDIO_PREFERENCE;
private String STORAGE_USE_SAF_PREFERENCE;
private String downloadPathVideoPreference;
private String downloadPathAudioPreference;
private String storageUseSafPreference;
private Preference prefPathVideo;
private Preference prefPathAudio;
@ -47,16 +46,16 @@ public class DownloadSettingsFragment extends BasePreferenceFragment {
private Context ctx;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DOWNLOAD_PATH_VIDEO_PREFERENCE = getString(R.string.download_path_video_key);
DOWNLOAD_PATH_AUDIO_PREFERENCE = getString(R.string.download_path_audio_key);
STORAGE_USE_SAF_PREFERENCE = getString(R.string.storage_use_saf);
downloadPathVideoPreference = getString(R.string.download_path_video_key);
downloadPathAudioPreference = getString(R.string.download_path_audio_key);
storageUseSafPreference = getString(R.string.storage_use_saf);
final String downloadStorageAsk = getString(R.string.downloads_storage_ask);
prefPathVideo = findPreference(DOWNLOAD_PATH_VIDEO_PREFERENCE);
prefPathAudio = findPreference(DOWNLOAD_PATH_AUDIO_PREFERENCE);
prefPathVideo = findPreference(downloadPathVideoPreference);
prefPathAudio = findPreference(downloadPathAudioPreference);
prefStorageAsk = findPreference(downloadStorageAsk);
updatePreferencesSummary();
@ -66,7 +65,8 @@ public class DownloadSettingsFragment extends BasePreferenceFragment {
prefStorageAsk.setSummary(R.string.downloads_storage_ask_summary);
}
if (hasInvalidPath(DOWNLOAD_PATH_VIDEO_PREFERENCE) || hasInvalidPath(DOWNLOAD_PATH_AUDIO_PREFERENCE)) {
if (hasInvalidPath(downloadPathVideoPreference)
|| hasInvalidPath(downloadPathAudioPreference)) {
updatePreferencesSummary();
}
@ -77,12 +77,12 @@ public class DownloadSettingsFragment extends BasePreferenceFragment {
}
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
addPreferencesFromResource(R.xml.download_settings);
}
@Override
public void onAttach(Context context) {
public void onAttach(final Context context) {
super.onAttach(context);
ctx = context;
}
@ -95,11 +95,14 @@ public class DownloadSettingsFragment extends BasePreferenceFragment {
}
private void updatePreferencesSummary() {
showPathInSummary(DOWNLOAD_PATH_VIDEO_PREFERENCE, R.string.download_path_summary, prefPathVideo);
showPathInSummary(DOWNLOAD_PATH_AUDIO_PREFERENCE, R.string.download_path_audio_summary, prefPathAudio);
showPathInSummary(downloadPathVideoPreference, R.string.download_path_summary,
prefPathVideo);
showPathInSummary(downloadPathAudioPreference, R.string.download_path_audio_summary,
prefPathAudio);
}
private void showPathInSummary(String prefKey, @StringRes int defaultString, Preference target) {
private void showPathInSummary(final String prefKey, @StringRes final int defaultString,
final Preference target) {
String rawUri = defaultPreferences.getString(prefKey, null);
if (rawUri == null || rawUri.isEmpty()) {
target.setSummary(getString(defaultString));
@ -124,33 +127,36 @@ public class DownloadSettingsFragment extends BasePreferenceFragment {
target.setSummary(rawUri);
}
private boolean isFileUri(String path) {
private boolean isFileUri(final String path) {
return path.charAt(0) == File.separatorChar || path.startsWith(ContentResolver.SCHEME_FILE);
}
private boolean hasInvalidPath(String prefKey) {
private boolean hasInvalidPath(final String prefKey) {
String value = defaultPreferences.getString(prefKey, null);
return value == null || value.isEmpty();
}
private void updatePathPickers(boolean enabled) {
private void updatePathPickers(final boolean enabled) {
prefPathVideo.setEnabled(enabled);
prefPathAudio.setEnabled(enabled);
}
// FIXME: after releasing the old path, all downloads created on the folder becomes inaccessible
private void forgetSAFTree(Context ctx, String oldPath) {
private void forgetSAFTree(final Context context, final String oldPath) {
if (IGNORE_RELEASE_ON_OLD_PATH) {
return;
}
if (oldPath == null || oldPath.isEmpty() || isFileUri(oldPath)) return;
if (oldPath == null || oldPath.isEmpty() || isFileUri(oldPath)) {
return;
}
try {
Uri uri = Uri.parse(oldPath);
ctx.getContentResolver().releasePersistableUriPermission(uri, StoredDirectoryHelper.PERMISSION_FLAGS);
ctx.revokeUriPermission(uri, StoredDirectoryHelper.PERMISSION_FLAGS);
context.getContentResolver()
.releasePersistableUriPermission(uri, StoredDirectoryHelper.PERMISSION_FLAGS);
context.revokeUriPermission(uri, StoredDirectoryHelper.PERMISSION_FLAGS);
Log.i(TAG, "Revoke old path permissions success on " + oldPath);
} catch (Exception err) {
@ -158,7 +164,7 @@ public class DownloadSettingsFragment extends BasePreferenceFragment {
}
}
private void showMessageDialog(@StringRes int title, @StringRes int message) {
private void showMessageDialog(@StringRes final int title, @StringRes final int message) {
AlertDialog.Builder msg = new AlertDialog.Builder(ctx);
msg.setTitle(title);
msg.setMessage(message);
@ -167,35 +173,40 @@ public class DownloadSettingsFragment extends BasePreferenceFragment {
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
public boolean onPreferenceTreeClick(final Preference preference) {
if (DEBUG) {
Log.d(TAG, "onPreferenceTreeClick() called with: preference = [" + preference + "]");
Log.d(TAG, "onPreferenceTreeClick() called with: "
+ "preference = [" + preference + "]");
}
String key = preference.getKey();
int request;
if (key.equals(STORAGE_USE_SAF_PREFERENCE)) {
Toast.makeText(getContext(), R.string.download_choose_new_path, Toast.LENGTH_LONG).show();
if (key.equals(storageUseSafPreference)) {
Toast.makeText(getContext(), R.string.download_choose_new_path,
Toast.LENGTH_LONG).show();
return true;
} else if (key.equals(DOWNLOAD_PATH_VIDEO_PREFERENCE)) {
} else if (key.equals(downloadPathVideoPreference)) {
request = REQUEST_DOWNLOAD_VIDEO_PATH;
} else if (key.equals(DOWNLOAD_PATH_AUDIO_PREFERENCE)) {
} else if (key.equals(downloadPathAudioPreference)) {
request = REQUEST_DOWNLOAD_AUDIO_PATH;
} else {
return super.onPreferenceTreeClick(preference);
}
Intent i;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && NewPipeSettings.useStorageAccessFramework(ctx)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
&& NewPipeSettings.useStorageAccessFramework(ctx)) {
i = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
.putExtra("android.content.extra.SHOW_ADVANCED", true)
.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION | StoredDirectoryHelper.PERMISSION_FLAGS);
.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
| StoredDirectoryHelper.PERMISSION_FLAGS);
} else {
i = new Intent(getActivity(), FilePickerActivityHelper.class)
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_MULTIPLE, false)
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_CREATE_DIR, true)
.putExtra(FilePickerActivityHelper.EXTRA_MODE, FilePickerActivityHelper.MODE_DIR);
.putExtra(FilePickerActivityHelper.EXTRA_MODE,
FilePickerActivityHelper.MODE_DIR);
}
startActivityForResult(i, request);
@ -204,24 +215,28 @@ public class DownloadSettingsFragment extends BasePreferenceFragment {
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
assureCorrectAppLanguage(getContext());
super.onActivityResult(requestCode, resultCode, data);
if (DEBUG) {
Log.d(TAG, "onActivityResult() called with: requestCode = [" + requestCode + "], " +
"resultCode = [" + resultCode + "], data = [" + data + "]"
Log.d(TAG, "onActivityResult() called with: "
+ "requestCode = [" + requestCode + "], "
+ "resultCode = [" + resultCode + "], data = [" + data + "]"
);
}
if (resultCode != Activity.RESULT_OK) return;
if (resultCode != Activity.RESULT_OK) {
return;
}
String key;
if (requestCode == REQUEST_DOWNLOAD_VIDEO_PATH)
key = DOWNLOAD_PATH_VIDEO_PREFERENCE;
else if (requestCode == REQUEST_DOWNLOAD_AUDIO_PATH)
key = DOWNLOAD_PATH_AUDIO_PREFERENCE;
else
if (requestCode == REQUEST_DOWNLOAD_VIDEO_PATH) {
key = downloadPathVideoPreference;
} else if (requestCode == REQUEST_DOWNLOAD_AUDIO_PATH) {
key = downloadPathAudioPreference;
} else {
return;
}
Uri uri = data.getData();
if (uri == null) {
@ -231,23 +246,28 @@ public class DownloadSettingsFragment extends BasePreferenceFragment {
// revoke permissions on the old save path (required for SAF only)
final Context ctx = getContext();
if (ctx == null) throw new NullPointerException("getContext()");
final Context context = getContext();
if (context == null) {
throw new NullPointerException("getContext()");
}
forgetSAFTree(ctx, defaultPreferences.getString(key, ""));
forgetSAFTree(context, defaultPreferences.getString(key, ""));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !FilePickerActivityHelper.isOwnFileUri(ctx, uri)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
&& !FilePickerActivityHelper.isOwnFileUri(context, uri)) {
// steps to acquire the selected path:
// 1. acquire permissions on the new save path
// 2. save the new path, if step(2) was successful
try {
ctx.grantUriPermission(ctx.getPackageName(), uri, StoredDirectoryHelper.PERMISSION_FLAGS);
context.grantUriPermission(context.getPackageName(), uri,
StoredDirectoryHelper.PERMISSION_FLAGS);
StoredDirectoryHelper mainStorage = new StoredDirectoryHelper(ctx, uri, null);
StoredDirectoryHelper mainStorage = new StoredDirectoryHelper(context, uri, null);
Log.i(TAG, "Acquiring tree success from " + uri.toString());
if (!mainStorage.canWrite())
if (!mainStorage.canWrite()) {
throw new IOException("No write permissions on " + uri.toString());
}
} catch (IOException err) {
Log.e(TAG, "Error acquiring tree from " + uri.toString(), err);
showMessageDialog(R.string.general_error, R.string.no_available_dir);
@ -256,7 +276,8 @@ public class DownloadSettingsFragment extends BasePreferenceFragment {
} else {
File target = Utils.getFileForUri(uri);
if (!target.canWrite()) {
showMessageDialog(R.string.download_to_sdcard_error_title, R.string.download_to_sdcard_error_message);
showMessageDialog(R.string.download_to_sdcard_error_title,
R.string.download_to_sdcard_error_message);
return;
}
uri = Uri.fromFile(target);

View file

@ -1,10 +1,11 @@
package org.schabi.newpipe.settings;
import android.os.Bundle;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.Preference;
import android.widget.Toast;
import org.schabi.newpipe.R;
import org.schabi.newpipe.local.history.HistoryRecordManager;
@ -25,7 +26,7 @@ public class HistorySettingsFragment extends BasePreferenceFragment {
private CompositeDisposable disposables;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
cacheWipeKey = getString(R.string.metadata_cache_wipe_key);
viewsHistoryClearKey = getString(R.string.clear_views_history_key);
@ -36,12 +37,12 @@ public class HistorySettingsFragment extends BasePreferenceFragment {
}
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
addPreferencesFromResource(R.xml.history_settings);
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
public boolean onPreferenceTreeClick(final Preference preference) {
if (preference.getKey().equals(cacheWipeKey)) {
InfoCache.getInstance().clearCache();
Toast.makeText(preference.getContext(), R.string.metadata_cache_wipe_complete_notice,
@ -53,7 +54,8 @@ public class HistorySettingsFragment extends BasePreferenceFragment {
.setTitle(R.string.delete_view_history_alert)
.setNegativeButton(R.string.cancel, ((dialog, which) -> dialog.dismiss()))
.setPositiveButton(R.string.delete, ((dialog, which) -> {
final Disposable onDeletePlaybackStates = recordManager.deleteCompelteStreamStateHistory()
final Disposable onDeletePlaybackStates
= recordManager.deleteCompelteStreamStateHistory()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
howManyDeleted -> Toast.makeText(getActivity(),
@ -86,7 +88,8 @@ public class HistorySettingsFragment extends BasePreferenceFragment {
final Disposable onClearOrphans = recordManager.removeOrphanedRecords()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
howManyDeleted -> {},
howManyDeleted -> {
},
throwable -> ErrorActivity.reportError(getContext(),
throwable,
SettingsActivity.class, null,
@ -109,7 +112,8 @@ public class HistorySettingsFragment extends BasePreferenceFragment {
.setNegativeButton(R.string.cancel, ((dialog, which) -> dialog.dismiss()))
.setPositiveButton(R.string.delete, ((dialog, which) -> {
final Disposable onDeletePlaybackStates = recordManager.deleteCompelteStreamStateHistory()
final Disposable onDeletePlaybackStates
= recordManager.deleteCompelteStreamStateHistory()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
howManyDeleted -> Toast.makeText(getActivity(),

View file

@ -1,6 +1,7 @@
package org.schabi.newpipe.settings;
import android.os.Bundle;
import androidx.preference.Preference;
import org.schabi.newpipe.BuildConfig;
@ -11,7 +12,7 @@ public class MainSettingsFragment extends BasePreferenceFragment {
public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
addPreferencesFromResource(R.xml.main_settings);
if (!CheckForNewAppVersionTask.isGithubApk()) {

View file

@ -1,3 +1,16 @@
package org.schabi.newpipe.settings;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Environment;
import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager;
import org.schabi.newpipe.R;
import java.io.File;
/*
* Created by k3b on 07.01.2016.
*
@ -18,46 +31,13 @@
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
package org.schabi.newpipe.settings;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Environment;
import androidx.preference.PreferenceManager;
import androidx.annotation.NonNull;
import org.schabi.newpipe.R;
import java.io.File;
/**
* Helper for global settings
* Helper class for global settings.
*/
public final class NewPipeSettings {
private NewPipeSettings() { }
/*
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
* NewPipeSettings.java is part of NewPipe.
*
* NewPipe 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.
*
* NewPipe 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 NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public class NewPipeSettings {
private NewPipeSettings() {
}
public static void initSettings(Context context) {
public static void initSettings(final Context context) {
PreferenceManager.setDefaultValues(context, R.xml.appearance_settings, true);
PreferenceManager.setDefaultValues(context, R.xml.content_settings, true);
PreferenceManager.setDefaultValues(context, R.xml.download_settings, true);
@ -70,19 +50,22 @@ public class NewPipeSettings {
getAudioDownloadFolder(context);
}
private static void getVideoDownloadFolder(Context context) {
private static void getVideoDownloadFolder(final Context context) {
getDir(context, R.string.download_path_video_key, Environment.DIRECTORY_MOVIES);
}
private static void getAudioDownloadFolder(Context context) {
private static void getAudioDownloadFolder(final Context context) {
getDir(context, R.string.download_path_audio_key, Environment.DIRECTORY_MUSIC);
}
private static void getDir(Context context, int keyID, String defaultDirectoryName) {
private static void getDir(final Context context, final int keyID,
final String defaultDirectoryName) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final String key = context.getString(keyID);
String downloadPath = prefs.getString(key, null);
if ((downloadPath != null) && (!downloadPath.isEmpty())) return;
if ((downloadPath != null) && (!downloadPath.isEmpty())) {
return;
}
SharedPreferences.Editor spEditor = prefs.edit();
spEditor.putString(key, getNewPipeChildFolderPathForDir(getDir(defaultDirectoryName)));
@ -90,15 +73,15 @@ public class NewPipeSettings {
}
@NonNull
public static File getDir(String defaultDirectoryName) {
public static File getDir(final String defaultDirectoryName) {
return new File(Environment.getExternalStorageDirectory(), defaultDirectoryName);
}
private static String getNewPipeChildFolderPathForDir(File dir) {
private static String getNewPipeChildFolderPathForDir(final File dir) {
return new File(dir, "NewPipe").toURI().toString();
}
public static boolean useStorageAccessFramework(Context context) {
public static boolean useStorageAccessFramework(final Context context) {
final String key = context.getString(R.string.storage_use_saf);
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);

View file

@ -53,23 +53,21 @@ import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
public class PeertubeInstanceListFragment extends Fragment {
private static final int MENU_ITEM_RESTORE_ID = 123456;
public InstanceListAdapter instanceListAdapter;
private List<PeertubeInstance> instanceList = new ArrayList<>();
private PeertubeInstance selectedInstance;
private String savedInstanceListKey;
public InstanceListAdapter instanceListAdapter;
private ProgressBar progressBar;
private SharedPreferences sharedPreferences;
private CompositeDisposable disposables = new CompositeDisposable();
/*//////////////////////////////////////////////////////////////////////////
// Lifecycle
//////////////////////////////////////////////////////////////////////////*/
private CompositeDisposable disposables = new CompositeDisposable();
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext());
@ -81,20 +79,23 @@ public class PeertubeInstanceListFragment extends Fragment {
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container,
final Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_instance_list, container, false);
}
@Override
public void onViewCreated(@NonNull View rootView, @Nullable Bundle savedInstanceState) {
public void onViewCreated(@NonNull final View rootView,
@Nullable final Bundle savedInstanceState) {
super.onViewCreated(rootView, savedInstanceState);
initViews(rootView);
}
private void initViews(@NonNull View rootView) {
private void initViews(@NonNull final View rootView) {
TextView instanceHelpTV = rootView.findViewById(R.id.instanceHelpTV);
instanceHelpTV.setText(getString(R.string.peertube_instance_url_help, getString(R.string.peertube_instance_list_url)));
instanceHelpTV.setText(getString(R.string.peertube_instance_url_help,
getString(R.string.peertube_instance_list_url)));
initButton(rootView);
@ -121,32 +122,34 @@ public class PeertubeInstanceListFragment extends Fragment {
super.onPause();
saveChanges();
}
@Override
public void onDestroy() {
super.onDestroy();
if (disposables != null) disposables.clear();
disposables = null;
}
/*//////////////////////////////////////////////////////////////////////////
// Menu
//////////////////////////////////////////////////////////////////////////*/
private final int MENU_ITEM_RESTORE_ID = 123456;
@Override
public void onDestroy() {
super.onDestroy();
if (disposables != null) {
disposables.clear();
}
disposables = null;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
final MenuItem restoreItem = menu.add(Menu.NONE, MENU_ITEM_RESTORE_ID, Menu.NONE, R.string.restore_defaults);
final MenuItem restoreItem = menu
.add(Menu.NONE, MENU_ITEM_RESTORE_ID, Menu.NONE, R.string.restore_defaults);
restoreItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
final int restoreIcon = ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_restore_defaults);
final int restoreIcon = ThemeHelper
.resolveResourceIdFromAttr(requireContext(), R.attr.ic_restore_defaults);
restoreItem.setIcon(AppCompatResources.getDrawable(requireContext(), restoreIcon));
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
public boolean onOptionsItemSelected(final MenuItem item) {
if (item.getItemId() == MENU_ITEM_RESTORE_ID) {
restoreDefaults();
return true;
@ -164,7 +167,7 @@ public class PeertubeInstanceListFragment extends Fragment {
instanceList.addAll(PeertubeHelper.getInstanceList(requireContext()));
}
private void selectInstance(PeertubeInstance instance) {
private void selectInstance(final PeertubeInstance instance) {
selectedInstance = PeertubeHelper.selectInstance(instance, requireContext());
sharedPreferences.edit().putBoolean(Constants.KEY_MAIN_PAGE_CHANGE, true).apply();
}
@ -172,7 +175,9 @@ public class PeertubeInstanceListFragment extends Fragment {
private void updateTitle() {
if (getActivity() instanceof AppCompatActivity) {
ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
if (actionBar != null) actionBar.setTitle(R.string.peertube_instance_url_title);
if (actionBar != null) {
actionBar.setTitle(R.string.peertube_instance_url_title);
}
}
}
@ -202,14 +207,14 @@ public class PeertubeInstanceListFragment extends Fragment {
.show();
}
private void initButton(View rootView) {
private void initButton(final View rootView) {
final FloatingActionButton fab = rootView.findViewById(R.id.addInstanceButton);
fab.setOnClickListener(v -> {
showAddItemDialog(requireContext());
});
}
private void showAddItemDialog(Context c) {
private void showAddItemDialog(final Context c) {
final EditText urlET = new EditText(c);
urlET.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI);
urlET.setHint(R.string.peertube_instance_add_help);
@ -226,46 +231,52 @@ public class PeertubeInstanceListFragment extends Fragment {
dialog.show();
}
private void addInstance(String url) {
private void addInstance(final String url) {
String cleanUrl = cleanUrl(url);
if(null == cleanUrl) return;
if (cleanUrl == null) {
return;
}
progressBar.setVisibility(View.VISIBLE);
Disposable disposable = Single.fromCallable(() -> {
PeertubeInstance instance = new PeertubeInstance(cleanUrl);
instance.fetchInstanceMetaData();
return instance;
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe((instance) -> {
progressBar.setVisibility(View.GONE);
add(instance);
}, e -> {
progressBar.setVisibility(View.GONE);
Toast.makeText(getActivity(), R.string.peertube_instance_add_fail, Toast.LENGTH_SHORT).show();
});
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe((instance) -> {
progressBar.setVisibility(View.GONE);
add(instance);
}, e -> {
progressBar.setVisibility(View.GONE);
Toast.makeText(getActivity(), R.string.peertube_instance_add_fail,
Toast.LENGTH_SHORT).show();
});
disposables.add(disposable);
}
@Nullable
private String cleanUrl(String url){
url = url.trim();
private String cleanUrl(final String url) {
String cleanUrl = url.trim();
// if protocol not present, add https
if(!url.startsWith("http")){
url = "https://" + url;
if (!cleanUrl.startsWith("http")) {
cleanUrl = "https://" + cleanUrl;
}
// remove trailing slash
url = url.replaceAll("/$", "");
cleanUrl = cleanUrl.replaceAll("/$", "");
// only allow https
if (!url.startsWith("https://")) {
Toast.makeText(getActivity(), R.string.peertube_instance_add_https_only, Toast.LENGTH_SHORT).show();
if (!cleanUrl.startsWith("https://")) {
Toast.makeText(getActivity(), R.string.peertube_instance_add_https_only,
Toast.LENGTH_SHORT).show();
return null;
}
// only allow if not already exists
for (PeertubeInstance instance : instanceList) {
if (instance.getUrl().equals(url)) {
Toast.makeText(getActivity(), R.string.peertube_instance_add_exists, Toast.LENGTH_SHORT).show();
if (instance.getUrl().equals(cleanUrl)) {
Toast.makeText(getActivity(), R.string.peertube_instance_add_exists,
Toast.LENGTH_SHORT).show();
return null;
}
}
return url;
return cleanUrl;
}
private void add(final PeertubeInstance instance) {
@ -277,30 +288,93 @@ public class PeertubeInstanceListFragment extends Fragment {
// List Handling
//////////////////////////////////////////////////////////////////////////*/
private class InstanceListAdapter extends RecyclerView.Adapter<InstanceListAdapter.TabViewHolder> {
private ItemTouchHelper itemTouchHelper;
private ItemTouchHelper.SimpleCallback getItemTouchCallback() {
return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
ItemTouchHelper.START | ItemTouchHelper.END) {
@Override
public int interpolateOutOfBoundsScroll(final RecyclerView recyclerView,
final int viewSize,
final int viewSizeOutOfBounds,
final int totalSize,
final long msSinceStartScroll) {
final int standardSpeed = super.interpolateOutOfBoundsScroll(recyclerView, viewSize,
viewSizeOutOfBounds, totalSize, msSinceStartScroll);
final int minimumAbsVelocity = Math.max(12,
Math.abs(standardSpeed));
return minimumAbsVelocity * (int) Math.signum(viewSizeOutOfBounds);
}
@Override
public boolean onMove(final RecyclerView recyclerView,
final RecyclerView.ViewHolder source,
final RecyclerView.ViewHolder target) {
if (source.getItemViewType() != target.getItemViewType()
|| instanceListAdapter == null) {
return false;
}
final int sourceIndex = source.getAdapterPosition();
final int targetIndex = target.getAdapterPosition();
instanceListAdapter.swapItems(sourceIndex, targetIndex);
return true;
}
@Override
public boolean isLongPressDragEnabled() {
return false;
}
@Override
public boolean isItemViewSwipeEnabled() {
return true;
}
@Override
public void onSwiped(final RecyclerView.ViewHolder viewHolder, final int swipeDir) {
int position = viewHolder.getAdapterPosition();
// do not allow swiping the selected instance
if (instanceList.get(position).getUrl().equals(selectedInstance.getUrl())) {
instanceListAdapter.notifyItemChanged(position);
return;
}
instanceList.remove(position);
instanceListAdapter.notifyItemRemoved(position);
if (instanceList.isEmpty()) {
instanceList.add(selectedInstance);
instanceListAdapter.notifyItemInserted(0);
}
}
};
}
private class InstanceListAdapter
extends RecyclerView.Adapter<InstanceListAdapter.TabViewHolder> {
private final LayoutInflater inflater;
private ItemTouchHelper itemTouchHelper;
private RadioButton lastChecked;
InstanceListAdapter(Context context, ItemTouchHelper itemTouchHelper) {
InstanceListAdapter(final Context context, final ItemTouchHelper itemTouchHelper) {
this.itemTouchHelper = itemTouchHelper;
this.inflater = LayoutInflater.from(context);
}
public void swapItems(int fromPosition, int toPosition) {
public void swapItems(final int fromPosition, final int toPosition) {
Collections.swap(instanceList, fromPosition, toPosition);
notifyItemMoved(fromPosition, toPosition);
}
@NonNull
@Override
public InstanceListAdapter.TabViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
public InstanceListAdapter.TabViewHolder onCreateViewHolder(@NonNull final ViewGroup parent,
final int viewType) {
View view = inflater.inflate(R.layout.item_instance, parent, false);
return new InstanceListAdapter.TabViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull InstanceListAdapter.TabViewHolder holder, int position) {
public void onBindViewHolder(@NonNull final InstanceListAdapter.TabViewHolder holder,
final int position) {
holder.bind(position, holder);
}
@ -316,7 +390,7 @@ public class PeertubeInstanceListFragment extends Fragment {
private RadioButton instanceRB;
private ImageView handle;
TabViewHolder(View itemView) {
TabViewHolder(final View itemView) {
super(itemView);
instanceIconView = itemView.findViewById(R.id.instanceIcon);
@ -327,7 +401,7 @@ public class PeertubeInstanceListFragment extends Fragment {
}
@SuppressLint("ClickableViewAccessibility")
void bind(int position, TabViewHolder holder) {
void bind(final int position, final TabViewHolder holder) {
handle.setOnTouchListener(getOnTouchListener(holder));
final PeertubeInstance instance = instanceList.get(position);
@ -367,61 +441,4 @@ public class PeertubeInstanceListFragment extends Fragment {
}
}
}
private ItemTouchHelper.SimpleCallback getItemTouchCallback() {
return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
ItemTouchHelper.START | ItemTouchHelper.END) {
@Override
public int interpolateOutOfBoundsScroll(RecyclerView recyclerView, int viewSize,
int viewSizeOutOfBounds, int totalSize,
long msSinceStartScroll) {
final int standardSpeed = super.interpolateOutOfBoundsScroll(recyclerView, viewSize,
viewSizeOutOfBounds, totalSize, msSinceStartScroll);
final int minimumAbsVelocity = Math.max(12,
Math.abs(standardSpeed));
return minimumAbsVelocity * (int) Math.signum(viewSizeOutOfBounds);
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source,
RecyclerView.ViewHolder target) {
if (source.getItemViewType() != target.getItemViewType() ||
instanceListAdapter == null) {
return false;
}
final int sourceIndex = source.getAdapterPosition();
final int targetIndex = target.getAdapterPosition();
instanceListAdapter.swapItems(sourceIndex, targetIndex);
return true;
}
@Override
public boolean isLongPressDragEnabled() {
return false;
}
@Override
public boolean isItemViewSwipeEnabled() {
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
int position = viewHolder.getAdapterPosition();
// do not allow swiping the selected instance
if(instanceList.get(position).getUrl().equals(selectedInstance.getUrl())) {
instanceListAdapter.notifyItemChanged(position);
return;
}
instanceList.remove(position);
instanceListAdapter.notifyItemRemoved(position);
if (instanceList.isEmpty()) {
instanceList.add(selectedInstance);
instanceListAdapter.notifyItemInserted(0);
}
}
};
}
}

View file

@ -3,16 +3,17 @@ package org.schabi.newpipe.settings;
import android.app.Activity;
import android.content.DialogInterface;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
@ -31,61 +32,56 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
* Created by Christian Schabesberger on 26.09.17.
* SelectChannelFragment.java is part of NewPipe.
*
* <p>
* NewPipe 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.
*
* </p>
* <p>
* NewPipe 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.
*
* </p>
* <p>
* You should have received a copy of the GNU General Public License
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
* </p>
*/
public class SelectChannelFragment extends DialogFragment {
/**
* This contains the base display options for images.
*/
public static final DisplayImageOptions DISPLAY_IMAGE_OPTIONS
= new DisplayImageOptions.Builder().cacheInMemory(true).build();
private final ImageLoader imageLoader = ImageLoader.getInstance();
OnSelectedLisener onSelectedLisener = null;
OnCancelListener onCancelListener = null;
private ProgressBar progressBar;
private TextView emptyView;
private RecyclerView recyclerView;
private List<SubscriptionEntity> subscriptions = new Vector<>();
/*//////////////////////////////////////////////////////////////////////////
// Interfaces
//////////////////////////////////////////////////////////////////////////*/
private TextView emptyView;
private RecyclerView recyclerView;
private List<SubscriptionEntity> subscriptions = new Vector<>();
public interface OnSelectedLisener {
void onChannelSelected(int serviceId, String url, String name);
}
OnSelectedLisener onSelectedLisener = null;
public void setOnSelectedLisener(OnSelectedLisener listener) {
public void setOnSelectedLisener(final OnSelectedLisener listener) {
onSelectedLisener = listener;
}
public interface OnCancelListener {
void onCancel();
}
OnCancelListener onCancelListener = null;
public void setOnCancelListener(OnCancelListener listener) {
public void setOnCancelListener(final OnCancelListener listener) {
onCancelListener = listener;
}
/*//////////////////////////////////////////////////////////////////////////
// Init
//////////////////////////////////////////////////////////////////////////*/
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container,
final Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.select_channel_fragment, container, false);
recyclerView = v.findViewById(R.id.items_list);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
@ -108,35 +104,36 @@ public class SelectChannelFragment extends DialogFragment {
return v;
}
/*//////////////////////////////////////////////////////////////////////////
// Handle actions
// Init
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onCancel(final DialogInterface dialogInterface) {
super.onCancel(dialogInterface);
if(onCancelListener != null) {
if (onCancelListener != null) {
onCancelListener.onCancel();
}
}
private void clickedItem(int position) {
if(onSelectedLisener != null) {
/*//////////////////////////////////////////////////////////////////////////
// Handle actions
//////////////////////////////////////////////////////////////////////////*/
private void clickedItem(final int position) {
if (onSelectedLisener != null) {
SubscriptionEntity entry = subscriptions.get(position);
onSelectedLisener.onChannelSelected(entry.getServiceId(), entry.getUrl(), entry.getName());
onSelectedLisener
.onChannelSelected(entry.getServiceId(), entry.getUrl(), entry.getName());
}
dismiss();
}
/*//////////////////////////////////////////////////////////////////////////
// Item handling
//////////////////////////////////////////////////////////////////////////*/
private void displayChannels(List<SubscriptionEntity> subscriptions) {
this.subscriptions = subscriptions;
private void displayChannels(final List<SubscriptionEntity> newSubscriptions) {
this.subscriptions = newSubscriptions;
progressBar.setVisibility(View.GONE);
if(subscriptions.isEmpty()) {
if (newSubscriptions.isEmpty()) {
emptyView.setVisibility(View.VISIBLE);
return;
}
@ -144,49 +141,75 @@ public class SelectChannelFragment extends DialogFragment {
}
/*//////////////////////////////////////////////////////////////////////////
// Item handling
//////////////////////////////////////////////////////////////////////////*/
private Observer<List<SubscriptionEntity>> getSubscriptionObserver() {
return new Observer<List<SubscriptionEntity>>() {
@Override
public void onSubscribe(Disposable d) {
public void onSubscribe(final Disposable d) { }
@Override
public void onNext(final List<SubscriptionEntity> newSubscriptions) {
displayChannels(newSubscriptions);
}
@Override
public void onNext(List<SubscriptionEntity> subscriptions) {
displayChannels(subscriptions);
}
@Override
public void onError(Throwable exception) {
public void onError(final Throwable exception) {
SelectChannelFragment.this.onError(exception);
}
@Override
public void onComplete() {
}
public void onComplete() { }
};
}
private class SelectChannelAdapter extends
RecyclerView.Adapter<SelectChannelAdapter.SelectChannelItemHolder> {
protected void onError(final Throwable e) {
final Activity activity = getActivity();
ErrorActivity.reportError(activity, e, activity.getClass(), null, ErrorActivity.ErrorInfo
.make(UserAction.UI_ERROR, "none", "", R.string.app_ui_crash));
}
public interface OnSelectedLisener {
void onChannelSelected(int serviceId, String url, String name);
}
/*//////////////////////////////////////////////////////////////////////////
// Error
//////////////////////////////////////////////////////////////////////////*/
public interface OnCancelListener {
void onCancel();
}
/*//////////////////////////////////////////////////////////////////////////
// ImageLoaderOptions
//////////////////////////////////////////////////////////////////////////*/
private class SelectChannelAdapter
extends RecyclerView.Adapter<SelectChannelAdapter.SelectChannelItemHolder> {
@Override
public SelectChannelItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
public SelectChannelItemHolder onCreateViewHolder(final ViewGroup parent,
final int viewType) {
View item = LayoutInflater.from(parent.getContext())
.inflate(R.layout.select_channel_item, parent, false);
return new SelectChannelItemHolder(item);
}
@Override
public void onBindViewHolder(SelectChannelItemHolder holder, final int position) {
public void onBindViewHolder(final SelectChannelItemHolder holder, final int position) {
SubscriptionEntity entry = subscriptions.get(position);
holder.titleView.setText(entry.getName());
holder.view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
public void onClick(final View view) {
clickedItem(position);
}
});
imageLoader.displayImage(entry.getAvatarUrl(), holder.thumbnailView, DISPLAY_IMAGE_OPTIONS);
imageLoader.displayImage(entry.getAvatarUrl(), holder.thumbnailView,
DISPLAY_IMAGE_OPTIONS);
}
@Override
@ -195,41 +218,15 @@ public class SelectChannelFragment extends DialogFragment {
}
public class SelectChannelItemHolder extends RecyclerView.ViewHolder {
public SelectChannelItemHolder(View v) {
public final View view;
public final CircleImageView thumbnailView;
public final TextView titleView;
SelectChannelItemHolder(final View v) {
super(v);
this.view = v;
thumbnailView = v.findViewById(R.id.itemThumbnailView);
titleView = v.findViewById(R.id.itemTitleView);
}
public final View view;
public final CircleImageView thumbnailView;
public final TextView titleView;
}
}
/*//////////////////////////////////////////////////////////////////////////
// Error
//////////////////////////////////////////////////////////////////////////*/
protected void onError(Throwable e) {
final Activity activity = getActivity();
ErrorActivity.reportError(activity, e,
activity.getClass(),
null,
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
"none", "", R.string.app_ui_crash));
}
/*//////////////////////////////////////////////////////////////////////////
// ImageLoaderOptions
//////////////////////////////////////////////////////////////////////////*/
/**
* Base display options
*/
public static final DisplayImageOptions DISPLAY_IMAGE_OPTIONS =
new DisplayImageOptions.Builder()
.cacheInMemory(true)
.build();
}

View file

@ -3,17 +3,17 @@ package org.schabi.newpipe.settings;
import android.app.Activity;
import android.content.DialogInterface;
import android.os.Bundle;
import androidx.fragment.app.DialogFragment;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.schabi.newpipe.MainActivity;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.DialogFragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
@ -28,51 +28,45 @@ import java.util.Vector;
/**
* Created by Christian Schabesberger on 09.10.17.
* SelectKioskFragment.java is part of NewPipe.
*
* <p>
* NewPipe 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.
*
* </p>
* <p>
* NewPipe 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.
*
* </p>
* <p>
* You should have received a copy of the GNU General Public License
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
* </p>
*/
public class SelectKioskFragment extends DialogFragment {
private RecyclerView recyclerView = null;
private SelectKioskAdapter selectKioskAdapter = null;
private static final boolean DEBUG = MainActivity.DEBUG;
RecyclerView recyclerView = null;
SelectKioskAdapter selectKioskAdapter = null;
/*//////////////////////////////////////////////////////////////////////////
/*//////////////////////////////////////////////////////////////////////////
// Interfaces
//////////////////////////////////////////////////////////////////////////*/
private OnSelectedLisener onSelectedLisener = null;
private OnCancelListener onCancelListener = null;
public interface OnSelectedLisener {
void onKioskSelected(int serviceId, String kioskId, String kioskName);
}
OnSelectedLisener onSelectedLisener = null;
public void setOnSelectedLisener(OnSelectedLisener listener) {
public void setOnSelectedLisener(final OnSelectedLisener listener) {
onSelectedLisener = listener;
}
public interface OnCancelListener {
void onCancel();
}
OnCancelListener onCancelListener = null;
public void setOnCancelListener(OnCancelListener listener) {
public void setOnCancelListener(final OnCancelListener listener) {
onCancelListener = listener;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
final Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.select_kiosk_fragment, container, false);
recyclerView = v.findViewById(R.id.items_list);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
@ -86,52 +80,55 @@ public class SelectKioskFragment extends DialogFragment {
return v;
}
/*//////////////////////////////////////////////////////////////////////////
// Handle actions
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onCancel(final DialogInterface dialogInterface) {
super.onCancel(dialogInterface);
if(onCancelListener != null) {
if (onCancelListener != null) {
onCancelListener.onCancel();
}
}
private void clickedItem(SelectKioskAdapter.Entry entry) {
if(onSelectedLisener != null) {
private void clickedItem(final SelectKioskAdapter.Entry entry) {
if (onSelectedLisener != null) {
onSelectedLisener.onKioskSelected(entry.serviceId, entry.kioskId, entry.kioskName);
}
dismiss();
}
/*//////////////////////////////////////////////////////////////////////////
// Handle actions
//////////////////////////////////////////////////////////////////////////*/
protected void onError(final Throwable e) {
final Activity activity = getActivity();
ErrorActivity.reportError(activity, e, activity.getClass(), null, ErrorActivity.ErrorInfo
.make(UserAction.UI_ERROR, "none", "", R.string.app_ui_crash));
}
public interface OnSelectedLisener {
void onKioskSelected(int serviceId, String kioskId, String kioskName);
}
public interface OnCancelListener {
void onCancel();
}
/*//////////////////////////////////////////////////////////////////////////
// Error
//////////////////////////////////////////////////////////////////////////*/
private class SelectKioskAdapter
extends RecyclerView.Adapter<SelectKioskAdapter.SelectKioskItemHolder> {
public class Entry {
public Entry (int i, int si, String ki, String kn){
icon = i; serviceId=si; kioskId=ki; kioskName = kn;
}
final int icon;
final int serviceId;
final String kioskId;
final String kioskName;
}
private final List<Entry> kioskList = new Vector<>();
public SelectKioskAdapter()
throws Exception {
for(StreamingService service : NewPipe.getServices()) {
for(String kioskId : service.getKioskList().getAvailableKiosks()) {
SelectKioskAdapter() throws Exception {
for (StreamingService service : NewPipe.getServices()) {
for (String kioskId : service.getKioskList().getAvailableKiosks()) {
String name = String.format(getString(R.string.service_kiosk_string),
service.getServiceInfo().getName(),
KioskTranslator.getTranslatedKioskName(kioskId, getContext()));
kioskList.add(new Entry(
ServiceHelper.getIcon(service.getServiceId()),
service.getServiceId(),
kioskId,
name));
kioskList.add(new Entry(ServiceHelper.getIcon(service.getServiceId()),
service.getServiceId(), kioskId, name));
}
}
}
@ -140,47 +137,50 @@ public class SelectKioskFragment extends DialogFragment {
return kioskList.size();
}
public SelectKioskItemHolder onCreateViewHolder(ViewGroup parent, int type) {
public SelectKioskItemHolder onCreateViewHolder(final ViewGroup parent, final int type) {
View item = LayoutInflater.from(parent.getContext())
.inflate(R.layout.select_kiosk_item, parent, false);
return new SelectKioskItemHolder(item);
}
public void onBindViewHolder(final SelectKioskItemHolder holder, final int position) {
final Entry entry = kioskList.get(position);
holder.titleView.setText(entry.kioskName);
holder.thumbnailView
.setImageDrawable(ContextCompat.getDrawable(getContext(), entry.icon));
holder.view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View view) {
clickedItem(entry);
}
});
}
class Entry {
final int icon;
final int serviceId;
final String kioskId;
final String kioskName;
Entry(final int i, final int si, final String ki, final String kn) {
icon = i;
serviceId = si;
kioskId = ki;
kioskName = kn;
}
}
public class SelectKioskItemHolder extends RecyclerView.ViewHolder {
public SelectKioskItemHolder(View v) {
public final View view;
final ImageView thumbnailView;
final TextView titleView;
SelectKioskItemHolder(final View v) {
super(v);
this.view = v;
thumbnailView = v.findViewById(R.id.itemThumbnailView);
titleView = v.findViewById(R.id.itemTitleView);
}
public final View view;
public final ImageView thumbnailView;
public final TextView titleView;
}
public void onBindViewHolder(SelectKioskItemHolder holder, final int position) {
final Entry entry = kioskList.get(position);
holder.titleView.setText(entry.kioskName);
holder.thumbnailView.setImageDrawable(ContextCompat.getDrawable(getContext(), entry.icon));
holder.view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
clickedItem(entry);
}
});
}
}
/*//////////////////////////////////////////////////////////////////////////
// Error
//////////////////////////////////////////////////////////////////////////*/
protected void onError(Throwable e) {
final Activity activity = getActivity();
ErrorActivity.reportError(activity, e,
activity.getClass(),
null,
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
"none", "", R.string.app_ui_crash));
}
}

View file

@ -2,15 +2,16 @@ package org.schabi.newpipe.settings;
import android.content.Context;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.appcompat.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import org.schabi.newpipe.R;
import org.schabi.newpipe.util.ThemeHelper;
@ -36,14 +37,15 @@ import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public class SettingsActivity extends AppCompatActivity implements BasePreferenceFragment.OnPreferenceStartFragmentCallback {
public class SettingsActivity extends AppCompatActivity
implements BasePreferenceFragment.OnPreferenceStartFragmentCallback {
public static void initSettings(Context context) {
public static void initSettings(final Context context) {
NewPipeSettings.initSettings(context);
}
@Override
protected void onCreate(Bundle savedInstanceBundle) {
protected void onCreate(final Bundle savedInstanceBundle) {
setTheme(ThemeHelper.getSettingsThemeStyle(this));
assureCorrectAppLanguage(this);
super.onCreate(savedInstanceBundle);
@ -60,7 +62,7 @@ public class SettingsActivity extends AppCompatActivity implements BasePreferenc
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
public boolean onCreateOptionsMenu(final Menu menu) {
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
@ -71,22 +73,27 @@ public class SettingsActivity extends AppCompatActivity implements BasePreferenc
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
public boolean onOptionsItemSelected(final MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
finish();
} else getSupportFragmentManager().popBackStack();
} else {
getSupportFragmentManager().popBackStack();
}
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onPreferenceStartFragment(PreferenceFragmentCompat caller, Preference preference) {
Fragment fragment = Fragment.instantiate(this, preference.getFragment(), preference.getExtras());
public boolean onPreferenceStartFragment(final PreferenceFragmentCompat caller,
final Preference preference) {
Fragment fragment = Fragment
.instantiate(this, preference.getFragment(), preference.getExtras());
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.animator.custom_fade_in, R.animator.custom_fade_out, R.animator.custom_fade_in, R.animator.custom_fade_out)
.setCustomAnimations(R.animator.custom_fade_in, R.animator.custom_fade_out,
R.animator.custom_fade_in, R.animator.custom_fade_out)
.replace(R.id.fragment_holder, fragment)
.addToBackStack(null)
.commit();

View file

@ -1,15 +1,22 @@
package org.schabi.newpipe.settings;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import org.schabi.newpipe.R;
public class UpdateSettingsFragment extends BasePreferenceFragment {
private Preference.OnPreferenceChangeListener updatePreferenceChange
= (preference, newValue) -> {
defaultPreferences.edit()
.putBoolean(getString(R.string.update_app_key), (boolean) newValue).apply();
return true;
};
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String updateToggleKey = getString(R.string.update_app_key);
@ -17,16 +24,7 @@ public class UpdateSettingsFragment extends BasePreferenceFragment {
}
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
addPreferencesFromResource(R.xml.update_settings);
}
private Preference.OnPreferenceChangeListener updatePreferenceChange
= (preference, newValue) -> {
defaultPreferences.edit().putBoolean(getString(R.string.update_app_key),
(boolean) newValue).apply();
return true;
};
}

View file

@ -5,44 +5,45 @@ import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.text.format.DateUtils;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.preference.ListPreference;
import com.google.android.material.snackbar.Snackbar;
import java.util.LinkedList;
import java.util.List;
import org.schabi.newpipe.R;
import org.schabi.newpipe.util.PermissionHelper;
public class VideoAudioSettingsFragment extends BasePreferenceFragment {
import java.util.LinkedList;
import java.util.List;
public class VideoAudioSettingsFragment extends BasePreferenceFragment {
private SharedPreferences.OnSharedPreferenceChangeListener listener;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
updateSeekOptions();
listener = (sharedPreferences, s) -> {
// on M and above, if user chooses to minimise to popup player on exit and the app doesn't have
// display over other apps permission, show a snackbar to let the user give permission
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
s.equals(getString(R.string.minimize_on_exit_key))) {
// on M and above, if user chooses to minimise to popup player on exit
// and the app doesn't have display over other apps permission,
// show a snackbar to let the user give permission
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& s.equals(getString(R.string.minimize_on_exit_key))) {
String newSetting = sharedPreferences.getString(s, null);
if (newSetting != null
&& newSetting.equals(getString(R.string.minimize_on_exit_popup_key))
&& !Settings.canDrawOverlays(getContext())) {
Snackbar.make(getListView(), R.string.permission_display_over_apps, Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.settings,
view -> PermissionHelper.checkSystemAlertWindowPermission(getContext()))
Snackbar.make(getListView(), R.string.permission_display_over_apps,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.settings, view ->
PermissionHelper.checkSystemAlertWindowPermission(getContext()))
.show();
}
@ -53,22 +54,23 @@ public class VideoAudioSettingsFragment extends BasePreferenceFragment {
}
/**
* Update fast-forward/-rewind seek duration options according to language and inexact seek setting.
* Update fast-forward/-rewind seek duration options
* according to language and inexact seek setting.
* Exoplayer can't seek 5 seconds in audio when using inexact seek.
*/
private void updateSeekOptions() {
//initializing R.array.seek_duration_description to display the translation of seconds
// initializing R.array.seek_duration_description to display the translation of seconds
final Resources res = getResources();
final String[] durationsValues = res.getStringArray(R.array.seek_duration_value);
final List<String> displayedDurationValues = new LinkedList<>();
final List<String> displayedDescriptionValues = new LinkedList<>();
int currentDurationValue;
final boolean inexactSeek = getPreferenceManager().getSharedPreferences()
.getBoolean(res.getString(R.string.use_inexact_seek_key), false);
.getBoolean(res.getString(R.string.use_inexact_seek_key), false);
for (String durationsValue : durationsValues) {
currentDurationValue =
Integer.parseInt(durationsValue) / (int) DateUtils.SECOND_IN_MILLIS;
Integer.parseInt(durationsValue) / (int) DateUtils.SECOND_IN_MILLIS;
if (inexactSeek && currentDurationValue % 10 == 5) {
continue;
}
@ -76,15 +78,17 @@ public class VideoAudioSettingsFragment extends BasePreferenceFragment {
displayedDurationValues.add(durationsValue);
try {
displayedDescriptionValues.add(String.format(
res.getQuantityString(R.plurals.seconds,
currentDurationValue),
currentDurationValue));
res.getQuantityString(R.plurals.seconds,
currentDurationValue),
currentDurationValue));
} catch (Resources.NotFoundException ignored) {
//if this happens, the translation is missing, and the english string will be displayed instead
// if this happens, the translation is missing,
// and the english string will be displayed instead
}
}
final ListPreference durations = (ListPreference) findPreference(getString(R.string.seek_duration_key));
final ListPreference durations = (ListPreference) findPreference(
getString(R.string.seek_duration_key));
durations.setEntryValues(displayedDurationValues.toArray(new CharSequence[0]));
durations.setEntries(displayedDescriptionValues.toArray(new CharSequence[0]));
final int selectedDuration = Integer.parseInt(durations.getValue());
@ -93,28 +97,30 @@ public class VideoAudioSettingsFragment extends BasePreferenceFragment {
durations.setValue(Integer.toString(newDuration * (int) DateUtils.SECOND_IN_MILLIS));
Toast toast = Toast
.makeText(getContext(),
getString(R.string.new_seek_duration_toast, newDuration),
Toast.LENGTH_LONG);
.makeText(getContext(),
getString(R.string.new_seek_duration_toast, newDuration),
Toast.LENGTH_LONG);
toast.show();
}
}
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
addPreferencesFromResource(R.xml.video_audio_settings);
}
@Override
public void onResume() {
super.onResume();
getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(listener);
getPreferenceManager().getSharedPreferences()
.registerOnSharedPreferenceChangeListener(listener);
}
@Override
public void onPause() {
super.onPause();
getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(listener);
getPreferenceManager().getSharedPreferences()
.unregisterOnSharedPreferenceChangeListener(listener);
}
}

View file

@ -3,23 +3,23 @@ package org.schabi.newpipe.settings.tabs;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatImageView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatImageView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.util.ThemeHelper;
public class AddTabDialog {
public final class AddTabDialog {
private final AlertDialog dialog;
AddTabDialog(@NonNull final Context context,
@NonNull final ChooseTabListItem[] items,
AddTabDialog(@NonNull final Context context, @NonNull final ChooseTabListItem[] items,
@NonNull final DialogInterface.OnClickListener actions) {
dialog = new AlertDialog.Builder(context)
@ -32,29 +32,32 @@ public class AddTabDialog {
dialog.show();
}
public static final class ChooseTabListItem {
static final class ChooseTabListItem {
final int tabId;
final String itemName;
@DrawableRes final int itemIcon;
@DrawableRes
final int itemIcon;
ChooseTabListItem(Context context, Tab tab) {
ChooseTabListItem(final Context context, final Tab tab) {
this(tab.getTabId(), tab.getTabName(context), tab.getTabIconRes(context));
}
ChooseTabListItem(int tabId, String itemName, @DrawableRes int itemIcon) {
ChooseTabListItem(final int tabId, final String itemName,
@DrawableRes final int itemIcon) {
this.tabId = tabId;
this.itemName = itemName;
this.itemIcon = itemIcon;
}
}
private static class DialogListAdapter extends BaseAdapter {
private static final class DialogListAdapter extends BaseAdapter {
private final LayoutInflater inflater;
private final ChooseTabListItem[] items;
@DrawableRes private final int fallbackIcon;
@DrawableRes
private final int fallbackIcon;
private DialogListAdapter(Context context, ChooseTabListItem[] items) {
private DialogListAdapter(final Context context, final ChooseTabListItem[] items) {
this.inflater = LayoutInflater.from(context);
this.items = items;
this.fallbackIcon = ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_hot);
@ -66,17 +69,18 @@ public class AddTabDialog {
}
@Override
public ChooseTabListItem getItem(int position) {
public ChooseTabListItem getItem(final int position) {
return items[position];
}
@Override
public long getItemId(int position) {
public long getItemId(final int position) {
return getItem(position).tabId;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
public View getView(final int position, final View view, final ViewGroup parent) {
View convertView = view;
if (convertView == null) {
convertView = inflater.inflate(R.layout.list_choose_tabs_dialog, parent, false);
}

View file

@ -4,18 +4,6 @@ import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import androidx.fragment.app.Fragment;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.appcompat.widget.AppCompatImageView;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.ItemTouchHelper;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -26,6 +14,20 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.appcompat.widget.AppCompatImageView;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.report.ErrorActivity;
@ -42,17 +44,18 @@ import java.util.List;
import static org.schabi.newpipe.settings.tabs.Tab.typeFrom;
public class ChooseTabsFragment extends Fragment {
private static final int MENU_ITEM_RESTORE_ID = 123456;
private ChooseTabsFragment.SelectedTabsAdapter selectedTabsAdapter;
private TabsManager tabsManager;
private List<Tab> tabList = new ArrayList<>();
public ChooseTabsFragment.SelectedTabsAdapter selectedTabsAdapter;
/*//////////////////////////////////////////////////////////////////////////
// Lifecycle
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
tabsManager = TabsManager.getManager(requireContext());
@ -62,12 +65,14 @@ public class ChooseTabsFragment extends Fragment {
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container,
final Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_choose_tabs, container, false);
}
@Override
public void onViewCreated(@NonNull View rootView, @Nullable Bundle savedInstanceState) {
public void onViewCreated(@NonNull final View rootView,
@Nullable final Bundle savedInstanceState) {
super.onViewCreated(rootView, savedInstanceState);
initButton(rootView);
@ -88,31 +93,31 @@ public class ChooseTabsFragment extends Fragment {
updateTitle();
}
/*//////////////////////////////////////////////////////////////////////////
// Menu
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onPause() {
super.onPause();
saveChanges();
}
/*//////////////////////////////////////////////////////////////////////////
// Menu
//////////////////////////////////////////////////////////////////////////*/
private final int MENU_ITEM_RESTORE_ID = 123456;
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
final MenuItem restoreItem = menu.add(Menu.NONE, MENU_ITEM_RESTORE_ID, Menu.NONE, R.string.restore_defaults);
final MenuItem restoreItem = menu.add(Menu.NONE, MENU_ITEM_RESTORE_ID, Menu.NONE,
R.string.restore_defaults);
restoreItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
final int restoreIcon = ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_restore_defaults);
final int restoreIcon = ThemeHelper.resolveResourceIdFromAttr(requireContext(),
R.attr.ic_restore_defaults);
restoreItem.setIcon(AppCompatResources.getDrawable(requireContext(), restoreIcon));
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
public boolean onOptionsItemSelected(final MenuItem item) {
if (item.getItemId() == MENU_ITEM_RESTORE_ID) {
restoreDefaults();
return true;
@ -133,7 +138,9 @@ public class ChooseTabsFragment extends Fragment {
private void updateTitle() {
if (getActivity() instanceof AppCompatActivity) {
ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
if (actionBar != null) actionBar.setTitle(R.string.main_page_content);
if (actionBar != null) {
actionBar.setTitle(R.string.main_page_content);
}
}
}
@ -154,7 +161,7 @@ public class ChooseTabsFragment extends Fragment {
.show();
}
private void initButton(View rootView) {
private void initButton(final View rootView) {
final FloatingActionButton fab = rootView.findViewById(R.id.addTabsButton);
fab.setOnClickListener(v -> {
final ChooseTabListItem[] availableTabs = getAvailableTabs(requireContext());
@ -179,37 +186,37 @@ public class ChooseTabsFragment extends Fragment {
selectedTabsAdapter.notifyDataSetChanged();
}
private void addTab(int tabId) {
private void addTab(final int tabId) {
final Tab.Type type = typeFrom(tabId);
if (type == null) {
ErrorActivity.reportError(requireContext(), new IllegalStateException("Tab id not found: " + tabId), null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none", "Choosing tabs on settings", 0));
ErrorActivity.reportError(requireContext(),
new IllegalStateException("Tab id not found: " + tabId), null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Choosing tabs on settings", 0));
return;
}
switch (type) {
case KIOSK: {
SelectKioskFragment selectFragment = new SelectKioskFragment();
selectFragment.setOnSelectedLisener((serviceId, kioskId, kioskName) ->
case KIOSK:
SelectKioskFragment selectKioskFragment = new SelectKioskFragment();
selectKioskFragment.setOnSelectedLisener((serviceId, kioskId, kioskName) ->
addTab(new Tab.KioskTab(serviceId, kioskId)));
selectFragment.show(requireFragmentManager(), "select_kiosk");
selectKioskFragment.show(requireFragmentManager(), "select_kiosk");
return;
}
case CHANNEL: {
SelectChannelFragment selectFragment = new SelectChannelFragment();
selectFragment.setOnSelectedLisener((serviceId, url, name) ->
case CHANNEL:
SelectChannelFragment selectChannelFragment = new SelectChannelFragment();
selectChannelFragment.setOnSelectedLisener((serviceId, url, name) ->
addTab(new Tab.ChannelTab(serviceId, url, name)));
selectFragment.show(requireFragmentManager(), "select_channel");
selectChannelFragment.show(requireFragmentManager(), "select_channel");
return;
}
default:
addTab(type.getTab());
break;
}
}
public ChooseTabListItem[] getAvailableTabs(Context context) {
public ChooseTabListItem[] getAvailableTabs(final Context context) {
final ArrayList<ChooseTabListItem> returnList = new ArrayList<>();
for (Tab.Type type : Tab.Type.values()) {
@ -217,21 +224,25 @@ public class ChooseTabsFragment extends Fragment {
switch (type) {
case BLANK:
if (!tabList.contains(tab)) {
returnList.add(new ChooseTabListItem(tab.getTabId(), getString(R.string.blank_page_summary),
returnList.add(new ChooseTabListItem(tab.getTabId(),
getString(R.string.blank_page_summary),
tab.getTabIconRes(context)));
}
break;
case KIOSK:
returnList.add(new ChooseTabListItem(tab.getTabId(), getString(R.string.kiosk_page_summary),
returnList.add(new ChooseTabListItem(tab.getTabId(),
getString(R.string.kiosk_page_summary),
ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_hot)));
break;
case CHANNEL:
returnList.add(new ChooseTabListItem(tab.getTabId(), getString(R.string.channel_page_summary),
returnList.add(new ChooseTabListItem(tab.getTabId(),
getString(R.string.channel_page_summary),
tab.getTabIconRes(context)));
break;
case DEFAULT_KIOSK:
if (!tabList.contains(tab)) {
returnList.add(new ChooseTabListItem(tab.getTabId(), getString(R.string.default_kiosk_page_summary),
returnList.add(new ChooseTabListItem(tab.getTabId(),
getString(R.string.default_kiosk_page_summary),
ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_hot)));
}
break;
@ -250,29 +261,88 @@ public class ChooseTabsFragment extends Fragment {
// List Handling
//////////////////////////////////////////////////////////////////////////*/
private class SelectedTabsAdapter extends RecyclerView.Adapter<ChooseTabsFragment.SelectedTabsAdapter.TabViewHolder> {
private ItemTouchHelper itemTouchHelper;
private final LayoutInflater inflater;
private ItemTouchHelper.SimpleCallback getItemTouchCallback() {
return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
ItemTouchHelper.START | ItemTouchHelper.END) {
@Override
public int interpolateOutOfBoundsScroll(final RecyclerView recyclerView,
final int viewSize,
final int viewSizeOutOfBounds,
final int totalSize,
final long msSinceStartScroll) {
final int standardSpeed = super.interpolateOutOfBoundsScroll(recyclerView, viewSize,
viewSizeOutOfBounds, totalSize, msSinceStartScroll);
final int minimumAbsVelocity = Math.max(12,
Math.abs(standardSpeed));
return minimumAbsVelocity * (int) Math.signum(viewSizeOutOfBounds);
}
SelectedTabsAdapter(Context context, ItemTouchHelper itemTouchHelper) {
@Override
public boolean onMove(final RecyclerView recyclerView,
final RecyclerView.ViewHolder source,
final RecyclerView.ViewHolder target) {
if (source.getItemViewType() != target.getItemViewType()
|| selectedTabsAdapter == null) {
return false;
}
final int sourceIndex = source.getAdapterPosition();
final int targetIndex = target.getAdapterPosition();
selectedTabsAdapter.swapItems(sourceIndex, targetIndex);
return true;
}
@Override
public boolean isLongPressDragEnabled() {
return false;
}
@Override
public boolean isItemViewSwipeEnabled() {
return true;
}
@Override
public void onSwiped(final RecyclerView.ViewHolder viewHolder, final int swipeDir) {
int position = viewHolder.getAdapterPosition();
tabList.remove(position);
selectedTabsAdapter.notifyItemRemoved(position);
if (tabList.isEmpty()) {
tabList.add(Tab.Type.BLANK.getTab());
selectedTabsAdapter.notifyItemInserted(0);
}
}
};
}
private class SelectedTabsAdapter
extends RecyclerView.Adapter<ChooseTabsFragment.SelectedTabsAdapter.TabViewHolder> {
private final LayoutInflater inflater;
private ItemTouchHelper itemTouchHelper;
SelectedTabsAdapter(final Context context, final ItemTouchHelper itemTouchHelper) {
this.itemTouchHelper = itemTouchHelper;
this.inflater = LayoutInflater.from(context);
}
public void swapItems(int fromPosition, int toPosition) {
public void swapItems(final int fromPosition, final int toPosition) {
Collections.swap(tabList, fromPosition, toPosition);
notifyItemMoved(fromPosition, toPosition);
}
@NonNull
@Override
public ChooseTabsFragment.SelectedTabsAdapter.TabViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
public ChooseTabsFragment.SelectedTabsAdapter.TabViewHolder onCreateViewHolder(
@NonNull final ViewGroup parent, final int viewType) {
View view = inflater.inflate(R.layout.list_choose_tabs, parent, false);
return new ChooseTabsFragment.SelectedTabsAdapter.TabViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ChooseTabsFragment.SelectedTabsAdapter.TabViewHolder holder, int position) {
public void onBindViewHolder(
@NonNull final ChooseTabsFragment.SelectedTabsAdapter.TabViewHolder holder,
final int position) {
holder.bind(position, holder);
}
@ -286,7 +356,7 @@ public class ChooseTabsFragment extends Fragment {
private TextView tabNameView;
private ImageView handle;
TabViewHolder(View itemView) {
TabViewHolder(final View itemView) {
super(itemView);
tabNameView = itemView.findViewById(R.id.tabName);
@ -295,7 +365,7 @@ public class ChooseTabsFragment extends Fragment {
}
@SuppressLint("ClickableViewAccessibility")
void bind(int position, TabViewHolder holder) {
void bind(final int position, final TabViewHolder holder) {
handle.setOnTouchListener(getOnTouchListener(holder));
final Tab tab = tabList.get(position);
@ -314,10 +384,12 @@ public class ChooseTabsFragment extends Fragment {
tabName = getString(R.string.default_kiosk_page_summary);
break;
case KIOSK:
tabName = NewPipe.getNameOfService(((Tab.KioskTab) tab).getKioskServiceId()) + "/" + tab.getTabName(requireContext());
tabName = NewPipe.getNameOfService(((Tab.KioskTab) tab)
.getKioskServiceId()) + "/" + tab.getTabName(requireContext());
break;
case CHANNEL:
tabName = NewPipe.getNameOfService(((Tab.ChannelTab) tab).getChannelServiceId()) + "/" + tab.getTabName(requireContext());
tabName = NewPipe.getNameOfService(((Tab.ChannelTab) tab)
.getChannelServiceId()) + "/" + tab.getTabName(requireContext());
break;
default:
tabName = tab.getTabName(requireContext());
@ -342,56 +414,4 @@ public class ChooseTabsFragment extends Fragment {
}
}
}
private ItemTouchHelper.SimpleCallback getItemTouchCallback() {
return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
ItemTouchHelper.START | ItemTouchHelper.END) {
@Override
public int interpolateOutOfBoundsScroll(RecyclerView recyclerView, int viewSize,
int viewSizeOutOfBounds, int totalSize,
long msSinceStartScroll) {
final int standardSpeed = super.interpolateOutOfBoundsScroll(recyclerView, viewSize,
viewSizeOutOfBounds, totalSize, msSinceStartScroll);
final int minimumAbsVelocity = Math.max(12,
Math.abs(standardSpeed));
return minimumAbsVelocity * (int) Math.signum(viewSizeOutOfBounds);
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source,
RecyclerView.ViewHolder target) {
if (source.getItemViewType() != target.getItemViewType() ||
selectedTabsAdapter == null) {
return false;
}
final int sourceIndex = source.getAdapterPosition();
final int targetIndex = target.getAdapterPosition();
selectedTabsAdapter.swapItems(sourceIndex, targetIndex);
return true;
}
@Override
public boolean isLongPressDragEnabled() {
return false;
}
@Override
public boolean isItemViewSwipeEnabled() {
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
int position = viewHolder.getAdapterPosition();
tabList.remove(position);
selectedTabsAdapter.notifyItemRemoved(position);
if (tabList.isEmpty()) {
tabList.add(Tab.Type.BLANK.getTab());
selectedTabsAdapter.notifyItemInserted(0);
}
}
};
}
}

View file

@ -31,59 +31,16 @@ import org.schabi.newpipe.util.ThemeHelper;
import java.util.Objects;
public abstract class Tab {
Tab() {
}
private static final String JSON_TAB_ID_KEY = "tab_id";
Tab(@NonNull JsonObject jsonObject) {
Tab() { }
Tab(@NonNull final JsonObject jsonObject) {
readDataFromJson(jsonObject);
}
public abstract int getTabId();
public abstract String getTabName(Context context);
@DrawableRes public abstract int getTabIconRes(Context context);
/**
* Return a instance of the fragment that this tab represent.
*/
public abstract Fragment getFragment(Context context) throws ExtractionException;
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
return obj instanceof Tab && obj.getClass().equals(this.getClass())
&& ((Tab) obj).getTabId() == this.getTabId();
}
/*//////////////////////////////////////////////////////////////////////////
// JSON Handling
//////////////////////////////////////////////////////////////////////////*/
private static final String JSON_TAB_ID_KEY = "tab_id";
public void writeJsonOn(JsonSink jsonSink) {
jsonSink.object();
jsonSink.value(JSON_TAB_ID_KEY, getTabId());
writeDataToJson(jsonSink);
jsonSink.end();
}
protected void writeDataToJson(JsonSink writerSink) {
// No-op
}
protected void readDataFromJson(JsonObject jsonObject) {
// No-op
}
/*//////////////////////////////////////////////////////////////////////////
// Tab Handling
//////////////////////////////////////////////////////////////////////////*/
@Nullable
public static Tab from(@NonNull JsonObject jsonObject) {
public static Tab from(@NonNull final JsonObject jsonObject) {
final int tabId = jsonObject.getInt(Tab.JSON_TAB_ID_KEY, -1);
if (tabId == -1) {
@ -99,7 +56,7 @@ public abstract class Tab {
}
@Nullable
public static Type typeFrom(int tabId) {
public static Type typeFrom(final int tabId) {
for (Type available : Type.values()) {
if (available.getTabId() == tabId) {
return available;
@ -109,7 +66,7 @@ public abstract class Tab {
}
@Nullable
private static Tab from(final int tabId, @Nullable JsonObject jsonObject) {
private static Tab from(final int tabId, @Nullable final JsonObject jsonObject) {
final Type type = typeFrom(tabId);
if (type == null) {
@ -128,6 +85,56 @@ public abstract class Tab {
return type.getTab();
}
/*//////////////////////////////////////////////////////////////////////////
// JSON Handling
//////////////////////////////////////////////////////////////////////////*/
public abstract int getTabId();
public abstract String getTabName(Context context);
@DrawableRes
public abstract int getTabIconRes(Context context);
/**
* Return a instance of the fragment that this tab represent.
*
* @param context Android app context
* @return the fragment this tab represents
*/
public abstract Fragment getFragment(Context context) throws ExtractionException;
/*//////////////////////////////////////////////////////////////////////////
// Tab Handling
//////////////////////////////////////////////////////////////////////////*/
@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
}
return obj instanceof Tab && obj.getClass().equals(this.getClass())
&& ((Tab) obj).getTabId() == this.getTabId();
}
public void writeJsonOn(final JsonSink jsonSink) {
jsonSink.object();
jsonSink.value(JSON_TAB_ID_KEY, getTabId());
writeDataToJson(jsonSink);
jsonSink.end();
}
protected void writeDataToJson(final JsonSink writerSink) {
// No-op
}
protected void readDataFromJson(final JsonObject jsonObject) {
// No-op
}
/*//////////////////////////////////////////////////////////////////////////
// Implementations
//////////////////////////////////////////////////////////////////////////*/
@ -144,7 +151,7 @@ public abstract class Tab {
private Tab tab;
Type(Tab tab) {
Type(final Tab tab) {
this.tab = tab;
}
@ -166,18 +173,18 @@ public abstract class Tab {
}
@Override
public String getTabName(Context context) {
public String getTabName(final Context context) {
return "NewPipe"; //context.getString(R.string.blank_page_summary);
}
@DrawableRes
@Override
public int getTabIconRes(Context context) {
public int getTabIconRes(final Context context) {
return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_blank_page);
}
@Override
public BlankFragment getFragment(Context context) {
public BlankFragment getFragment(final Context context) {
return new BlankFragment();
}
}
@ -191,18 +198,18 @@ public abstract class Tab {
}
@Override
public String getTabName(Context context) {
public String getTabName(final Context context) {
return context.getString(R.string.tab_subscriptions);
}
@DrawableRes
@Override
public int getTabIconRes(Context context) {
public int getTabIconRes(final Context context) {
return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_channel);
}
@Override
public SubscriptionFragment getFragment(Context context) {
public SubscriptionFragment getFragment(final Context context) {
return new SubscriptionFragment();
}
@ -217,18 +224,18 @@ public abstract class Tab {
}
@Override
public String getTabName(Context context) {
public String getTabName(final Context context) {
return context.getString(R.string.fragment_feed_title);
}
@DrawableRes
@Override
public int getTabIconRes(Context context) {
public int getTabIconRes(final Context context) {
return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.rss);
}
@Override
public FeedFragment getFragment(Context context) {
public FeedFragment getFragment(final Context context) {
return new FeedFragment();
}
}
@ -242,18 +249,18 @@ public abstract class Tab {
}
@Override
public String getTabName(Context context) {
public String getTabName(final Context context) {
return context.getString(R.string.tab_bookmarks);
}
@DrawableRes
@Override
public int getTabIconRes(Context context) {
public int getTabIconRes(final Context context) {
return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_bookmark);
}
@Override
public BookmarkFragment getFragment(Context context) {
public BookmarkFragment getFragment(final Context context) {
return new BookmarkFragment();
}
}
@ -267,41 +274,39 @@ public abstract class Tab {
}
@Override
public String getTabName(Context context) {
public String getTabName(final Context context) {
return context.getString(R.string.title_activity_history);
}
@DrawableRes
@Override
public int getTabIconRes(Context context) {
public int getTabIconRes(final Context context) {
return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.history);
}
@Override
public StatisticsPlaylistFragment getFragment(Context context) {
public StatisticsPlaylistFragment getFragment(final Context context) {
return new StatisticsPlaylistFragment();
}
}
public static class KioskTab extends Tab {
public static final int ID = 5;
private int kioskServiceId;
private String kioskId;
private static final String JSON_KIOSK_SERVICE_ID_KEY = "service_id";
private static final String JSON_KIOSK_ID_KEY = "kiosk_id";
private int kioskServiceId;
private String kioskId;
private KioskTab() {
this(-1, "<no-id>");
}
public KioskTab(int kioskServiceId, String kioskId) {
public KioskTab(final int kioskServiceId, final String kioskId) {
this.kioskServiceId = kioskServiceId;
this.kioskId = kioskId;
}
public KioskTab(JsonObject jsonObject) {
public KioskTab(final JsonObject jsonObject) {
super(jsonObject);
}
@ -311,13 +316,13 @@ public abstract class Tab {
}
@Override
public String getTabName(Context context) {
public String getTabName(final Context context) {
return KioskTranslator.getTranslatedKioskName(kioskId, context);
}
@DrawableRes
@Override
public int getTabIconRes(Context context) {
public int getTabIconRes(final Context context) {
final int kioskIcon = KioskTranslator.getKioskIcons(kioskId, context);
if (kioskIcon <= 0) {
@ -328,26 +333,25 @@ public abstract class Tab {
}
@Override
public KioskFragment getFragment(Context context) throws ExtractionException {
public KioskFragment getFragment(final Context context) throws ExtractionException {
return KioskFragment.getInstance(kioskServiceId, kioskId);
}
@Override
protected void writeDataToJson(JsonSink writerSink) {
protected void writeDataToJson(final JsonSink writerSink) {
writerSink.value(JSON_KIOSK_SERVICE_ID_KEY, kioskServiceId)
.value(JSON_KIOSK_ID_KEY, kioskId);
}
@Override
protected void readDataFromJson(JsonObject jsonObject) {
protected void readDataFromJson(final JsonObject jsonObject) {
kioskServiceId = jsonObject.getInt(JSON_KIOSK_SERVICE_ID_KEY, -1);
kioskId = jsonObject.getString(JSON_KIOSK_ID_KEY, "<no-id>");
}
@Override
public boolean equals(Object obj) {
return super.equals(obj) &&
kioskServiceId == ((KioskTab) obj).kioskServiceId
public boolean equals(final Object obj) {
return super.equals(obj) && kioskServiceId == ((KioskTab) obj).kioskServiceId
&& Objects.equals(kioskId, ((KioskTab) obj).kioskId);
}
@ -362,26 +366,25 @@ public abstract class Tab {
public static class ChannelTab extends Tab {
public static final int ID = 6;
private int channelServiceId;
private String channelUrl;
private String channelName;
private static final String JSON_CHANNEL_SERVICE_ID_KEY = "channel_service_id";
private static final String JSON_CHANNEL_URL_KEY = "channel_url";
private static final String JSON_CHANNEL_NAME_KEY = "channel_name";
private int channelServiceId;
private String channelUrl;
private String channelName;
private ChannelTab() {
this(-1, "<no-url>", "<no-name>");
}
public ChannelTab(int channelServiceId, String channelUrl, String channelName) {
public ChannelTab(final int channelServiceId, final String channelUrl,
final String channelName) {
this.channelServiceId = channelServiceId;
this.channelUrl = channelUrl;
this.channelName = channelName;
}
public ChannelTab(JsonObject jsonObject) {
public ChannelTab(final JsonObject jsonObject) {
super(jsonObject);
}
@ -391,39 +394,38 @@ public abstract class Tab {
}
@Override
public String getTabName(Context context) {
public String getTabName(final Context context) {
return channelName;
}
@DrawableRes
@Override
public int getTabIconRes(Context context) {
public int getTabIconRes(final Context context) {
return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_channel);
}
@Override
public ChannelFragment getFragment(Context context) {
public ChannelFragment getFragment(final Context context) {
return ChannelFragment.getInstance(channelServiceId, channelUrl, channelName);
}
@Override
protected void writeDataToJson(JsonSink writerSink) {
protected void writeDataToJson(final JsonSink writerSink) {
writerSink.value(JSON_CHANNEL_SERVICE_ID_KEY, channelServiceId)
.value(JSON_CHANNEL_URL_KEY, channelUrl)
.value(JSON_CHANNEL_NAME_KEY, channelName);
}
@Override
protected void readDataFromJson(JsonObject jsonObject) {
protected void readDataFromJson(final JsonObject jsonObject) {
channelServiceId = jsonObject.getInt(JSON_CHANNEL_SERVICE_ID_KEY, -1);
channelUrl = jsonObject.getString(JSON_CHANNEL_URL_KEY, "<no-url>");
channelName = jsonObject.getString(JSON_CHANNEL_NAME_KEY, "<no-name>");
}
@Override
public boolean equals(Object obj) {
return super.equals(obj) &&
channelServiceId == ((ChannelTab) obj).channelServiceId
public boolean equals(final Object obj) {
return super.equals(obj) && channelServiceId == ((ChannelTab) obj).channelServiceId
&& Objects.equals(channelUrl, ((ChannelTab) obj).channelUrl)
&& Objects.equals(channelName, ((ChannelTab) obj).channelName);
}
@ -450,22 +452,22 @@ public abstract class Tab {
}
@Override
public String getTabName(Context context) {
public String getTabName(final Context context) {
return KioskTranslator.getTranslatedKioskName(getDefaultKioskId(context), context);
}
@DrawableRes
@Override
public int getTabIconRes(Context context) {
public int getTabIconRes(final Context context) {
return KioskTranslator.getKioskIcons(getDefaultKioskId(context), context);
}
@Override
public DefaultKioskFragment getFragment(Context context) throws ExtractionException {
public DefaultKioskFragment getFragment(final Context context) {
return new DefaultKioskFragment();
}
private String getDefaultKioskId(Context context) {
private String getDefaultKioskId(final Context context) {
final int kioskServiceId = ServiceHelper.getSelectedServiceId(context);
String kioskId = "";
@ -474,7 +476,8 @@ public abstract class Tab {
kioskId = service.getKioskList().getDefaultKioskId();
} catch (ExtractionException e) {
ErrorActivity.reportError(context, e, null, null,
ErrorActivity.ErrorInfo.make(UserAction.REQUESTED_KIOSK, "none", "Loading default kiosk from selected service", 0));
ErrorActivity.ErrorInfo.make(UserAction.REQUESTED_KIOSK, "none",
"Loading default kiosk from selected service", 0));
}
return kioskId;
}

View file

@ -1,5 +1,7 @@
package org.schabi.newpipe.settings.tabs;
import androidx.annotation.Nullable;
import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject;
import com.grack.nanojson.JsonParser;
@ -12,33 +14,19 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import androidx.annotation.Nullable;
/**
* Class to get a JSON representation of a list of tabs, and the other way around.
*/
public class TabsJsonHelper {
public final class TabsJsonHelper {
private static final String JSON_TABS_ARRAY_KEY = "tabs";
private static final List<Tab> FALLBACK_INITIAL_TABS_LIST = Collections.unmodifiableList(Arrays.asList(
Tab.Type.DEFAULT_KIOSK.getTab(),
Tab.Type.SUBSCRIPTIONS.getTab(),
Tab.Type.BOOKMARKS.getTab()
));
private static final List<Tab> FALLBACK_INITIAL_TABS_LIST = Collections.unmodifiableList(
Arrays.asList(
Tab.Type.DEFAULT_KIOSK.getTab(),
Tab.Type.SUBSCRIPTIONS.getTab(),
Tab.Type.BOOKMARKS.getTab()));
public static class InvalidJsonException extends Exception {
private InvalidJsonException() {
super();
}
private InvalidJsonException(String message) {
super(message);
}
private InvalidJsonException(Throwable cause) {
super(cause);
}
}
private TabsJsonHelper() { }
/**
* Try to reads the passed JSON and returns the list of tabs if no error were encountered.
@ -52,7 +40,8 @@ public class TabsJsonHelper {
* @return a list of {@link Tab tabs}.
* @throws InvalidJsonException if the JSON string is not valid
*/
public static List<Tab> getTabsFromJson(@Nullable String tabsJson) throws InvalidJsonException {
public static List<Tab> getTabsFromJson(@Nullable final String tabsJson)
throws InvalidJsonException {
if (tabsJson == null || tabsJson.isEmpty()) {
return getDefaultTabs();
}
@ -65,11 +54,14 @@ public class TabsJsonHelper {
final JsonArray tabsArray = outerJsonObject.getArray(JSON_TABS_ARRAY_KEY);
if (tabsArray == null) {
throw new InvalidJsonException("JSON doesn't contain \"" + JSON_TABS_ARRAY_KEY + "\" array");
throw new InvalidJsonException("JSON doesn't contain \"" + JSON_TABS_ARRAY_KEY
+ "\" array");
}
for (Object o : tabsArray) {
if (!(o instanceof JsonObject)) continue;
if (!(o instanceof JsonObject)) {
continue;
}
final Tab tab = Tab.from((JsonObject) o);
@ -94,13 +86,15 @@ public class TabsJsonHelper {
* @param tabList a list of {@link Tab tabs}.
* @return a JSON string representing the list of tabs
*/
public static String getJsonToSave(@Nullable List<Tab> tabList) {
public static String getJsonToSave(@Nullable final List<Tab> tabList) {
final JsonStringWriter jsonWriter = JsonWriter.string();
jsonWriter.object();
jsonWriter.array(JSON_TABS_ARRAY_KEY);
if (tabList != null) for (Tab tab : tabList) {
tab.writeJsonOn(jsonWriter);
if (tabList != null) {
for (Tab tab : tabList) {
tab.writeJsonOn(jsonWriter);
}
}
jsonWriter.end();
@ -108,7 +102,21 @@ public class TabsJsonHelper {
return jsonWriter.done();
}
public static List<Tab> getDefaultTabs(){
public static List<Tab> getDefaultTabs() {
return FALLBACK_INITIAL_TABS_LIST;
}
}
public static final class InvalidJsonException extends Exception {
private InvalidJsonException() {
super();
}
private InvalidJsonException(final String message) {
super(message);
}
private InvalidJsonException(final Throwable cause) {
super(cause);
}
}
}

View file

@ -9,21 +9,23 @@ import org.schabi.newpipe.R;
import java.util.List;
public class TabsManager {
public final class TabsManager {
private final SharedPreferences sharedPreferences;
private final String savedTabsKey;
private final Context context;
private SavedTabsChangeListener savedTabsChangeListener;
private SharedPreferences.OnSharedPreferenceChangeListener preferenceChangeListener;
public static TabsManager getManager(Context context) {
return new TabsManager(context);
}
private TabsManager(Context context) {
private TabsManager(final Context context) {
this.context = context;
this.sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
this.savedTabsKey = context.getString(R.string.saved_tabs_key);
}
public static TabsManager getManager(final Context context) {
return new TabsManager(context);
}
public List<Tab> getTabs() {
final String savedJson = sharedPreferences.getString(savedTabsKey, null);
try {
@ -34,11 +36,15 @@ public class TabsManager {
}
}
public void saveTabs(List<Tab> tabList) {
public void saveTabs(final List<Tab> tabList) {
final String jsonToSave = TabsJsonHelper.getJsonToSave(tabList);
sharedPreferences.edit().putString(savedTabsKey, jsonToSave).apply();
}
/*//////////////////////////////////////////////////////////////////////////
// Listener
//////////////////////////////////////////////////////////////////////////*/
public void resetTabs() {
sharedPreferences.edit().remove(savedTabsKey).apply();
}
@ -47,18 +53,7 @@ public class TabsManager {
return TabsJsonHelper.getDefaultTabs();
}
/*//////////////////////////////////////////////////////////////////////////
// Listener
//////////////////////////////////////////////////////////////////////////*/
public interface SavedTabsChangeListener {
void onTabsChanged();
}
private SavedTabsChangeListener savedTabsChangeListener;
private SharedPreferences.OnSharedPreferenceChangeListener preferenceChangeListener;
public void setSavedTabsListener(SavedTabsChangeListener listener) {
public void setSavedTabsListener(final SavedTabsChangeListener listener) {
if (preferenceChangeListener != null) {
sharedPreferences.unregisterOnSharedPreferenceChangeListener(preferenceChangeListener);
}
@ -76,13 +71,19 @@ public class TabsManager {
}
private SharedPreferences.OnSharedPreferenceChangeListener getPreferenceChangeListener() {
return (sharedPreferences, key) -> {
return (sp, key) -> {
if (key.equals(savedTabsKey)) {
if (savedTabsChangeListener != null) savedTabsChangeListener.onTabsChanged();
if (savedTabsChangeListener != null) {
savedTabsChangeListener.onTabsChanged();
}
}
};
}
public interface SavedTabsChangeListener {
void onTabsChanged();
}
}