mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-09-24 08:40:51 +02:00
Compare commits
48 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9290ed490f | ||
![]() |
1bda812c75 | ||
![]() |
ab82406c98 | ||
![]() |
be763349df | ||
![]() |
91764ad601 | ||
![]() |
1c4031aa38 | ||
![]() |
c3bc648dd4 | ||
![]() |
c97d794272 | ||
![]() |
8e7d2e91e9 | ||
![]() |
cf0fbbbd3d | ||
![]() |
7d3ede7946 | ||
![]() |
1aa308eb5d | ||
![]() |
4bfd30e34a | ||
![]() |
0f46c90688 | ||
![]() |
aa2173fc4a | ||
![]() |
932cb1d19c | ||
![]() |
18b038d8e4 | ||
![]() |
2ac71c75c0 | ||
![]() |
4067ba5232 | ||
![]() |
ab47dd5a5b | ||
![]() |
3130910307 | ||
![]() |
22113439a4 | ||
![]() |
2d25a9ad7a | ||
![]() |
8f0a2cc2f0 | ||
![]() |
4ca39258c9 | ||
![]() |
d0c0238b8b | ||
![]() |
f9f08e5169 | ||
![]() |
cfc51b2401 | ||
![]() |
54c2704cb5 | ||
![]() |
9b51c946fe | ||
![]() |
06f38cbcb4 | ||
![]() |
f368d6b257 | ||
![]() |
f4301da14d | ||
![]() |
f34c09f165 | ||
![]() |
6d1db56512 | ||
![]() |
b70c07d004 | ||
![]() |
f9f48a5eb6 | ||
![]() |
14e4e73444 | ||
![]() |
f2ce4d2daf | ||
![]() |
468ebdda87 | ||
![]() |
a1f0fb3b14 | ||
![]() |
3c9f4de234 | ||
![]() |
8ae411619f | ||
![]() |
61e5c9121a | ||
![]() |
8a3cf0d5dc | ||
![]() |
ecb5df65ac | ||
![]() |
5f1e98a0d3 | ||
![]() |
3b9a477499 |
@@ -46,6 +46,7 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
|
|||||||
* Search channels
|
* Search channels
|
||||||
* Watch videos from a channel
|
* Watch videos from a channel
|
||||||
* Orbot/Tor support (not yet directly)
|
* Orbot/Tor support (not yet directly)
|
||||||
|
* 1080p/2k/4k support
|
||||||
|
|
||||||
### Coming Features
|
### Coming Features
|
||||||
|
|
||||||
@@ -56,7 +57,6 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
|
|||||||
* Search/Watch Playlists
|
* Search/Watch Playlists
|
||||||
* Queeing videos
|
* Queeing videos
|
||||||
* Subtitles support
|
* Subtitles support
|
||||||
* 1080p support
|
|
||||||
* livestream support
|
* livestream support
|
||||||
* ... and many more
|
* ... and many more
|
||||||
|
|
||||||
|
@@ -2,14 +2,14 @@ apply plugin: 'com.android.application'
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 25
|
compileSdkVersion 25
|
||||||
buildToolsVersion '25.0.0'
|
buildToolsVersion '25.0.2'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "org.schabi.newpipe"
|
applicationId "org.schabi.newpipe"
|
||||||
minSdkVersion 15
|
minSdkVersion 15
|
||||||
targetSdkVersion 25
|
targetSdkVersion 25
|
||||||
versionCode 29
|
versionCode 30
|
||||||
versionName "0.9.2"
|
versionName "0.9.3"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
@@ -35,17 +35,17 @@ dependencies {
|
|||||||
testCompile 'org.mockito:mockito-core:1.10.19'
|
testCompile 'org.mockito:mockito-core:1.10.19'
|
||||||
testCompile 'org.json:json:20160810'
|
testCompile 'org.json:json:20160810'
|
||||||
|
|
||||||
compile 'com.android.support:appcompat-v7:25.1.0'
|
compile 'com.android.support:appcompat-v7:25.3.1'
|
||||||
compile 'com.android.support:support-v4:25.1.0'
|
compile 'com.android.support:support-v4:25.3.1'
|
||||||
compile 'com.android.support:design:25.1.0'
|
compile 'com.android.support:design:25.3.1'
|
||||||
compile 'com.android.support:recyclerview-v7:25.1.0'
|
compile 'com.android.support:recyclerview-v7:25.3.1'
|
||||||
compile 'org.jsoup:jsoup:1.8.3'
|
compile 'org.jsoup:jsoup:1.8.3'
|
||||||
compile 'org.mozilla:rhino:1.7.7'
|
compile 'org.mozilla:rhino:1.7.7'
|
||||||
compile 'info.guardianproject.netcipher:netcipher:1.2'
|
compile 'info.guardianproject.netcipher:netcipher:1.2'
|
||||||
compile 'de.hdodenhof:circleimageview:2.0.0'
|
compile 'de.hdodenhof:circleimageview:2.0.0'
|
||||||
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
|
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
|
||||||
compile 'com.github.nirhart:parallaxscroll:1.0'
|
compile 'com.github.nirhart:parallaxscroll:1.0'
|
||||||
compile 'com.google.code.gson:gson:2.4'
|
compile 'com.google.code.gson:gson:2.7'
|
||||||
compile 'com.nononsenseapps:filepicker:3.0.0'
|
compile 'com.nononsenseapps:filepicker:3.0.0'
|
||||||
compile 'ch.acra:acra:4.9.0'
|
compile 'ch.acra:acra:4.9.0'
|
||||||
compile 'com.google.android.exoplayer:exoplayer:r2.3.1'
|
compile 'com.google.android.exoplayer:exoplayer:r2.3.1'
|
||||||
|
@@ -40,7 +40,7 @@
|
|||||||
android:label="@string/background_player_name" />
|
android:label="@string/background_player_name" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".player.ExoPlayerActivity"
|
android:name=".player.MainVideoPlayer"
|
||||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
|
@@ -20,10 +20,6 @@ package org.schabi.newpipe;
|
|||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Singleton:
|
* Singleton:
|
||||||
* Used to send data between certain Activity/Services within the same process.
|
* Used to send data between certain Activity/Services within the same process.
|
||||||
@@ -39,8 +35,5 @@ public class ActivityCommunicator {
|
|||||||
return activityCommunicator;
|
return activityCommunicator;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Thumbnail send from VideoItemDetailFragment to BackgroundPlayer
|
|
||||||
public volatile Bitmap backgroundPlayerThumbnail;
|
|
||||||
|
|
||||||
public volatile Class returnActivity;
|
public volatile Class returnActivity;
|
||||||
}
|
}
|
||||||
|
@@ -45,7 +45,7 @@ import org.schabi.newpipe.util.PermissionHelper;
|
|||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity implements OnItemSelectedListener {
|
public class MainActivity extends AppCompatActivity implements OnItemSelectedListener {
|
||||||
private static final String TAG = MainActivity.class.toString();
|
//private static final String TAG = "MainActivity";
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Activity's LifeCycle
|
// Activity's LifeCycle
|
||||||
@@ -57,12 +57,23 @@ public class MainActivity extends AppCompatActivity implements OnItemSelectedLis
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
||||||
if (savedInstanceState == null) initFragments();
|
|
||||||
|
if (getSupportFragmentManager() != null && getSupportFragmentManager().getBackStackEntryCount() == 0) {
|
||||||
|
initFragments();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onNewIntent(Intent intent) {
|
protected void onNewIntent(Intent intent) {
|
||||||
|
if (intent != null) {
|
||||||
|
// Return if launched from a launcher (e.g. Nova Launcher, Pixel Launcher ...)
|
||||||
|
// to not destroy the already created backstack
|
||||||
|
String action = intent.getAction();
|
||||||
|
if ((action != null && action.equals(Intent.ACTION_MAIN)) && intent.hasCategory(Intent.CATEGORY_LAUNCHER)) return;
|
||||||
|
}
|
||||||
|
|
||||||
super.onNewIntent(intent);
|
super.onNewIntent(intent);
|
||||||
|
setIntent(intent);
|
||||||
handleIntent(intent);
|
handleIntent(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Submodule app/src/main/java/org/schabi/newpipe/extractor updated: b587d175bb...08457de763
@@ -413,4 +413,9 @@ public class ChannelFragment extends Fragment implements ChannelExtractorWorker.
|
|||||||
public void onError(int messageId) {
|
public void onError(int messageId) {
|
||||||
Toast.makeText(activity, messageId, Toast.LENGTH_LONG).show();
|
Toast.makeText(activity, messageId, Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUnrecoverableError(Exception exception) {
|
||||||
|
activity.finish();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -88,7 +88,7 @@ class ActionBarHandler {
|
|||||||
VideoStream item = videoStreams.get(i);
|
VideoStream item = videoStreams.get(i);
|
||||||
itemArray[i] = MediaFormat.getNameById(item.format) + " " + item.resolution;
|
itemArray[i] = MediaFormat.getNameById(item.format) + " " + item.resolution;
|
||||||
}
|
}
|
||||||
int defaultResolution = Utils.getPreferredResolution(activity, videoStreams);
|
int defaultResolution = Utils.getDefaultResolution(activity, videoStreams);
|
||||||
|
|
||||||
ArrayAdapter<String> itemAdapter = new ArrayAdapter<>(activity.getBaseContext(),
|
ArrayAdapter<String> itemAdapter = new ArrayAdapter<>(activity.getBaseContext(),
|
||||||
android.R.layout.simple_spinner_dropdown_item, itemArray);
|
android.R.layout.simple_spinner_dropdown_item, itemArray);
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
717
app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
Normal file
717
app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,19 +0,0 @@
|
|||||||
package org.schabi.newpipe.player;
|
|
||||||
|
|
||||||
public interface StateInterface {
|
|
||||||
int STATE_LOADING = 123;
|
|
||||||
int STATE_PLAYING = 124;
|
|
||||||
int STATE_BUFFERING = 125;
|
|
||||||
int STATE_PAUSED = 126;
|
|
||||||
int STATE_PAUSED_SEEK = 127;
|
|
||||||
int STATE_COMPLETED = 128;
|
|
||||||
|
|
||||||
void changeState(int state);
|
|
||||||
|
|
||||||
void onLoading();
|
|
||||||
void onPlaying();
|
|
||||||
void onBuffering();
|
|
||||||
void onPaused();
|
|
||||||
void onPausedSeek();
|
|
||||||
void onCompleted();
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@@ -121,6 +121,7 @@ public class ErrorActivity extends AppCompatActivity {
|
|||||||
Intent intent = new Intent(context, ErrorActivity.class);
|
Intent intent = new Intent(context, ErrorActivity.class);
|
||||||
intent.putExtra(ERROR_INFO, errorInfo);
|
intent.putExtra(ERROR_INFO, errorInfo);
|
||||||
intent.putExtra(ERROR_LIST, elToSl(el));
|
intent.putExtra(ERROR_LIST, elToSl(el));
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
}
|
}
|
||||||
}).show();
|
}).show();
|
||||||
@@ -130,6 +131,7 @@ public class ErrorActivity extends AppCompatActivity {
|
|||||||
Intent intent = new Intent(context, ErrorActivity.class);
|
Intent intent = new Intent(context, ErrorActivity.class);
|
||||||
intent.putExtra(ERROR_INFO, errorInfo);
|
intent.putExtra(ERROR_INFO, errorInfo);
|
||||||
intent.putExtra(ERROR_LIST, elToSl(el));
|
intent.putExtra(ERROR_LIST, elToSl(el));
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,7 +182,7 @@ public class ErrorActivity extends AppCompatActivity {
|
|||||||
Intent intent = new Intent(context, ErrorActivity.class);
|
Intent intent = new Intent(context, ErrorActivity.class);
|
||||||
intent.putExtra(ERROR_INFO, errorInfo);
|
intent.putExtra(ERROR_INFO, errorInfo);
|
||||||
intent.putExtra(ERROR_LIST, el);
|
intent.putExtra(ERROR_LIST, el);
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -22,7 +22,6 @@ import org.schabi.newpipe.MainActivity;
|
|||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
||||||
|
|
||||||
@@ -53,6 +52,7 @@ public class SettingsFragment extends PreferenceFragment
|
|||||||
SharedPreferences.OnSharedPreferenceChangeListener prefListener;
|
SharedPreferences.OnSharedPreferenceChangeListener prefListener;
|
||||||
// get keys
|
// get keys
|
||||||
String DEFAULT_RESOLUTION_PREFERENCE;
|
String DEFAULT_RESOLUTION_PREFERENCE;
|
||||||
|
String DEFAULT_POPUP_RESOLUTION_PREFERENCE;
|
||||||
String PREFERRED_VIDEO_FORMAT_PREFERENCE;
|
String PREFERRED_VIDEO_FORMAT_PREFERENCE;
|
||||||
String DEFAULT_AUDIO_FORMAT_PREFERENCE;
|
String DEFAULT_AUDIO_FORMAT_PREFERENCE;
|
||||||
String SEARCH_LANGUAGE_PREFERENCE;
|
String SEARCH_LANGUAGE_PREFERENCE;
|
||||||
@@ -61,6 +61,7 @@ public class SettingsFragment extends PreferenceFragment
|
|||||||
String USE_TOR_KEY;
|
String USE_TOR_KEY;
|
||||||
String THEME;
|
String THEME;
|
||||||
private ListPreference defaultResolutionPreference;
|
private ListPreference defaultResolutionPreference;
|
||||||
|
private ListPreference defaultPopupResolutionPreference;
|
||||||
private ListPreference preferredVideoFormatPreference;
|
private ListPreference preferredVideoFormatPreference;
|
||||||
private ListPreference defaultAudioFormatPreference;
|
private ListPreference defaultAudioFormatPreference;
|
||||||
private ListPreference searchLanguagePreference;
|
private ListPreference searchLanguagePreference;
|
||||||
@@ -80,6 +81,7 @@ public class SettingsFragment extends PreferenceFragment
|
|||||||
|
|
||||||
// get keys
|
// get keys
|
||||||
DEFAULT_RESOLUTION_PREFERENCE = getString(R.string.default_resolution_key);
|
DEFAULT_RESOLUTION_PREFERENCE = getString(R.string.default_resolution_key);
|
||||||
|
DEFAULT_POPUP_RESOLUTION_PREFERENCE = getString(R.string.default_popup_resolution_key);
|
||||||
PREFERRED_VIDEO_FORMAT_PREFERENCE = getString(R.string.preferred_video_format_key);
|
PREFERRED_VIDEO_FORMAT_PREFERENCE = getString(R.string.preferred_video_format_key);
|
||||||
DEFAULT_AUDIO_FORMAT_PREFERENCE = getString(R.string.default_audio_format_key);
|
DEFAULT_AUDIO_FORMAT_PREFERENCE = getString(R.string.default_audio_format_key);
|
||||||
SEARCH_LANGUAGE_PREFERENCE = getString(R.string.search_language_key);
|
SEARCH_LANGUAGE_PREFERENCE = getString(R.string.search_language_key);
|
||||||
@@ -91,6 +93,8 @@ public class SettingsFragment extends PreferenceFragment
|
|||||||
// get pref objects
|
// get pref objects
|
||||||
defaultResolutionPreference =
|
defaultResolutionPreference =
|
||||||
(ListPreference) findPreference(DEFAULT_RESOLUTION_PREFERENCE);
|
(ListPreference) findPreference(DEFAULT_RESOLUTION_PREFERENCE);
|
||||||
|
defaultPopupResolutionPreference =
|
||||||
|
(ListPreference) findPreference(DEFAULT_POPUP_RESOLUTION_PREFERENCE);
|
||||||
preferredVideoFormatPreference =
|
preferredVideoFormatPreference =
|
||||||
(ListPreference) findPreference(PREFERRED_VIDEO_FORMAT_PREFERENCE);
|
(ListPreference) findPreference(PREFERRED_VIDEO_FORMAT_PREFERENCE);
|
||||||
defaultAudioFormatPreference =
|
defaultAudioFormatPreference =
|
||||||
@@ -103,6 +107,9 @@ public class SettingsFragment extends PreferenceFragment
|
|||||||
|
|
||||||
final String currentTheme = defaultPreferences.getString(THEME, "Light");
|
final String currentTheme = defaultPreferences.getString(THEME, "Light");
|
||||||
|
|
||||||
|
// TODO: Clean this, as the class is already implementing the class
|
||||||
|
// and those double equals...
|
||||||
|
|
||||||
prefListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
|
prefListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
|
||||||
@@ -260,6 +267,9 @@ public class SettingsFragment extends PreferenceFragment
|
|||||||
defaultResolutionPreference.setSummary(
|
defaultResolutionPreference.setSummary(
|
||||||
defaultPreferences.getString(DEFAULT_RESOLUTION_PREFERENCE,
|
defaultPreferences.getString(DEFAULT_RESOLUTION_PREFERENCE,
|
||||||
getString(R.string.default_resolution_value)));
|
getString(R.string.default_resolution_value)));
|
||||||
|
defaultPopupResolutionPreference.setSummary(
|
||||||
|
defaultPreferences.getString(DEFAULT_POPUP_RESOLUTION_PREFERENCE,
|
||||||
|
getString(R.string.default_popup_resolution_value)));
|
||||||
preferredVideoFormatPreference.setSummary(
|
preferredVideoFormatPreference.setSummary(
|
||||||
defaultPreferences.getString(PREFERRED_VIDEO_FORMAT_PREFERENCE,
|
defaultPreferences.getString(PREFERRED_VIDEO_FORMAT_PREFERENCE,
|
||||||
getString(R.string.preferred_video_format_default)));
|
getString(R.string.preferred_video_format_default)));
|
||||||
|
@@ -8,12 +8,60 @@ import org.schabi.newpipe.MainActivity;
|
|||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
import org.schabi.newpipe.extractor.stream_info.AudioStream;
|
||||||
|
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
||||||
import org.schabi.newpipe.fragments.OnItemSelectedListener;
|
import org.schabi.newpipe.fragments.OnItemSelectedListener;
|
||||||
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
|
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
|
||||||
|
import org.schabi.newpipe.player.BackgroundPlayer;
|
||||||
|
import org.schabi.newpipe.player.BasePlayer;
|
||||||
|
import org.schabi.newpipe.player.VideoPlayer;
|
||||||
|
|
||||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||||
public class NavigationHelper {
|
public class NavigationHelper {
|
||||||
|
|
||||||
|
public static Intent getOpenVideoPlayerIntent(Context context, Class targetClazz, StreamInfo info, int selectedStreamIndex) {
|
||||||
|
Intent mIntent = new Intent(context, targetClazz)
|
||||||
|
.putExtra(BasePlayer.VIDEO_TITLE, info.title)
|
||||||
|
.putExtra(BasePlayer.VIDEO_URL, info.webpage_url)
|
||||||
|
.putExtra(BasePlayer.VIDEO_THUMBNAIL_URL, info.thumbnail_url)
|
||||||
|
.putExtra(BasePlayer.CHANNEL_NAME, info.uploader)
|
||||||
|
.putExtra(VideoPlayer.INDEX_SEL_VIDEO_STREAM, selectedStreamIndex)
|
||||||
|
.putExtra(VideoPlayer.VIDEO_STREAMS_LIST, Utils.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false))
|
||||||
|
.putExtra(VideoPlayer.VIDEO_ONLY_AUDIO_STREAM, Utils.getHighestQualityAudio(info.audio_streams));
|
||||||
|
if (info.start_position > 0) mIntent.putExtra(BasePlayer.START_POSITION, info.start_position * 1000);
|
||||||
|
return mIntent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Intent getOpenVideoPlayerIntent(Context context, Class targetClazz, VideoPlayer instance) {
|
||||||
|
return new Intent(context, targetClazz)
|
||||||
|
.putExtra(BasePlayer.VIDEO_TITLE, instance.getVideoTitle())
|
||||||
|
.putExtra(BasePlayer.VIDEO_URL, instance.getVideoUrl())
|
||||||
|
.putExtra(BasePlayer.VIDEO_THUMBNAIL_URL, instance.getVideoThumbnailUrl())
|
||||||
|
.putExtra(BasePlayer.CHANNEL_NAME, instance.getChannelName())
|
||||||
|
.putExtra(VideoPlayer.INDEX_SEL_VIDEO_STREAM, instance.getSelectedStreamIndex())
|
||||||
|
.putExtra(VideoPlayer.VIDEO_STREAMS_LIST, instance.getVideoStreamsList())
|
||||||
|
.putExtra(VideoPlayer.VIDEO_ONLY_AUDIO_STREAM, instance.getAudioStream())
|
||||||
|
.putExtra(BasePlayer.START_POSITION, ((int) instance.getPlayer().getCurrentPosition()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Intent getOpenBackgroundPlayerIntent(Context context, StreamInfo info) {
|
||||||
|
return getOpenBackgroundPlayerIntent(context, info, info.audio_streams.get(Utils.getPreferredAudioFormat(context, info.audio_streams)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Intent getOpenBackgroundPlayerIntent(Context context, StreamInfo info, AudioStream audioStream) {
|
||||||
|
Intent mIntent = new Intent(context, BackgroundPlayer.class)
|
||||||
|
.putExtra(BasePlayer.VIDEO_TITLE, info.title)
|
||||||
|
.putExtra(BasePlayer.VIDEO_URL, info.webpage_url)
|
||||||
|
.putExtra(BasePlayer.VIDEO_THUMBNAIL_URL, info.thumbnail_url)
|
||||||
|
.putExtra(BasePlayer.CHANNEL_NAME, info.uploader)
|
||||||
|
.putExtra(BasePlayer.CHANNEL_NAME, info.uploader)
|
||||||
|
.putExtra(BackgroundPlayer.AUDIO_STREAM, audioStream);
|
||||||
|
if (info.start_position > 0) mIntent.putExtra(BasePlayer.START_POSITION, info.start_position * 1000);
|
||||||
|
return mIntent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Through Interface (faster)
|
// Through Interface (faster)
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@@ -9,28 +9,31 @@ import org.schabi.newpipe.extractor.MediaFormat;
|
|||||||
import org.schabi.newpipe.extractor.stream_info.AudioStream;
|
import org.schabi.newpipe.extractor.stream_info.AudioStream;
|
||||||
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
public class Utils {
|
public class Utils {
|
||||||
|
|
||||||
|
private static final List<String> HIGH_RESOLUTION_LIST = Arrays.asList("1440p", "2160p", "1440p60", "2160p60");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the index of the default stream in the list, based on the
|
* Return the index of the default stream in the list, based on the parameters
|
||||||
* preferred resolution and format chosen in the settings
|
* defaultResolution and preferredFormat
|
||||||
|
*
|
||||||
|
* @param videoStreams the list that will be extracted the index
|
||||||
*
|
*
|
||||||
* @param videoStreams the list that will be extracted the index
|
|
||||||
* @return index of the preferred resolution&format
|
* @return index of the preferred resolution&format
|
||||||
*/
|
*/
|
||||||
public static int getPreferredResolution(Context context, List<VideoStream> videoStreams) {
|
public static int getDefaultResolution(String defaultResolution, String preferredFormat, List<VideoStream> videoStreams) {
|
||||||
SharedPreferences defaultPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
if (defaultPreferences == null) return 0;
|
|
||||||
|
|
||||||
String defaultResolution = defaultPreferences
|
if (defaultResolution.equals("Best resolution")) {
|
||||||
.getString(context.getString(R.string.default_resolution_key),
|
return 0;
|
||||||
context.getString(R.string.default_resolution_value));
|
}
|
||||||
|
|
||||||
String preferredFormat = defaultPreferences
|
|
||||||
.getString(context.getString(R.string.preferred_video_format_key),
|
|
||||||
context.getString(R.string.preferred_video_format_default));
|
|
||||||
|
|
||||||
// first try to find the one with the right resolution
|
// first try to find the one with the right resolution
|
||||||
int selectedFormat = 0;
|
int selectedFormat = 0;
|
||||||
@@ -50,16 +53,69 @@ public class Utils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (selectedFormat == 0 && !videoStreams.get(selectedFormat).resolution.contains(defaultResolution.replace("p60", "p"))) {
|
||||||
|
// Maybe there's no 60 fps variant available, so fallback to the normal version
|
||||||
|
String replace = defaultResolution.replace("p60", "p");
|
||||||
|
for (int i = 0; i < videoStreams.size(); i++) {
|
||||||
|
VideoStream item = videoStreams.get(i);
|
||||||
|
if (replace.equals(item.resolution)) selectedFormat = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// than try to find the one with the right resolution and format
|
||||||
|
for (int i = 0; i < videoStreams.size(); i++) {
|
||||||
|
VideoStream item = videoStreams.get(i);
|
||||||
|
if (replace.equals(item.resolution)
|
||||||
|
&& preferredFormat.equals(MediaFormat.getNameById(item.format))) {
|
||||||
|
selectedFormat = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// this is actually an error,
|
// this is actually an error,
|
||||||
// but maybe there is really no stream fitting to the default value.
|
// but maybe there is really no stream fitting to the default value.
|
||||||
return selectedFormat;
|
return selectedFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see #getDefaultResolution(String, String, List)
|
||||||
|
*/
|
||||||
|
public static int getDefaultResolution(Context context, List<VideoStream> videoStreams) {
|
||||||
|
SharedPreferences defaultPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
if (defaultPreferences == null) return 0;
|
||||||
|
|
||||||
|
String defaultResolution = defaultPreferences
|
||||||
|
.getString(context.getString(R.string.default_resolution_key), context.getString(R.string.default_resolution_value));
|
||||||
|
|
||||||
|
String preferredFormat = defaultPreferences
|
||||||
|
.getString(context.getString(R.string.preferred_video_format_key), context.getString(R.string.preferred_video_format_default));
|
||||||
|
|
||||||
|
return getDefaultResolution(defaultResolution, preferredFormat, videoStreams);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see #getDefaultResolution(String, String, List)
|
||||||
|
*/
|
||||||
|
public static int getPopupDefaultResolution(Context context, List<VideoStream> videoStreams) {
|
||||||
|
SharedPreferences defaultPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
if (defaultPreferences == null) return 0;
|
||||||
|
|
||||||
|
String defaultResolution = defaultPreferences
|
||||||
|
.getString(context.getString(R.string.default_popup_resolution_key), context.getString(R.string.default_popup_resolution_value));
|
||||||
|
|
||||||
|
String preferredFormat = defaultPreferences
|
||||||
|
.getString(context.getString(R.string.preferred_video_format_key), context.getString(R.string.preferred_video_format_default));
|
||||||
|
|
||||||
|
return getDefaultResolution(defaultResolution, preferredFormat, videoStreams);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the index of the default stream in the list, based on the
|
* Return the index of the default stream in the list, based on the
|
||||||
* preferred audio format chosen in the settings
|
* preferred audio format chosen in the settings
|
||||||
*
|
*
|
||||||
|
* @param context context to get the preferred audio format
|
||||||
* @param audioStreams the list that will be extracted the index
|
* @param audioStreams the list that will be extracted the index
|
||||||
|
*
|
||||||
* @return index of the preferred format
|
* @return index of the preferred format
|
||||||
*/
|
*/
|
||||||
public static int getPreferredAudioFormat(Context context, List<AudioStream> audioStreams) {
|
public static int getPreferredAudioFormat(Context context, List<AudioStream> audioStreams) {
|
||||||
@@ -80,12 +136,138 @@ public class Utils {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int highestQualityIndex = 0;
|
||||||
|
|
||||||
|
// Try to find a audio stream with the preferred format
|
||||||
|
for (int i = 0; i < audioStreams.size(); i++) if (audioStreams.get(i).format == preferredFormat) highestQualityIndex = i;
|
||||||
|
|
||||||
|
// Try to find a audio stream with the highest bitrate and preferred format
|
||||||
for (int i = 0; i < audioStreams.size(); i++) {
|
for (int i = 0; i < audioStreams.size(); i++) {
|
||||||
if (audioStreams.get(i).format == preferredFormat) {
|
AudioStream audioStream = audioStreams.get(i);
|
||||||
return i;
|
if (audioStream.avgBitrate > audioStreams.get(highestQualityIndex).avgBitrate
|
||||||
|
&& audioStream.format == preferredFormat) highestQualityIndex = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return highestQualityIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the audio from the list with the highest bitrate
|
||||||
|
*
|
||||||
|
* @param audioStreams list the audio streams
|
||||||
|
* @return audio with highest average bitrate
|
||||||
|
*/
|
||||||
|
public static AudioStream getHighestQualityAudio(List<AudioStream> audioStreams) {
|
||||||
|
int highestQualityIndex = 0;
|
||||||
|
|
||||||
|
for (int i = 1; i < audioStreams.size(); i++) {
|
||||||
|
AudioStream audioStream = audioStreams.get(i);
|
||||||
|
if (audioStream.avgBitrate > audioStreams.get(highestQualityIndex).avgBitrate) highestQualityIndex = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return audioStreams.get(highestQualityIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Join the two lists of video streams (video_only and normal videos), and sort them according with preferred format
|
||||||
|
* chosen by the user
|
||||||
|
*
|
||||||
|
* @param context context to search for the format to give preference
|
||||||
|
* @param videoStreams normal videos list
|
||||||
|
* @param videoOnlyStreams video only stream list
|
||||||
|
* @param ascendingOrder true -> smallest to greatest | false -> greatest to smallest
|
||||||
|
* @return the sorted list
|
||||||
|
*/
|
||||||
|
public static ArrayList<VideoStream> getSortedStreamVideosList(Context context, List<VideoStream> videoStreams, List<VideoStream> videoOnlyStreams, boolean ascendingOrder) {
|
||||||
|
boolean showHigherResolutions = PreferenceManager.getDefaultSharedPreferences(context).getBoolean(context.getString(R.string.show_higher_resolutions_key), false);
|
||||||
|
String preferredFormatString = PreferenceManager.getDefaultSharedPreferences(context).getString(context.getString(R.string.preferred_video_format_key), context.getString(R.string.preferred_video_format_default));
|
||||||
|
MediaFormat preferredFormat = MediaFormat.WEBM;
|
||||||
|
switch (preferredFormatString) {
|
||||||
|
case "WebM":
|
||||||
|
preferredFormat = MediaFormat.WEBM;
|
||||||
|
break;
|
||||||
|
case "MPEG-4":
|
||||||
|
preferredFormat = MediaFormat.MPEG_4;
|
||||||
|
break;
|
||||||
|
case "3GPP":
|
||||||
|
preferredFormat = MediaFormat.v3GPP;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return getSortedStreamVideosList(preferredFormat, showHigherResolutions, videoStreams, videoOnlyStreams, ascendingOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Join the two lists of video streams (video_only and normal videos), and sort them according with preferred format
|
||||||
|
* chosen by the user
|
||||||
|
*
|
||||||
|
* @param preferredFormat format to give preference
|
||||||
|
* @param showHigherResolutions show >1080p resolutions
|
||||||
|
* @param videoStreams normal videos list
|
||||||
|
* @param videoOnlyStreams video only stream list
|
||||||
|
* @param ascendingOrder true -> smallest to greatest | false -> greatest to smallest @return the sorted list
|
||||||
|
* @return the sorted list
|
||||||
|
*/
|
||||||
|
public static ArrayList<VideoStream> getSortedStreamVideosList(MediaFormat preferredFormat, boolean showHigherResolutions, List<VideoStream> videoStreams, List<VideoStream> videoOnlyStreams, boolean ascendingOrder) {
|
||||||
|
ArrayList<VideoStream> retList = new ArrayList<>();
|
||||||
|
HashMap<String, VideoStream> hashMap = new HashMap<>();
|
||||||
|
|
||||||
|
if (videoOnlyStreams != null) {
|
||||||
|
for (VideoStream stream : videoOnlyStreams) {
|
||||||
|
if (!showHigherResolutions && HIGH_RESOLUTION_LIST.contains(stream.resolution)) continue;
|
||||||
|
retList.add(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (videoStreams != null) {
|
||||||
|
for (VideoStream stream : videoStreams) {
|
||||||
|
if (!showHigherResolutions && HIGH_RESOLUTION_LIST.contains(stream.resolution)) continue;
|
||||||
|
retList.add(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
// Add all to the hashmap
|
||||||
|
for (VideoStream videoStream : retList) hashMap.put(videoStream.resolution, videoStream);
|
||||||
|
|
||||||
|
// Override the values when the key == resolution, with the preferredFormat
|
||||||
|
for (VideoStream videoStream : retList) {
|
||||||
|
if (videoStream.format == preferredFormat.id) hashMap.put(videoStream.resolution, videoStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
retList.clear();
|
||||||
|
retList.addAll(hashMap.values());
|
||||||
|
sortStreamList(retList, ascendingOrder);
|
||||||
|
return retList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort the streams list depending on the parameter ascendingOrder;
|
||||||
|
* <p>
|
||||||
|
* It works like that:<br>
|
||||||
|
* - Take a string resolution, remove the letters, replace "0p60" (for 60fps videos) with "1"
|
||||||
|
* and sort by the greatest:<br>
|
||||||
|
* <blockquote><pre>
|
||||||
|
* 720p -> 720
|
||||||
|
* 720p60 -> 721
|
||||||
|
* 360p -> 360
|
||||||
|
* 1080p -> 1080
|
||||||
|
* 1080p60 -> 1081
|
||||||
|
* <p>
|
||||||
|
* ascendingOrder ? 360 < 720 < 721 < 1080 < 1081
|
||||||
|
* !ascendingOrder ? 1081 < 1080 < 721 < 720 < 360/pre></blockquote>
|
||||||
|
* <p>
|
||||||
|
* @param videoStreams list that the sorting will be applied
|
||||||
|
* @param ascendingOrder true -> smallest to greatest | false -> greatest to smallest
|
||||||
|
*/
|
||||||
|
public static void sortStreamList(List<VideoStream> videoStreams, final boolean ascendingOrder) {
|
||||||
|
Collections.sort(videoStreams, new Comparator<VideoStream>() {
|
||||||
|
@Override
|
||||||
|
public int compare(VideoStream o1, VideoStream o2) {
|
||||||
|
int res1 = Integer.parseInt(o1.resolution.replace("0p60", "1").replaceAll("[^\\d.]", ""));
|
||||||
|
int res2 = Integer.parseInt(o2.resolution.replace("0p60", "1").replaceAll("[^\\d.]", ""));
|
||||||
|
|
||||||
|
return ascendingOrder ? res1 - res2 : res2 - res1;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -33,6 +33,11 @@ public class ChannelExtractorWorker extends ExtractorWorker {
|
|||||||
public interface OnChannelInfoReceive {
|
public interface OnChannelInfoReceive {
|
||||||
void onReceive(ChannelInfo info);
|
void onReceive(ChannelInfo info);
|
||||||
void onError(int messageId);
|
void onError(int messageId);
|
||||||
|
/**
|
||||||
|
* Called when an unrecoverable error has occurred.
|
||||||
|
* <p> This is a good place to finish the caller. </p>
|
||||||
|
*/
|
||||||
|
void onUnrecoverableError(Exception exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,9 +79,11 @@ public class ChannelExtractorWorker extends ExtractorWorker {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void handleException(Exception exception, int serviceId, String url) {
|
protected void handleException(final Exception exception, int serviceId, String url) {
|
||||||
|
if (callback == null || getHandler() == null || isInterrupted()) return;
|
||||||
|
|
||||||
if (exception instanceof IOException) {
|
if (exception instanceof IOException) {
|
||||||
if (callback != null) getHandler().post(new Runnable() {
|
getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
callback.onError(R.string.network_error);
|
callback.onError(R.string.network_error);
|
||||||
@@ -84,10 +91,20 @@ public class ChannelExtractorWorker extends ExtractorWorker {
|
|||||||
});
|
});
|
||||||
} else if (exception instanceof ParsingException || exception instanceof ExtractionException) {
|
} else if (exception instanceof ParsingException || exception instanceof ExtractionException) {
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_CHANNEL, getServiceName(), url, R.string.parsing_error));
|
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_CHANNEL, getServiceName(), url, R.string.parsing_error));
|
||||||
finishIfActivity();
|
getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onUnrecoverableError(exception);
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_CHANNEL, getServiceName(), url, R.string.general_error));
|
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_CHANNEL, getServiceName(), url, R.string.general_error));
|
||||||
finishIfActivity();
|
getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onUnrecoverableError(exception);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -135,13 +135,6 @@ public abstract class ExtractorWorker extends Thread {
|
|||||||
this.service = null;
|
this.service = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* If the context passed in the constructor is an {@link Activity}, finish it.
|
|
||||||
*/
|
|
||||||
protected void finishIfActivity() {
|
|
||||||
if (getContext() instanceof Activity) ((Activity) getContext()).finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Handler getHandler() {
|
public Handler getHandler() {
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
@@ -35,6 +35,12 @@ public class StreamExtractorWorker extends ExtractorWorker {
|
|||||||
void onBlockedByGemaError();
|
void onBlockedByGemaError();
|
||||||
void onContentErrorWithMessage(int messageId);
|
void onContentErrorWithMessage(int messageId);
|
||||||
void onContentError();
|
void onContentError();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an unrecoverable error has occurred.
|
||||||
|
* <p> This is a good place to finish the caller. </p>
|
||||||
|
*/
|
||||||
|
void onUnrecoverableError(Exception exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,7 +68,7 @@ public class StreamExtractorWorker extends ExtractorWorker {
|
|||||||
|
|
||||||
if (streamInfo != null && !streamInfo.errors.isEmpty()) handleErrorsDuringExtraction(streamInfo.errors, ErrorActivity.REQUESTED_STREAM);
|
if (streamInfo != null && !streamInfo.errors.isEmpty()) handleErrorsDuringExtraction(streamInfo.errors, ErrorActivity.REQUESTED_STREAM);
|
||||||
|
|
||||||
if (callback != null && streamInfo != null && !isInterrupted()) getHandler().post(new Runnable() {
|
if (callback != null && getHandler() != null && streamInfo != null && !isInterrupted()) getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (isInterrupted() || callback == null) return;
|
if (isInterrupted() || callback == null) return;
|
||||||
@@ -75,37 +81,39 @@ public class StreamExtractorWorker extends ExtractorWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void handleException(final Exception exception, int serviceId, String url) {
|
protected void handleException(final Exception exception, int serviceId, final String url) {
|
||||||
|
if (callback == null || getHandler() == null || isInterrupted()) return;
|
||||||
|
|
||||||
if (exception instanceof ReCaptchaException) {
|
if (exception instanceof ReCaptchaException) {
|
||||||
if (callback != null) getHandler().post(new Runnable() {
|
getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
callback.onReCaptchaException();
|
callback.onReCaptchaException();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (exception instanceof IOException) {
|
} else if (exception instanceof IOException) {
|
||||||
if (callback != null) getHandler().post(new Runnable() {
|
getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
callback.onError(R.string.network_error);
|
callback.onError(R.string.network_error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (exception instanceof YoutubeStreamExtractor.GemaException) {
|
} else if (exception instanceof YoutubeStreamExtractor.GemaException) {
|
||||||
if (callback != null) getHandler().post(new Runnable() {
|
getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
callback.onBlockedByGemaError();
|
callback.onBlockedByGemaError();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (exception instanceof YoutubeStreamExtractor.LiveStreamException) {
|
} else if (exception instanceof YoutubeStreamExtractor.LiveStreamException) {
|
||||||
if (callback != null) getHandler().post(new Runnable() {
|
getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
callback.onContentErrorWithMessage(R.string.live_streams_not_supported);
|
callback.onContentErrorWithMessage(R.string.live_streams_not_supported);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (exception instanceof StreamExtractor.ContentNotAvailableException) {
|
} else if (exception instanceof StreamExtractor.ContentNotAvailableException) {
|
||||||
if (callback != null) getHandler().post(new Runnable() {
|
getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
callback.onContentError();
|
callback.onContentError();
|
||||||
@@ -114,7 +122,12 @@ public class StreamExtractorWorker extends ExtractorWorker {
|
|||||||
} else if (exception instanceof YoutubeStreamExtractor.DecryptException) {
|
} else if (exception instanceof YoutubeStreamExtractor.DecryptException) {
|
||||||
// custom service related exceptions
|
// custom service related exceptions
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.youtube_signature_decryption_error));
|
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.youtube_signature_decryption_error));
|
||||||
finishIfActivity();
|
getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onUnrecoverableError(exception);
|
||||||
|
}
|
||||||
|
});
|
||||||
} else if (exception instanceof StreamInfo.StreamExctractException) {
|
} else if (exception instanceof StreamInfo.StreamExctractException) {
|
||||||
if (!streamInfo.errors.isEmpty()) {
|
if (!streamInfo.errors.isEmpty()) {
|
||||||
// !!! if this case ever kicks in someone gets kicked out !!!
|
// !!! if this case ever kicks in someone gets kicked out !!!
|
||||||
@@ -122,13 +135,29 @@ public class StreamExtractorWorker extends ExtractorWorker {
|
|||||||
} else {
|
} else {
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), streamInfo.errors, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.could_not_get_stream));
|
ErrorActivity.reportError(getHandler(), getContext(), streamInfo.errors, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.could_not_get_stream));
|
||||||
}
|
}
|
||||||
finishIfActivity();
|
|
||||||
|
getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onUnrecoverableError(exception);
|
||||||
|
}
|
||||||
|
});
|
||||||
} else if (exception instanceof ParsingException) {
|
} else if (exception instanceof ParsingException) {
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.parsing_error));
|
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.parsing_error));
|
||||||
finishIfActivity();
|
getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onUnrecoverableError(exception);
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.general_error));
|
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.general_error));
|
||||||
finishIfActivity();
|
getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onUnrecoverableError(exception);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
17
app/src/main/res/drawable/custom_progress_bar.xml
Normal file
17
app/src/main/res/drawable/custom_progress_bar.xml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:id="@android:id/background">
|
||||||
|
<shape>
|
||||||
|
<solid android:color="@color/gray"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item android:id="@android:id/progress">
|
||||||
|
<clip>
|
||||||
|
<shape>
|
||||||
|
<solid android:color="@color/middle_gray"/>
|
||||||
|
</shape>
|
||||||
|
</clip>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
</layer-list>
|
@@ -1,16 +1,17 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:id="@+id/notificationContent"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="fill_parent"
|
android:layout_height="64dp"
|
||||||
android:layout_height="wrap_content"
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
android:clickable="true"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:background="@color/background_notification_color">
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/notificationContent"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="64dp"
|
android:layout_height="64dp"
|
||||||
|
android:background="@color/background_notification_color"
|
||||||
|
android:clickable="true"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
@@ -18,65 +19,102 @@
|
|||||||
android:id="@+id/notificationCover"
|
android:id="@+id/notificationCover"
|
||||||
android:layout_width="64dp"
|
android:layout_width="64dp"
|
||||||
android:layout_height="64dp"
|
android:layout_height="64dp"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
android:src="@drawable/dummy_thumbnail"
|
android:src="@drawable/dummy_thumbnail"
|
||||||
android:scaleType="centerCrop"/>
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginLeft="8dp"
|
android:layout_marginLeft="8dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:orientation="vertical" >
|
android:orientation="vertical"
|
||||||
|
tools:ignore="RtlHardcoded">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/notificationSongName"
|
android:id="@+id/notificationSongName"
|
||||||
style="@android:style/TextAppearance.StatusBar.EventContent.Title"
|
style="@android:style/TextAppearance.StatusBar.EventContent.Title"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
|
android:ellipsize="end"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:ellipsize="marquee"
|
android:maxLines="1"
|
||||||
android:singleLine="true"
|
android:textSize="14sp"
|
||||||
android:text="title" />
|
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis nec aliquam augue, eget cursus est. Ut id tristique enim, ut scelerisque tellus. Sed ultricies ipsum non mauris ultricies, commodo malesuada velit porta."/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/notificationArtist"
|
android:id="@+id/notificationArtist"
|
||||||
|
android:layout_width="match_parent"
|
||||||
style="@android:style/TextAppearance.StatusBar.EventContent"
|
style="@android:style/TextAppearance.StatusBar.EventContent"
|
||||||
android:layout_width="wrap_content"
|
android:ellipsize="end"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:ellipsize="marquee"
|
android:maxLines="1"
|
||||||
android:singleLine="true"
|
android:textSize="12sp"
|
||||||
android:text="artist" />
|
tools:text="Duis posuere arcu condimentum lobortis mattis."/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/notificationRewind"
|
android:id="@+id/notificationRepeat"
|
||||||
android:layout_width="40dp"
|
android:layout_width="40dp"
|
||||||
android:layout_height="40dp"
|
android:layout_height="match_parent"
|
||||||
android:layout_margin="5dp"
|
android:background="#00000000"
|
||||||
android:background="#00ffffff"
|
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:scaleType="fitXY"
|
android:padding="5dp"
|
||||||
android:src="@drawable/ic_action_av_fast_rewind" />
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/ic_repeat_white"
|
||||||
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/notificationFRewind"
|
||||||
|
android:layout_width="45dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="#00000000"
|
||||||
|
android:clickable="true"
|
||||||
|
android:padding="5dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/ic_action_av_fast_rewind"
|
||||||
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/notificationPlayPause"
|
android:id="@+id/notificationPlayPause"
|
||||||
android:layout_width="40dp"
|
android:layout_width="45dp"
|
||||||
android:layout_height="40dp"
|
android:layout_height="match_parent"
|
||||||
android:layout_margin="5dp"
|
android:background="#00000000"
|
||||||
android:background="#00ffffff"
|
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:scaleType="fitXY"
|
android:src="@drawable/ic_pause_white"
|
||||||
android:src="@drawable/ic_pause_white" />
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/notificationFForward"
|
||||||
|
android:layout_width="45dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="#00000000"
|
||||||
|
android:clickable="true"
|
||||||
|
android:padding="5dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/ic_action_av_fast_forward"
|
||||||
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/notificationStop"
|
android:id="@+id/notificationStop"
|
||||||
android:layout_width="40dp"
|
android:layout_width="40dp"
|
||||||
android:layout_height="40dp"
|
android:layout_height="40dp"
|
||||||
android:layout_margin="5dp"
|
android:layout_marginLeft="5dp"
|
||||||
android:background="#00ffffff"
|
android:background="#00000000"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:scaleType="fitXY"
|
android:padding="5dp"
|
||||||
android:src="@drawable/ic_close_white" />
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/ic_close_white"
|
||||||
|
tools:ignore="ContentDescription,RtlHardcoded"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
</RelativeLayout>
|
android:id="@+id/notificationProgressBar"
|
||||||
|
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="3dp"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:layout_marginLeft="64dp"
|
||||||
|
android:progressDrawable="@drawable/custom_progress_bar"
|
||||||
|
tools:ignore="RtlHardcoded"
|
||||||
|
tools:progress="52"/>
|
||||||
|
</FrameLayout>
|
@@ -1,4 +1,153 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/notificationContent"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="128dp"
|
||||||
|
android:background="@color/background_notification_color"
|
||||||
|
android:clickable="true"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/notificationCover"
|
||||||
|
android:layout_width="128dp"
|
||||||
|
android:layout_height="128dp"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:src="@drawable/dummy_thumbnail"
|
||||||
|
tools:ignore="ContentDescription,RtlHardcoded"/>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/notificationStop"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:background="#00000000"
|
||||||
|
android:clickable="true"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/ic_close_white"
|
||||||
|
tools:ignore="ContentDescription,RtlHardcoded"/>
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_toLeftOf="@+id/notificationStop"
|
||||||
|
android:layout_toRightOf="@+id/notificationCover"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="8dp"
|
||||||
|
tools:ignore="RtlHardcoded,RtlSymmetry">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/notificationSongName"
|
||||||
|
style="@android:style/TextAppearance.StatusBar.EventContent.Title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:textSize="14sp"
|
||||||
|
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis nec aliquam augue, eget cursus est. Ut id tristique enim, ut scelerisque tellus. Sed ultricies ipsum non mauris ultricies, commodo malesuada velit porta."/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/notificationArtist"
|
||||||
|
style="@android:style/TextAppearance.StatusBar.EventContent"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textSize="12sp"
|
||||||
|
tools:text="Duis posuere arcu condimentum lobortis mattis."/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/notificationProgressBar"
|
||||||
|
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="2dp"
|
||||||
|
android:layout_alignTop="@+id/notificationControls"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginRight="8dp"
|
||||||
|
android:layout_toRightOf="@+id/notificationCover"
|
||||||
|
android:progressDrawable="@drawable/custom_progress_bar"
|
||||||
|
tools:ignore="RtlHardcoded"
|
||||||
|
tools:progress="52"/>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/notificationControls"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:layout_toRightOf="@+id/notificationCover"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
tools:ignore="RtlHardcoded">
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/notificationRepeat"
|
||||||
|
android:layout_width="45dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:background="#00000000"
|
||||||
|
android:clickable="true"
|
||||||
|
android:paddingBottom="4dp"
|
||||||
|
android:paddingLeft="11dp"
|
||||||
|
android:paddingRight="11dp"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/ic_repeat_white"
|
||||||
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/notificationFRewind"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginRight="5dp"
|
||||||
|
android:layout_toLeftOf="@+id/notificationPlayPause"
|
||||||
|
android:background="#00000000"
|
||||||
|
android:clickable="true"
|
||||||
|
android:padding="2dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/ic_action_av_fast_rewind"
|
||||||
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/notificationPlayPause"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginRight="5dp"
|
||||||
|
android:layout_toLeftOf="@+id/notificationFForward"
|
||||||
|
android:background="#00000000"
|
||||||
|
android:padding="2dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/ic_pause_white"
|
||||||
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/notificationFForward"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginRight="8dp"
|
||||||
|
android:background="#00000000"
|
||||||
|
android:clickable="true"
|
||||||
|
android:padding="2dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/ic_action_av_fast_forward"
|
||||||
|
tools:ignore="ContentDescription"/>
|
||||||
|
</RelativeLayout>
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:id="@+id/notificationContent"
|
android:id="@+id/notificationContent"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
@@ -93,4 +242,4 @@
|
|||||||
android:layout_alignParentLeft="true" />
|
android:layout_alignParentLeft="true" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>-->
|
@@ -29,20 +29,23 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/notificationSongName"
|
android:id="@+id/notificationSongName"
|
||||||
style="@android:style/TextAppearance.StatusBar.EventContent.Title"
|
style="@android:style/TextAppearance.StatusBar.EventContent.Title"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
|
android:ellipsize="end"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:ellipsize="marquee"
|
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
tools:text="a long, long, long, long, long title"/>
|
android:textSize="14sp"
|
||||||
|
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis nec aliquam augue, eget cursus est. Ut id tristique enim, ut scelerisque tellus. Sed ultricies ipsum non mauris ultricies, commodo malesuada velit porta."/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/notificationArtist"
|
android:id="@+id/notificationArtist"
|
||||||
|
android:layout_width="match_parent"
|
||||||
style="@android:style/TextAppearance.StatusBar.EventContent"
|
style="@android:style/TextAppearance.StatusBar.EventContent"
|
||||||
android:layout_width="wrap_content"
|
android:ellipsize="end"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:ellipsize="marquee"
|
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
tools:text="a long, long artist"/>
|
android:textSize="12sp"
|
||||||
|
tools:text="Duis posuere arcu condimentum lobortis mattis."/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
<string name="background_player_name">Reproductor de fondu NewPipe</string>
|
<string name="background_player_name">Reproductor de fondu NewPipe</string>
|
||||||
<string name="view_count_text">%1$s visiones</string>
|
<string name="view_count_text">%1$s visiones</string>
|
||||||
<string name="upload_date_text">Espublizáu\'l %1$s</string>
|
<string name="upload_date_text">Espublizáu\'l %1$s</string>
|
||||||
<string name="no_player_found">Nun s\'alcontró\'l reproductor de tresmisiones. ¿Instalar VLC?</string>
|
<string name="no_player_found">Nun s\'alcontró un reproductor de fluxos. ¿Quies instalar VLC?</string>
|
||||||
<string name="install">Instalar</string>
|
<string name="install">Instalar</string>
|
||||||
<string name="cancel">Encaboxar</string>
|
<string name="cancel">Encaboxar</string>
|
||||||
<string name="open_in_browser">Abrir nel restolador</string>
|
<string name="open_in_browser">Abrir nel restolador</string>
|
||||||
@@ -129,7 +129,7 @@
|
|||||||
<string name="msg_exists">Yá esiste\'l ficheru</string>
|
<string name="msg_exists">Yá esiste\'l ficheru</string>
|
||||||
<string name="msg_url_malform">URL malformada o internet non disponible</string>
|
<string name="msg_url_malform">URL malformada o internet non disponible</string>
|
||||||
<string name="msg_running_detail">Calca pa detallles</string>
|
<string name="msg_running_detail">Calca pa detallles</string>
|
||||||
<string name="msg_wait">Espera, por favor...</string>
|
<string name="msg_wait">Espera, por favor…</string>
|
||||||
<string name="msg_copied">Copióse al cartafueyu.</string>
|
<string name="msg_copied">Copióse al cartafueyu.</string>
|
||||||
<string name="no_available_dir">Esbilla un direutoriu de descarga disponible, por favor.</string>
|
<string name="no_available_dir">Esbilla un direutoriu de descarga disponible, por favor.</string>
|
||||||
|
|
||||||
@@ -147,5 +147,40 @@
|
|||||||
<string name="search_page">"Guetar páxina: "</string>
|
<string name="search_page">"Guetar páxina: "</string>
|
||||||
<string name="could_not_load_image">Nun pudo cargase la imaxe</string>
|
<string name="could_not_load_image">Nun pudo cargase la imaxe</string>
|
||||||
<string name="app_ui_crash">Cascó l\'aplicación/IU</string>
|
<string name="app_ui_crash">Cascó l\'aplicación/IU</string>
|
||||||
<string name="info_labels"></string>
|
<string name="info_labels"/>
|
||||||
|
<string name="open_in_popup_mode">Abrir en ventanu emerxente</string>
|
||||||
|
<string name="popup_mode_share_menu_title">Mou de ventanu emerxente de NewPipe</string>
|
||||||
|
|
||||||
|
<string name="preferred_video_format_title">Formatu preferíu de videu</string>
|
||||||
|
<string name="black_theme_title">Prietu</string>
|
||||||
|
|
||||||
|
<string name="popup_playing_toast">Reproduciendo en ventanu emerxente</string>
|
||||||
|
<string name="all">Too</string>
|
||||||
|
<string name="channel">Canal</string>
|
||||||
|
<string name="yes">Sí</string>
|
||||||
|
<string name="later">Más sero</string>
|
||||||
|
<string name="disabled">Deshabilitóse</string>
|
||||||
|
|
||||||
|
<string name="use_old_player_title">Usar reproductor vieyu</string>
|
||||||
|
<string name="videos">vídeos</string>
|
||||||
|
<string name="subscriber">soscriptor</string>
|
||||||
|
<string name="subscriber_plural">soscriptores</string>
|
||||||
|
<string name="subscribe">Soscribise</string>
|
||||||
|
<string name="views">visiones</string>
|
||||||
|
<string name="short_thousand">M</string>
|
||||||
|
<string name="short_million">Mill</string>
|
||||||
|
<string name="short_billion">MMill</string>
|
||||||
|
<string name="restart_title">Reaniciar</string>
|
||||||
|
|
||||||
|
<string name="msg_restart">Tienes de reaniciar l\'aplicación p\'aplicar el tema.
|
||||||
|
|
||||||
|
¿Quies reaniciala agora?</string>
|
||||||
|
<string name="msg_popup_permission">Precísase esti permisu pa
|
||||||
|
abrir en ventanu emerxente</string>
|
||||||
|
|
||||||
|
<string name="action_settings">Axustes</string>
|
||||||
|
<string name="reCaptchaActivity">reCAPTCHA</string>
|
||||||
|
<string name="reCaptcha_title">Prueba reCAPTCHA</string>
|
||||||
|
<string name="recaptcha_request_toast">Prueba reCAPTCHA solicitada</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -135,7 +135,7 @@
|
|||||||
<string name="switch_mode">Zwischen Liste und Gitter umschalten</string>
|
<string name="switch_mode">Zwischen Liste und Gitter umschalten</string>
|
||||||
|
|
||||||
<string name="videos">Videos</string>
|
<string name="videos">Videos</string>
|
||||||
<string name="subscriber">Abonnenten</string>
|
<string name="subscriber">Abonnent</string>
|
||||||
<string name="views">Aufrufe</string>
|
<string name="views">Aufrufe</string>
|
||||||
<string name="short_thousand">Tsd.</string>
|
<string name="short_thousand">Tsd.</string>
|
||||||
<string name="short_million">Mio.</string>
|
<string name="short_million">Mio.</string>
|
||||||
@@ -186,4 +186,17 @@ Möchten Sie jetzt neu starten?</string>
|
|||||||
<string name="disabled">Deaktiviert</string>
|
<string name="disabled">Deaktiviert</string>
|
||||||
|
|
||||||
<string name="use_old_player_title">Benutze den alten Player</string>
|
<string name="use_old_player_title">Benutze den alten Player</string>
|
||||||
|
<string name="open_in_popup_mode">Im Popup-Modus öffnen</string>
|
||||||
|
<string name="preferred_video_format_title">Bevorzugtes Videoformat</string>
|
||||||
|
<string name="popup_playing_toast">Spiele im Popup-Modus ab</string>
|
||||||
|
<string name="popup_mode_share_menu_title">NewPipe Popup-Modus</string>
|
||||||
|
|
||||||
|
<string name="subscriber_plural">Abonnenten</string>
|
||||||
|
<string name="msg_popup_permission">Diese Berechtigung ist für das
|
||||||
|
Öffnen im Popup-Modus erforderlich</string>
|
||||||
|
|
||||||
|
<string name="use_old_player_summary">Mediaframework Player der vorherigen Version.</string>
|
||||||
|
<string name="default_popup_resolution_title">Standardauflösung des Popups</string>
|
||||||
|
<string name="show_higher_resolutions_title">Zeige höhere Auflösungen an</string>
|
||||||
|
<string name="show_higher_resolutions_summary">Nur einige Geräte unterstützen das Abspielen von 2k-/4k-Videos</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -19,11 +19,11 @@
|
|||||||
<string name="download_path_title">Ruta de descarga de vídeo</string>
|
<string name="download_path_title">Ruta de descarga de vídeo</string>
|
||||||
<string name="download_path_summary">Ruta para almacenar los vídeos descargados.</string>
|
<string name="download_path_summary">Ruta para almacenar los vídeos descargados.</string>
|
||||||
<string name="download_path_dialog_title">Introducir directorio de descargas para vídeos</string>
|
<string name="download_path_dialog_title">Introducir directorio de descargas para vídeos</string>
|
||||||
<string name="default_resolution_title">Resolución por defecto</string>
|
<string name="default_resolution_title">Resolución de vídeo por defecto</string>
|
||||||
<string name="play_with_kodi_title">Reproducir con Kodi</string>
|
<string name="play_with_kodi_title">Reproducir con Kodi</string>
|
||||||
<string name="kore_not_found">Aplicación Kore no encontrada. ¿Instalar Kore?</string>
|
<string name="kore_not_found">Aplicación Kore no encontrada. ¿Instalar Kore?</string>
|
||||||
<string name="show_play_with_kodi_title">Mostrar opción \"Reproducir con Kodi\"</string>
|
<string name="show_play_with_kodi_title">Mostrar opción \"Reproducir con Kodi\"</string>
|
||||||
<string name="show_play_with_kodi_summary">Muestra una opción para reproducir el vídeo con Kodi Media Center.</string>
|
<string name="show_play_with_kodi_summary">Mostrar una opción para reproducir el vídeo con Kodi Media Center.</string>
|
||||||
<string name="play_audio">Audio</string>
|
<string name="play_audio">Audio</string>
|
||||||
<string name="default_audio_format_title">Formato de audio por defecto</string>
|
<string name="default_audio_format_title">Formato de audio por defecto</string>
|
||||||
<string name="webm_description">WebM — formato libre</string>
|
<string name="webm_description">WebM — formato libre</string>
|
||||||
@@ -131,13 +131,13 @@
|
|||||||
<string name="checksum">Checksum</string>
|
<string name="checksum">Checksum</string>
|
||||||
|
|
||||||
<string name="add">Nueva misión</string>
|
<string name="add">Nueva misión</string>
|
||||||
<string name="finish">Listo</string>
|
<string name="finish">Ok</string>
|
||||||
<string name="switch_mode">Cambiar entre lista y cuadrícula</string>
|
<string name="switch_mode">Cambiar entre lista y cuadrícula</string>
|
||||||
|
|
||||||
|
|
||||||
<string name="msg_url">URL de descarga</string>
|
<string name="msg_url">URL de descarga</string>
|
||||||
<string name="msg_name">Nombre del archivo</string>
|
<string name="msg_name">Nombre del archivo</string>
|
||||||
<string name="msg_threads">Hilos de conexión</string>
|
<string name="msg_threads">Conexiones simultáneas</string>
|
||||||
<string name="msg_fetch_filename">Obtener nombre de archivo</string>
|
<string name="msg_fetch_filename">Obtener nombre de archivo</string>
|
||||||
<string name="msg_error">Error</string>
|
<string name="msg_error">Error</string>
|
||||||
<string name="msg_server_unsupported">Servidor no soportado</string>
|
<string name="msg_server_unsupported">Servidor no soportado</string>
|
||||||
@@ -191,4 +191,8 @@ abrir en modo popup</string>
|
|||||||
<string name="preferred_video_format_title">Formato de vídeo preferido</string>
|
<string name="preferred_video_format_title">Formato de vídeo preferido</string>
|
||||||
<string name="disabled">Desactivado</string>
|
<string name="disabled">Desactivado</string>
|
||||||
|
|
||||||
|
<string name="subscriber_plural">Suscriptores</string>
|
||||||
|
<string name="show_higher_resolutions_title">Mostrar resoluciones más altas</string>
|
||||||
|
<string name="show_higher_resolutions_summary">Solo algunos dispositivos soportan reproducción de vídeos de 2k/4k</string>
|
||||||
|
<string name="default_popup_resolution_title">Resolución predeterminada del popup</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -190,4 +190,8 @@ membuka di mode popup</string>
|
|||||||
<string name="disabled">Dinonaktifkan</string>
|
<string name="disabled">Dinonaktifkan</string>
|
||||||
|
|
||||||
<string name="preferred_video_format_title">Pilihan format video</string>
|
<string name="preferred_video_format_title">Pilihan format video</string>
|
||||||
|
<string name="subscriber_plural">subscriber</string>
|
||||||
|
<string name="default_popup_resolution_title">Resolusi popup bawaan</string>
|
||||||
|
<string name="show_higher_resolutions_title">Tampilkan resolusi yang lebih tinggi</string>
|
||||||
|
<string name="show_higher_resolutions_summary">Hanya perangkat tertentu yang mendukung pemutaran video 2k/4k</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -197,4 +197,8 @@
|
|||||||
<string name="disabled">無効</string>
|
<string name="disabled">無効</string>
|
||||||
|
|
||||||
<string name="preferred_video_format_title">お好みのビデオ フォーマット</string>
|
<string name="preferred_video_format_title">お好みのビデオ フォーマット</string>
|
||||||
|
<string name="subscriber_plural">購読者</string>
|
||||||
|
<string name="default_popup_resolution_title">デフォルトのポップアップ解像度</string>
|
||||||
|
<string name="show_higher_resolutions_title">高い解像度で表示</string>
|
||||||
|
<string name="show_higher_resolutions_summary">一部のデバイスのみ 2K/4K ビデオの再生をサポートしています</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -188,4 +188,8 @@ te openen in pop-upmodus</string>
|
|||||||
<string name="preferred_video_format_title">Voorkeursvideoformaat</string>
|
<string name="preferred_video_format_title">Voorkeursvideoformaat</string>
|
||||||
<string name="disabled">Uitgeschakeld</string>
|
<string name="disabled">Uitgeschakeld</string>
|
||||||
|
|
||||||
|
<string name="subscriber_plural">abonnees</string>
|
||||||
|
<string name="default_popup_resolution_title">Standaardresolutie voor pop-up</string>
|
||||||
|
<string name="show_higher_resolutions_title">Hogere resoluties weergeven</string>
|
||||||
|
<string name="show_higher_resolutions_summary">Video\'s afspelen in 2k/4k wordt maar op sommige apparaten ondersteund</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user