Add preferred player
|
@ -124,7 +124,7 @@
|
||||||
<activity
|
<activity
|
||||||
android:name=".RouterActivity"
|
android:name=".RouterActivity"
|
||||||
android:taskAffinity=""
|
android:taskAffinity=""
|
||||||
android:theme="@android:style/Theme.NoDisplay">
|
android:theme="@style/RouterActivityThemeDark">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>
|
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>
|
||||||
|
@ -175,17 +175,21 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND"/>
|
<action android:name="android.intent.action.SEND"/>
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
|
||||||
<data android:mimeType="text/plain"/>
|
<data android:mimeType="text/plain"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name=".RouterPlayerActivity$FetcherService"
|
||||||
|
android:exported="false"/>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".RouterPopupActivity"
|
android:name=".RouterPlayerActivity"
|
||||||
android:label="@string/popup_mode_share_menu_title"
|
android:excludeFromRecents="true"
|
||||||
|
android:label="@string/preferred_player_share_menu_title"
|
||||||
android:taskAffinity=""
|
android:taskAffinity=""
|
||||||
android:theme="@style/PopupPermissionsTheme">
|
android:theme="@style/RouterActivityThemeDark">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>
|
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>
|
||||||
|
@ -204,6 +208,11 @@
|
||||||
<data android:pathPrefix="/embed/"/>
|
<data android:pathPrefix="/embed/"/>
|
||||||
<data android:pathPrefix="/watch"/>
|
<data android:pathPrefix="/watch"/>
|
||||||
<data android:pathPrefix="/attribution_link"/>
|
<data android:pathPrefix="/attribution_link"/>
|
||||||
|
<!-- channel prefix -->
|
||||||
|
<data android:pathPrefix="/channel/"/>
|
||||||
|
<data android:pathPrefix="/user/"/>
|
||||||
|
<!-- playlist prefix -->
|
||||||
|
<data android:pathPrefix="/playlist"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|
|
@ -77,17 +77,6 @@ public abstract class BaseFragment extends Fragment {
|
||||||
protected void initListeners() {
|
protected void initListeners() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
|
||||||
// Utils
|
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
|
||||||
|
|
||||||
protected final int resolveResourceIdFromAttr(@AttrRes int attr) {
|
|
||||||
TypedArray a = activity.getTheme().obtainStyledAttributes(new int[]{attr});
|
|
||||||
int attributeResourceId = a.getResourceId(0, 0);
|
|
||||||
a.recycle();
|
|
||||||
return attributeResourceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// DisplayImageOptions default configurations
|
// DisplayImageOptions default configurations
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
|
@ -3,14 +3,25 @@ package org.schabi.newpipe;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
|
import org.schabi.newpipe.report.UserAction;
|
||||||
|
import org.schabi.newpipe.util.ExtractorHelper;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
/**
|
import icepick.Icepick;
|
||||||
|
import icepick.State;
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
|
||||||
|
/*
|
||||||
* Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org>
|
* Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org>
|
||||||
* RouterActivity.java is part of NewPipe.
|
* RouterActivity.java is part of NewPipe.
|
||||||
*
|
*
|
||||||
|
@ -34,23 +45,71 @@ import java.util.HashSet;
|
||||||
*/
|
*/
|
||||||
public class RouterActivity extends AppCompatActivity {
|
public class RouterActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
@State
|
||||||
|
protected String currentUrl;
|
||||||
|
protected CompositeDisposable disposables = new CompositeDisposable();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
Icepick.restoreInstanceState(this, savedInstanceState);
|
||||||
|
|
||||||
String videoUrl = getUrl(getIntent());
|
if (TextUtils.isEmpty(currentUrl)) {
|
||||||
handleUrl(videoUrl);
|
currentUrl = getUrl(getIntent());
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(currentUrl)) {
|
||||||
|
Toast.makeText(this, R.string.invalid_url_toast, Toast.LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSaveInstanceState(Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
Icepick.saveInstanceState(this, outState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
handleUrl(currentUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void handleUrl(String url) {
|
protected void handleUrl(String url) {
|
||||||
boolean success = NavigationHelper.openByLink(this, url);
|
disposables.add(Observable
|
||||||
if (!success) {
|
.fromCallable(() -> NavigationHelper.getIntentByLink(this, url))
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(intent -> {
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||||
|
startActivity(intent);
|
||||||
|
|
||||||
|
finish();
|
||||||
|
}, this::handleError)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void handleError(Throwable error) {
|
||||||
|
error.printStackTrace();
|
||||||
|
|
||||||
|
if (error instanceof ExtractionException) {
|
||||||
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
||||||
|
} else {
|
||||||
|
ExtractorHelper.handleGeneralException(this, -1, null, error, UserAction.SOMETHING_ELSE, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
|
||||||
|
disposables.clear();
|
||||||
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Utils
|
// Utils
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -71,7 +130,8 @@ public class RouterActivity extends AppCompatActivity {
|
||||||
} else if (intent.getStringExtra(Intent.EXTRA_TEXT) != null) {
|
} else if (intent.getStringExtra(Intent.EXTRA_TEXT) != null) {
|
||||||
//this means that vidoe was called through share menu
|
//this means that vidoe was called through share menu
|
||||||
String extraText = intent.getStringExtra(Intent.EXTRA_TEXT);
|
String extraText = intent.getStringExtra(Intent.EXTRA_TEXT);
|
||||||
videoUrl = getUris(extraText)[0];
|
final String[] uris = getUris(extraText);
|
||||||
|
videoUrl = uris.length > 0 ? uris[0] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return videoUrl;
|
return videoUrl;
|
||||||
|
|
413
app/src/main/java/org/schabi/newpipe/RouterPlayerActivity.java
Normal file
|
@ -0,0 +1,413 @@
|
||||||
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
|
import android.app.IntentService;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.PersistableBundle;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.annotation.DrawableRes;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.NotificationCompat;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.ContextThemeWrapper;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.RadioButton;
|
||||||
|
import android.widget.RadioGroup;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.Info;
|
||||||
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
|
import org.schabi.newpipe.extractor.ServiceList;
|
||||||
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
import org.schabi.newpipe.extractor.StreamingService.LinkType;
|
||||||
|
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
|
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||||
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
|
import org.schabi.newpipe.player.helper.PlayerHelper;
|
||||||
|
import org.schabi.newpipe.playlist.ChannelPlayQueue;
|
||||||
|
import org.schabi.newpipe.playlist.PlayQueue;
|
||||||
|
import org.schabi.newpipe.playlist.PlaylistPlayQueue;
|
||||||
|
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||||
|
import org.schabi.newpipe.report.UserAction;
|
||||||
|
import org.schabi.newpipe.util.ExtractorHelper;
|
||||||
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
|
import org.schabi.newpipe.util.PermissionHelper;
|
||||||
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import icepick.State;
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
import io.reactivex.Single;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.disposables.Disposable;
|
||||||
|
import io.reactivex.functions.Consumer;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
|
||||||
|
import static org.schabi.newpipe.util.ThemeHelper.resolveResourceIdFromAttr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the url from the intent and open it in the chosen preferred player
|
||||||
|
*/
|
||||||
|
public class RouterPlayerActivity extends RouterActivity {
|
||||||
|
|
||||||
|
@State
|
||||||
|
protected int currentServiceId = -1;
|
||||||
|
private StreamingService currentService;
|
||||||
|
@State
|
||||||
|
protected LinkType currentLinkType;
|
||||||
|
@State
|
||||||
|
protected int selectedRadioPosition = -1;
|
||||||
|
protected int selectedPreviously = -1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
|
||||||
|
super.onCreate(savedInstanceState, persistentState);
|
||||||
|
setTheme(ThemeHelper.isLightThemeSelected(this) ? R.style.RouterActivityThemeLight : R.style.RouterActivityThemeDark);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleUrl(String url) {
|
||||||
|
disposables.add(Observable
|
||||||
|
.fromCallable(() -> {
|
||||||
|
if (currentServiceId == -1) {
|
||||||
|
currentService = NewPipe.getServiceByUrl(url);
|
||||||
|
currentServiceId = currentService.getServiceId();
|
||||||
|
currentLinkType = currentService.getLinkTypeByUrl(url);
|
||||||
|
currentUrl = NavigationHelper.getCleanUrl(currentService, url, currentLinkType);
|
||||||
|
} else {
|
||||||
|
currentService = NewPipe.getService(currentServiceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentLinkType != LinkType.NONE;
|
||||||
|
})
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(result -> {
|
||||||
|
if (result) {
|
||||||
|
onSuccess();
|
||||||
|
} else {
|
||||||
|
onError();
|
||||||
|
}
|
||||||
|
}, this::handleError));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onError() {
|
||||||
|
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onSuccess() {
|
||||||
|
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
boolean isExtVideoEnabled = preferences.getBoolean(getString(R.string.use_external_video_player_key), false);
|
||||||
|
boolean isExtAudioEnabled = preferences.getBoolean(getString(R.string.use_external_audio_player_key), false);
|
||||||
|
|
||||||
|
if ((isExtAudioEnabled || isExtVideoEnabled) && currentLinkType != LinkType.STREAM) {
|
||||||
|
Toast.makeText(this, R.string.external_player_unsupported_link_type, Toast.LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add some sort of "capabilities" field to services (audio only, video and audio, etc.)
|
||||||
|
if (currentService == ServiceList.SoundCloud.getService()) {
|
||||||
|
handleChoice(getString(R.string.background_player_key));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String playerChoiceKey = preferences.getString(getString(R.string.preferred_player_key), getString(R.string.preferred_player_default));
|
||||||
|
final String alwaysAskKey = getString(R.string.always_ask_player_key);
|
||||||
|
|
||||||
|
if (playerChoiceKey.equals(alwaysAskKey)) {
|
||||||
|
showDialog();
|
||||||
|
} else {
|
||||||
|
handleChoice(playerChoiceKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showDialog() {
|
||||||
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
final ContextThemeWrapper themeWrapper = new ContextThemeWrapper(this,
|
||||||
|
ThemeHelper.isLightThemeSelected(this) ? R.style.LightTheme : R.style.DarkTheme);
|
||||||
|
|
||||||
|
LayoutInflater inflater = LayoutInflater.from(themeWrapper);
|
||||||
|
final LinearLayout rootLayout = (LinearLayout) inflater.inflate(R.layout.preferred_player_dialog_view, null, false);
|
||||||
|
final RadioGroup radioGroup = rootLayout.findViewById(android.R.id.list);
|
||||||
|
|
||||||
|
final AdapterChoiceItem[] choices = {
|
||||||
|
new AdapterChoiceItem(getString(R.string.video_player_key), getString(R.string.video_player),
|
||||||
|
resolveResourceIdFromAttr(themeWrapper, R.attr.play)),
|
||||||
|
new AdapterChoiceItem(getString(R.string.background_player_key), getString(R.string.background_player),
|
||||||
|
resolveResourceIdFromAttr(themeWrapper, R.attr.audio)),
|
||||||
|
new AdapterChoiceItem(getString(R.string.popup_player_key), getString(R.string.popup_player),
|
||||||
|
resolveResourceIdFromAttr(themeWrapper, R.attr.popup))
|
||||||
|
};
|
||||||
|
|
||||||
|
final DialogInterface.OnClickListener dialogButtonsClickListener = (dialog, which) -> {
|
||||||
|
final int indexOfChild = radioGroup.indexOfChild(radioGroup.findViewById(radioGroup.getCheckedRadioButtonId()));
|
||||||
|
final AdapterChoiceItem choice = choices[indexOfChild];
|
||||||
|
|
||||||
|
handleChoice(choice.key);
|
||||||
|
|
||||||
|
if (which == DialogInterface.BUTTON_POSITIVE) {
|
||||||
|
preferences.edit().putString(getString(R.string.preferred_player_key), choice.key).apply();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
final AlertDialog alertDialog = new AlertDialog.Builder(themeWrapper)
|
||||||
|
.setTitle(R.string.preferred_player_share_menu_title)
|
||||||
|
.setView(radioGroup)
|
||||||
|
.setCancelable(true)
|
||||||
|
.setNegativeButton(R.string.just_once, dialogButtonsClickListener)
|
||||||
|
.setPositiveButton(R.string.always, dialogButtonsClickListener)
|
||||||
|
.setOnDismissListener((dialog) -> finish())
|
||||||
|
.create();
|
||||||
|
|
||||||
|
alertDialog.setOnShowListener(dialog -> {
|
||||||
|
setDialogButtonsState(alertDialog, radioGroup.getCheckedRadioButtonId() != -1);
|
||||||
|
});
|
||||||
|
|
||||||
|
radioGroup.setOnCheckedChangeListener((group, checkedId) -> setDialogButtonsState(alertDialog, true));
|
||||||
|
final View.OnClickListener radioButtonsClickListener = v -> {
|
||||||
|
final int indexOfChild = radioGroup.indexOfChild(v);
|
||||||
|
if (indexOfChild == -1) return;
|
||||||
|
|
||||||
|
selectedPreviously = selectedRadioPosition;
|
||||||
|
selectedRadioPosition = indexOfChild;
|
||||||
|
|
||||||
|
if (selectedPreviously == selectedRadioPosition) {
|
||||||
|
handleChoice(choices[selectedRadioPosition].key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int id = 12345;
|
||||||
|
for (AdapterChoiceItem item : choices) {
|
||||||
|
final RadioButton radioButton = (RadioButton) inflater.inflate(R.layout.list_radio_icon_item, null);
|
||||||
|
radioButton.setText(item.description);
|
||||||
|
radioButton.setCompoundDrawablesWithIntrinsicBounds(item.icon, 0, 0, 0);
|
||||||
|
radioButton.setChecked(false);
|
||||||
|
radioButton.setId(id++);
|
||||||
|
radioButton.setLayoutParams(new RadioGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
radioButton.setOnClickListener(radioButtonsClickListener);
|
||||||
|
radioGroup.addView(radioButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedRadioPosition == -1) {
|
||||||
|
final String lastSelectedPlayer = preferences.getString(getString(R.string.preferred_player_last_selected_key), null);
|
||||||
|
if (!TextUtils.isEmpty(lastSelectedPlayer)) {
|
||||||
|
for (int i = 0; i < choices.length; i++) {
|
||||||
|
AdapterChoiceItem c = choices[i];
|
||||||
|
if (lastSelectedPlayer.equals(c.key)) {
|
||||||
|
selectedRadioPosition = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedRadioPosition = Math.min(Math.max(-1, selectedRadioPosition), choices.length - 1);
|
||||||
|
if (selectedRadioPosition != -1) {
|
||||||
|
((RadioButton) radioGroup.getChildAt(selectedRadioPosition)).setChecked(true);
|
||||||
|
}
|
||||||
|
selectedPreviously = selectedRadioPosition;
|
||||||
|
|
||||||
|
alertDialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDialogButtonsState(AlertDialog dialog, boolean state) {
|
||||||
|
final Button negativeButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
|
||||||
|
final Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
|
||||||
|
if (negativeButton == null || positiveButton == null) return;
|
||||||
|
|
||||||
|
negativeButton.setEnabled(state);
|
||||||
|
positiveButton.setEnabled(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleChoice(final String playerChoiceKey) {
|
||||||
|
if (Arrays.asList(getResources().getStringArray(R.array.preferred_player_values_list)).contains(playerChoiceKey)) {
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(this).edit()
|
||||||
|
.putString(getString(R.string.preferred_player_last_selected_key), playerChoiceKey).apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (playerChoiceKey.equals(getString(R.string.popup_player_key)) && !PermissionHelper.isPopupEnabled(this)) {
|
||||||
|
PermissionHelper.showPopupEnablementToast(this);
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Intent intent = new Intent(this, FetcherService.class);
|
||||||
|
intent.putExtra(FetcherService.KEY_CHOICE, new Choice(currentService.getServiceId(), currentLinkType, currentUrl, playerChoiceKey));
|
||||||
|
startService(intent);
|
||||||
|
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class AdapterChoiceItem {
|
||||||
|
final String description, key;
|
||||||
|
@DrawableRes
|
||||||
|
final int icon;
|
||||||
|
|
||||||
|
AdapterChoiceItem(String key, String description, int icon) {
|
||||||
|
this.description = description;
|
||||||
|
this.key = key;
|
||||||
|
this.icon = icon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Choice implements Serializable {
|
||||||
|
final int serviceId;
|
||||||
|
final String url, playerChoice;
|
||||||
|
final LinkType linkType;
|
||||||
|
|
||||||
|
Choice(int serviceId, LinkType linkType, String url, String playerChoice) {
|
||||||
|
this.serviceId = serviceId;
|
||||||
|
this.linkType = linkType;
|
||||||
|
this.url = url;
|
||||||
|
this.playerChoice = playerChoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return serviceId + ":" + url + " > " + linkType + " ::: " + playerChoice;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Service Fetcher
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
public static class FetcherService extends IntentService {
|
||||||
|
|
||||||
|
private static final int ID = 456;
|
||||||
|
public static final String KEY_CHOICE = "key_choice";
|
||||||
|
private Disposable fetcher;
|
||||||
|
|
||||||
|
public FetcherService() {
|
||||||
|
super(FetcherService.class.getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
startForeground(ID, createNotification().build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onHandleIntent(@Nullable Intent intent) {
|
||||||
|
if (intent == null) return;
|
||||||
|
|
||||||
|
final Serializable serializable = intent.getSerializableExtra(KEY_CHOICE);
|
||||||
|
if (!(serializable instanceof Choice)) return;
|
||||||
|
Choice playerChoice = (Choice) serializable;
|
||||||
|
handleChoice(playerChoice);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleChoice(Choice choice) {
|
||||||
|
Single<? extends Info> single = null;
|
||||||
|
UserAction userAction = UserAction.SOMETHING_ELSE;
|
||||||
|
|
||||||
|
switch (choice.linkType) {
|
||||||
|
case STREAM:
|
||||||
|
single = ExtractorHelper.getStreamInfo(choice.serviceId, choice.url, false);
|
||||||
|
userAction = UserAction.REQUESTED_STREAM;
|
||||||
|
break;
|
||||||
|
case CHANNEL:
|
||||||
|
single = ExtractorHelper.getChannelInfo(choice.serviceId, choice.url, false);
|
||||||
|
userAction = UserAction.REQUESTED_CHANNEL;
|
||||||
|
break;
|
||||||
|
case PLAYLIST:
|
||||||
|
single = ExtractorHelper.getPlaylistInfo(choice.serviceId, choice.url, false);
|
||||||
|
userAction = UserAction.REQUESTED_PLAYLIST;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (single != null) {
|
||||||
|
final UserAction finalUserAction = userAction;
|
||||||
|
final Consumer<Info> resultHandler = getResultHandler(choice);
|
||||||
|
fetcher = single
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(info -> {
|
||||||
|
resultHandler.accept(info);
|
||||||
|
if (fetcher != null) fetcher.dispose();
|
||||||
|
}, throwable -> ExtractorHelper.handleGeneralException(this,
|
||||||
|
choice.serviceId, choice.url, throwable, finalUserAction, ", opened with " + choice.playerChoice));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Consumer<Info> getResultHandler(Choice choice) {
|
||||||
|
return info -> {
|
||||||
|
final String videoPlayerKey = getString(R.string.video_player_key);
|
||||||
|
final String backgroundPlayerKey = getString(R.string.background_player_key);
|
||||||
|
final String popupPlayerKey = getString(R.string.popup_player_key);
|
||||||
|
|
||||||
|
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
boolean isExtVideoEnabled = preferences.getBoolean(getString(R.string.use_external_video_player_key), false);
|
||||||
|
boolean isExtAudioEnabled = preferences.getBoolean(getString(R.string.use_external_audio_player_key), false);
|
||||||
|
boolean useOldVideoPlayer = PlayerHelper.isUsingOldPlayer(this);
|
||||||
|
|
||||||
|
PlayQueue playQueue;
|
||||||
|
String playerChoice = choice.playerChoice;
|
||||||
|
|
||||||
|
if (info instanceof StreamInfo) {
|
||||||
|
if (playerChoice.equals(backgroundPlayerKey) && isExtAudioEnabled) {
|
||||||
|
NavigationHelper.playOnExternalAudioPlayer(this, (StreamInfo) info);
|
||||||
|
|
||||||
|
} else if (playerChoice.equals(videoPlayerKey) && isExtVideoEnabled) {
|
||||||
|
NavigationHelper.playOnExternalVideoPlayer(this, (StreamInfo) info);
|
||||||
|
|
||||||
|
} else if (playerChoice.equals(videoPlayerKey) && useOldVideoPlayer) {
|
||||||
|
NavigationHelper.playOnOldVideoPlayer(this, (StreamInfo) info);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
playQueue = new SinglePlayQueue((StreamInfo) info);
|
||||||
|
|
||||||
|
if (playerChoice.equals(videoPlayerKey)) {
|
||||||
|
NavigationHelper.playOnMainPlayer(this, playQueue);
|
||||||
|
} else if (playerChoice.equals(backgroundPlayerKey)) {
|
||||||
|
NavigationHelper.enqueueOnBackgroundPlayer(this, playQueue, true);
|
||||||
|
} else if (playerChoice.equals(popupPlayerKey)) {
|
||||||
|
NavigationHelper.enqueueOnPopupPlayer(this, playQueue, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info instanceof ChannelInfo || info instanceof PlaylistInfo) {
|
||||||
|
playQueue = info instanceof ChannelInfo ? new ChannelPlayQueue((ChannelInfo) info) : new PlaylistPlayQueue((PlaylistInfo) info);
|
||||||
|
|
||||||
|
if (playerChoice.equals(videoPlayerKey)) {
|
||||||
|
NavigationHelper.playOnMainPlayer(this, playQueue);
|
||||||
|
} else if (playerChoice.equals(backgroundPlayerKey)) {
|
||||||
|
NavigationHelper.playOnBackgroundPlayer(this, playQueue);
|
||||||
|
} else if (playerChoice.equals(popupPlayerKey)) {
|
||||||
|
NavigationHelper.playOnPopupPlayer(this, playQueue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
stopForeground(true);
|
||||||
|
if (fetcher != null) fetcher.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private NotificationCompat.Builder createNotification() {
|
||||||
|
return new NotificationCompat.Builder(this, getString(R.string.notification_channel_id))
|
||||||
|
.setOngoing(true)
|
||||||
|
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
|
||||||
|
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||||
|
.setContentTitle(getString(R.string.preferred_player_fetcher_notification_title))
|
||||||
|
.setContentText(getString(R.string.preferred_player_fetcher_notification_message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,49 +0,0 @@
|
||||||
package org.schabi.newpipe;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
|
||||||
import org.schabi.newpipe.player.PopupVideoPlayer;
|
|
||||||
import org.schabi.newpipe.util.Constants;
|
|
||||||
import org.schabi.newpipe.util.PermissionHelper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the url from the intent and open a popup player
|
|
||||||
*/
|
|
||||||
public class RouterPopupActivity extends RouterActivity {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void handleUrl(String url) {
|
|
||||||
if (!PermissionHelper.isPopupEnabled(this)) {
|
|
||||||
PermissionHelper.showPopupEnablementToast(this);
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
StreamingService service;
|
|
||||||
try {
|
|
||||||
service = NewPipe.getServiceByUrl(url);
|
|
||||||
} catch (ExtractionException e) {
|
|
||||||
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Intent callIntent = new Intent(this, PopupVideoPlayer.class);
|
|
||||||
switch (service.getLinkTypeByUrl(url)) {
|
|
||||||
case STREAM:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callIntent.putExtra(Constants.KEY_URL, url);
|
|
||||||
callIntent.putExtra(Constants.KEY_SERVICE_ID, service.getServiceId());
|
|
||||||
startService(callIntent);
|
|
||||||
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -13,7 +13,6 @@ import android.support.annotation.DrawableRes;
|
||||||
import android.support.annotation.FloatRange;
|
import android.support.annotation.FloatRange;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.support.v4.text.TextUtilsCompat;
|
|
||||||
import android.support.v4.view.animation.FastOutSlowInInterpolator;
|
import android.support.v4.view.animation.FastOutSlowInInterpolator;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
|
@ -25,7 +24,6 @@ import android.text.util.Linkify;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Gravity;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
|
@ -37,7 +35,6 @@ import android.widget.FrameLayout;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.PopupMenu;
|
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.Spinner;
|
import android.widget.Spinner;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
@ -51,7 +48,6 @@ import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.ReCaptchaActivity;
|
import org.schabi.newpipe.ReCaptchaActivity;
|
||||||
import org.schabi.newpipe.download.DownloadDialog;
|
import org.schabi.newpipe.download.DownloadDialog;
|
||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.MediaFormat;
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
|
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||||
|
@ -80,6 +76,7 @@ import org.schabi.newpipe.util.ListHelper;
|
||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.util.Localization;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.util.PermissionHelper;
|
import org.schabi.newpipe.util.PermissionHelper;
|
||||||
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -394,7 +391,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||||
|
|
||||||
if (relatedStreamsView.getChildCount() > initialCount) {
|
if (relatedStreamsView.getChildCount() > initialCount) {
|
||||||
relatedStreamsView.removeViews(initialCount, relatedStreamsView.getChildCount() - (initialCount));
|
relatedStreamsView.removeViews(initialCount, relatedStreamsView.getChildCount() - (initialCount));
|
||||||
relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, resolveResourceIdFromAttr(R.attr.expand)));
|
relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.expand)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,7 +401,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||||
//Log.d(TAG, "i = " + i);
|
//Log.d(TAG, "i = " + i);
|
||||||
relatedStreamsView.addView(infoItemBuilder.buildView(relatedStreamsView, item));
|
relatedStreamsView.addView(infoItemBuilder.buildView(relatedStreamsView, item));
|
||||||
}
|
}
|
||||||
relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, resolveResourceIdFromAttr(R.attr.collapse)));
|
relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.collapse)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -579,7 +576,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||||
relatedStreamRootLayout.setVisibility(View.VISIBLE);
|
relatedStreamRootLayout.setVisibility(View.VISIBLE);
|
||||||
relatedStreamExpandButton.setVisibility(View.VISIBLE);
|
relatedStreamExpandButton.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, resolveResourceIdFromAttr(R.attr.expand)));
|
relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.expand)));
|
||||||
} else {
|
} else {
|
||||||
if (info.getNextVideo() == null) relatedStreamRootLayout.setVisibility(View.GONE);
|
if (info.getNextVideo() == null) relatedStreamRootLayout.setVisibility(View.GONE);
|
||||||
relatedStreamExpandButton.setVisibility(View.GONE);
|
relatedStreamExpandButton.setVisibility(View.GONE);
|
||||||
|
@ -807,7 +804,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||||
if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 16) {
|
if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 16) {
|
||||||
openNormalBackgroundPlayer(append);
|
openNormalBackgroundPlayer(append);
|
||||||
} else {
|
} else {
|
||||||
openExternalBackgroundPlayer(audioStream);
|
NavigationHelper.playOnExternalPlayer(activity, currentInfo.getName(), currentInfo.getUploaderName(), audioStream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -841,13 +838,12 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(this.getString(R.string.use_external_video_player_key), false)) {
|
if (PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(this.getString(R.string.use_external_video_player_key), false)) {
|
||||||
openExternalVideoPlayer(selectedVideoStream);
|
NavigationHelper.playOnExternalPlayer(activity, currentInfo.getName(), currentInfo.getUploaderName(), selectedVideoStream);
|
||||||
} else {
|
} else {
|
||||||
openNormalPlayer(selectedVideoStream);
|
openNormalPlayer(selectedVideoStream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void openNormalBackgroundPlayer(final boolean append) {
|
private void openNormalBackgroundPlayer(final boolean append) {
|
||||||
final PlayQueue itemQueue = new SinglePlayQueue(currentInfo);
|
final PlayQueue itemQueue = new SinglePlayQueue(currentInfo);
|
||||||
if (append) {
|
if (append) {
|
||||||
|
@ -857,40 +853,6 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openExternalBackgroundPlayer(AudioStream audioStream) {
|
|
||||||
Intent intent;
|
|
||||||
intent = new Intent();
|
|
||||||
try {
|
|
||||||
intent.setAction(Intent.ACTION_VIEW);
|
|
||||||
intent.setDataAndType(Uri.parse(audioStream.getUrl()), audioStream.getFormat().getMimeType());
|
|
||||||
intent.putExtra(Intent.EXTRA_TITLE, currentInfo.getName());
|
|
||||||
intent.putExtra("title", currentInfo.getName());
|
|
||||||
activity.startActivity(intent);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
|
||||||
builder.setMessage(R.string.no_player_found)
|
|
||||||
.setPositiveButton(R.string.install, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
Intent intent = new Intent();
|
|
||||||
intent.setAction(Intent.ACTION_VIEW);
|
|
||||||
intent.setData(Uri.parse(activity.getString(R.string.fdroid_vlc_url)));
|
|
||||||
activity.startActivity(intent);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
Log.i(TAG, "You unlocked a secret unicorn.");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
builder.create().show();
|
|
||||||
Log.e(TAG, "Either no Streaming player for audio was installed, or something important crashed:");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void openNormalPlayer(VideoStream selectedVideoStream) {
|
private void openNormalPlayer(VideoStream selectedVideoStream) {
|
||||||
Intent mIntent;
|
Intent mIntent;
|
||||||
boolean useOldPlayer = PlayerHelper.isUsingOldPlayer(activity) || (Build.VERSION.SDK_INT < 16);
|
boolean useOldPlayer = PlayerHelper.isUsingOldPlayer(activity) || (Build.VERSION.SDK_INT < 16);
|
||||||
|
@ -909,33 +871,6 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||||
startActivity(mIntent);
|
startActivity(mIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openExternalVideoPlayer(VideoStream selectedVideoStream) {
|
|
||||||
// External Player
|
|
||||||
Intent intent = new Intent();
|
|
||||||
try {
|
|
||||||
intent.setAction(Intent.ACTION_VIEW)
|
|
||||||
.setDataAndType(Uri.parse(selectedVideoStream.getUrl()), selectedVideoStream.getFormat().getMimeType())
|
|
||||||
.putExtra(Intent.EXTRA_TITLE, currentInfo.getName())
|
|
||||||
.putExtra("title", currentInfo.getName());
|
|
||||||
this.startActivity(intent);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
|
||||||
builder.setMessage(R.string.no_player_found)
|
|
||||||
.setPositiveButton(R.string.install, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
Intent intent = new Intent()
|
|
||||||
.setAction(Intent.ACTION_VIEW)
|
|
||||||
.setData(Uri.parse(getString(R.string.fdroid_vlc_url)));
|
|
||||||
startActivity(intent);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setNegativeButton(R.string.cancel, null);
|
|
||||||
builder.create().show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Utils
|
// Utils
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -1212,4 +1147,4 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||||
|
|
||||||
showError(getString(R.string.blocked_by_gema), false, R.drawable.gruese_die_gema);
|
showError(getString(R.string.blocked_by_gema), false, R.drawable.gruese_die_gema);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -111,6 +111,7 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||||
public static final String PLAYBACK_QUALITY = "playback_quality";
|
public static final String PLAYBACK_QUALITY = "playback_quality";
|
||||||
public static final String PLAY_QUEUE = "play_queue";
|
public static final String PLAY_QUEUE = "play_queue";
|
||||||
public static final String APPEND_ONLY = "append_only";
|
public static final String APPEND_ONLY = "append_only";
|
||||||
|
public static final String SELECT_ON_APPEND = "select_on_append";
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Playback
|
// Playback
|
||||||
|
@ -218,7 +219,13 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||||
|
|
||||||
// Resolve append intents
|
// Resolve append intents
|
||||||
if (intent.getBooleanExtra(APPEND_ONLY, false) && playQueue != null) {
|
if (intent.getBooleanExtra(APPEND_ONLY, false) && playQueue != null) {
|
||||||
|
int sizeBeforeAppend = playQueue.size();
|
||||||
playQueue.append(queue.getStreams());
|
playQueue.append(queue.getStreams());
|
||||||
|
|
||||||
|
if (intent.getBooleanExtra(SELECT_ON_APPEND, false) && queue.getStreams().size() > 0) {
|
||||||
|
playQueue.setIndex(sizeBeforeAppend);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,6 @@ import android.content.res.Configuration;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.PixelFormat;
|
import android.graphics.PixelFormat;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
@ -49,20 +48,12 @@ import android.widget.PopupMenu;
|
||||||
import android.widget.RemoteViews;
|
import android.widget.RemoteViews;
|
||||||
import android.widget.SeekBar;
|
import android.widget.SeekBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.google.android.exoplayer2.PlaybackParameters;
|
import com.google.android.exoplayer2.PlaybackParameters;
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
|
|
||||||
import org.schabi.newpipe.BuildConfig;
|
import org.schabi.newpipe.BuildConfig;
|
||||||
import org.schabi.newpipe.MainActivity;
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.ReCaptchaActivity;
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
|
||||||
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
|
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||||
import org.schabi.newpipe.player.event.PlayerEventListener;
|
import org.schabi.newpipe.player.event.PlayerEventListener;
|
||||||
|
@ -70,22 +61,12 @@ import org.schabi.newpipe.player.helper.LockManager;
|
||||||
import org.schabi.newpipe.player.helper.PlayerHelper;
|
import org.schabi.newpipe.player.helper.PlayerHelper;
|
||||||
import org.schabi.newpipe.player.old.PlayVideoActivity;
|
import org.schabi.newpipe.player.old.PlayVideoActivity;
|
||||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||||
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.report.UserAction;
|
|
||||||
import org.schabi.newpipe.util.Constants;
|
|
||||||
import org.schabi.newpipe.util.ExtractorHelper;
|
|
||||||
import org.schabi.newpipe.util.ListHelper;
|
import org.schabi.newpipe.util.ListHelper;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
|
||||||
import io.reactivex.disposables.Disposable;
|
|
||||||
import io.reactivex.schedulers.Schedulers;
|
|
||||||
|
|
||||||
import static org.schabi.newpipe.player.helper.PlayerHelper.isUsingOldPlayer;
|
import static org.schabi.newpipe.player.helper.PlayerHelper.isUsingOldPlayer;
|
||||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||||
|
|
||||||
|
@ -125,8 +106,8 @@ public final class PopupVideoPlayer extends Service {
|
||||||
private RemoteViews notRemoteView;
|
private RemoteViews notRemoteView;
|
||||||
|
|
||||||
private VideoPlayerImpl playerImpl;
|
private VideoPlayerImpl playerImpl;
|
||||||
private Disposable currentWorker;
|
|
||||||
private LockManager lockManager;
|
private LockManager lockManager;
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Service-Activity Binder
|
// Service-Activity Binder
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -157,21 +138,8 @@ public final class PopupVideoPlayer extends Service {
|
||||||
if (playerImpl.getPlayer() == null) initPopup();
|
if (playerImpl.getPlayer() == null) initPopup();
|
||||||
if (!playerImpl.isPlaying()) playerImpl.getPlayer().setPlayWhenReady(true);
|
if (!playerImpl.isPlaying()) playerImpl.getPlayer().setPlayWhenReady(true);
|
||||||
|
|
||||||
if (intent != null && intent.getStringExtra(Constants.KEY_URL) != null) {
|
playerImpl.handleIntent(intent);
|
||||||
final int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
|
|
||||||
final String url = intent.getStringExtra(Constants.KEY_URL);
|
|
||||||
|
|
||||||
playerImpl.setStartedFromNewPipe(false);
|
|
||||||
|
|
||||||
final FetcherHandler fetcherRunnable = new FetcherHandler(this, serviceId, url);
|
|
||||||
currentWorker = ExtractorHelper.getStreamInfo(serviceId,url,false)
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe(fetcherRunnable::onReceive, fetcherRunnable::onError);
|
|
||||||
} else {
|
|
||||||
playerImpl.setStartedFromNewPipe(true);
|
|
||||||
playerImpl.handleIntent(intent);
|
|
||||||
}
|
|
||||||
return START_NOT_STICKY;
|
return START_NOT_STICKY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,7 +270,6 @@ public final class PopupVideoPlayer extends Service {
|
||||||
}
|
}
|
||||||
if (lockManager != null) lockManager.releaseWifiAndCpu();
|
if (lockManager != null) lockManager.releaseWifiAndCpu();
|
||||||
if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID);
|
if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID);
|
||||||
if (currentWorker != null) currentWorker.dispose();
|
|
||||||
mBinder = null;
|
mBinder = null;
|
||||||
playerImpl = null;
|
playerImpl = null;
|
||||||
|
|
||||||
|
@ -452,7 +419,6 @@ public final class PopupVideoPlayer extends Service {
|
||||||
this.getPlaybackPitch(),
|
this.getPlaybackPitch(),
|
||||||
this.getPlaybackQuality()
|
this.getPlaybackQuality()
|
||||||
);
|
);
|
||||||
if (!isStartedFromNewPipe()) intent.putExtra(VideoPlayer.STARTED_FROM_NEWPIPE, false);
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
} else {
|
} else {
|
||||||
intent = new Intent(PopupVideoPlayer.this, PlayVideoActivity.class)
|
intent = new Intent(PopupVideoPlayer.this, PlayVideoActivity.class)
|
||||||
|
@ -862,63 +828,4 @@ public final class PopupVideoPlayer extends Service {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetcher handler used if open by a link out of NewPipe
|
|
||||||
*/
|
|
||||||
private class FetcherHandler {
|
|
||||||
private final int serviceId;
|
|
||||||
private final String url;
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
private final Handler mainHandler;
|
|
||||||
|
|
||||||
private FetcherHandler(Context context, int serviceId, String url) {
|
|
||||||
this.mainHandler = new Handler(PopupVideoPlayer.this.getMainLooper());
|
|
||||||
this.context = context;
|
|
||||||
this.url = url;
|
|
||||||
this.serviceId = serviceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onReceive(final StreamInfo info) {
|
|
||||||
mainHandler.post(() -> {
|
|
||||||
final Intent intent = NavigationHelper.getPlayerIntent(getApplicationContext(),
|
|
||||||
PopupVideoPlayer.class, new SinglePlayQueue(info));
|
|
||||||
playerImpl.handleIntent(intent);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onError(final Throwable exception) {
|
|
||||||
if (DEBUG) Log.d(TAG, "onError() called with: exception = [" + exception + "]");
|
|
||||||
exception.printStackTrace();
|
|
||||||
mainHandler.post(() -> {
|
|
||||||
if (exception instanceof ReCaptchaException) {
|
|
||||||
onReCaptchaException();
|
|
||||||
} else if (exception instanceof IOException) {
|
|
||||||
Toast.makeText(context, R.string.network_error, Toast.LENGTH_LONG).show();
|
|
||||||
} else if (exception instanceof YoutubeStreamExtractor.GemaException) {
|
|
||||||
Toast.makeText(context, R.string.blocked_by_gema, Toast.LENGTH_LONG).show();
|
|
||||||
} else if (exception instanceof YoutubeStreamExtractor.LiveStreamException) {
|
|
||||||
Toast.makeText(context, R.string.live_streams_not_supported, Toast.LENGTH_LONG).show();
|
|
||||||
} else if (exception instanceof ContentNotAvailableException) {
|
|
||||||
Toast.makeText(context, R.string.content_not_available, Toast.LENGTH_LONG).show();
|
|
||||||
} else {
|
|
||||||
int errorId = exception instanceof YoutubeStreamExtractor.DecryptException ? R.string.youtube_signature_decryption_error :
|
|
||||||
exception instanceof ParsingException ? R.string.parsing_error : R.string.general_error;
|
|
||||||
ErrorActivity.reportError(mainHandler, context, exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(UserAction.REQUESTED_STREAM, NewPipe.getNameOfService(serviceId), url, errorId));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
stopSelf();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onReCaptchaException() {
|
|
||||||
Toast.makeText(context, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
|
|
||||||
// Starting ReCaptcha Challenge Activity
|
|
||||||
Intent intent = new Intent(context, ReCaptchaActivity.class);
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
context.startActivity(intent);
|
|
||||||
stopSelf();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -39,7 +39,6 @@ import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.SurfaceView;
|
import android.view.SurfaceView;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.PopupMenu;
|
import android.widget.PopupMenu;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
|
@ -85,12 +84,6 @@ public abstract class VideoPlayer extends BasePlayer
|
||||||
public static final boolean DEBUG = BasePlayer.DEBUG;
|
public static final boolean DEBUG = BasePlayer.DEBUG;
|
||||||
public final String TAG;
|
public final String TAG;
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
|
||||||
// Intent
|
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
|
||||||
|
|
||||||
public static final String STARTED_FROM_NEWPIPE = "started_from_newpipe";
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Player
|
// Player
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -102,7 +95,6 @@ public abstract class VideoPlayer extends BasePlayer
|
||||||
|
|
||||||
protected String playbackQuality;
|
protected String playbackQuality;
|
||||||
|
|
||||||
private boolean startedFromNewPipe = true;
|
|
||||||
protected boolean wasPlaying = false;
|
protected boolean wasPlaying = false;
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -695,14 +687,6 @@ public abstract class VideoPlayer extends BasePlayer
|
||||||
return availableStreams.get(selectedStreamIndex);
|
return availableStreams.get(selectedStreamIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isStartedFromNewPipe() {
|
|
||||||
return startedFromNewPipe;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStartedFromNewPipe(boolean startedFromNewPipe) {
|
|
||||||
this.startedFromNewPipe = startedFromNewPipe;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Handler getControlsVisibilityHandler() {
|
public Handler getControlsVisibilityHandler() {
|
||||||
return controlsVisibilityHandler;
|
return controlsVisibilityHandler;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,10 @@ public final class ChannelPlayQueue extends AbstractInfoPlayQueue<ChannelInfo, C
|
||||||
super(item);
|
super(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ChannelPlayQueue(final ChannelInfo info) {
|
||||||
|
this(info.getServiceId(), info.getUrl(), info.getNextStreamsUrl(), info.getRelatedStreams(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
public ChannelPlayQueue(final int serviceId,
|
public ChannelPlayQueue(final int serviceId,
|
||||||
final String url,
|
final String url,
|
||||||
final String nextPageUrl,
|
final String nextPageUrl,
|
||||||
|
|
|
@ -15,6 +15,10 @@ public final class PlaylistPlayQueue extends AbstractInfoPlayQueue<PlaylistInfo,
|
||||||
super(item);
|
super(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PlaylistPlayQueue(final PlaylistInfo info) {
|
||||||
|
this(info.getServiceId(), info.getUrl(), info.getNextStreamsUrl(), info.getRelatedStreams(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
public PlaylistPlayQueue(final int serviceId,
|
public PlaylistPlayQueue(final int serviceId,
|
||||||
final String url,
|
final String url,
|
||||||
final String nextPageUrl,
|
final String nextPageUrl,
|
||||||
|
|
|
@ -19,29 +19,38 @@
|
||||||
|
|
||||||
package org.schabi.newpipe.util;
|
package org.schabi.newpipe.util;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Handler;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.schabi.newpipe.MainActivity;
|
import org.schabi.newpipe.MainActivity;
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.ReCaptchaActivity;
|
||||||
import org.schabi.newpipe.extractor.Info;
|
import org.schabi.newpipe.extractor.Info;
|
||||||
import org.schabi.newpipe.extractor.ListExtractor.NextItemsResult;
|
import org.schabi.newpipe.extractor.ListExtractor.NextItemsResult;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
||||||
import org.schabi.newpipe.extractor.kiosk.KioskInfo;
|
import org.schabi.newpipe.extractor.kiosk.KioskInfo;
|
||||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||||
import org.schabi.newpipe.extractor.search.SearchEngine;
|
import org.schabi.newpipe.extractor.search.SearchEngine;
|
||||||
import org.schabi.newpipe.extractor.search.SearchResult;
|
import org.schabi.newpipe.extractor.search.SearchResult;
|
||||||
|
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
import org.schabi.newpipe.report.UserAction;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InterruptedIOException;
|
import java.io.InterruptedIOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
|
|
||||||
import io.reactivex.Maybe;
|
import io.reactivex.Maybe;
|
||||||
import io.reactivex.MaybeSource;
|
|
||||||
import io.reactivex.Single;
|
import io.reactivex.Single;
|
||||||
import io.reactivex.annotations.NonNull;
|
import io.reactivex.annotations.NonNull;
|
||||||
import io.reactivex.functions.Consumer;
|
|
||||||
import io.reactivex.functions.Function;
|
|
||||||
|
|
||||||
public final class ExtractorHelper {
|
public final class ExtractorHelper {
|
||||||
private static final String TAG = ExtractorHelper.class.getSimpleName();
|
private static final String TAG = ExtractorHelper.class.getSimpleName();
|
||||||
|
@ -198,6 +207,37 @@ public final class ExtractorHelper {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple and general error handler that show a Toast for known exceptions, and for others, opens the report error activity with the (optional) error message.
|
||||||
|
*/
|
||||||
|
public static void handleGeneralException(Context context, int serviceId, String url, Throwable exception, UserAction userAction, String optionalErrorMessage) {
|
||||||
|
final Handler handler = new Handler(context.getMainLooper());
|
||||||
|
|
||||||
|
handler.post(() -> {
|
||||||
|
if (exception instanceof ReCaptchaException) {
|
||||||
|
Toast.makeText(context, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
|
||||||
|
// Starting ReCaptcha Challenge Activity
|
||||||
|
Intent intent = new Intent(context, ReCaptchaActivity.class);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
context.startActivity(intent);
|
||||||
|
} else if (exception instanceof IOException) {
|
||||||
|
Toast.makeText(context, R.string.network_error, Toast.LENGTH_LONG).show();
|
||||||
|
} else if (exception instanceof YoutubeStreamExtractor.GemaException) {
|
||||||
|
Toast.makeText(context, R.string.blocked_by_gema, Toast.LENGTH_LONG).show();
|
||||||
|
} else if (exception instanceof YoutubeStreamExtractor.LiveStreamException) {
|
||||||
|
Toast.makeText(context, R.string.live_streams_not_supported, Toast.LENGTH_LONG).show();
|
||||||
|
} else if (exception instanceof ContentNotAvailableException) {
|
||||||
|
Toast.makeText(context, R.string.content_not_available, Toast.LENGTH_LONG).show();
|
||||||
|
} else {
|
||||||
|
int errorId = exception instanceof YoutubeStreamExtractor.DecryptException ? R.string.youtube_signature_decryption_error :
|
||||||
|
exception instanceof ParsingException ? R.string.parsing_error : R.string.general_error;
|
||||||
|
ErrorActivity.reportError(handler, context, exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(userAction,
|
||||||
|
serviceId == -1 ? "none" : NewPipe.getNameOfService(serviceId), url + (optionalErrorMessage == null ? "" : optionalErrorMessage), errorId));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if throwable have the cause that can be assignable from the causes to check.
|
* Check if throwable have the cause that can be assignable from the causes to check.
|
||||||
*
|
*
|
||||||
|
@ -263,17 +303,4 @@ public final class ExtractorHelper {
|
||||||
InterruptedIOException.class,
|
InterruptedIOException.class,
|
||||||
InterruptedException.class);
|
InterruptedException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toUpperCase(String value) {
|
|
||||||
StringBuilder sb = new StringBuilder(value);
|
|
||||||
for (int index = 0; index < sb.length(); index++) {
|
|
||||||
char c = sb.charAt(index);
|
|
||||||
if (Character.isLowerCase(c)) {
|
|
||||||
sb.setCharAt(index, Character.toUpperCase(c));
|
|
||||||
} else {
|
|
||||||
sb.setCharAt(index, Character.toLowerCase(c));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/**
|
/*
|
||||||
* Copyright 2017 Mauricio Colli <mauriciocolli@outlook.com>
|
* Copyright 2017 Mauricio Colli <mauriciocolli@outlook.com>
|
||||||
* InfoCache.java is part of NewPipe
|
* InfoCache.java is part of NewPipe
|
||||||
*
|
*
|
||||||
|
|
|
@ -9,6 +9,8 @@ import android.os.Build;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.app.FragmentManager;
|
import android.support.v4.app.FragmentManager;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
|
@ -21,6 +23,10 @@ import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.ServiceList;
|
import org.schabi.newpipe.extractor.ServiceList;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
|
import org.schabi.newpipe.extractor.stream.AudioStream;
|
||||||
|
import org.schabi.newpipe.extractor.stream.Stream;
|
||||||
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
|
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||||
import org.schabi.newpipe.fragments.MainFragment;
|
import org.schabi.newpipe.fragments.MainFragment;
|
||||||
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
|
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
|
||||||
import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
|
import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
|
||||||
|
@ -36,9 +42,12 @@ import org.schabi.newpipe.player.MainVideoPlayer;
|
||||||
import org.schabi.newpipe.player.PopupVideoPlayer;
|
import org.schabi.newpipe.player.PopupVideoPlayer;
|
||||||
import org.schabi.newpipe.player.PopupVideoPlayerActivity;
|
import org.schabi.newpipe.player.PopupVideoPlayerActivity;
|
||||||
import org.schabi.newpipe.player.VideoPlayer;
|
import org.schabi.newpipe.player.VideoPlayer;
|
||||||
|
import org.schabi.newpipe.player.old.PlayVideoActivity;
|
||||||
import org.schabi.newpipe.playlist.PlayQueue;
|
import org.schabi.newpipe.playlist.PlayQueue;
|
||||||
import org.schabi.newpipe.settings.SettingsActivity;
|
import org.schabi.newpipe.settings.SettingsActivity;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||||
public class NavigationHelper {
|
public class NavigationHelper {
|
||||||
public static final String MAIN_FRAGMENT_TAG = "main_fragment_tag";
|
public static final String MAIN_FRAGMENT_TAG = "main_fragment_tag";
|
||||||
|
@ -46,6 +55,7 @@ public class NavigationHelper {
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Players
|
// Players
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
public static Intent getPlayerIntent(final Context context,
|
public static Intent getPlayerIntent(final Context context,
|
||||||
final Class targetClazz,
|
final Class targetClazz,
|
||||||
final PlayQueue playQueue,
|
final PlayQueue playQueue,
|
||||||
|
@ -65,9 +75,11 @@ public class NavigationHelper {
|
||||||
|
|
||||||
public static Intent getPlayerEnqueueIntent(final Context context,
|
public static Intent getPlayerEnqueueIntent(final Context context,
|
||||||
final Class targetClazz,
|
final Class targetClazz,
|
||||||
final PlayQueue playQueue) {
|
final PlayQueue playQueue,
|
||||||
|
final boolean selectOnAppend) {
|
||||||
return getPlayerIntent(context, targetClazz, playQueue)
|
return getPlayerIntent(context, targetClazz, playQueue)
|
||||||
.putExtra(BasePlayer.APPEND_ONLY, true);
|
.putExtra(BasePlayer.APPEND_ONLY, true)
|
||||||
|
.putExtra(BasePlayer.SELECT_ON_APPEND, selectOnAppend);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Intent getPlayerIntent(final Context context,
|
public static Intent getPlayerIntent(final Context context,
|
||||||
|
@ -84,16 +96,39 @@ public class NavigationHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void playOnMainPlayer(final Context context, final PlayQueue queue) {
|
public static void playOnMainPlayer(final Context context, final PlayQueue queue) {
|
||||||
context.startActivity(getPlayerIntent(context, MainVideoPlayer.class, queue));
|
final Intent playerIntent = getPlayerIntent(context, MainVideoPlayer.class, queue);
|
||||||
|
playerIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
context.startActivity(playerIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void playOnPopupPlayer(final Activity activity, final PlayQueue queue) {
|
public static void playOnOldVideoPlayer(Context context, StreamInfo info) {
|
||||||
if (!PermissionHelper.isPopupEnabled(activity)) {
|
ArrayList<VideoStream> videoStreamsList = new ArrayList<>(ListHelper.getSortedStreamVideosList(context, info.getVideoStreams(), null, false));
|
||||||
PermissionHelper.showPopupEnablementToast(activity);
|
int index = ListHelper.getDefaultResolutionIndex(context, videoStreamsList);
|
||||||
|
|
||||||
|
if (index == -1) {
|
||||||
|
Toast.makeText(context, R.string.video_streams_empty, Toast.LENGTH_SHORT).show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Toast.makeText(activity, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
|
|
||||||
activity.startService(getPlayerIntent(activity, PopupVideoPlayer.class, queue));
|
VideoStream videoStream = videoStreamsList.get(index);
|
||||||
|
Intent intent = new Intent(context, PlayVideoActivity.class)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
.putExtra(PlayVideoActivity.VIDEO_TITLE, info.getName())
|
||||||
|
.putExtra(PlayVideoActivity.STREAM_URL, videoStream.getUrl())
|
||||||
|
.putExtra(PlayVideoActivity.VIDEO_URL, info.getUrl())
|
||||||
|
.putExtra(PlayVideoActivity.START_POSITION, info.getStartPosition());
|
||||||
|
|
||||||
|
context.startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void playOnPopupPlayer(final Context context, final PlayQueue queue) {
|
||||||
|
if (!PermissionHelper.isPopupEnabled(context)) {
|
||||||
|
PermissionHelper.showPopupEnablementToast(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.makeText(context, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
|
||||||
|
context.startService(getPlayerIntent(context, PopupVideoPlayer.class, queue));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void playOnBackgroundPlayer(final Context context, final PlayQueue queue) {
|
public static void playOnBackgroundPlayer(final Context context, final PlayQueue queue) {
|
||||||
|
@ -101,19 +136,92 @@ public class NavigationHelper {
|
||||||
context.startService(getPlayerIntent(context, BackgroundPlayer.class, queue));
|
context.startService(getPlayerIntent(context, BackgroundPlayer.class, queue));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void enqueueOnPopupPlayer(final Activity activity, final PlayQueue queue) {
|
public static void enqueueOnPopupPlayer(final Context context, final PlayQueue queue) {
|
||||||
if (!PermissionHelper.isPopupEnabled(activity)) {
|
enqueueOnPopupPlayer(context, queue, false);
|
||||||
PermissionHelper.showPopupEnablementToast(activity);
|
}
|
||||||
|
|
||||||
|
public static void enqueueOnPopupPlayer(final Context context, final PlayQueue queue, boolean selectOnAppend) {
|
||||||
|
if (!PermissionHelper.isPopupEnabled(context)) {
|
||||||
|
PermissionHelper.showPopupEnablementToast(context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Toast.makeText(activity, R.string.popup_playing_append, Toast.LENGTH_SHORT).show();
|
|
||||||
activity.startService(getPlayerEnqueueIntent(activity, PopupVideoPlayer.class, queue));
|
Toast.makeText(context, R.string.popup_playing_append, Toast.LENGTH_SHORT).show();
|
||||||
|
context.startService(getPlayerEnqueueIntent(context, PopupVideoPlayer.class, queue, selectOnAppend));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue) {
|
public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue) {
|
||||||
Toast.makeText(context, R.string.background_player_append, Toast.LENGTH_SHORT).show();
|
enqueueOnBackgroundPlayer(context, queue, false);
|
||||||
context.startService(getPlayerEnqueueIntent(context, BackgroundPlayer.class, queue));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue, boolean selectOnAppend) {
|
||||||
|
Toast.makeText(context, R.string.background_player_append, Toast.LENGTH_SHORT).show();
|
||||||
|
context.startService(getPlayerEnqueueIntent(context, BackgroundPlayer.class, queue, selectOnAppend));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// External Players
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
public static void playOnExternalAudioPlayer(Context context, StreamInfo info) {
|
||||||
|
final int index = ListHelper.getDefaultAudioFormat(context, info.getAudioStreams());
|
||||||
|
|
||||||
|
if (index == -1) {
|
||||||
|
Toast.makeText(context, R.string.audio_streams_empty, Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioStream audioStream = info.getAudioStreams().get(index);
|
||||||
|
playOnExternalPlayer(context, info.getName(), info.getUploaderName(), audioStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void playOnExternalVideoPlayer(Context context, StreamInfo info) {
|
||||||
|
ArrayList<VideoStream> videoStreamsList = new ArrayList<>(ListHelper.getSortedStreamVideosList(context, info.getVideoStreams(), null, false));
|
||||||
|
int index = ListHelper.getDefaultResolutionIndex(context, videoStreamsList);
|
||||||
|
|
||||||
|
if (index == -1) {
|
||||||
|
Toast.makeText(context, R.string.video_streams_empty, Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoStream videoStream = videoStreamsList.get(index);
|
||||||
|
playOnExternalPlayer(context, info.getName(), info.getUploaderName(), videoStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void playOnExternalPlayer(Context context, String name, String artist, Stream stream) {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
|
intent.setDataAndType(Uri.parse(stream.getUrl()), stream.getFormat().getMimeType());
|
||||||
|
intent.putExtra(Intent.EXTRA_TITLE, name);
|
||||||
|
intent.putExtra("title", name);
|
||||||
|
intent.putExtra("artist", artist);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
|
||||||
|
resolveActivityOrAskToInstall(context, intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void resolveActivityOrAskToInstall(Context context, Intent intent) {
|
||||||
|
if (intent.resolveActivity(context.getPackageManager()) != null) {
|
||||||
|
context.startActivity(intent);
|
||||||
|
} else {
|
||||||
|
if (context instanceof Activity) {
|
||||||
|
new AlertDialog.Builder(context)
|
||||||
|
.setMessage(R.string.no_player_found)
|
||||||
|
.setPositiveButton(R.string.install, (dialog, which) -> {
|
||||||
|
Intent i = new Intent();
|
||||||
|
i.setAction(Intent.ACTION_VIEW);
|
||||||
|
i.setData(Uri.parse(context.getString(R.string.fdroid_vlc_url)));
|
||||||
|
context.startActivity(i);
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.cancel, (dialog, which) -> Log.i("NavigationHelper", "You unlocked a secret unicorn."))
|
||||||
|
.show();
|
||||||
|
//Log.e("NavigationHelper", "Either no Streaming player for audio was installed, or something important crashed:");
|
||||||
|
} else {
|
||||||
|
Toast.makeText(context, R.string.no_player_found_toast, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Through FragmentManager
|
// Through FragmentManager
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -287,19 +395,6 @@ public class NavigationHelper {
|
||||||
// Link handling
|
// Link handling
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
public static boolean openByLink(Context context, String url) {
|
|
||||||
Intent intentByLink;
|
|
||||||
try {
|
|
||||||
intentByLink = getIntentByLink(context, url);
|
|
||||||
} catch (ExtractionException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
intentByLink.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
intentByLink.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
|
||||||
context.startActivity(intentByLink);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Intent getOpenIntent(Context context, String url, int serviceId, StreamingService.LinkType type) {
|
private static Intent getOpenIntent(Context context, String url, int serviceId, StreamingService.LinkType type) {
|
||||||
Intent mIntent = new Intent(context, MainActivity.class);
|
Intent mIntent = new Intent(context, MainActivity.class);
|
||||||
mIntent.putExtra(Constants.KEY_SERVICE_ID, serviceId);
|
mIntent.putExtra(Constants.KEY_SERVICE_ID, serviceId);
|
||||||
|
@ -317,15 +412,14 @@ public class NavigationHelper {
|
||||||
throw new ExtractionException("Service not supported at the moment");
|
throw new ExtractionException("Service not supported at the moment");
|
||||||
}
|
}
|
||||||
|
|
||||||
int serviceId = service.getServiceId();
|
|
||||||
StreamingService.LinkType linkType = service.getLinkTypeByUrl(url);
|
StreamingService.LinkType linkType = service.getLinkTypeByUrl(url);
|
||||||
|
|
||||||
if (linkType == StreamingService.LinkType.NONE) {
|
if (linkType == StreamingService.LinkType.NONE) {
|
||||||
throw new ExtractionException("Url not known to service. service=" + serviceId + " url=" + url);
|
throw new ExtractionException("Url not known to service. service=" + service + " url=" + url);
|
||||||
}
|
}
|
||||||
|
|
||||||
url = getCleanUrl(service, url, linkType);
|
url = getCleanUrl(service, url, linkType);
|
||||||
Intent rIntent = getOpenIntent(context, url, serviceId, linkType);
|
Intent rIntent = getOpenIntent(context, url, service.getServiceId(), linkType);
|
||||||
|
|
||||||
switch (linkType) {
|
switch (linkType) {
|
||||||
case STREAM:
|
case STREAM:
|
||||||
|
@ -337,7 +431,7 @@ public class NavigationHelper {
|
||||||
return rIntent;
|
return rIntent;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getCleanUrl(StreamingService service, String dirtyUrl, StreamingService.LinkType linkType) throws ExtractionException {
|
public static String getCleanUrl(StreamingService service, String dirtyUrl, StreamingService.LinkType linkType) throws ExtractionException {
|
||||||
switch (linkType) {
|
switch (linkType) {
|
||||||
case STREAM:
|
case STREAM:
|
||||||
return service.getStreamUrlIdHandler().cleanUrl(dirtyUrl);
|
return service.getStreamUrlIdHandler().cleanUrl(dirtyUrl);
|
||||||
|
@ -351,7 +445,6 @@ public class NavigationHelper {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static Uri openMarketUrl(String packageName) {
|
private static Uri openMarketUrl(String packageName) {
|
||||||
return Uri.parse("market://details")
|
return Uri.parse("market://details")
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
|
|
|
@ -20,7 +20,6 @@ import org.schabi.newpipe.R;
|
||||||
public class PermissionHelper {
|
public class PermissionHelper {
|
||||||
public static final int PERMISSION_WRITE_STORAGE = 778;
|
public static final int PERMISSION_WRITE_STORAGE = 778;
|
||||||
public static final int PERMISSION_READ_STORAGE = 777;
|
public static final int PERMISSION_READ_STORAGE = 777;
|
||||||
public static final int PERMISSION_SYSTEM_ALERT_WINDOW = 779;
|
|
||||||
|
|
||||||
|
|
||||||
public static boolean checkStoragePermissions(Activity activity) {
|
public static boolean checkStoragePermissions(Activity activity) {
|
||||||
|
@ -80,27 +79,25 @@ public class PermissionHelper {
|
||||||
* In order to be able to draw over other apps, the permission android.permission.SYSTEM_ALERT_WINDOW have to be granted.
|
* In order to be able to draw over other apps, the permission android.permission.SYSTEM_ALERT_WINDOW have to be granted.
|
||||||
* <p>
|
* <p>
|
||||||
* On < API 23 (MarshMallow) the permission was granted when the user installed the application (via AndroidManifest),
|
* On < API 23 (MarshMallow) the permission was granted when the user installed the application (via AndroidManifest),
|
||||||
* on > 23, however, it have to start a activity asking the user if he agree.
|
* on > 23, however, it have to start a activity asking the user if he agrees.
|
||||||
* <p>
|
* <p>
|
||||||
* This method just return if canDraw over other apps, if it doesn't, try to get the permission,
|
* This method just return if the app has permission to draw over other apps, and if it doesn't, it will try to get the permission.
|
||||||
* it does not get the result of the startActivityForResult, if the user accept, the next time that he tries to open
|
|
||||||
* it will return true.
|
|
||||||
*
|
*
|
||||||
* @param activity context to startActivityForResult
|
|
||||||
* @return returns {@link Settings#canDrawOverlays(Context)}
|
* @return returns {@link Settings#canDrawOverlays(Context)}
|
||||||
**/
|
**/
|
||||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||||
public static boolean checkSystemAlertWindowPermission(Activity activity) {
|
public static boolean checkSystemAlertWindowPermission(Context context) {
|
||||||
if (!Settings.canDrawOverlays(activity)) {
|
if (!Settings.canDrawOverlays(context)) {
|
||||||
Intent i = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + activity.getPackageName()));
|
Intent i = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + context.getPackageName()));
|
||||||
activity.startActivityForResult(i, PERMISSION_SYSTEM_ALERT_WINDOW);
|
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
context.startActivity(i);
|
||||||
return false;
|
return false;
|
||||||
}else return true;
|
}else return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isPopupEnabled(Activity activity) {
|
public static boolean isPopupEnabled(Context context) {
|
||||||
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
|
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
|
||||||
PermissionHelper.checkSystemAlertWindowPermission(activity);
|
PermissionHelper.checkSystemAlertWindowPermission(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void showPopupEnablementToast(Context context) {
|
public static void showPopupEnablementToast(Context context) {
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
package org.schabi.newpipe.util;
|
package org.schabi.newpipe.util;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.annotation.AttrRes;
|
||||||
|
import android.support.annotation.StyleRes;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
|
||||||
|
@ -13,17 +16,7 @@ public class ThemeHelper {
|
||||||
* @param context context that the theme will be applied
|
* @param context context that the theme will be applied
|
||||||
*/
|
*/
|
||||||
public static void setTheme(Context context) {
|
public static void setTheme(Context context) {
|
||||||
String lightTheme = context.getResources().getString(R.string.light_theme_key);
|
context.setTheme(getSelectedThemeStyle(context));
|
||||||
String darkTheme = context.getResources().getString(R.string.dark_theme_key);
|
|
||||||
String blackTheme = context.getResources().getString(R.string.black_theme_key);
|
|
||||||
|
|
||||||
String selectedTheme = getSelectedTheme(context);
|
|
||||||
|
|
||||||
if (selectedTheme.equals(lightTheme)) context.setTheme(R.style.LightTheme);
|
|
||||||
else if (selectedTheme.equals(blackTheme)) context.setTheme(R.style.BlackTheme);
|
|
||||||
else if (selectedTheme.equals(darkTheme)) context.setTheme(R.style.DarkTheme);
|
|
||||||
// Fallback
|
|
||||||
else context.setTheme(R.style.DarkTheme);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,9 +28,34 @@ public class ThemeHelper {
|
||||||
return getSelectedTheme(context).equals(context.getResources().getString(R.string.light_theme_key));
|
return getSelectedTheme(context).equals(context.getResources().getString(R.string.light_theme_key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@StyleRes
|
||||||
|
public static int getSelectedThemeStyle(Context context) {
|
||||||
|
String lightTheme = context.getResources().getString(R.string.light_theme_key);
|
||||||
|
String darkTheme = context.getResources().getString(R.string.dark_theme_key);
|
||||||
|
String blackTheme = context.getResources().getString(R.string.black_theme_key);
|
||||||
|
|
||||||
|
String selectedTheme = getSelectedTheme(context);
|
||||||
|
|
||||||
|
if (selectedTheme.equals(lightTheme)) return R.style.LightTheme;
|
||||||
|
else if (selectedTheme.equals(blackTheme)) return R.style.BlackTheme;
|
||||||
|
else if (selectedTheme.equals(darkTheme)) return R.style.DarkTheme;
|
||||||
|
// Fallback
|
||||||
|
else return R.style.DarkTheme;
|
||||||
|
}
|
||||||
|
|
||||||
public static String getSelectedTheme(Context context) {
|
public static String getSelectedTheme(Context context) {
|
||||||
String themeKey = context.getString(R.string.theme_key);
|
String themeKey = context.getString(R.string.theme_key);
|
||||||
String defaultTheme = context.getResources().getString(R.string.default_theme_value);
|
String defaultTheme = context.getResources().getString(R.string.default_theme_value);
|
||||||
return PreferenceManager.getDefaultSharedPreferences(context).getString(themeKey, defaultTheme);
|
return PreferenceManager.getDefaultSharedPreferences(context).getString(themeKey, defaultTheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a resource id from a resource styled according to the the context's theme.
|
||||||
|
*/
|
||||||
|
public static int resolveResourceIdFromAttr(Context context, @AttrRes int attr) {
|
||||||
|
TypedArray a = context.getTheme().obtainStyledAttributes(new int[]{attr});
|
||||||
|
int attributeResourceId = a.getResourceId(0, 0);
|
||||||
|
a.recycle();
|
||||||
|
return attributeResourceId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
BIN
app/src/main/res/drawable-hdpi/ic_play_arrow_black_24dp.png
Normal file
After Width: | Height: | Size: 194 B |
BIN
app/src/main/res/drawable-hdpi/ic_play_arrow_white_24dp.png
Normal file
After Width: | Height: | Size: 195 B |
BIN
app/src/main/res/drawable-mdpi/ic_play_arrow_black_24dp.png
Normal file
After Width: | Height: | Size: 150 B |
BIN
app/src/main/res/drawable-mdpi/ic_play_arrow_white_24dp.png
Normal file
After Width: | Height: | Size: 157 B |
BIN
app/src/main/res/drawable-xhdpi/ic_play_arrow_black_24dp.png
Normal file
After Width: | Height: | Size: 208 B |
BIN
app/src/main/res/drawable-xhdpi/ic_play_arrow_white_24dp.png
Normal file
After Width: | Height: | Size: 220 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_play_arrow_black_24dp.png
Normal file
After Width: | Height: | Size: 265 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png
Normal file
After Width: | Height: | Size: 283 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_play_arrow_black_24dp.png
Normal file
After Width: | Height: | Size: 320 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_play_arrow_white_24dp.png
Normal file
After Width: | Height: | Size: 343 B |
7
app/src/main/res/drawable/dark_checked_selector.xml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:enterFadeDuration="200" android:exitFadeDuration="200">
|
||||||
|
<item android:drawable="@color/dark_ripple_color" android:state_checked="true" android:state_pressed="true" />
|
||||||
|
<item android:drawable="@color/dark_ripple_color" android:state_pressed="true" />
|
||||||
|
<item android:drawable="@color/dark_selected_color" android:state_checked="true" />
|
||||||
|
<item android:drawable="@android:color/transparent" />
|
||||||
|
</selector>
|
7
app/src/main/res/drawable/light_checked_selector.xml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:enterFadeDuration="200" android:exitFadeDuration="200">
|
||||||
|
<item android:drawable="@color/light_ripple_color" android:state_checked="true" android:state_pressed="true" />
|
||||||
|
<item android:drawable="@color/light_ripple_color" android:state_pressed="true" />
|
||||||
|
<item android:drawable="@color/light_selected_color" android:state_checked="true" />
|
||||||
|
<item android:drawable="@android:color/transparent" />
|
||||||
|
</selector>
|
19
app/src/main/res/layout/list_radio_icon_item.xml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RadioButton xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:button="@null"
|
||||||
|
android:drawablePadding="24dp"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:minHeight="?attr/listPreferredItemHeightSmall"
|
||||||
|
android:paddingEnd="?attr/listPreferredItemPaddingRight"
|
||||||
|
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
|
||||||
|
android:paddingRight="?attr/listPreferredItemPaddingRight"
|
||||||
|
android:paddingStart="?attr/listPreferredItemPaddingLeft"
|
||||||
|
android:background="?attr/checked_selector_drawable"
|
||||||
|
android:textColor="?attr/textColorAlertDialogListItem"
|
||||||
|
tools:drawableLeft="?attr/play"
|
||||||
|
tools:text="Lorem ipsum dolor sit amet" />
|
6
app/src/main/res/layout/preferred_player_dialog_view.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RadioGroup xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@android:id/list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="?attr/listPreferredItemPaddingLeft" />
|
|
@ -22,10 +22,12 @@
|
||||||
<attr name="selected" format="reference"/>
|
<attr name="selected" format="reference"/>
|
||||||
<attr name="search_add" format="reference"/>
|
<attr name="search_add" format="reference"/>
|
||||||
<attr name="options" format="reference"/>
|
<attr name="options" format="reference"/>
|
||||||
|
<attr name="play" format="reference"/>
|
||||||
|
|
||||||
<!-- Can't refer to colors directly into drawable's xml-->
|
<!-- Can't refer to colors directly into drawable's xml-->
|
||||||
<attr name="toolbar_shadow_drawable" format="reference"/>
|
<attr name="toolbar_shadow_drawable" format="reference"/>
|
||||||
<attr name="selector_drawable" format="reference"/>
|
<attr name="selector_drawable" format="reference"/>
|
||||||
|
<attr name="checked_selector_drawable" format="reference"/>
|
||||||
|
|
||||||
<attr name="separator_color" format="color"/>
|
<attr name="separator_color" format="color"/>
|
||||||
<attr name="queue_background_color" format="color"/>
|
<attr name="queue_background_color" format="color"/>
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
<color name="light_youtube_accent_color">#000000</color>
|
<color name="light_youtube_accent_color">#000000</color>
|
||||||
<color name="light_separator_color">#32000000</color>
|
<color name="light_separator_color">#32000000</color>
|
||||||
<color name="light_ripple_color">#48868686</color>
|
<color name="light_ripple_color">#48868686</color>
|
||||||
|
<color name="light_selected_color">#2a868686</color>
|
||||||
<color name="light_contrast_background_color">#1fa6a6a6</color>
|
<color name="light_contrast_background_color">#1fa6a6a6</color>
|
||||||
<color name="light_shadow_start_color">#5a000000</color>
|
<color name="light_shadow_start_color">#5a000000</color>
|
||||||
<color name="light_license_background_color">#ffffff</color>
|
<color name="light_license_background_color">#ffffff</color>
|
||||||
|
@ -21,6 +22,7 @@
|
||||||
<color name="dark_youtube_accent_color">#FFFFFF</color>
|
<color name="dark_youtube_accent_color">#FFFFFF</color>
|
||||||
<color name="dark_separator_color">#0affffff</color>
|
<color name="dark_separator_color">#0affffff</color>
|
||||||
<color name="dark_ripple_color">#48ffffff</color>
|
<color name="dark_ripple_color">#48ffffff</color>
|
||||||
|
<color name="dark_selected_color">#2affffff</color>
|
||||||
<color name="dark_contrast_background_color">#1f717171</color>
|
<color name="dark_contrast_background_color">#1f717171</color>
|
||||||
<color name="dark_shadow_start_color">#82000000</color>
|
<color name="dark_shadow_start_color">#82000000</color>
|
||||||
<color name="dark_license_background_color">#424242</color>
|
<color name="dark_license_background_color">#424242</color>
|
||||||
|
|
|
@ -149,6 +149,29 @@
|
||||||
|
|
||||||
<string name="default_file_charset_value" translatable="false">@string/charset_most_special_characters_value</string>
|
<string name="default_file_charset_value" translatable="false">@string/charset_most_special_characters_value</string>
|
||||||
|
|
||||||
|
<!-- Preferred player -->
|
||||||
|
<string name="preferred_player_key" translatable="false">preferred_player_key</string>
|
||||||
|
<string name="preferred_player_default" translatable="false">@string/always_ask_player_key</string>
|
||||||
|
<string name="preferred_player_last_selected_key" translatable="false">preferred_player_last_selected</string>
|
||||||
|
|
||||||
|
<string name="video_player_key" translatable="false">video_player</string>
|
||||||
|
<string name="background_player_key" translatable="false">background_player</string>
|
||||||
|
<string name="popup_player_key" translatable="false">popup_player</string>
|
||||||
|
<string name="always_ask_player_key" translatable="false">always_ask_player</string>
|
||||||
|
|
||||||
|
<string-array name="preferred_player_description_list" translatable="false">
|
||||||
|
<item>@string/video_player</item>
|
||||||
|
<item>@string/background_player</item>
|
||||||
|
<item>@string/popup_player</item>
|
||||||
|
<item>@string/always_ask_player</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="preferred_player_values_list" translatable="false">
|
||||||
|
<item>@string/video_player_key</item>
|
||||||
|
<item>@string/background_player_key</item>
|
||||||
|
<item>@string/popup_player_key</item>
|
||||||
|
<item>@string/always_ask_player_key</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
<!-- alternatively, load these from some local android data store -->
|
<!-- alternatively, load these from some local android data store -->
|
||||||
<string-array name="language_codes" translatable="false">
|
<string-array name="language_codes" translatable="false">
|
||||||
<item>af</item>
|
<item>af</item>
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
<string name="view_count_text">%1$s views</string>
|
<string name="view_count_text">%1$s views</string>
|
||||||
<string name="upload_date_text">Published on %1$s</string>
|
<string name="upload_date_text">Published on %1$s</string>
|
||||||
<string name="no_player_found">No stream player found. Do you want to install VLC?</string>
|
<string name="no_player_found">No stream player found. Do you want to install VLC?</string>
|
||||||
|
<string name="no_player_found_toast">No stream player found (you can install VLC to play it)</string>
|
||||||
<string name="install">Install</string>
|
<string name="install">Install</string>
|
||||||
<string name="cancel">Cancel</string>
|
<string name="cancel">Cancel</string>
|
||||||
<string name="fdroid_vlc_url" translatable="false">https://f-droid.org/repository/browse/?fdfilter=vlc&fdid=org.videolan.vlc</string>
|
<string name="fdroid_vlc_url" translatable="false">https://f-droid.org/repository/browse/?fdfilter=vlc&fdid=org.videolan.vlc</string>
|
||||||
|
@ -119,6 +120,8 @@
|
||||||
<string name="best_resolution">Best resolution</string>
|
<string name="best_resolution">Best resolution</string>
|
||||||
<string name="undo">Undo</string>
|
<string name="undo">Undo</string>
|
||||||
<string name="play_all">Play All</string>
|
<string name="play_all">Play All</string>
|
||||||
|
<string name="always">Always</string>
|
||||||
|
<string name="just_once">Just Once</string>
|
||||||
|
|
||||||
<string name="notification_channel_id" translatable="false">newpipe</string>
|
<string name="notification_channel_id" translatable="false">newpipe</string>
|
||||||
<string name="notification_channel_name">NewPipe Notification</string>
|
<string name="notification_channel_name">NewPipe Notification</string>
|
||||||
|
@ -148,6 +151,10 @@
|
||||||
<string name="player_stream_failure">Failed to play this stream</string>
|
<string name="player_stream_failure">Failed to play this stream</string>
|
||||||
<string name="player_unrecoverable_failure">Unrecoverable player error occurred</string>
|
<string name="player_unrecoverable_failure">Unrecoverable player error occurred</string>
|
||||||
<string name="player_recoverable_failure">Recovering from player error</string>
|
<string name="player_recoverable_failure">Recovering from player error</string>
|
||||||
|
<string name="external_player_unsupported_link_type">External players don\'t support these types of links</string>
|
||||||
|
<string name="invalid_url_toast">Invalid URL</string>
|
||||||
|
<string name="video_streams_empty">No video streams found</string>
|
||||||
|
<string name="audio_streams_empty">No audio streams found</string>
|
||||||
|
|
||||||
<!-- error activity -->
|
<!-- error activity -->
|
||||||
<string name="sorry_string">Sorry, that should not have happened.</string>
|
<string name="sorry_string">Sorry, that should not have happened.</string>
|
||||||
|
@ -336,4 +343,17 @@
|
||||||
<string name="drawer_close">Close Drawer</string>
|
<string name="drawer_close">Close Drawer</string>
|
||||||
<string name="youtube" translatable="false">YouTube</string>
|
<string name="youtube" translatable="false">YouTube</string>
|
||||||
<string name="soundcloud" translatable="false">SoundCloud</string>
|
<string name="soundcloud" translatable="false">SoundCloud</string>
|
||||||
|
|
||||||
|
<!-- Preferred player -->
|
||||||
|
<string name="preferred_player_share_menu_title">@string/preferred_player_settings_title</string>
|
||||||
|
<string name="preferred_player_share_menu_dialog_title">Open with preferred player</string>
|
||||||
|
<string name="preferred_player_settings_title">Preferred player</string>
|
||||||
|
|
||||||
|
<string name="video_player">Video player</string>
|
||||||
|
<string name="background_player">Background player</string>
|
||||||
|
<string name="popup_player">Popup player</string>
|
||||||
|
<string name="always_ask_player">Always ask</string>
|
||||||
|
|
||||||
|
<string name="preferred_player_fetcher_notification_title">Getting info…</string>
|
||||||
|
<string name="preferred_player_fetcher_notification_message">"The requested content is loading"</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -29,9 +29,11 @@
|
||||||
<item name="selected">@drawable/ic_fiber_manual_record_black_24dp</item>
|
<item name="selected">@drawable/ic_fiber_manual_record_black_24dp</item>
|
||||||
<item name="search_add">@drawable/ic_arrow_top_left_black_24dp</item>
|
<item name="search_add">@drawable/ic_arrow_top_left_black_24dp</item>
|
||||||
<item name="options">@drawable/ic_more_vert_black_24dp</item>
|
<item name="options">@drawable/ic_more_vert_black_24dp</item>
|
||||||
|
<item name="play">@drawable/ic_play_arrow_black_24dp</item>
|
||||||
|
|
||||||
<item name="separator_color">@color/light_separator_color</item>
|
<item name="separator_color">@color/light_separator_color</item>
|
||||||
<item name="contrast_background_color">@color/light_contrast_background_color</item>
|
<item name="contrast_background_color">@color/light_contrast_background_color</item>
|
||||||
|
<item name="checked_selector_drawable">@drawable/light_checked_selector</item>
|
||||||
<item name="queue_background_color">@color/light_queue_background_color</item>
|
<item name="queue_background_color">@color/light_queue_background_color</item>
|
||||||
<item name="toolbar_shadow_drawable">@drawable/toolbar_shadow_light</item>
|
<item name="toolbar_shadow_drawable">@drawable/toolbar_shadow_light</item>
|
||||||
<item name="selector_drawable">@drawable/light_selector</item>
|
<item name="selector_drawable">@drawable/light_selector</item>
|
||||||
|
@ -69,9 +71,11 @@
|
||||||
<item name="selected">@drawable/ic_fiber_manual_record_white_24dp</item>
|
<item name="selected">@drawable/ic_fiber_manual_record_white_24dp</item>
|
||||||
<item name="search_add">@drawable/ic_arrow_top_left_white_24dp</item>
|
<item name="search_add">@drawable/ic_arrow_top_left_white_24dp</item>
|
||||||
<item name="options">@drawable/ic_more_vert_white_24dp</item>
|
<item name="options">@drawable/ic_more_vert_white_24dp</item>
|
||||||
|
<item name="play">@drawable/ic_play_arrow_white_24dp</item>
|
||||||
|
|
||||||
<item name="separator_color">@color/dark_separator_color</item>
|
<item name="separator_color">@color/dark_separator_color</item>
|
||||||
<item name="contrast_background_color">@color/dark_contrast_background_color</item>
|
<item name="contrast_background_color">@color/dark_contrast_background_color</item>
|
||||||
|
<item name="checked_selector_drawable">@drawable/dark_checked_selector</item>
|
||||||
<item name="queue_background_color">@color/dark_queue_background_color</item>
|
<item name="queue_background_color">@color/dark_queue_background_color</item>
|
||||||
<item name="toolbar_shadow_drawable">@drawable/toolbar_shadow_dark</item>
|
<item name="toolbar_shadow_drawable">@drawable/toolbar_shadow_dark</item>
|
||||||
<item name="selector_drawable">@drawable/dark_selector</item>
|
<item name="selector_drawable">@drawable/dark_selector</item>
|
||||||
|
@ -164,12 +168,27 @@
|
||||||
<item name="android:background">@color/dark_youtube_primary_color</item>
|
<item name="android:background">@color/dark_youtube_primary_color</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="PopupPermissionsTheme" parent="Theme.AppCompat.NoActionBar">
|
<style name="RouterActivityThemeLight" parent="LightTheme">
|
||||||
|
<item name="colorPrimary">@android:color/transparent</item>
|
||||||
|
<item name="colorPrimaryDark">@android:color/transparent</item>
|
||||||
|
<item name="colorAccent">@android:color/transparent</item>
|
||||||
|
|
||||||
<item name="android:windowNoTitle">true</item>
|
<item name="android:windowNoTitle">true</item>
|
||||||
<item name="android:windowBackground">@android:color/transparent</item>
|
<item name="android:windowBackground">@android:color/transparent</item>
|
||||||
<item name="android:colorBackgroundCacheHint">@null</item>
|
<item name="android:colorBackgroundCacheHint">@null</item>
|
||||||
<item name="android:windowIsTranslucent">true</item>
|
<item name="android:windowIsTranslucent">true</item>
|
||||||
<item name="android:windowAnimationStyle">@android:style/Animation</item>
|
<item name="android:windowAnimationStyle">@null</item>
|
||||||
<item name="android:windowNoDisplay">true</item>
|
</style>
|
||||||
|
|
||||||
|
<style name="RouterActivityThemeDark" parent="DarkTheme">
|
||||||
|
<item name="colorPrimary">@android:color/transparent</item>
|
||||||
|
<item name="colorPrimaryDark">@android:color/transparent</item>
|
||||||
|
<item name="colorAccent">@android:color/transparent</item>
|
||||||
|
|
||||||
|
<item name="android:windowNoTitle">true</item>
|
||||||
|
<item name="android:windowBackground">@android:color/transparent</item>
|
||||||
|
<item name="android:colorBackgroundCacheHint">@null</item>
|
||||||
|
<item name="android:windowIsTranslucent">true</item>
|
||||||
|
<item name="android:windowAnimationStyle">@null</item>
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen
|
<PreferenceScreen
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:title="@string/settings_category_video_audio_title">
|
android:title="@string/settings_category_video_audio_title">
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
|
@ -81,6 +80,14 @@
|
||||||
android:summary="@string/autoplay_by_calling_app_summary"
|
android:summary="@string/autoplay_by_calling_app_summary"
|
||||||
android:title="@string/autoplay_by_calling_app_title"/>
|
android:title="@string/autoplay_by_calling_app_title"/>
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:defaultValue="@string/preferred_player_default"
|
||||||
|
android:entries="@array/preferred_player_description_list"
|
||||||
|
android:entryValues="@array/preferred_player_values_list"
|
||||||
|
android:key="@string/preferred_player_key"
|
||||||
|
android:summary="%s"
|
||||||
|
android:title="@string/preferred_player_settings_title"/>
|
||||||
|
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:key="@string/resume_on_audio_focus_gain_key"
|
android:key="@string/resume_on_audio_focus_gain_key"
|
||||||
|
|