Merge pull request #13026 from dustdfg/kotlin_merged2
Convert a bunch of files to kotlin
This commit is contained in:
commit
492f6d18bb
16 changed files with 564 additions and 646 deletions
|
|
@ -1,50 +0,0 @@
|
||||||
package org.schabi.newpipe;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) Hans-Christoph Steiner 2016 <hans@eds.org>
|
|
||||||
* ExitActivity.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 ExitActivity extends Activity {
|
|
||||||
|
|
||||||
public static void exitAndRemoveFromRecentApps(final Activity activity) {
|
|
||||||
final Intent intent = new Intent(activity, ExitActivity.class);
|
|
||||||
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
|
|
||||||
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
|
|
||||||
| Intent.FLAG_ACTIVITY_CLEAR_TASK
|
|
||||||
| Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
|
||||||
|
|
||||||
activity.startActivity(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
|
||||||
@Override
|
|
||||||
protected void onCreate(final Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
finishAndRemoveTask();
|
|
||||||
|
|
||||||
NavigationHelper.restartApp(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
36
app/src/main/java/org/schabi/newpipe/ExitActivity.kt
Normal file
36
app/src/main/java/org/schabi/newpipe/ExitActivity.kt
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2016-2026 NewPipe contributors <https://newpipe.net>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.schabi.newpipe
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import org.schabi.newpipe.util.NavigationHelper
|
||||||
|
|
||||||
|
class ExitActivity : Activity() {
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
finishAndRemoveTask()
|
||||||
|
NavigationHelper.restartApp(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun exitAndRemoveFromRecentApps(activity: Activity) {
|
||||||
|
val intent = Intent(activity, ExitActivity::class.java)
|
||||||
|
intent.addFlags(
|
||||||
|
Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
or Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
|
||||||
|
or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||||
|
or Intent.FLAG_ACTIVITY_NO_ANIMATION
|
||||||
|
)
|
||||||
|
|
||||||
|
activity.startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
package org.schabi.newpipe.settings.export;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import java.io.ObjectStreamClass;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An {@link ObjectInputStream} that only allows preferences-related types to be deserialized, to
|
|
||||||
* prevent injections. The only allowed types are: all primitive types, all boxed primitive types,
|
|
||||||
* null, strings. HashMap, HashSet and arrays of previously defined types are also allowed. Sources:
|
|
||||||
* <a href="https://wiki.sei.cmu.edu/confluence/display/java/SER00-J.+Enable+serialization+compatibility+during+class+evolution">
|
|
||||||
* cmu.edu
|
|
||||||
* </a>,
|
|
||||||
* <a href="https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html#harden-your-own-javaioobjectinputstream">
|
|
||||||
* OWASP cheatsheet
|
|
||||||
* </a>,
|
|
||||||
* <a href="https://commons.apache.org/proper/commons-io/apidocs/src-html/org/apache/commons/io/serialization/ValidatingObjectInputStream.html#line-118">
|
|
||||||
* Apache's {@code ValidatingObjectInputStream}
|
|
||||||
* </a>
|
|
||||||
*/
|
|
||||||
public class PreferencesObjectInputStream extends ObjectInputStream {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Primitive types, strings and other built-in types do not pass through resolveClass() but
|
|
||||||
* instead have a custom encoding; see
|
|
||||||
* <a href="https://docs.oracle.com/javase/6/docs/platform/serialization/spec/protocol.html#10152">
|
|
||||||
* official docs</a>.
|
|
||||||
*/
|
|
||||||
private static final Set<String> CLASS_WHITELIST = Set.of(
|
|
||||||
"java.lang.Boolean",
|
|
||||||
"java.lang.Byte",
|
|
||||||
"java.lang.Character",
|
|
||||||
"java.lang.Short",
|
|
||||||
"java.lang.Integer",
|
|
||||||
"java.lang.Long",
|
|
||||||
"java.lang.Float",
|
|
||||||
"java.lang.Double",
|
|
||||||
"java.lang.Void",
|
|
||||||
"java.util.HashMap",
|
|
||||||
"java.util.HashSet"
|
|
||||||
);
|
|
||||||
|
|
||||||
public PreferencesObjectInputStream(final InputStream in) throws IOException {
|
|
||||||
super(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Class<?> resolveClass(final ObjectStreamClass desc)
|
|
||||||
throws ClassNotFoundException, IOException {
|
|
||||||
if (CLASS_WHITELIST.contains(desc.getName())) {
|
|
||||||
return super.resolveClass(desc);
|
|
||||||
} else {
|
|
||||||
throw new ClassNotFoundException("Class not allowed: " + desc.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024-2026 NewPipe contributors <https://newpipe.net>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.schabi.newpipe.settings.export
|
||||||
|
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.ObjectInputStream
|
||||||
|
import java.io.ObjectStreamClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An [ObjectInputStream] that only allows preferences-related types to be deserialized, to
|
||||||
|
* prevent injections. The only allowed types are: all primitive types, all boxed primitive types,
|
||||||
|
* null, strings. HashMap, HashSet and arrays of previously defined types are also allowed. Sources:
|
||||||
|
* [cmu.edu](https://wiki.sei.cmu.edu/confluence/display/java/SER00-J.+Enable+serialization+compatibility+during+class+evolution) * ,
|
||||||
|
* [OWASP cheatsheet](https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html#harden-your-own-javaioobjectinputstream) * ,
|
||||||
|
* [Apache's `ValidatingObjectInputStream`](https://commons.apache.org/proper/commons-io/apidocs/src-html/org/apache/commons/io/serialization/ValidatingObjectInputStream.html#line-118) *
|
||||||
|
*/
|
||||||
|
class PreferencesObjectInputStream(stream: InputStream) : ObjectInputStream(stream) {
|
||||||
|
@Throws(ClassNotFoundException::class, IOException::class)
|
||||||
|
override fun resolveClass(desc: ObjectStreamClass): Class<*> {
|
||||||
|
if (desc.name in CLASS_WHITELIST) {
|
||||||
|
return super.resolveClass(desc)
|
||||||
|
} else {
|
||||||
|
throw ClassNotFoundException("Class not allowed: $desc.name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Primitive types, strings and other built-in types do not pass through resolveClass() but
|
||||||
|
* instead have a custom encoding; see
|
||||||
|
* [
|
||||||
|
* official docs](https://docs.oracle.com/javase/6/docs/platform/serialization/spec/protocol.html#10152).
|
||||||
|
*/
|
||||||
|
private val CLASS_WHITELIST = setOf<String>(
|
||||||
|
"java.lang.Boolean",
|
||||||
|
"java.lang.Byte",
|
||||||
|
"java.lang.Character",
|
||||||
|
"java.lang.Short",
|
||||||
|
"java.lang.Integer",
|
||||||
|
"java.lang.Long",
|
||||||
|
"java.lang.Float",
|
||||||
|
"java.lang.Double",
|
||||||
|
"java.lang.Void",
|
||||||
|
"java.util.HashMap",
|
||||||
|
"java.util.HashSet"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
package org.schabi.newpipe.util;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
|
|
||||||
import androidx.preference.PreferenceManager;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For preferences with dependencies and multiple use case,
|
|
||||||
* this class can be used to reduce the lines of code.
|
|
||||||
*/
|
|
||||||
public final class DependentPreferenceHelper {
|
|
||||||
|
|
||||||
private DependentPreferenceHelper() {
|
|
||||||
// no instance
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Option `Resume playback` depends on `Watch history`, this method can be used to retrieve if
|
|
||||||
* `Resume playback` and its dependencies are all enabled.
|
|
||||||
*
|
|
||||||
* @param context the Android context
|
|
||||||
* @return returns true if `Resume playback` and `Watch history` are both enabled
|
|
||||||
*/
|
|
||||||
public static boolean getResumePlaybackEnabled(final Context context) {
|
|
||||||
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
|
|
||||||
return prefs.getBoolean(context.getString(
|
|
||||||
R.string.enable_watch_history_key), true)
|
|
||||||
&& prefs.getBoolean(context.getString(
|
|
||||||
R.string.enable_playback_resume_key), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Option `Position in lists` depends on `Watch history`, this method can be used to retrieve if
|
|
||||||
* `Position in lists` and its dependencies are all enabled.
|
|
||||||
*
|
|
||||||
* @param context the Android context
|
|
||||||
* @return returns true if `Positions in lists` and `Watch history` are both enabled
|
|
||||||
*/
|
|
||||||
public static boolean getPositionsInListsEnabled(final Context context) {
|
|
||||||
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
|
|
||||||
return prefs.getBoolean(context.getString(
|
|
||||||
R.string.enable_watch_history_key), true)
|
|
||||||
&& prefs.getBoolean(context.getString(
|
|
||||||
R.string.enable_playback_state_lists_key), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023-2026 NewPipe contributors <https://newpipe.net>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.schabi.newpipe.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import org.schabi.newpipe.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For preferences with dependencies and multiple use case,
|
||||||
|
* this class can be used to reduce the lines of code.
|
||||||
|
*/
|
||||||
|
object DependentPreferenceHelper {
|
||||||
|
/**
|
||||||
|
* Option `Resume playback` depends on `Watch history`, this method can be used to retrieve if
|
||||||
|
* `Resume playback` and its dependencies are all enabled.
|
||||||
|
*
|
||||||
|
* @param context the Android context
|
||||||
|
* @return returns true if `Resume playback` and `Watch history` are both enabled
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun getResumePlaybackEnabled(context: Context): Boolean {
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
|
||||||
|
return prefs.getBoolean(context.getString(R.string.enable_watch_history_key), true) &&
|
||||||
|
prefs.getBoolean(context.getString(R.string.enable_playback_resume_key), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Option `Position in lists` depends on `Watch history`, this method can be used to retrieve if
|
||||||
|
* `Position in lists` and its dependencies are all enabled.
|
||||||
|
*
|
||||||
|
* @param context the Android context
|
||||||
|
* @return returns true if `Positions in lists` and `Watch history` are both enabled
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun getPositionsInListsEnabled(context: Context): Boolean {
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
|
||||||
|
return prefs.getBoolean(context.getString(R.string.enable_watch_history_key), true) &&
|
||||||
|
prefs.getBoolean(context.getString(R.string.enable_playback_state_lists_key), true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
package org.schabi.newpipe.util;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.text.Selection;
|
|
||||||
import android.text.Spannable;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
|
||||||
import org.schabi.newpipe.views.NewPipeEditText;
|
|
||||||
import org.schabi.newpipe.views.NewPipeTextView;
|
|
||||||
|
|
||||||
public final class NewPipeTextViewHelper {
|
|
||||||
private NewPipeTextViewHelper() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Share the selected text of {@link NewPipeTextView NewPipeTextViews} and
|
|
||||||
* {@link NewPipeEditText NewPipeEditTexts} with
|
|
||||||
* {@link ShareUtils#shareText(Context, String, String)}.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* This allows EMUI users to get the Android share sheet instead of the EMUI share sheet when
|
|
||||||
* using the {@code Share} command of the popup menu which appears when selecting text.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param textView the {@link TextView} on which sharing the selected text. It should be a
|
|
||||||
* {@link NewPipeTextView} or a {@link NewPipeEditText} (even if
|
|
||||||
* {@link TextView standard TextViews} are supported).
|
|
||||||
*/
|
|
||||||
public static void shareSelectedTextWithShareUtils(@NonNull final TextView textView) {
|
|
||||||
final CharSequence textViewText = textView.getText();
|
|
||||||
shareSelectedTextIfNotNullAndNotEmpty(textView, getSelectedText(textView, textViewText));
|
|
||||||
if (textViewText instanceof Spannable) {
|
|
||||||
Selection.setSelection((Spannable) textViewText, textView.getSelectionEnd());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static CharSequence getSelectedText(@NonNull final TextView textView,
|
|
||||||
@Nullable final CharSequence text) {
|
|
||||||
if (!textView.hasSelection() || text == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int start = textView.getSelectionStart();
|
|
||||||
final int end = textView.getSelectionEnd();
|
|
||||||
return String.valueOf(start > end ? text.subSequence(end, start)
|
|
||||||
: text.subSequence(start, end));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void shareSelectedTextIfNotNullAndNotEmpty(
|
|
||||||
@NonNull final TextView textView,
|
|
||||||
@Nullable final CharSequence selectedText) {
|
|
||||||
if (selectedText != null && selectedText.length() != 0) {
|
|
||||||
ShareUtils.shareText(textView.getContext(), "", selectedText.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021-2026 NewPipe contributors <https://newpipe.net>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.schabi.newpipe.util
|
||||||
|
|
||||||
|
import android.text.Selection
|
||||||
|
import android.text.Spannable
|
||||||
|
import android.widget.TextView
|
||||||
|
import org.schabi.newpipe.util.external_communication.ShareUtils
|
||||||
|
|
||||||
|
object NewPipeTextViewHelper {
|
||||||
|
/**
|
||||||
|
* Share the selected text of [NewPipeTextViews][org.schabi.newpipe.views.NewPipeTextView] and
|
||||||
|
* [NewPipeEditTexts][org.schabi.newpipe.views.NewPipeEditText] with
|
||||||
|
* [ShareUtils.shareText].
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This allows EMUI users to get the Android share sheet instead of the EMUI share sheet when
|
||||||
|
* using the `Share` command of the popup menu which appears when selecting text.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param textView the [TextView] on which sharing the selected text. It should be a
|
||||||
|
* [org.schabi.newpipe.views.NewPipeTextView] or a [org.schabi.newpipe.views.NewPipeEditText]
|
||||||
|
* (even if [standard TextViews][TextView] are supported).
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun shareSelectedTextWithShareUtils(textView: TextView) {
|
||||||
|
val textViewText = textView.getText()
|
||||||
|
shareSelectedTextIfNotNullAndNotEmpty(textView, getSelectedText(textView, textViewText))
|
||||||
|
if (textViewText is Spannable) {
|
||||||
|
Selection.setSelection(textViewText, textView.selectionEnd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSelectedText(textView: TextView, text: CharSequence?): CharSequence? {
|
||||||
|
if (!textView.hasSelection() || text == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val start = textView.selectionStart
|
||||||
|
val end = textView.selectionEnd
|
||||||
|
return if (start > end) {
|
||||||
|
text.subSequence(end, start)
|
||||||
|
} else {
|
||||||
|
text.subSequence(start, end)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun shareSelectedTextIfNotNullAndNotEmpty(
|
||||||
|
textView: TextView,
|
||||||
|
selectedText: CharSequence?
|
||||||
|
) {
|
||||||
|
if (!selectedText.isNullOrEmpty()) {
|
||||||
|
ShareUtils.shareText(textView.context, "", selectedText.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
package org.schabi.newpipe.util;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
|
|
||||||
import androidx.preference.PreferenceManager;
|
|
||||||
|
|
||||||
import com.grack.nanojson.JsonArray;
|
|
||||||
import com.grack.nanojson.JsonObject;
|
|
||||||
import com.grack.nanojson.JsonParser;
|
|
||||||
import com.grack.nanojson.JsonParserException;
|
|
||||||
import com.grack.nanojson.JsonStringWriter;
|
|
||||||
import com.grack.nanojson.JsonWriter;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
import org.schabi.newpipe.extractor.ServiceList;
|
|
||||||
import org.schabi.newpipe.extractor.services.peertube.PeertubeInstance;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public final class PeertubeHelper {
|
|
||||||
private PeertubeHelper() { }
|
|
||||||
|
|
||||||
public static List<PeertubeInstance> getInstanceList(final Context context) {
|
|
||||||
final SharedPreferences sharedPreferences = PreferenceManager
|
|
||||||
.getDefaultSharedPreferences(context);
|
|
||||||
final String savedInstanceListKey = context.getString(R.string.peertube_instance_list_key);
|
|
||||||
final String savedJson = sharedPreferences.getString(savedInstanceListKey, null);
|
|
||||||
if (null == savedJson) {
|
|
||||||
return List.of(getCurrentInstance());
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
final JsonArray array = JsonParser.object().from(savedJson).getArray("instances");
|
|
||||||
final List<PeertubeInstance> result = new ArrayList<>();
|
|
||||||
for (final Object o : array) {
|
|
||||||
if (o instanceof JsonObject) {
|
|
||||||
final JsonObject instance = (JsonObject) o;
|
|
||||||
final String name = instance.getString("name");
|
|
||||||
final String url = instance.getString("url");
|
|
||||||
result.add(new PeertubeInstance(url, name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
} catch (final JsonParserException e) {
|
|
||||||
return List.of(getCurrentInstance());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PeertubeInstance selectInstance(final PeertubeInstance instance,
|
|
||||||
final Context context) {
|
|
||||||
final SharedPreferences sharedPreferences = PreferenceManager
|
|
||||||
.getDefaultSharedPreferences(context);
|
|
||||||
final String selectedInstanceKey =
|
|
||||||
context.getString(R.string.peertube_selected_instance_key);
|
|
||||||
final JsonStringWriter jsonWriter = JsonWriter.string().object();
|
|
||||||
jsonWriter.value("name", instance.getName());
|
|
||||||
jsonWriter.value("url", instance.getUrl());
|
|
||||||
final String jsonToSave = jsonWriter.end().done();
|
|
||||||
sharedPreferences.edit().putString(selectedInstanceKey, jsonToSave).apply();
|
|
||||||
ServiceList.PeerTube.setInstance(instance);
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PeertubeInstance getCurrentInstance() {
|
|
||||||
return ServiceList.PeerTube.getInstance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
52
app/src/main/java/org/schabi/newpipe/util/PeertubeHelper.kt
Normal file
52
app/src/main/java/org/schabi/newpipe/util/PeertubeHelper.kt
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2019-2026 NewPipe contributors <https://newpipe.net>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.schabi.newpipe.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.grack.nanojson.JsonObject
|
||||||
|
import com.grack.nanojson.JsonParser
|
||||||
|
import com.grack.nanojson.JsonWriter
|
||||||
|
import org.schabi.newpipe.R
|
||||||
|
import org.schabi.newpipe.extractor.ServiceList
|
||||||
|
import org.schabi.newpipe.extractor.services.peertube.PeertubeInstance
|
||||||
|
|
||||||
|
object PeertubeHelper {
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
val currentInstance: PeertubeInstance
|
||||||
|
get() = ServiceList.PeerTube.instance
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getInstanceList(context: Context): List<PeertubeInstance> {
|
||||||
|
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val savedInstanceListKey = context.getString(R.string.peertube_instance_list_key)
|
||||||
|
val savedJson = sharedPreferences.getString(savedInstanceListKey, null)
|
||||||
|
?: return listOf(currentInstance)
|
||||||
|
|
||||||
|
return runCatching {
|
||||||
|
JsonParser.`object`().from(savedJson).getArray("instances")
|
||||||
|
.filterIsInstance<JsonObject>()
|
||||||
|
.map { PeertubeInstance(it.getString("url"), it.getString("name")) }
|
||||||
|
}.getOrDefault(listOf(currentInstance))
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun selectInstance(instance: PeertubeInstance, context: Context): PeertubeInstance {
|
||||||
|
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val selectedInstanceKey = context.getString(R.string.peertube_selected_instance_key)
|
||||||
|
|
||||||
|
val jsonWriter = JsonWriter.string().`object`()
|
||||||
|
jsonWriter.value("name", instance.name)
|
||||||
|
jsonWriter.value("url", instance.url)
|
||||||
|
val jsonToSave = jsonWriter.end().done()
|
||||||
|
|
||||||
|
sharedPreferences.edit { putString(selectedInstanceKey, jsonToSave) }
|
||||||
|
ServiceList.PeerTube.instance = instance
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
package org.schabi.newpipe.util;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
|
||||||
import androidx.preference.PreferenceManager;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
import org.schabi.newpipe.databinding.PlaylistControlBinding;
|
|
||||||
import org.schabi.newpipe.fragments.list.playlist.PlaylistControlViewHolder;
|
|
||||||
import org.schabi.newpipe.player.PlayerType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility class for play buttons and their respective click listeners.
|
|
||||||
*/
|
|
||||||
public final class PlayButtonHelper {
|
|
||||||
|
|
||||||
private PlayButtonHelper() {
|
|
||||||
// utility class
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize {@link android.view.View.OnClickListener OnClickListener}
|
|
||||||
* and {@link android.view.View.OnLongClickListener OnLongClickListener} for playlist control
|
|
||||||
* buttons defined in {@link R.layout#playlist_control}.
|
|
||||||
*
|
|
||||||
* @param activity The activity to use for the {@link android.widget.Toast Toast}.
|
|
||||||
* @param playlistControlBinding The binding of the
|
|
||||||
* {@link R.layout#playlist_control playlist control layout}.
|
|
||||||
* @param fragment The fragment to get the play queue from.
|
|
||||||
*/
|
|
||||||
public static void initPlaylistControlClickListener(
|
|
||||||
@NonNull final AppCompatActivity activity,
|
|
||||||
@NonNull final PlaylistControlBinding playlistControlBinding,
|
|
||||||
@NonNull final PlaylistControlViewHolder fragment) {
|
|
||||||
// click listener
|
|
||||||
playlistControlBinding.playlistCtrlPlayAllButton.setOnClickListener(view -> {
|
|
||||||
NavigationHelper.playOnMainPlayer(activity, fragment.getPlayQueue());
|
|
||||||
showHoldToAppendToastIfNeeded(activity);
|
|
||||||
});
|
|
||||||
playlistControlBinding.playlistCtrlPlayPopupButton.setOnClickListener(view -> {
|
|
||||||
NavigationHelper.playOnPopupPlayer(activity, fragment.getPlayQueue(), false);
|
|
||||||
showHoldToAppendToastIfNeeded(activity);
|
|
||||||
});
|
|
||||||
playlistControlBinding.playlistCtrlPlayBgButton.setOnClickListener(view -> {
|
|
||||||
NavigationHelper.playOnBackgroundPlayer(activity, fragment.getPlayQueue(), false);
|
|
||||||
showHoldToAppendToastIfNeeded(activity);
|
|
||||||
});
|
|
||||||
|
|
||||||
// long click listener
|
|
||||||
playlistControlBinding.playlistCtrlPlayAllButton.setOnLongClickListener(view -> {
|
|
||||||
NavigationHelper.enqueueOnPlayer(activity, fragment.getPlayQueue(), PlayerType.MAIN);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
playlistControlBinding.playlistCtrlPlayPopupButton.setOnLongClickListener(view -> {
|
|
||||||
NavigationHelper.enqueueOnPlayer(activity, fragment.getPlayQueue(), PlayerType.POPUP);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
playlistControlBinding.playlistCtrlPlayBgButton.setOnLongClickListener(view -> {
|
|
||||||
NavigationHelper.enqueueOnPlayer(activity, fragment.getPlayQueue(), PlayerType.AUDIO);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the "hold to append" toast if the corresponding preference is enabled.
|
|
||||||
*
|
|
||||||
* @param context The context to show the toast.
|
|
||||||
*/
|
|
||||||
private static void showHoldToAppendToastIfNeeded(@NonNull final Context context) {
|
|
||||||
if (shouldShowHoldToAppendTip(context)) {
|
|
||||||
Toast.makeText(context, R.string.hold_to_append, Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the "hold to append" toast should be shown.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* The tip is shown if the corresponding preference is enabled.
|
|
||||||
* This is the default behaviour.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param context The context to get the preference.
|
|
||||||
* @return {@code true} if the tip should be shown, {@code false} otherwise.
|
|
||||||
*/
|
|
||||||
public static boolean shouldShowHoldToAppendTip(@NonNull final Context context) {
|
|
||||||
return PreferenceManager.getDefaultSharedPreferences(context)
|
|
||||||
.getBoolean(context.getString(R.string.show_hold_to_append_key), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023-2026 NewPipe contributors <https://newpipe.net>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.schabi.newpipe.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.View
|
||||||
|
import android.view.View.OnLongClickListener
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import org.schabi.newpipe.R
|
||||||
|
import org.schabi.newpipe.databinding.PlaylistControlBinding
|
||||||
|
import org.schabi.newpipe.fragments.list.playlist.PlaylistControlViewHolder
|
||||||
|
import org.schabi.newpipe.player.PlayerType
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for play buttons and their respective click listeners.
|
||||||
|
*/
|
||||||
|
object PlayButtonHelper {
|
||||||
|
/**
|
||||||
|
* Initialize [OnClickListener][View.OnClickListener]
|
||||||
|
* and [OnLongClickListener][OnLongClickListener] for playlist control
|
||||||
|
* buttons defined in [R.layout.playlist_control].
|
||||||
|
*
|
||||||
|
* @param activity The activity to use for the [Toast][Toast].
|
||||||
|
* @param playlistControlBinding The binding of the
|
||||||
|
* [playlist control layout][R.layout.playlist_control].
|
||||||
|
* @param fragment The fragment to get the play queue from.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun initPlaylistControlClickListener(
|
||||||
|
activity: AppCompatActivity,
|
||||||
|
playlistControlBinding: PlaylistControlBinding,
|
||||||
|
fragment: PlaylistControlViewHolder
|
||||||
|
) {
|
||||||
|
// click listener
|
||||||
|
playlistControlBinding.playlistCtrlPlayAllButton.setOnClickListener {
|
||||||
|
NavigationHelper.playOnMainPlayer(activity, fragment.getPlayQueue())
|
||||||
|
showHoldToAppendToastIfNeeded(activity)
|
||||||
|
}
|
||||||
|
playlistControlBinding.playlistCtrlPlayPopupButton.setOnClickListener {
|
||||||
|
NavigationHelper.playOnPopupPlayer(activity, fragment.getPlayQueue(), false)
|
||||||
|
showHoldToAppendToastIfNeeded(activity)
|
||||||
|
}
|
||||||
|
playlistControlBinding.playlistCtrlPlayBgButton.setOnClickListener {
|
||||||
|
NavigationHelper.playOnBackgroundPlayer(activity, fragment.getPlayQueue(), false)
|
||||||
|
showHoldToAppendToastIfNeeded(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
// long click listener
|
||||||
|
playlistControlBinding.playlistCtrlPlayAllButton.setOnLongClickListener {
|
||||||
|
NavigationHelper.enqueueOnPlayer(activity, fragment.getPlayQueue(), PlayerType.MAIN)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
playlistControlBinding.playlistCtrlPlayPopupButton.setOnLongClickListener {
|
||||||
|
NavigationHelper.enqueueOnPlayer(activity, fragment.getPlayQueue(), PlayerType.POPUP)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
playlistControlBinding.playlistCtrlPlayBgButton.setOnLongClickListener {
|
||||||
|
NavigationHelper.enqueueOnPlayer(activity, fragment.getPlayQueue(), PlayerType.AUDIO)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the "hold to append" toast if the corresponding preference is enabled.
|
||||||
|
*
|
||||||
|
* @param context The context to show the toast.
|
||||||
|
*/
|
||||||
|
private fun showHoldToAppendToastIfNeeded(context: Context) {
|
||||||
|
if (shouldShowHoldToAppendTip(context)) {
|
||||||
|
Toast.makeText(context, R.string.hold_to_append, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the "hold to append" toast should be shown.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* The tip is shown if the corresponding preference is enabled.
|
||||||
|
* This is the default behaviour.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param context The context to get the preference.
|
||||||
|
* @return `true` if the tip should be shown, `false` otherwise.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun shouldShowHoldToAppendTip(context: Context): Boolean {
|
||||||
|
return PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
.getBoolean(context.getString(R.string.show_hold_to_append_key), true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,213 +0,0 @@
|
||||||
package org.schabi.newpipe.util;
|
|
||||||
|
|
||||||
import static org.schabi.newpipe.extractor.ServiceList.SoundCloud;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
|
|
||||||
import androidx.annotation.DrawableRes;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.annotation.StringRes;
|
|
||||||
import androidx.preference.PreferenceManager;
|
|
||||||
|
|
||||||
import com.grack.nanojson.JsonObject;
|
|
||||||
import com.grack.nanojson.JsonParser;
|
|
||||||
import com.grack.nanojson.JsonParserException;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
|
||||||
import org.schabi.newpipe.extractor.ServiceList;
|
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
|
||||||
import org.schabi.newpipe.extractor.services.peertube.PeertubeInstance;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public final class ServiceHelper {
|
|
||||||
private static final StreamingService DEFAULT_FALLBACK_SERVICE = ServiceList.YouTube;
|
|
||||||
|
|
||||||
private ServiceHelper() { }
|
|
||||||
|
|
||||||
@DrawableRes
|
|
||||||
public static int getIcon(final int serviceId) {
|
|
||||||
switch (serviceId) {
|
|
||||||
case 0:
|
|
||||||
return R.drawable.ic_smart_display;
|
|
||||||
case 1:
|
|
||||||
return R.drawable.ic_cloud;
|
|
||||||
case 2:
|
|
||||||
return R.drawable.ic_placeholder_media_ccc;
|
|
||||||
case 3:
|
|
||||||
return R.drawable.ic_placeholder_peertube;
|
|
||||||
case 4:
|
|
||||||
return R.drawable.ic_placeholder_bandcamp;
|
|
||||||
default:
|
|
||||||
return R.drawable.ic_circle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getTranslatedFilterString(final String filter, final Context c) {
|
|
||||||
switch (filter) {
|
|
||||||
case "all":
|
|
||||||
return c.getString(R.string.all);
|
|
||||||
case "videos":
|
|
||||||
case "sepia_videos":
|
|
||||||
case "music_videos":
|
|
||||||
return c.getString(R.string.videos_string);
|
|
||||||
case "channels":
|
|
||||||
return c.getString(R.string.channels);
|
|
||||||
case "playlists":
|
|
||||||
case "music_playlists":
|
|
||||||
return c.getString(R.string.playlists);
|
|
||||||
case "tracks":
|
|
||||||
return c.getString(R.string.tracks);
|
|
||||||
case "users":
|
|
||||||
return c.getString(R.string.users);
|
|
||||||
case "conferences":
|
|
||||||
return c.getString(R.string.conferences);
|
|
||||||
case "events":
|
|
||||||
return c.getString(R.string.events);
|
|
||||||
case "music_songs":
|
|
||||||
return c.getString(R.string.songs);
|
|
||||||
case "music_albums":
|
|
||||||
return c.getString(R.string.albums);
|
|
||||||
case "music_artists":
|
|
||||||
return c.getString(R.string.artists);
|
|
||||||
default:
|
|
||||||
return filter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a resource string with instructions for importing subscriptions for each service.
|
|
||||||
*
|
|
||||||
* @param serviceId service to get the instructions for
|
|
||||||
* @return the string resource containing the instructions or -1 if the service don't support it
|
|
||||||
*/
|
|
||||||
@StringRes
|
|
||||||
public static int getImportInstructions(final int serviceId) {
|
|
||||||
switch (serviceId) {
|
|
||||||
case 0:
|
|
||||||
return R.string.import_youtube_instructions;
|
|
||||||
case 1:
|
|
||||||
return R.string.import_soundcloud_instructions;
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For services that support importing from a channel url, return a hint that will
|
|
||||||
* be used in the EditText that the user will type in his channel url.
|
|
||||||
*
|
|
||||||
* @param serviceId service to get the hint for
|
|
||||||
* @return the hint's string resource or -1 if the service don't support it
|
|
||||||
*/
|
|
||||||
@StringRes
|
|
||||||
public static int getImportInstructionsHint(final int serviceId) {
|
|
||||||
switch (serviceId) {
|
|
||||||
case 1:
|
|
||||||
return R.string.import_soundcloud_instructions_hint;
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getSelectedServiceId(final Context context) {
|
|
||||||
return Optional.ofNullable(getSelectedService(context))
|
|
||||||
.orElse(DEFAULT_FALLBACK_SERVICE)
|
|
||||||
.getServiceId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static StreamingService getSelectedService(final Context context) {
|
|
||||||
final String serviceName = PreferenceManager.getDefaultSharedPreferences(context)
|
|
||||||
.getString(context.getString(R.string.current_service_key),
|
|
||||||
context.getString(R.string.default_service_value));
|
|
||||||
|
|
||||||
try {
|
|
||||||
return NewPipe.getService(serviceName);
|
|
||||||
} catch (final ExtractionException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public static String getNameOfServiceById(final int serviceId) {
|
|
||||||
return ServiceList.all().stream()
|
|
||||||
.filter(s -> s.getServiceId() == serviceId)
|
|
||||||
.findFirst()
|
|
||||||
.map(StreamingService::getServiceInfo)
|
|
||||||
.map(StreamingService.ServiceInfo::getName)
|
|
||||||
.orElse("<unknown>");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param serviceId the id of the service
|
|
||||||
* @return the service corresponding to the provided id
|
|
||||||
* @throws java.util.NoSuchElementException if there is no service with the provided id
|
|
||||||
*/
|
|
||||||
@NonNull
|
|
||||||
public static StreamingService getServiceById(final int serviceId) {
|
|
||||||
return ServiceList.all().stream()
|
|
||||||
.filter(s -> s.getServiceId() == serviceId)
|
|
||||||
.findFirst()
|
|
||||||
.orElseThrow();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setSelectedServiceId(final Context context, final int serviceId) {
|
|
||||||
String serviceName;
|
|
||||||
try {
|
|
||||||
serviceName = NewPipe.getService(serviceId).getServiceInfo().getName();
|
|
||||||
} catch (final ExtractionException e) {
|
|
||||||
serviceName = DEFAULT_FALLBACK_SERVICE.getServiceInfo().getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
setSelectedServicePreferences(context, serviceName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void setSelectedServicePreferences(final Context context,
|
|
||||||
final String serviceName) {
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(context).edit().
|
|
||||||
putString(context.getString(R.string.current_service_key), serviceName).apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long getCacheExpirationMillis(final int serviceId) {
|
|
||||||
if (serviceId == SoundCloud.getServiceId()) {
|
|
||||||
return TimeUnit.MILLISECONDS.convert(5, TimeUnit.MINUTES);
|
|
||||||
} else {
|
|
||||||
return TimeUnit.MILLISECONDS.convert(1, TimeUnit.HOURS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void initService(final Context context, final int serviceId) {
|
|
||||||
if (serviceId == ServiceList.PeerTube.getServiceId()) {
|
|
||||||
final SharedPreferences sharedPreferences = PreferenceManager
|
|
||||||
.getDefaultSharedPreferences(context);
|
|
||||||
final String json = sharedPreferences.getString(context.getString(
|
|
||||||
R.string.peertube_selected_instance_key), null);
|
|
||||||
if (null == json) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final JsonObject jsonObject;
|
|
||||||
try {
|
|
||||||
jsonObject = JsonParser.object().from(json);
|
|
||||||
} catch (final JsonParserException e) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final String name = jsonObject.getString("name");
|
|
||||||
final String url = jsonObject.getString("url");
|
|
||||||
final PeertubeInstance instance = new PeertubeInstance(url, name);
|
|
||||||
ServiceList.PeerTube.setInstance(instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void initServices(final Context context) {
|
|
||||||
for (final StreamingService s : ServiceList.all()) {
|
|
||||||
initService(context, s.getServiceId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
168
app/src/main/java/org/schabi/newpipe/util/ServiceHelper.kt
Normal file
168
app/src/main/java/org/schabi/newpipe/util/ServiceHelper.kt
Normal file
|
|
@ -0,0 +1,168 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2018-2026 NewPipe contributors <https://newpipe.net>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.schabi.newpipe.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.grack.nanojson.JsonParser
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import org.schabi.newpipe.R
|
||||||
|
import org.schabi.newpipe.extractor.NewPipe
|
||||||
|
import org.schabi.newpipe.extractor.ServiceList
|
||||||
|
import org.schabi.newpipe.extractor.StreamingService
|
||||||
|
import org.schabi.newpipe.extractor.services.peertube.PeertubeInstance
|
||||||
|
import org.schabi.newpipe.ktx.getStringSafe
|
||||||
|
|
||||||
|
object ServiceHelper {
|
||||||
|
private val DEFAULT_FALLBACK_SERVICE: StreamingService = ServiceList.YouTube
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
@DrawableRes
|
||||||
|
fun getIcon(serviceId: Int): Int {
|
||||||
|
return when (serviceId) {
|
||||||
|
0 -> R.drawable.ic_smart_display
|
||||||
|
1 -> R.drawable.ic_cloud
|
||||||
|
2 -> R.drawable.ic_placeholder_media_ccc
|
||||||
|
3 -> R.drawable.ic_placeholder_peertube
|
||||||
|
4 -> R.drawable.ic_placeholder_bandcamp
|
||||||
|
else -> R.drawable.ic_circle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getTranslatedFilterString(filter: String, context: Context): String {
|
||||||
|
return when (filter) {
|
||||||
|
"all" -> context.getString(R.string.all)
|
||||||
|
"videos", "sepia_videos", "music_videos" -> context.getString(R.string.videos_string)
|
||||||
|
"channels" -> context.getString(R.string.channels)
|
||||||
|
"playlists", "music_playlists" -> context.getString(R.string.playlists)
|
||||||
|
"tracks" -> context.getString(R.string.tracks)
|
||||||
|
"users" -> context.getString(R.string.users)
|
||||||
|
"conferences" -> context.getString(R.string.conferences)
|
||||||
|
"events" -> context.getString(R.string.events)
|
||||||
|
"music_songs" -> context.getString(R.string.songs)
|
||||||
|
"music_albums" -> context.getString(R.string.albums)
|
||||||
|
"music_artists" -> context.getString(R.string.artists)
|
||||||
|
else -> filter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a resource string with instructions for importing subscriptions for each service.
|
||||||
|
*
|
||||||
|
* @param serviceId service to get the instructions for
|
||||||
|
* @return the string resource containing the instructions or -1 if the service don't support it
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
@StringRes
|
||||||
|
fun getImportInstructions(serviceId: Int): Int {
|
||||||
|
return when (serviceId) {
|
||||||
|
0 -> R.string.import_youtube_instructions
|
||||||
|
1 -> R.string.import_soundcloud_instructions
|
||||||
|
else -> -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For services that support importing from a channel url, return a hint that will
|
||||||
|
* be used in the EditText that the user will type in his channel url.
|
||||||
|
*
|
||||||
|
* @param serviceId service to get the hint for
|
||||||
|
* @return the hint's string resource or -1 if the service don't support it
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
@StringRes
|
||||||
|
fun getImportInstructionsHint(serviceId: Int): Int {
|
||||||
|
return when (serviceId) {
|
||||||
|
1 -> R.string.import_soundcloud_instructions_hint
|
||||||
|
else -> -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getSelectedServiceId(context: Context): Int {
|
||||||
|
return (getSelectedService(context) ?: DEFAULT_FALLBACK_SERVICE).serviceId
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getSelectedService(context: Context): StreamingService? {
|
||||||
|
val serviceName: String = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
.getStringSafe(
|
||||||
|
context.getString(R.string.current_service_key),
|
||||||
|
context.getString(R.string.default_service_value)
|
||||||
|
)
|
||||||
|
|
||||||
|
return runCatching { NewPipe.getService(serviceName) }.getOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getNameOfServiceById(serviceId: Int): String {
|
||||||
|
return ServiceList.all().stream()
|
||||||
|
.filter { it.serviceId == serviceId }
|
||||||
|
.findFirst()
|
||||||
|
.map(StreamingService::getServiceInfo)
|
||||||
|
.map(StreamingService.ServiceInfo::getName)
|
||||||
|
.orElse("<unknown>")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param serviceId the id of the service
|
||||||
|
* @return the service corresponding to the provided id
|
||||||
|
* @throws java.util.NoSuchElementException if there is no service with the provided id
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun getServiceById(serviceId: Int): StreamingService {
|
||||||
|
return ServiceList.all().firstNotNullOf { it.takeIf { it.serviceId == serviceId } }
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun setSelectedServiceId(context: Context, serviceId: Int) {
|
||||||
|
val serviceName = runCatching { NewPipe.getService(serviceId).serviceInfo.name }
|
||||||
|
.getOrDefault(DEFAULT_FALLBACK_SERVICE.serviceInfo.name)
|
||||||
|
|
||||||
|
setSelectedServicePreferences(context, serviceName)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setSelectedServicePreferences(context: Context, serviceName: String?) {
|
||||||
|
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
sharedPreferences.edit { putString(context.getString(R.string.current_service_key), serviceName) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getCacheExpirationMillis(serviceId: Int): Long {
|
||||||
|
return if (serviceId == ServiceList.SoundCloud.serviceId) {
|
||||||
|
TimeUnit.MILLISECONDS.convert(5, TimeUnit.MINUTES)
|
||||||
|
} else {
|
||||||
|
TimeUnit.MILLISECONDS.convert(1, TimeUnit.HOURS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun initService(context: Context, serviceId: Int) {
|
||||||
|
if (serviceId == ServiceList.PeerTube.serviceId) {
|
||||||
|
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val json = sharedPreferences.getString(
|
||||||
|
context.getString(R.string.peertube_selected_instance_key),
|
||||||
|
null
|
||||||
|
) ?: return
|
||||||
|
|
||||||
|
val jsonObject = runCatching { JsonParser.`object`().from(json) }
|
||||||
|
.getOrElse { return@initService }
|
||||||
|
|
||||||
|
ServiceList.PeerTube.instance = PeertubeInstance(
|
||||||
|
jsonObject.getString("url"),
|
||||||
|
jsonObject.getString("name")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun initServices(context: Context) {
|
||||||
|
ServiceList.all().forEach { initService(context, it.serviceId) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
package org.schabi.newpipe.util;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility class for {@link StreamType}.
|
|
||||||
*/
|
|
||||||
public final class StreamTypeUtil {
|
|
||||||
private StreamTypeUtil() {
|
|
||||||
// No impl pls
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the {@link StreamType} of a stream is a livestream.
|
|
||||||
*
|
|
||||||
* @param streamType the stream type of the stream
|
|
||||||
* @return whether the stream type is {@link StreamType#AUDIO_STREAM},
|
|
||||||
* {@link StreamType#AUDIO_LIVE_STREAM} or {@link StreamType#POST_LIVE_AUDIO_STREAM}
|
|
||||||
*/
|
|
||||||
public static boolean isAudio(final StreamType streamType) {
|
|
||||||
return streamType == StreamType.AUDIO_STREAM
|
|
||||||
|| streamType == StreamType.AUDIO_LIVE_STREAM
|
|
||||||
|| streamType == StreamType.POST_LIVE_AUDIO_STREAM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the {@link StreamType} of a stream is a livestream.
|
|
||||||
*
|
|
||||||
* @param streamType the stream type of the stream
|
|
||||||
* @return whether the stream type is {@link StreamType#VIDEO_STREAM},
|
|
||||||
* {@link StreamType#LIVE_STREAM} or {@link StreamType#POST_LIVE_STREAM}
|
|
||||||
*/
|
|
||||||
public static boolean isVideo(final StreamType streamType) {
|
|
||||||
return streamType == StreamType.VIDEO_STREAM
|
|
||||||
|| streamType == StreamType.LIVE_STREAM
|
|
||||||
|| streamType == StreamType.POST_LIVE_STREAM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the {@link StreamType} of a stream is a livestream.
|
|
||||||
*
|
|
||||||
* @param streamType the stream type of the stream
|
|
||||||
* @return whether the stream type is {@link StreamType#LIVE_STREAM} or
|
|
||||||
* {@link StreamType#AUDIO_LIVE_STREAM}
|
|
||||||
*/
|
|
||||||
public static boolean isLiveStream(final StreamType streamType) {
|
|
||||||
return streamType == StreamType.LIVE_STREAM
|
|
||||||
|| streamType == StreamType.AUDIO_LIVE_STREAM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
54
app/src/main/java/org/schabi/newpipe/util/StreamTypeUtil.kt
Normal file
54
app/src/main/java/org/schabi/newpipe/util/StreamTypeUtil.kt
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021-2026 NewPipe contributors <https://newpipe.net>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.schabi.newpipe.util
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.stream.StreamType
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for [StreamType].
|
||||||
|
*/
|
||||||
|
object StreamTypeUtil {
|
||||||
|
/**
|
||||||
|
* Check if the [StreamType] of a stream is a livestream.
|
||||||
|
*
|
||||||
|
* @param streamType the stream type of the stream
|
||||||
|
* @return whether the stream type is [StreamType.AUDIO_STREAM],
|
||||||
|
* [StreamType.AUDIO_LIVE_STREAM] or [StreamType.POST_LIVE_AUDIO_STREAM]
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun isAudio(streamType: StreamType): Boolean {
|
||||||
|
return streamType == StreamType.AUDIO_STREAM ||
|
||||||
|
streamType == StreamType.AUDIO_LIVE_STREAM ||
|
||||||
|
streamType == StreamType.POST_LIVE_AUDIO_STREAM
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the [StreamType] of a stream is a livestream.
|
||||||
|
*
|
||||||
|
* @param streamType the stream type of the stream
|
||||||
|
* @return whether the stream type is [StreamType.VIDEO_STREAM],
|
||||||
|
* [StreamType.LIVE_STREAM] or [StreamType.POST_LIVE_STREAM]
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun isVideo(streamType: StreamType): Boolean {
|
||||||
|
return streamType == StreamType.VIDEO_STREAM ||
|
||||||
|
streamType == StreamType.LIVE_STREAM ||
|
||||||
|
streamType == StreamType.POST_LIVE_STREAM
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the [StreamType] of a stream is a livestream.
|
||||||
|
*
|
||||||
|
* @param streamType the stream type of the stream
|
||||||
|
* @return whether the stream type is [StreamType.LIVE_STREAM] or
|
||||||
|
* [StreamType.AUDIO_LIVE_STREAM]
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun isLiveStream(streamType: StreamType): Boolean {
|
||||||
|
return streamType == StreamType.LIVE_STREAM ||
|
||||||
|
streamType == StreamType.AUDIO_LIVE_STREAM
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue