merge RouterActivity and RouterVideoActivity
change share title fixed compatiblity issue rename info_screen to show_info
|
@ -55,7 +55,7 @@ dependencies {
|
||||||
exclude module: 'support-annotations'
|
exclude module: 'support-annotations'
|
||||||
}
|
}
|
||||||
|
|
||||||
implementation 'com.github.TeamNewPipe:NewPipeExtractor:4fb49d54b5'
|
implementation 'com.github.TeamNewPipe:NewPipeExtractor:e51bc58a856dcf3'
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
testImplementation 'org.mockito:mockito-core:1.10.19'
|
testImplementation 'org.mockito:mockito-core:1.10.19'
|
||||||
|
|
|
@ -122,8 +122,12 @@
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".RouterActivity"
|
android:name=".RouterActivity"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:label="@string/preferred_player_share_menu_title"
|
||||||
android:taskAffinity=""
|
android:taskAffinity=""
|
||||||
android:theme="@style/RouterActivityThemeDark">
|
android:theme="@style/RouterActivityThemeDark">
|
||||||
|
|
||||||
|
<!-- Youtube filter -->
|
||||||
<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"/>
|
||||||
|
@ -169,6 +173,41 @@
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
|
<data android:scheme="vnd.youtube"/>
|
||||||
|
<data android:scheme="vnd.youtube.launch"/>
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<!-- Hooktube filter -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>
|
||||||
|
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
|
<data android:scheme="http"/>
|
||||||
|
<data android:scheme="https"/>
|
||||||
|
<data android:host="hooktube.com"/>
|
||||||
|
<data android:host="*.hooktube.com"/>
|
||||||
|
<!-- video prefix -->
|
||||||
|
<data android:pathPrefix="/v/"/>
|
||||||
|
<data android:pathPrefix="/embed/"/>
|
||||||
|
<data android:pathPrefix="/watch"/>
|
||||||
|
<!-- channel prefix -->
|
||||||
|
<data android:pathPrefix="/channel/"/>
|
||||||
|
<data android:pathPrefix="/user/"/>
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<!-- Soundcloud filter -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>
|
||||||
|
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
<data android:scheme="http"/>
|
<data android:scheme="http"/>
|
||||||
<data android:scheme="https"/>
|
<data android:scheme="https"/>
|
||||||
<data android:host="soundcloud.com"/>
|
<data android:host="soundcloud.com"/>
|
||||||
|
@ -176,17 +215,8 @@
|
||||||
<data android:host="www.soundcloud.com"/>
|
<data android:host="www.soundcloud.com"/>
|
||||||
<data android:pathPrefix="/"/>
|
<data android:pathPrefix="/"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
|
||||||
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>
|
|
||||||
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<!-- Share filter -->
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
|
||||||
|
|
||||||
<data android:scheme="vnd.youtube"/>
|
|
||||||
<data android:scheme="vnd.youtube.launch"/>
|
|
||||||
</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"/>
|
||||||
|
@ -195,68 +225,7 @@
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".RouterPlayerActivity$FetcherService"
|
android:name=".RouterActivity$FetcherService"
|
||||||
android:exported="false"/>
|
android:exported="false"/>
|
||||||
|
|
||||||
<activity
|
|
||||||
android:name=".RouterPlayerActivity"
|
|
||||||
android:excludeFromRecents="true"
|
|
||||||
android:label="@string/preferred_player_share_menu_title"
|
|
||||||
android:taskAffinity=""
|
|
||||||
android:theme="@style/RouterActivityThemeDark">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
|
||||||
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>
|
|
||||||
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
|
||||||
|
|
||||||
<data android:scheme="http"/>
|
|
||||||
<data android:scheme="https"/>
|
|
||||||
<data android:host="youtube.com"/>
|
|
||||||
<data android:host="m.youtube.com"/>
|
|
||||||
<data android:host="www.youtube.com"/>
|
|
||||||
<!-- video prefix -->
|
|
||||||
<data android:pathPrefix="/v/"/>
|
|
||||||
<data android:pathPrefix="/embed/"/>
|
|
||||||
<data android:pathPrefix="/watch"/>
|
|
||||||
<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>
|
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
|
||||||
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>
|
|
||||||
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
|
||||||
|
|
||||||
<data android:scheme="http"/>
|
|
||||||
<data android:scheme="https"/>
|
|
||||||
<data android:host="youtu.be"/>
|
|
||||||
<data android:pathPrefix="/"/>
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
|
||||||
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>
|
|
||||||
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
|
||||||
|
|
||||||
<data android:scheme="vnd.youtube"/>
|
|
||||||
<data android:scheme="vnd.youtube.launch"/>
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.SEND"/>
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
|
||||||
<data android:mimeType="text/plain"/>
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
|
@ -1,51 +1,80 @@
|
||||||
package org.schabi.newpipe;
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
|
import android.app.IntentService;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
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.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
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 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.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.report.UserAction;
|
||||||
import org.schabi.newpipe.util.ExtractorHelper;
|
import org.schabi.newpipe.util.ExtractorHelper;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
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 java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
import icepick.Icepick;
|
import icepick.Icepick;
|
||||||
import icepick.State;
|
import icepick.State;
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
|
import io.reactivex.Single;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
|
import io.reactivex.disposables.Disposable;
|
||||||
|
import io.reactivex.functions.Consumer;
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
|
||||||
/*
|
import static org.schabi.newpipe.util.ThemeHelper.resolveResourceIdFromAttr;
|
||||||
* Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org>
|
|
||||||
* RouterActivity.java is part of NewPipe.
|
|
||||||
*
|
|
||||||
* NewPipe is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* NewPipe is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This Acitivty is designed to route share/open intents to the specified service, and
|
* Get the url from the intent and open it in the chosen preferred player
|
||||||
* to the part of the service which can handle the url.
|
|
||||||
*/
|
*/
|
||||||
public class RouterActivity extends AppCompatActivity {
|
public class RouterActivity extends AppCompatActivity {
|
||||||
|
|
||||||
@State
|
@State
|
||||||
|
protected int currentServiceId = -1;
|
||||||
|
private StreamingService currentService;
|
||||||
|
@State
|
||||||
|
protected LinkType currentLinkType;
|
||||||
|
@State
|
||||||
|
protected int selectedRadioPosition = -1;
|
||||||
|
protected int selectedPreviously = -1;
|
||||||
|
|
||||||
protected String currentUrl;
|
protected String currentUrl;
|
||||||
protected CompositeDisposable disposables = new CompositeDisposable();
|
protected CompositeDisposable disposables = new CompositeDisposable();
|
||||||
|
|
||||||
|
@ -62,6 +91,10 @@ public class RouterActivity extends AppCompatActivity {
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTheme(ThemeHelper.isLightThemeSelected(this)
|
||||||
|
? R.style.RouterActivityThemeLight
|
||||||
|
: R.style.RouterActivityThemeDark);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -73,25 +106,43 @@ public class RouterActivity extends AppCompatActivity {
|
||||||
@Override
|
@Override
|
||||||
protected void onStart() {
|
protected void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
|
|
||||||
handleUrl(currentUrl);
|
handleUrl(currentUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void handleUrl(String url) {
|
@Override
|
||||||
disposables.add(Observable
|
protected void onDestroy() {
|
||||||
.fromCallable(() -> NavigationHelper.getIntentByLink(this, url))
|
super.onDestroy();
|
||||||
.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();
|
disposables.clear();
|
||||||
}, this::handleError)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void handleError(Throwable error) {
|
private 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleError(Throwable error) {
|
||||||
error.printStackTrace();
|
error.printStackTrace();
|
||||||
|
|
||||||
if (error instanceof ExtractionException) {
|
if (error instanceof ExtractionException) {
|
||||||
|
@ -103,11 +154,339 @@ public class RouterActivity extends AppCompatActivity {
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void onError() {
|
||||||
protected void onDestroy() {
|
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
||||||
super.onDestroy();
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
disposables.clear();
|
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) {
|
||||||
|
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.info_screen_key), getString(R.string.show_info),
|
||||||
|
resolveResourceIdFromAttr(themeWrapper, R.attr.info)),
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop and bypass FetcherService if InfoScreen was selected since
|
||||||
|
// StreamDetailFragment can fetch data itself
|
||||||
|
if(playerChoiceKey.equals(getString(R.string.info_screen_key))) {
|
||||||
|
disposables.add(Observable
|
||||||
|
.fromCallable(() -> NavigationHelper.getIntentByLink(this, currentUrl))
|
||||||
|
.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)
|
||||||
|
);
|
||||||
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -119,9 +498,9 @@ public class RouterActivity extends AppCompatActivity {
|
||||||
* brackets (\p{P}). See http://www.regular-expressions.info/unicode.html for
|
* brackets (\p{P}). See http://www.regular-expressions.info/unicode.html for
|
||||||
* more details.
|
* more details.
|
||||||
*/
|
*/
|
||||||
protected final static String REGEX_REMOVE_FROM_URL = "[\\p{Z}\\p{P}]";
|
private final static String REGEX_REMOVE_FROM_URL = "[\\p{Z}\\p{P}]";
|
||||||
|
|
||||||
protected String getUrl(Intent intent) {
|
private String getUrl(Intent intent) {
|
||||||
// first gather data and find service
|
// first gather data and find service
|
||||||
String videoUrl = null;
|
String videoUrl = null;
|
||||||
if (intent.getData() != null) {
|
if (intent.getData() != null) {
|
||||||
|
@ -137,7 +516,7 @@ public class RouterActivity extends AppCompatActivity {
|
||||||
return videoUrl;
|
return videoUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String removeHeadingGibberish(final String input) {
|
private String removeHeadingGibberish(final String input) {
|
||||||
int start = 0;
|
int start = 0;
|
||||||
for (int i = input.indexOf("://") - 1; i >= 0; i--) {
|
for (int i = input.indexOf("://") - 1; i >= 0; i--) {
|
||||||
if (!input.substring(i, i + 1).matches("\\p{L}")) {
|
if (!input.substring(i, i + 1).matches("\\p{L}")) {
|
||||||
|
@ -148,7 +527,7 @@ public class RouterActivity extends AppCompatActivity {
|
||||||
return input.substring(start, input.length());
|
return input.substring(start, input.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String trim(final String input) {
|
private String trim(final String input) {
|
||||||
if (input == null || input.length() < 1) {
|
if (input == null || input.length() < 1) {
|
||||||
return input;
|
return input;
|
||||||
} else {
|
} else {
|
||||||
|
@ -188,5 +567,4 @@ public class RouterActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
return result.toArray(new String[result.size()]);
|
return result.toArray(new String[result.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,413 +0,0 @@
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -47,7 +47,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
||||||
// Constants
|
// Constants
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
private static final int FALLBACK_SERVICE_ID = ServiceList.YouTube.getId();
|
private static final int FALLBACK_SERVICE_ID = ServiceList.YouTube.getServiceId();
|
||||||
private static final String FALLBACK_CHANNEL_URL = "https://www.youtube.com/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ";
|
private static final String FALLBACK_CHANNEL_URL = "https://www.youtube.com/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ";
|
||||||
private static final String FALLBACK_CHANNEL_NAME = "Music";
|
private static final String FALLBACK_CHANNEL_NAME = "Music";
|
||||||
private static final String FALLBACK_KIOSK_ID = "Trending";
|
private static final String FALLBACK_KIOSK_ID = "Trending";
|
||||||
|
|
|
@ -73,7 +73,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
|
||||||
.putString(getString(R.string.main_page_selectd_kiosk_id), kioskId).apply();
|
.putString(getString(R.string.main_page_selectd_kiosk_id), kioskId).apply();
|
||||||
String serviceName = "";
|
String serviceName = "";
|
||||||
try {
|
try {
|
||||||
serviceName = NewPipe.getService(service_id).getServiceInfo().name;
|
serviceName = NewPipe.getService(service_id).getServiceInfo().getName();
|
||||||
} catch (ExtractionException e) {
|
} catch (ExtractionException e) {
|
||||||
onError(e);
|
onError(e);
|
||||||
}
|
}
|
||||||
|
@ -245,7 +245,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
|
||||||
|
|
||||||
String summary =
|
String summary =
|
||||||
String.format(getString(R.string.service_kiosk_string),
|
String.format(getString(R.string.service_kiosk_string),
|
||||||
service.getServiceInfo().name,
|
service.getServiceInfo().getName(),
|
||||||
kioskName);
|
kioskName);
|
||||||
|
|
||||||
mainPagePref.setSummary(summary);
|
mainPagePref.setSummary(summary);
|
||||||
|
|
|
@ -122,11 +122,11 @@ public class SelectKioskFragment extends DialogFragment {
|
||||||
|
|
||||||
for(StreamingService service : NewPipe.getServices()) {
|
for(StreamingService service : NewPipe.getServices()) {
|
||||||
//TODO: Multi-service support
|
//TODO: Multi-service support
|
||||||
if (service.getServiceId() != ServiceList.YouTube.getId()) continue;
|
if (service.getServiceId() != ServiceList.YouTube.getServiceId()) continue;
|
||||||
|
|
||||||
for(String kioskId : service.getKioskList().getAvailableKiosks()) {
|
for(String kioskId : service.getKioskList().getAvailableKiosks()) {
|
||||||
String name = String.format(getString(R.string.service_kiosk_string),
|
String name = String.format(getString(R.string.service_kiosk_string),
|
||||||
service.getServiceInfo().name,
|
service.getServiceInfo().getName(),
|
||||||
KioskTranslator.getTranslatedKioskName(kioskId, getContext()));
|
KioskTranslator.getTranslatedKioskName(kioskId, getContext()));
|
||||||
kioskList.add(new Entry(
|
kioskList.add(new Entry(
|
||||||
ServiceHelper.getIcon(service.getServiceId()),
|
ServiceHelper.getIcon(service.getServiceId()),
|
||||||
|
|
|
@ -12,7 +12,7 @@ import org.schabi.newpipe.extractor.StreamingService;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
|
|
||||||
public class ServiceHelper {
|
public class ServiceHelper {
|
||||||
private static final StreamingService DEFAULT_FALLBACK_SERVICE = ServiceList.YouTube.getService();
|
private static final StreamingService DEFAULT_FALLBACK_SERVICE = ServiceList.YouTube;
|
||||||
|
|
||||||
@DrawableRes
|
@DrawableRes
|
||||||
public static int getIcon(int serviceId) {
|
public static int getIcon(int serviceId) {
|
||||||
|
@ -45,9 +45,9 @@ public class ServiceHelper {
|
||||||
public static void setSelectedServiceId(Context context, int serviceId) {
|
public static void setSelectedServiceId(Context context, int serviceId) {
|
||||||
String serviceName;
|
String serviceName;
|
||||||
try {
|
try {
|
||||||
serviceName = NewPipe.getService(serviceId).getServiceInfo().name;
|
serviceName = NewPipe.getService(serviceId).getServiceInfo().getName();
|
||||||
} catch (ExtractionException e) {
|
} catch (ExtractionException e) {
|
||||||
serviceName = DEFAULT_FALLBACK_SERVICE.getServiceInfo().name;
|
serviceName = DEFAULT_FALLBACK_SERVICE.getServiceInfo().getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedServicePreferences(context, serviceName);
|
setSelectedServicePreferences(context, serviceName);
|
||||||
|
@ -55,7 +55,7 @@ public class ServiceHelper {
|
||||||
|
|
||||||
public static void setSelectedServiceId(Context context, String serviceName) {
|
public static void setSelectedServiceId(Context context, String serviceName) {
|
||||||
int serviceId = NewPipe.getIdOfService(serviceName);
|
int serviceId = NewPipe.getIdOfService(serviceName);
|
||||||
if (serviceId == -1) serviceName = DEFAULT_FALLBACK_SERVICE.getServiceInfo().name;
|
if (serviceId == -1) serviceName = DEFAULT_FALLBACK_SERVICE.getServiceInfo().getName();
|
||||||
|
|
||||||
setSelectedServicePreferences(context, serviceName);
|
setSelectedServicePreferences(context, serviceName);
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@ public class ThemeHelper {
|
||||||
else if (selectedTheme.equals(blackTheme)) themeName = "BlackTheme";
|
else if (selectedTheme.equals(blackTheme)) themeName = "BlackTheme";
|
||||||
else if (selectedTheme.equals(darkTheme)) themeName = "DarkTheme";
|
else if (selectedTheme.equals(darkTheme)) themeName = "DarkTheme";
|
||||||
|
|
||||||
themeName += "." + service.getServiceInfo().name;
|
themeName += "." + service.getServiceInfo().getName();
|
||||||
int resourceId = context.getResources().getIdentifier(themeName, "style", context.getPackageName());
|
int resourceId = context.getResources().getIdentifier(themeName, "style", context.getPackageName());
|
||||||
|
|
||||||
if (resourceId > 0) {
|
if (resourceId > 0) {
|
||||||
|
|
BIN
app/src/main/res/drawable-hdpi/ic_info_outline_black_24dp.png
Normal file
After Width: | Height: | Size: 487 B |
BIN
app/src/main/res/drawable-hdpi/ic_info_outline_white_24dp.png
Normal file
After Width: | Height: | Size: 485 B |
BIN
app/src/main/res/drawable-mdpi/ic_info_outline_black_24dp.png
Normal file
After Width: | Height: | Size: 323 B |
BIN
app/src/main/res/drawable-mdpi/ic_info_outline_white_24dp.png
Normal file
After Width: | Height: | Size: 320 B |
BIN
app/src/main/res/drawable-xhdpi/ic_info_outline_black_24dp.png
Normal file
After Width: | Height: | Size: 640 B |
BIN
app/src/main/res/drawable-xhdpi/ic_info_outline_white_24dp.png
Normal file
After Width: | Height: | Size: 655 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_info_outline_black_24dp.png
Normal file
After Width: | Height: | Size: 940 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_info_outline_white_24dp.png
Normal file
After Width: | Height: | Size: 953 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_info_outline_black_24dp.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_info_outline_white_24dp.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
|
@ -3,6 +3,7 @@
|
||||||
<!--TODO: preffix these with "ic_"-->
|
<!--TODO: preffix these with "ic_"-->
|
||||||
<attr name="thumbs_up" format="reference"/>
|
<attr name="thumbs_up" format="reference"/>
|
||||||
<attr name="thumbs_down" format="reference"/>
|
<attr name="thumbs_down" format="reference"/>
|
||||||
|
<attr name="info" format="reference"/>
|
||||||
<attr name="audio" format="reference"/>
|
<attr name="audio" format="reference"/>
|
||||||
<attr name="download" format="reference"/>
|
<attr name="download" format="reference"/>
|
||||||
<attr name="share" format="reference"/>
|
<attr name="share" format="reference"/>
|
||||||
|
|
|
@ -155,6 +155,7 @@
|
||||||
<string name="preferred_player_default" translatable="false">@string/always_ask_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="preferred_player_last_selected_key" translatable="false">preferred_player_last_selected</string>
|
||||||
|
|
||||||
|
<string name="info_screen_key" translatable="false">info_screen</string>
|
||||||
<string name="video_player_key" translatable="false">video_player</string>
|
<string name="video_player_key" translatable="false">video_player</string>
|
||||||
<string name="background_player_key" translatable="false">background_player</string>
|
<string name="background_player_key" translatable="false">background_player</string>
|
||||||
<string name="popup_player_key" translatable="false">popup_player</string>
|
<string name="popup_player_key" translatable="false">popup_player</string>
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
<string name="channel_unsubscribed">Channel unsubscribed</string>
|
<string name="channel_unsubscribed">Channel unsubscribed</string>
|
||||||
<string name="subscription_change_failed">Unable to change subscription</string>
|
<string name="subscription_change_failed">Unable to change subscription</string>
|
||||||
<string name="subscription_update_failed">Unable to update subscription</string>
|
<string name="subscription_update_failed">Unable to update subscription</string>
|
||||||
|
<string name="show_info">Show info</string>
|
||||||
|
|
||||||
<string name="tab_main">Main</string>
|
<string name="tab_main">Main</string>
|
||||||
<string name="tab_subscriptions">Subscriptions</string>
|
<string name="tab_subscriptions">Subscriptions</string>
|
||||||
|
@ -368,7 +369,7 @@
|
||||||
|
|
||||||
|
|
||||||
<!-- Preferred player -->
|
<!-- Preferred player -->
|
||||||
<string name="preferred_player_share_menu_title" translatable="false">@string/preferred_player_settings_title</string>
|
<string name="preferred_player_share_menu_title" translatable="false">NewPipe</string>
|
||||||
<string name="preferred_player_share_menu_dialog_title">Open with preferred player</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="preferred_player_settings_title">Preferred player</string>
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
<item name="thumbs_up">@drawable/ic_thumb_up_black_24dp</item>
|
<item name="thumbs_up">@drawable/ic_thumb_up_black_24dp</item>
|
||||||
<item name="thumbs_down">@drawable/ic_thumb_down_black_24dp</item>
|
<item name="thumbs_down">@drawable/ic_thumb_down_black_24dp</item>
|
||||||
|
<item name="info">@drawable/ic_info_outline_black_24dp</item>
|
||||||
<item name="audio">@drawable/ic_headset_black_24dp</item>
|
<item name="audio">@drawable/ic_headset_black_24dp</item>
|
||||||
<item name="clear_history">@drawable/ic_delete_sweep_white_24dp</item>
|
<item name="clear_history">@drawable/ic_delete_sweep_white_24dp</item>
|
||||||
<item name="download">@drawable/ic_file_download_black_24dp</item>
|
<item name="download">@drawable/ic_file_download_black_24dp</item>
|
||||||
|
@ -69,6 +70,7 @@
|
||||||
<item name="thumbs_up">@drawable/ic_thumb_up_white_24dp</item>
|
<item name="thumbs_up">@drawable/ic_thumb_up_white_24dp</item>
|
||||||
<item name="thumbs_down">@drawable/ic_thumb_down_white_24dp</item>
|
<item name="thumbs_down">@drawable/ic_thumb_down_white_24dp</item>
|
||||||
<item name="audio">@drawable/ic_headset_white_24dp</item>
|
<item name="audio">@drawable/ic_headset_white_24dp</item>
|
||||||
|
<item name="info">@drawable/ic_info_outline_white_24dp</item>
|
||||||
<item name="clear_history">@drawable/ic_delete_sweep_black_24dp</item>
|
<item name="clear_history">@drawable/ic_delete_sweep_black_24dp</item>
|
||||||
<item name="download">@drawable/ic_file_download_white_24dp</item>
|
<item name="download">@drawable/ic_file_download_white_24dp</item>
|
||||||
<item name="share">@drawable/ic_share_white_24dp</item>
|
<item name="share">@drawable/ic_share_white_24dp</item>
|
||||||
|
|
|
@ -74,12 +74,6 @@
|
||||||
android:layout="@layout/settings_category_header_layout"
|
android:layout="@layout/settings_category_header_layout"
|
||||||
android:title="@string/settings_category_player_behavior_title">
|
android:title="@string/settings_category_player_behavior_title">
|
||||||
|
|
||||||
<SwitchPreference
|
|
||||||
android:defaultValue="false"
|
|
||||||
android:key="@string/autoplay_through_intent_key"
|
|
||||||
android:summary="@string/autoplay_by_calling_app_summary"
|
|
||||||
android:title="@string/autoplay_by_calling_app_title"/>
|
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:defaultValue="@string/preferred_player_default"
|
android:defaultValue="@string/preferred_player_default"
|
||||||
android:entries="@array/preferred_player_description_list"
|
android:entries="@array/preferred_player_description_list"
|
||||||
|
|