1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2026-01-14 02:32:40 +00:00

Compare commits

...

65 Commits

Author SHA1 Message Date
Christian Schabesberger
6f015349e8 moved on to v0.9.1 2017-04-08 17:17:58 +02:00
Christian Schabesberger
a37802c2b9 add prefered video format 2017-04-08 17:17:11 +02:00
Florian
4fa3baf5e1 Translated using Weblate (French)
Currently translated at 99.3% (158 of 159 strings)
2017-04-07 12:45:03 +02:00
Oscar Hemelaar
ef8c2c81d5 Translated using Weblate (Italian)
Currently translated at 98.7% (157 of 159 strings)
2017-04-07 03:45:31 +02:00
Mladen Pejaković
8c80d8c457 Translated using Weblate (Serbian)
Currently translated at 99.3% (158 of 159 strings)
2017-04-07 00:46:25 +02:00
Matej U
1596872c54 Translated using Weblate (Slovenian)
Currently translated at 100.0% (159 of 159 strings)
2017-04-05 14:47:22 +02:00
Oscar Hemelaar
da8873fa78 Translated using Weblate (French)
Currently translated at 99.3% (158 of 159 strings)
2017-04-05 12:14:32 +02:00
Oscar Hemelaar
ecd8439b3f Translated using Weblate (French)
Currently translated at 98.1% (156 of 159 strings)

if it's for a video restart button, it's "Relancer"
2017-04-05 02:53:08 +02:00
Oscar Hemelaar
5d178532ac Translated using Weblate (French)
Currently translated at 97.4% (155 of 159 strings)
2017-04-05 02:52:24 +02:00
Oscar Hemelaar
08e40a013d Translated using Weblate (French)
Currently translated at 96.8% (154 of 159 strings)

or "OK" ?
2017-04-05 02:50:49 +02:00
Oscar Hemelaar
c136f7363c Translated using Weblate (French)
Currently translated at 96.2% (153 of 159 strings)
2017-04-05 02:50:26 +02:00
Florian
a60f10d739 Translated using Weblate (French)
Currently translated at 90.5% (144 of 159 strings)
2017-04-04 21:45:44 +02:00
naofum
ae46afcb42 Translated using Weblate (Japanese)
Currently translated at 100.0% (159 of 159 strings)
2017-04-04 16:15:27 +02:00
mueller-ma
bffb9f6800 Translated using Weblate (German)
Currently translated at 95.5% (152 of 159 strings)
2017-04-03 09:29:35 +02:00
Tobias Groza
79aa9ad04b Translated using Weblate (German)
Currently translated at 94.9% (151 of 159 strings)
2017-04-03 09:26:41 +02:00
ff5714f04a Translated using Weblate (French)
Currently translated at 89.9% (143 of 159 strings)
2017-04-02 18:59:25 +02:00
zmni
ce499a9766 Translated using Weblate (Indonesian)
Currently translated at 100.0% (159 of 159 strings)
2017-04-02 16:49:33 +02:00
Weblate
d3bb8b7651 Merge remote-tracking branch 'origin/master' 2017-04-02 16:48:37 +02:00
Freddy Morán Jr
6d9c23c4cb Translated using Weblate (Spanish)
Currently translated at 100.0% (158 of 158 strings)
2017-04-02 16:48:36 +02:00
Nathan Follens
b95a9332a9 Translated using Weblate (Dutch)
Currently translated at 100.0% (158 of 158 strings)
2017-04-02 16:48:35 +02:00
zmni
609715eb5c Translated using Weblate (Indonesian)
Currently translated at 100.0% (158 of 158 strings)
2017-04-02 16:48:33 +02:00
Christian Schabesberger
a1266c919c Merge branch 'fix-next-video' of git://github.com/mauriciocolli/NewPipe into strfx 2017-04-02 15:56:41 +02:00
Christian Schabesberger
a1925a0302 Merge branch 'master' of github.com:theScrabi/NewPipe 2017-04-02 15:48:53 +02:00
Christian Schabesberger
a7a4c03372 update features list, and moved on to v0.9.0 2017-04-02 15:47:21 +02:00
Christian Schabesberger
37201600e0 Merge pull request #494 from mueller-ma/patch-1
Fix list in readme
2017-04-02 15:44:14 +02:00
mueller-ma
a94f40ed62 Fix list in readme 2017-04-02 14:33:52 +02:00
naofum
0b08cf8c76 Translated using Weblate (Japanese)
Currently translated at 100.0% (158 of 158 strings)
2017-04-01 13:36:04 +02:00
Mauricio Colli
33e29be7db Fix next video and refactor
- Refactor VideoItemDetailActivity, StreamExtractorWorker
- Remove redundant styles
- Change dimensions
- Nicer animation/transitions
2017-03-31 20:39:54 -03:00
Freddy Morán Jr
bd1c7851c7 Translated using Weblate (Portuguese)
Currently translated at 100.0% (158 of 158 strings)
2017-04-01 01:02:49 +02:00
Freddy Morán Jr
82faea5965 Translated using Weblate (Spanish)
Currently translated at 100.0% (158 of 158 strings)
2017-04-01 00:51:34 +02:00
Weblate
0bbbfd3217 Merge remote-tracking branch 'origin/master' 2017-04-01 00:44:52 +02:00
Freddy Morán Jr
fb578ecda8 Translated using Weblate (Spanish)
Currently translated at 100.0% (156 of 156 strings)
2017-04-01 00:44:41 +02:00
Christian Schabesberger
e804647a65 make exoplayer default player 2017-03-29 09:17:03 +02:00
Christian Schabesberger
c3c3a94593 Merge branch 'feature-popup-fullscreen' of git://github.com/mauriciocolli/NewPipe into toogle 2017-03-29 08:48:00 +02:00
Mauricio Colli
9d55569f80 Fix keep screen on 2017-03-28 09:12:03 -03:00
Christian Schabesberger
5dd8271c15 Merge pull request #489 from mauriciocolli/master
Fix travis and some clean-up
2017-03-27 23:53:04 +02:00
Mauricio Colli
7a4a54c3ea Fix travis
- Remove duplicate of AndroidManifest
- Remove some non-translatable strings from "ar" translation, and general clean-up of other
2017-03-27 16:34:37 -03:00
Mauricio Colli
7c9078a625 Fix non-commited file 2017-03-27 11:26:36 -03:00
Mauricio Colli
71ae342f52 Implement screen orientation toggle 2017-03-27 10:12:22 -03:00
Mauricio Colli
b43c56085d Implement fullscreen and quality selector
- Implement cache
- Abstract player
- Quality selector
- Fullscreen switcher
- Change some drawables
2017-03-27 01:08:16 -03:00
Christian Schabesberger
83d2ab95e0 Merge branch 'master' of github.com:TeamNewPipe/NewPipe 2017-03-26 17:29:25 +02:00
Christian Schabesberger
20a8d7372c fix broken overlay on .svg icon 2017-03-26 17:29:16 +02:00
Weblate
c36ba88db7 Merge remote-tracking branch 'origin/master' 2017-03-25 14:05:16 +01:00
Nathan Follens
4aa23023ee Translated using Weblate (Dutch)
Currently translated at 100.0% (156 of 156 strings)
2017-03-25 14:05:13 +01:00
Christian Schabesberger
8735cf931a update to new backend with playlist support 2017-03-22 20:14:56 +01:00
Weblate
2473069326 Merge remote-tracking branch 'origin/master' 2017-03-21 09:44:34 +01:00
vesp
03bab57a97 Translated using Weblate (Czech)
Currently translated at 99.3% (155 of 156 strings)
2017-03-21 09:44:32 +01:00
Christian Schabesberger
e3baf69533 fix channel rotation problem 2017-03-17 20:35:22 +01:00
Christian Schabesberger
461f747af1 add IOException 2017-03-17 20:18:44 +01:00
Christian Schabesberger
028872a7d8 Merge pull request #483 from mauriciocolli/patch-2
Fix bug when fetching unavailable content
2017-03-17 17:10:03 +01:00
Christian Schabesberger
a1483b6c55 Merge pull request #481 from mauriciocolli/patch-1
Handle embed links
2017-03-17 17:09:13 +01:00
mauriciocolli
e406ba094c Improve bug detection
- Show a message with the appropriate error (network error)
2017-03-17 12:09:20 -03:00
mauriciocolli
c100d15ba8 Fix bug when fetching unavailable content
- Fix #482
- When opening a invalid/deleted/unavailable video, the popup was just printing the error, now it shows a message to the user and exits
2017-03-17 11:50:27 -03:00
mauriciocolli
b4ea592638 Handle embed links
- Fix #480
- Add /embed/ to intent filter, as the app already handles these type of links internally
2017-03-17 00:27:59 -03:00
Sérgio Marques
16b757d9a3 Translated using Weblate (Portuguese)
Currently translated at 100.0% (156 of 156 strings)
2017-03-17 00:46:17 +01:00
Sérgio Marques
2aa801a392 Translated using Weblate (Portuguese)
Currently translated at 100.0% (156 of 156 strings)
2017-03-14 23:24:24 +01:00
Matej U
b838344526 Translated using Weblate (Slovenian)
Currently translated at 100.0% (156 of 156 strings)
2017-03-13 10:30:41 +01:00
zmni
233a3df222 Translated using Weblate (Indonesian)
Currently translated at 100.0% (156 of 156 strings)
2017-03-11 13:27:00 +01:00
naofum
da6661b1ea Translated using Weblate (Japanese)
Currently translated at 100.0% (156 of 156 strings)
2017-03-11 12:37:49 +01:00
Mladen Pejaković
c6e120fc51 Translated using Weblate (Serbian)
Currently translated at 100.0% (156 of 156 strings)
2017-03-10 23:09:42 +01:00
Marian Hanzel
c416a1254d Translated using Weblate (Slovak)
Currently translated at 100.0% (156 of 156 strings)
2017-03-10 23:07:10 +01:00
Weblate
2aa4f6ddda Merge remote-tracking branch 'origin/master' 2017-03-10 21:45:11 +01:00
Enrico Monese
129597023d Translated using Weblate (Italian)
Currently translated at 100.0% (154 of 154 strings)
2017-03-10 21:45:10 +01:00
vesp
02aed86b7e Translated using Weblate (Czech)
Currently translated at 98.7% (152 of 154 strings)
2017-03-10 21:45:10 +01:00
Tobias Groza
e44f4b5823 Translated using Weblate (German)
Currently translated at 98.7% (152 of 154 strings)
2017-03-10 21:45:08 +01:00
119 changed files with 4316 additions and 4788 deletions

View File

@@ -11,29 +11,9 @@ android:
- android-25
# Additional components
- extra-android-support
- extra-android-m2repository
- extra-google-m2repository
# Emulators
- sys-img-armeabi-v7a-android-21
- sys-img-armeabi-v7a-android-19
- sys-img-armeabi-v7a-android-15
env:
global:
- ADB_INSTALL_TIMEOUT=8 # minutes (2 by default)
- GRADLE_OPTS=-Xmx512m # give gradle more memory since it seem to fail otherwise
matrix:
- ANDROID_TARGET=android-21 ANDROID_ABI=armeabi-v7a
before_script:
- echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI
- emulator -avd test -no-skin -no-audio -no-window &
- android-wait-for-emulator
- adb shell input keyevent 82 &
script: ./gradlew --info build connectedCheck
script: ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleDebug lintDebug testDebugUnitTest
licenses:
- '.+'

View File

@@ -37,23 +37,27 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
* Listen to YouTube videos (experimental)
* Select the streaming player to watch the video with
* Download videos
* Download audio only * Open a video in Kodi
* Download audio only
* Open a video in Kodi
* Show Next/Related videos
* Search YouTube in a specific language
* Watch age restricted material
* Display general information about channels
* Search channels
* Watch videos from a channel
* Orbot/Tor support (not yet directly)
### Coming Features
* Orbot/Tor support
* Bookmarks
* View history
* Search history
* Subscribe to channels
* Watch videos from a channel
* Search/Watch Playlists
* Queeing videos
* Subtitles support
* 1080p support
* livestream support
* ... and many more
### Multiservice support

View File

@@ -8,8 +8,8 @@ android {
applicationId "org.schabi.newpipe"
minSdkVersion 15
targetSdkVersion 25
versionCode 26
versionName "0.8.12"
versionCode 28
versionName "0.9.1"
}
buildTypes {
release {
@@ -32,6 +32,9 @@ android {
dependencies {
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:1.10.19'
testCompile 'org.json:json:20160810'
compile 'com.android.support:appcompat-v7:25.1.0'
compile 'com.android.support:support-v4:25.1.0'
compile 'com.android.support:design:25.1.0'
@@ -45,8 +48,5 @@ dependencies {
compile 'com.google.code.gson:gson:2.4'
compile 'com.nononsenseapps:filepicker:3.0.0'
compile 'ch.acra:acra:4.9.0'
compile 'com.devbrackets.android:exomedia:3.1.1'
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:1.10.19'
testCompile 'org.json:json:20160810'
compile 'com.google.android.exoplayer:exoplayer:r2.3.1'
}

View File

@@ -51,24 +51,7 @@
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:label="@string/app_name"
android:launchMode="singleInstance"
android:theme="@style/PlayerTheme">
<intent-filter>
<action android:name="org.schabi.newpipe.exoplayer.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:scheme="content" />
<data android:scheme="asset" />
<data android:scheme="file" />
</intent-filter>
</activity>
<service
android:name=".player.BackgroundPlayer"
android:exported="false"
android:label="@string/background_player_name" />
android:theme="@style/PlayerTheme"/>
<activity
android:name=".settings.SettingsActivity"
@@ -138,6 +121,7 @@
<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 -->
@@ -179,7 +163,7 @@
<activity android:name=".PopupActivity"
android:theme="@android:style/Theme.NoDisplay"
android:label="NewPipe Popup mode">
android:label="@string/popup_mode_share_menu_title">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
@@ -195,6 +179,7 @@
<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 -->
@@ -238,4 +223,4 @@
</application>
</manifest>
</manifest>

View File

@@ -19,7 +19,7 @@ import android.widget.Toast;
import com.nostra13.universalimageloader.core.ImageLoader;
import org.schabi.newpipe.detail.VideoItemDetailFragment;
import org.schabi.newpipe.detail.VideoItemDetailActivity;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.channel.ChannelExtractor;
@@ -31,10 +31,12 @@ import org.schabi.newpipe.info_list.InfoListAdapter;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.settings.SettingsActivity;
import org.schabi.newpipe.util.NavStack;
import java.io.IOException;
import static android.os.Build.VERSION.SDK_INT;
import org.schabi.newpipe.util.ThemeHelper;
import java.io.IOException;
import static android.os.Build.VERSION.SDK_INT;
/**
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
@@ -147,6 +149,7 @@ public class ChannelActivity extends AppCompatActivity {
serviceId = savedInstanceState.getInt(NavStack.SERVICE_ID);
NavStack.getInstance()
.restoreSavedInstanceState(savedInstanceState);
handleIntent(getIntent());
}
}
@@ -298,7 +301,7 @@ public class ChannelActivity extends AppCompatActivity {
postNewErrorToast(h, R.string.network_error);
ioe.printStackTrace();
} catch(ParsingException pe) {
ErrorActivity.reportError(h, ChannelActivity.this, pe, VideoItemDetailFragment.class, null,
ErrorActivity.reportError(h, ChannelActivity.this, pe, VideoItemDetailActivity.class, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_CHANNEL,
service.getServiceInfo().name, channelUrl, R.string.parsing_error));
h.post(new Runnable() {
@@ -313,7 +316,7 @@ public class ChannelActivity extends AppCompatActivity {
if(service != null) {
name = service.getServiceInfo().name;
}
ErrorActivity.reportError(h, ChannelActivity.this, ex, VideoItemDetailFragment.class, null,
ErrorActivity.reportError(h, ChannelActivity.this, ex, VideoItemDetailActivity.class, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_CHANNEL,
name, channelUrl, R.string.parsing_error));
h.post(new Runnable() {
@@ -324,7 +327,7 @@ public class ChannelActivity extends AppCompatActivity {
});
ex.printStackTrace();
} catch(Exception e) {
ErrorActivity.reportError(h, ChannelActivity.this, e, VideoItemDetailFragment.class, null,
ErrorActivity.reportError(h, ChannelActivity.this, e, VideoItemDetailActivity.class, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_CHANNEL,
service.getServiceInfo().name, channelUrl, R.string.general_error));
h.post(new Runnable() {

View File

@@ -2,14 +2,12 @@ package org.schabi.newpipe;
import android.app.Activity;
import android.content.Intent;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.Toast;
import org.schabi.newpipe.detail.VideoItemDetailActivity;
import org.schabi.newpipe.detail.VideoItemDetailFragment;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.util.NavStack;
@@ -136,7 +134,7 @@ public class RouterActivity extends Activity {
break;
case STREAM:
callIntent.setClass(this, VideoItemDetailActivity.class);
callIntent.putExtra(VideoItemDetailFragment.AUTO_PLAY,
callIntent.putExtra(VideoItemDetailActivity.AUTO_PLAY,
PreferenceManager.getDefaultSharedPreferences(this)
.getBoolean(
getString(R.string.autoplay_through_intent_key), false));

View File

@@ -119,15 +119,32 @@ class ActionBarHandler {
.getString(activity.getString(R.string.default_resolution_key),
activity.getString(R.string.default_resolution_value));
String preferedFormat = defaultPreferences
.getString(activity.getString(R.string.preferred_video_format_key),
activity.getString(R.string.preferred_video_format_default));
// first try to find the one with the right resolution
int selectedFormat = 0;
for (int i = 0; i < videoStreams.size(); i++) {
VideoStream item = videoStreams.get(i);
if (defaultResolution.equals(item.resolution)) {
return i;
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 (defaultResolution.equals(item.resolution)
&& preferedFormat.equals(MediaFormat.getNameById(item.format))) {
selectedFormat = i;
}
}
// this is actually an error,
// but maybe there is really no stream fitting to the default value.
return 0;
return selectedFormat;
}
public void setupMenu(Menu menu, MenuInflater inflater) {

View File

@@ -0,0 +1,250 @@
package org.schabi.newpipe.detail;
import android.app.Activity;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
import org.schabi.newpipe.extractor.stream_info.StreamExtractor;
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
import org.schabi.newpipe.report.ErrorActivity;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Extract {@link StreamInfo} with {@link StreamExtractor} from the given url of the given service
*/
@SuppressWarnings("WeakerAccess")
public class StreamExtractorWorker extends Thread {
private static final String TAG = "StreamExtractorWorker";
private Activity activity;
private final String videoUrl;
private final int serviceId;
private OnStreamInfoReceivedListener callback;
private final AtomicBoolean isRunning = new AtomicBoolean(false);
private final Handler handler = new Handler();
public interface OnStreamInfoReceivedListener {
void onReceive(StreamInfo info);
void onError(int messageId);
void onReCaptchaException();
void onBlockedByGemaError();
void onContentErrorWithMessage(int messageId);
void onContentError();
}
public StreamExtractorWorker(Activity activity, String videoUrl, int serviceId, OnStreamInfoReceivedListener callback) {
this.serviceId = serviceId;
this.videoUrl = videoUrl;
this.activity = activity;
this.callback = callback;
}
/**
* Returns a new instance <b>already</b> started of {@link StreamExtractorWorker}.<br>
* The caller is responsible to check if {@link StreamExtractorWorker#isRunning()}, or {@link StreamExtractorWorker#cancel()} it
*
* @param serviceId id of the request service
* @param url videoUrl of the service (e.g. https://www.youtube.com/watch?v=HyHNuVaZJ-k)
* @param activity activity for error reporting purposes
* @param callback listener that will be called-back when events occur (check {@link OnStreamInfoReceivedListener})
* @return new instance already started of {@link StreamExtractorWorker}
*/
public static StreamExtractorWorker startExtractorThread(int serviceId, String url, Activity activity, OnStreamInfoReceivedListener callback) {
StreamExtractorWorker extractorThread = getExtractorThread(serviceId, url, activity, callback);
extractorThread.start();
return extractorThread;
}
/**
* Returns a new instance of {@link StreamExtractorWorker}.<br>
* The caller is responsible to check if {@link StreamExtractorWorker#isRunning()}, or {@link StreamExtractorWorker#cancel()}
* when it doesn't need it anymore
* <p>
* <b>Note:</b> this instance is <b>not</b> started yet
*
* @param serviceId id of the request service
* @param url videoUrl of the service (e.g. https://www.youtube.com/watch?v=HyHNuVaZJ-k)
* @param activity activity for error reporting purposes
* @param callback listener that will be called-back when events occur (check {@link OnStreamInfoReceivedListener})
* @return instance of {@link StreamExtractorWorker}
*/
public static StreamExtractorWorker getExtractorThread(int serviceId, String url, Activity activity, OnStreamInfoReceivedListener callback) {
return new StreamExtractorWorker(activity, url, serviceId, callback);
}
@Override
//Just ignore the errors for now
@SuppressWarnings("ConstantConditions")
public void run() {
// TODO: Improve error checking
// and this method in general
StreamInfo streamInfo = null;
StreamingService service;
try {
service = NewPipe.getService(serviceId);
} catch (Exception e) {
e.printStackTrace();
ErrorActivity.reportError(handler, activity, e, VideoItemDetailActivity.class, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
"", videoUrl, R.string.could_not_get_stream));
return;
}
try {
isRunning.set(true);
StreamExtractor streamExtractor = service.getExtractorInstance(videoUrl);
streamInfo = StreamInfo.getVideoInfo(streamExtractor);
final StreamInfo info = streamInfo;
if (callback != null) handler.post(new Runnable() {
@Override
public void run() {
callback.onReceive(info);
}
});
isRunning.set(false);
// look for errors during extraction
// this if statement only covers extra information.
// if these are not available or caused an error, they are just not available
// but don't render the stream information unusalbe.
if (streamInfo != null && !streamInfo.errors.isEmpty()) {
Log.e(TAG, "OCCURRED ERRORS DURING EXTRACTION:");
for (Throwable e : streamInfo.errors) {
e.printStackTrace();
Log.e(TAG, "------");
}
View rootView = activity != null ? activity.findViewById(R.id.video_item_detail) : null;
ErrorActivity.reportError(handler, activity,
streamInfo.errors, null, rootView,
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
service.getServiceInfo().name, videoUrl, 0 /* no message for the user */));
}
// These errors render the stream information unusable.
} catch (ReCaptchaException e) {
if (callback != null) handler.post(new Runnable() {
@Override
public void run() {
callback.onReCaptchaException();
}
});
} catch (IOException e) {
if (callback != null) handler.post(new Runnable() {
@Override
public void run() {
callback.onError(R.string.network_error);
}
});
if (callback != null) e.printStackTrace();
} catch (YoutubeStreamExtractor.DecryptException de) {
// custom service related exceptions
ErrorActivity.reportError(handler, activity, de, VideoItemDetailActivity.class, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
service.getServiceInfo().name, videoUrl, R.string.youtube_signature_decryption_error));
handler.post(new Runnable() {
@Override
public void run() {
activity.finish();
}
});
de.printStackTrace();
} catch (YoutubeStreamExtractor.GemaException ge) {
if (callback != null) handler.post(new Runnable() {
@Override
public void run() {
callback.onBlockedByGemaError();
}
});
} catch (YoutubeStreamExtractor.LiveStreamException e) {
if (callback != null) handler.post(new Runnable() {
@Override
public void run() {
callback.onContentErrorWithMessage(R.string.live_streams_not_supported);
}
});
}
// ----------------------------------------
catch (StreamExtractor.ContentNotAvailableException e) {
if (callback != null) handler.post(new Runnable() {
@Override
public void run() {
callback.onContentError();
}
});
e.printStackTrace();
} catch (StreamInfo.StreamExctractException e) {
if (!streamInfo.errors.isEmpty()) {
// !!! if this case ever kicks in someone gets kicked out !!!
ErrorActivity.reportError(handler, activity, e, VideoItemDetailActivity.class, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream));
} else {
ErrorActivity.reportError(handler, activity, streamInfo.errors, VideoItemDetailActivity.class, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream));
}
handler.post(new Runnable() {
@Override
public void run() {
activity.finish();
}
});
e.printStackTrace();
} catch (ParsingException e) {
ErrorActivity.reportError(handler, activity, e, VideoItemDetailActivity.class, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
service.getServiceInfo().name, videoUrl, R.string.parsing_error));
handler.post(new Runnable() {
@Override
public void run() {
activity.finish();
}
});
e.printStackTrace();
} catch (Exception e) {
ErrorActivity.reportError(handler, activity, e, VideoItemDetailActivity.class, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
service.getServiceInfo().name, videoUrl, R.string.general_error));
handler.post(new Runnable() {
@Override
public void run() {
activity.finish();
}
});
e.printStackTrace();
}
}
/**
* Return true if the extraction is not completed yet
*
* @return the value of the AtomicBoolean {@link #isRunning}
*/
public boolean isRunning() {
return isRunning.get();
}
/**
* Cancel this ExtractorThread, setting the callback to null, the AtomicBoolean {@link #isRunning} to false and interrupt this thread.
* <p>
* <b>Note:</b> Any I/O that is active in the moment that this method is called will be canceled and a Exception will be thrown, because of the {@link #interrupt()}.<br>
* This is useful when you don't want the resulting {@link StreamInfo} anymore, but don't want to waste bandwidth, otherwise it'd run till it receives the StreamInfo.
*/
public void cancel() {
this.callback = null;
this.isRunning.set(false);
this.interrupt();
}
}

View File

@@ -1,230 +0,0 @@
package org.schabi.newpipe.detail;
import android.app.Activity;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.stream_info.StreamExtractor;
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
import java.io.IOException;
/**
* Created by Christian Schabesberger on 02.08.16.
*
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
* StreamInfoWorker.java is part of NewPipe.
*
* NewPipe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NewPipe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public class StreamInfoWorker {
private static final String TAG = StreamInfoWorker.class.toString();
public interface OnStreamInfoReceivedListener {
void onReceive(StreamInfo info);
void onError(int messageId);
void onReCaptchaException();
void onBlockedByGemaError();
void onContentErrorWithMessage(int messageId);
void onContentError();
}
private class StreamExtractorRunnable implements Runnable {
private final Handler h = new Handler();
private StreamExtractor streamExtractor;
private final int serviceId;
private final String videoUrl;
private Activity a;
public StreamExtractorRunnable(Activity a, String videoUrl, int serviceId) {
this.serviceId = serviceId;
this.videoUrl = videoUrl;
this.a = a;
}
@Override
public void run() {
StreamInfo streamInfo = null;
StreamingService service = null;
try {
service = NewPipe.getService(serviceId);
} catch (Exception e) {
e.printStackTrace();
ErrorActivity.reportError(h, a, e, VideoItemDetailFragment.class, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
"", videoUrl, R.string.could_not_get_stream));
return;
}
try {
streamExtractor = service.getExtractorInstance(videoUrl);
streamInfo = StreamInfo.getVideoInfo(streamExtractor);
final StreamInfo info = streamInfo;
h.post(new Runnable() {
@Override
public void run() {
onStreamInfoReceivedListener.onReceive(info);
}
});
// look for errors during extraction
// this if statement only covers extra information.
// if these are not available or caused an error, they are just not available
// but don't render the stream information unusalbe.
if(streamInfo != null &&
!streamInfo.errors.isEmpty()) {
Log.e(TAG, "OCCURRED ERRORS DURING EXTRACTION:");
for (Throwable e : streamInfo.errors) {
e.printStackTrace();
Log.e(TAG, "------");
}
View rootView = a != null ? a.findViewById(R.id.video_item_detail) : null;
ErrorActivity.reportError(h, a,
streamInfo.errors, null, rootView,
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
service.getServiceInfo().name, videoUrl, 0 /* no message for the user */));
}
// These errors render the stream information unusable.
} catch (ReCaptchaException e) {
h.post(new Runnable() {
@Override
public void run() {
onStreamInfoReceivedListener.onReCaptchaException();
}
});
} catch (IOException e) {
h.post(new Runnable() {
@Override
public void run() {
onStreamInfoReceivedListener.onError(R.string.network_error);
}
});
e.printStackTrace();
} catch (YoutubeStreamExtractor.DecryptException de) {
// custom service related exceptions
ErrorActivity.reportError(h, a, de, VideoItemDetailFragment.class, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
service.getServiceInfo().name, videoUrl, R.string.youtube_signature_decryption_error));
h.post(new Runnable() {
@Override
public void run() {
a.finish();
}
});
de.printStackTrace();
} catch (YoutubeStreamExtractor.GemaException ge) {
h.post(new Runnable() {
@Override
public void run() {
onStreamInfoReceivedListener.onBlockedByGemaError();
}
});
} catch(YoutubeStreamExtractor.LiveStreamException e) {
h.post(new Runnable() {
@Override
public void run() {
onStreamInfoReceivedListener
.onContentErrorWithMessage(R.string.live_streams_not_supported);
}
});
}
// ----------------------------------------
catch(StreamExtractor.ContentNotAvailableException e) {
h.post(new Runnable() {
@Override
public void run() {
onStreamInfoReceivedListener
.onContentError();
}
});
e.printStackTrace();
} catch(StreamInfo.StreamExctractException e) {
if(!streamInfo.errors.isEmpty()) {
// !!! if this case ever kicks in someone gets kicked out !!!
ErrorActivity.reportError(h, a, e, VideoItemDetailFragment.class, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream));
} else {
ErrorActivity.reportError(h, a, streamInfo.errors, VideoItemDetailFragment.class, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream));
}
h.post(new Runnable() {
@Override
public void run() {
a.finish();
}
});
e.printStackTrace();
} catch (ParsingException e) {
ErrorActivity.reportError(h, a, e, VideoItemDetailFragment.class, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
service.getServiceInfo().name, videoUrl, R.string.parsing_error));
h.post(new Runnable() {
@Override
public void run() {
a.finish();
}
});
e.printStackTrace();
} catch(Exception e) {
ErrorActivity.reportError(h, a, e, VideoItemDetailFragment.class, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
service.getServiceInfo().name, videoUrl, R.string.general_error));
h.post(new Runnable() {
@Override
public void run() {
a.finish();
}
});
e.printStackTrace();
}
}
}
private static StreamInfoWorker streamInfoWorker = null;
private StreamExtractorRunnable runnable = null;
private OnStreamInfoReceivedListener onStreamInfoReceivedListener = null;
private StreamInfoWorker() {
}
public static StreamInfoWorker getInstance() {
return streamInfoWorker == null ? (streamInfoWorker = new StreamInfoWorker()) : streamInfoWorker;
}
public void search(int serviceId, String url, Activity a) {
runnable = new StreamExtractorRunnable(a, url, serviceId);
Thread thread = new Thread(runnable);
thread.start();
}
public void setOnStreamInfoReceivedListener(
OnStreamInfoReceivedListener onStreamInfoReceivedListener) {
this.onStreamInfoReceivedListener = onStreamInfoReceivedListener;
}
}

View File

@@ -1,77 +1,167 @@
package org.schabi.newpipe.detail;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.util.TypedValue;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import org.schabi.newpipe.App;
import com.nirhart.parallaxscroll.views.ParallaxScrollView;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
import org.schabi.newpipe.ActivityCommunicator;
import org.schabi.newpipe.ImageErrorLoadingListener;
import org.schabi.newpipe.Localization;
import org.schabi.newpipe.R;
import org.schabi.newpipe.ReCaptchaActivity;
import org.schabi.newpipe.download.DownloadDialog;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.stream_info.AudioStream;
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
import org.schabi.newpipe.extractor.stream_info.VideoStream;
import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.player.AbstractPlayer;
import org.schabi.newpipe.player.BackgroundPlayer;
import org.schabi.newpipe.player.ExoPlayerActivity;
import org.schabi.newpipe.player.PlayVideoActivity;
import org.schabi.newpipe.player.PopupVideoPlayer;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.util.NavStack;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ThemeHelper;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
* VideoItemDetailActivity.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/>.
*/
@SuppressWarnings("FieldCanBeLocal")
public class VideoItemDetailActivity extends AppCompatActivity implements StreamExtractorWorker.OnStreamInfoReceivedListener, SharedPreferences.OnSharedPreferenceChangeListener {
public class VideoItemDetailActivity extends AppCompatActivity {
private static final String TAG = VideoItemDetailActivity.class.toString();
private static final String TAG = "VideoItemDetailActivity";
private static final String KORE_PACKET = "org.xbmc.kore";
private VideoItemDetailFragment fragment;
/**
* The fragment argument representing the item ID that this fragment
* represents.
*/
public static final String AUTO_PLAY = "auto_play";
private ActionBarHandler actionBarHandler;
private InfoItemBuilder infoItemBuilder = null;
private StreamInfo currentStreamInfo = null;
private StreamExtractorWorker curExtractorThread;
private String videoUrl;
private int currentStreamingService = -1;
private int serviceId = -1;
protected void onCreate(Bundle savedInstanceState) {
private AtomicBoolean isLoading = new AtomicBoolean(false);
private boolean needUpdate = false;
private boolean autoPlayEnabled;
private boolean showRelatedStreams;
private ImageLoader imageLoader = ImageLoader.getInstance();
private DisplayImageOptions displayImageOptions =
new DisplayImageOptions.Builder().displayer(new FadeInBitmapDisplayer(400)).cacheInMemory(true).build();
private Bitmap streamThumbnail = null;
/*//////////////////////////////////////////////////////////////////////////
// Views
//////////////////////////////////////////////////////////////////////////*/
private ProgressBar loadingProgressBar;
private ParallaxScrollView parallaxScrollRootView;
private RelativeLayout contentRootLayout;
private Button thumbnailBackgroundButton;
private ImageView thumbnailImageView;
private ImageView thumbnailPlayButton;
private View videoTitleRoot;
private TextView videoTitleTextView;
private ImageView videoTitleToggleArrow;
private TextView videoCountView;
private RelativeLayout videoDescriptionRootLayout;
private TextView videoUploadDateView;
private TextView videoDescriptionView;
private Button uploaderButton;
private TextView uploaderTextView;
private ImageView uploaderThumb;
private TextView thumbsUpTextView;
private ImageView thumbsUpImageView;
private TextView thumbsDownTextView;
private ImageView thumbsDownImageView;
private TextView thumbsDisabledTextView;
private TextView nextStreamTitle;
private RelativeLayout relatedStreamRootLayout;
private LinearLayout relatedStreamsView;
/*//////////////////////////////////////////////////////////////////////////
// Activity's Lifecycle
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
showRelatedStreams = PreferenceManager.getDefaultSharedPreferences(this).getBoolean(getString(R.string.show_next_video_key), true);
PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this);
ThemeHelper.setTheme(this, true);
setContentView(R.layout.activity_videoitem_detail);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
// Show the Up button in the action bar.
try {
//noinspection ConstantConditions
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} catch(Exception e) {
Log.d(TAG, "Could not get SupportActionBar");
e.printStackTrace();
}
// savedInstanceState is non-null when there is fragment state
// saved from previous configurations of this activity
// (e.g. when rotating the screen from portrait to landscape).
// In this case, the fragment will automatically be re-added
// to its container so we don't need to manually add it.
// For more information, see the Fragments API guide at:
//
// http://developer.android.com/guide/components/fragments.html
//
if (getSupportActionBar() != null) getSupportActionBar().setDisplayHomeAsUpEnabled(true);
else Log.e(TAG, "Could not get SupportActionBar");
if (savedInstanceState == null) {
handleIntent(getIntent());
} else {
videoUrl = savedInstanceState.getString(NavStack.URL);
currentStreamingService = savedInstanceState.getInt(NavStack.SERVICE_ID);
NavStack.getInstance()
.restoreSavedInstanceState(savedInstanceState);
addFragment(savedInstanceState);
initViews();
initListeners();
handleIntent(getIntent());
}
@Override
protected void onResume() {
super.onResume();
// Currently only used for enable/disable related videos
// but can be extended for other live settings change
if (needUpdate) {
if (relatedStreamsView != null) initRelatedVideos(currentStreamInfo);
needUpdate = false;
}
}
@@ -82,74 +172,696 @@ public class VideoItemDetailActivity extends AppCompatActivity {
handleIntent(intent);
}
private void handleIntent(Intent intent) {
Bundle arguments = new Bundle();
boolean autoplay = false;
videoUrl = intent.getStringExtra(NavStack.URL);
currentStreamingService = intent.getIntExtra(NavStack.SERVICE_ID, -1);
if(intent.hasExtra(VideoItemDetailFragment.AUTO_PLAY)) {
arguments.putBoolean(VideoItemDetailFragment.AUTO_PLAY,
intent.getBooleanExtra(VideoItemDetailFragment.AUTO_PLAY, false));
}
arguments.putString(NavStack.URL, videoUrl);
arguments.putInt(NavStack.SERVICE_ID, currentStreamingService);
addFragment(arguments);
}
private void addFragment(final Bundle arguments) {
// Create the detail fragment and add it to the activity
// using a fragment transaction.
fragment = new VideoItemDetailFragment();
fragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction()
.replace(R.id.videoitem_detail_container, fragment)
.commit();
}
@Override
public void onResume() {
super.onResume();
App.checkStartTor(this);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(NavStack.URL, videoUrl);
outState.putInt(NavStack.SERVICE_ID, currentStreamingService);
outState.putBoolean(VideoItemDetailFragment.AUTO_PLAY, false);
NavStack.getInstance()
.onSaveInstanceState(outState);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
int id = item.getItemId();
if (id == android.R.id.home) {
// This ID represents the Home or Up button. In the case of this
// activity, the Up button is shown. Use NavUtils to allow users
// to navigate up one level in the application structure. For
// more details, see the Navigation pattern on Android Design:
// http://developer.android.com/design/patterns/navigation.html#up-vs-back
NavStack.getInstance()
.openMainActivity(this);
return true;
} else {
return super.onOptionsItemSelected(item);
}
}
@Override
public void onBackPressed() {
try {
NavStack.getInstance()
.navBack(this);
NavStack.getInstance().navBack(this);
} catch (Exception e) {
ErrorActivity.reportUiError(this, e);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case ReCaptchaActivity.RECAPTCHA_REQUEST:
if (resultCode == RESULT_OK) {
String videoUrl = getIntent().getStringExtra(NavStack.URL);
NavStack.getInstance().openDetailActivity(this, videoUrl, serviceId);
} else Log.e(TAG, "ReCaptcha failed");
break;
default:
Log.e(TAG, "Request code from activity not supported [" + requestCode + "]");
break;
}
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(getString(R.string.show_next_video_key))) {
showRelatedStreams = sharedPreferences.getBoolean(key, true);
needUpdate = true;
}
}
/*//////////////////////////////////////////////////////////////////////////
// Init
//////////////////////////////////////////////////////////////////////////*/
public void initViews() {
loadingProgressBar = (ProgressBar) findViewById(R.id.detail_loading_progress_bar);
parallaxScrollRootView = (ParallaxScrollView) findViewById(R.id.detail_main_content);
//thumbnailRootLayout = (RelativeLayout) findViewById(R.id.detail_thumbnail_root_layout);
thumbnailBackgroundButton = (Button) findViewById(R.id.detail_stream_thumbnail_background_button);
thumbnailImageView = (ImageView) findViewById(R.id.detail_thumbnail_image_view);
thumbnailPlayButton = (ImageView) findViewById(R.id.detail_thumbnail_play_button);
contentRootLayout = (RelativeLayout) findViewById(R.id.detail_content_root_layout);
videoTitleRoot = findViewById(R.id.detail_title_root_layout);
videoTitleTextView = (TextView) findViewById(R.id.detail_video_title_view);
videoTitleToggleArrow = (ImageView) findViewById(R.id.detail_toggle_description_view);
videoCountView = (TextView) findViewById(R.id.detail_view_count_view);
videoDescriptionRootLayout = (RelativeLayout) findViewById(R.id.detail_description_root_layout);
videoUploadDateView = (TextView) findViewById(R.id.detail_upload_date_view);
videoDescriptionView = (TextView) findViewById(R.id.detail_description_view);
//thumbsRootLayout = (LinearLayout) findViewById(R.id.detail_thumbs_root_layout);
thumbsUpTextView = (TextView) findViewById(R.id.detail_thumbs_up_count_view);
thumbsUpImageView = (ImageView) findViewById(R.id.detail_thumbs_up_img_view);
thumbsDownTextView = (TextView) findViewById(R.id.detail_thumbs_down_count_view);
thumbsDownImageView = (ImageView) findViewById(R.id.detail_thumbs_down_img_view);
thumbsDisabledTextView = (TextView) findViewById(R.id.detail_thumbs_disabled_view);
//uploaderRootLayout = (FrameLayout) findViewById(R.id.detail_uploader_root_layout);
uploaderButton = (Button) findViewById(R.id.detail_uploader_button);
uploaderTextView = (TextView) findViewById(R.id.detail_uploader_text_view);
uploaderThumb = (ImageView) findViewById(R.id.detail_uploader_thumbnail_view);
relatedStreamRootLayout = (RelativeLayout) findViewById(R.id.detail_related_streams_root_layout);
nextStreamTitle = (TextView) findViewById(R.id.detail_next_stream_title);
relatedStreamsView = (LinearLayout) findViewById(R.id.detail_related_streams_view);
actionBarHandler = new ActionBarHandler(this);
actionBarHandler.setupNavMenu(this);
videoDescriptionView.setMovementMethod(LinkMovementMethod.getInstance());
infoItemBuilder = new InfoItemBuilder(this, findViewById(android.R.id.content));
setHeightThumbnail();
}
private void initListeners() {
videoTitleRoot.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (videoDescriptionRootLayout.getVisibility() == View.VISIBLE) {
videoTitleTextView.setMaxLines(1);
videoDescriptionRootLayout.setVisibility(View.GONE);
videoTitleToggleArrow.setImageResource(R.drawable.arrow_down);
} else {
videoTitleTextView.setMaxLines(10);
videoDescriptionRootLayout.setVisibility(View.VISIBLE);
videoTitleToggleArrow.setImageResource(R.drawable.arrow_up);
}
}
});
thumbnailBackgroundButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!isLoading.get() && currentStreamInfo != null) playVideo(currentStreamInfo);
}
});
infoItemBuilder.setOnStreamInfoItemSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() {
@Override
public void selected(String url, int serviceId) {
NavStack.getInstance().openDetailActivity(VideoItemDetailActivity.this, url, serviceId);
}
});
uploaderButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
NavStack.getInstance().openChannelActivity(VideoItemDetailActivity.this, currentStreamInfo.channel_url, currentStreamInfo.service_id);
}
});
}
private void initThumbnailViews(StreamInfo info) {
if (info.thumbnail_url != null && !info.thumbnail_url.isEmpty()) {
imageLoader.displayImage(info.thumbnail_url, thumbnailImageView,
displayImageOptions, new SimpleImageLoadingListener() {
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
streamThumbnail = loadedImage;
if (streamThumbnail != null) {
// TODO: Change the thumbnail implementation
// When the thumbnail is not loaded yet, it not passes to the service in time
// so, I can notify the service through a broadcast, but the problem is
// when I click in another video, another thumbnail will be load, and will
// notify again, so I send the videoUrl and compare with the service's url
ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail = streamThumbnail;
Intent intent = new Intent(AbstractPlayer.ACTION_UPDATE_THUMB);
intent.putExtra(AbstractPlayer.VIDEO_URL, currentStreamInfo.webpage_url);
sendBroadcast(intent);
}
}
@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
ErrorActivity.reportError(VideoItemDetailActivity.this,
failReason.getCause(), null, findViewById(android.R.id.content),
ErrorActivity.ErrorInfo.make(ErrorActivity.LOAD_IMAGE,
NewPipe.getNameOfService(currentStreamInfo.service_id), imageUri,
R.string.could_not_load_thumbnails));
}
});
} else thumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark);
if (info.uploader_thumbnail_url != null && !info.uploader_thumbnail_url.isEmpty()) {
imageLoader.displayImage(info.uploader_thumbnail_url,
uploaderThumb, displayImageOptions,
new ImageErrorLoadingListener(this, findViewById(android.R.id.content), info.service_id));
}
}
private void initRelatedVideos(StreamInfo info) {
if (relatedStreamsView.getChildCount() > 0) relatedStreamsView.removeAllViews();
if (info.next_video != null && showRelatedStreams) {
nextStreamTitle.setVisibility(View.VISIBLE);
relatedStreamsView.addView(infoItemBuilder.buildView(relatedStreamsView, info.next_video));
relatedStreamsView.addView(getSeparatorView());
relatedStreamsView.setVisibility(View.VISIBLE);
} else nextStreamTitle.setVisibility(View.GONE);
if (info.related_streams != null && !info.related_streams.isEmpty() && showRelatedStreams) {
for (InfoItem item : info.related_streams) {
relatedStreamsView.addView(infoItemBuilder.buildView(relatedStreamsView, item));
}
relatedStreamsView.setVisibility(View.VISIBLE);
} else if (info.next_video == null) relatedStreamsView.setVisibility(View.GONE);
}
/*//////////////////////////////////////////////////////////////////////////
// Menu
//////////////////////////////////////////////////////////////////////////*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
actionBarHandler.setupMenu(menu, getMenuInflater());
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
NavStack.getInstance().openMainActivity(this);
return true;
}
return actionBarHandler.onItemSelected(item) || super.onOptionsItemSelected(item);
}
private void setupActionBarHandler(final StreamInfo info) {
actionBarHandler.setupStreamList(info.video_streams);
actionBarHandler.setOnShareListener(new ActionBarHandler.OnActionListener() {
@Override
public void onActionSelected(int selectedStreamId) {
if (isLoading.get()) return;
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_TEXT, info.webpage_url);
intent.setType("text/plain");
startActivity(Intent.createChooser(intent, VideoItemDetailActivity.this.getString(R.string.share_dialog_title)));
}
});
actionBarHandler.setOnOpenInBrowserListener(new ActionBarHandler.OnActionListener() {
@Override
public void onActionSelected(int selectedStreamId) {
if (isLoading.get()) return;
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(info.webpage_url));
startActivity(Intent.createChooser(intent, VideoItemDetailActivity.this.getString(R.string.choose_browser)));
}
});
actionBarHandler.setOnOpenInPopupListener(new ActionBarHandler.OnActionListener() {
@Override
public void onActionSelected(int selectedStreamId) {
if (isLoading.get()) return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !PermissionHelper.checkSystemAlertWindowPermission(VideoItemDetailActivity.this)) {
Toast.makeText(VideoItemDetailActivity.this, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
return;
}
if (streamThumbnail != null) ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail = streamThumbnail;
Intent i = new Intent(VideoItemDetailActivity.this, PopupVideoPlayer.class);
Toast.makeText(VideoItemDetailActivity.this, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
i.putExtra(AbstractPlayer.VIDEO_TITLE, info.title)
.putExtra(AbstractPlayer.CHANNEL_NAME, info.uploader)
.putExtra(AbstractPlayer.VIDEO_URL, info.webpage_url)
.putExtra(AbstractPlayer.INDEX_SEL_VIDEO_STREAM, selectedStreamId)
.putExtra(AbstractPlayer.VIDEO_STREAMS_LIST, new ArrayList<>(info.video_streams));
if (info.start_position > 0) i.putExtra(AbstractPlayer.START_POSITION, info.start_position * 1000);
VideoItemDetailActivity.this.startService(i);
}
});
actionBarHandler.setOnPlayWithKodiListener(new ActionBarHandler.OnActionListener() {
@Override
public void onActionSelected(int selectedStreamId) {
if (isLoading.get()) return;
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setPackage(KORE_PACKET);
intent.setData(Uri.parse(info.webpage_url.replace("https", "http")));
VideoItemDetailActivity.this.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
AlertDialog.Builder builder = new AlertDialog.Builder(VideoItemDetailActivity.this);
builder.setMessage(R.string.kore_not_found)
.setPositiveButton(R.string.install, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(VideoItemDetailActivity.this.getString(R.string.fdroid_kore_url)));
VideoItemDetailActivity.this.startActivity(intent);
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.create().show();
}
}
});
actionBarHandler.setOnDownloadListener(new ActionBarHandler.OnActionListener() {
@Override
public void onActionSelected(int selectedStreamId) {
if (isLoading.get() || !PermissionHelper.checkStoragePermissions(VideoItemDetailActivity.this)) {
return;
}
try {
Bundle args = new Bundle();
// Sometimes it may be that some information is not available due to changes fo the
// website which was crawled. Then the ui has to understand this and act right.
if (info.audio_streams != null) {
AudioStream audioStream =
info.audio_streams.get(getPreferredAudioStreamId(info));
String audioSuffix = "." + MediaFormat.getSuffixById(audioStream.format);
args.putString(DownloadDialog.AUDIO_URL, audioStream.url);
args.putString(DownloadDialog.FILE_SUFFIX_AUDIO, audioSuffix);
}
if (info.video_streams != null) {
VideoStream selectedStreamItem = info.video_streams.get(selectedStreamId);
String videoSuffix = "." + MediaFormat.getSuffixById(selectedStreamItem.format);
args.putString(DownloadDialog.FILE_SUFFIX_VIDEO, videoSuffix);
args.putString(DownloadDialog.VIDEO_URL, selectedStreamItem.url);
}
args.putString(DownloadDialog.TITLE, info.title);
DownloadDialog downloadDialog = DownloadDialog.newInstance(args);
downloadDialog.show(VideoItemDetailActivity.this.getSupportFragmentManager(), "downloadDialog");
} catch (Exception e) {
Toast.makeText(VideoItemDetailActivity.this,
R.string.could_not_setup_download_menu, Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
});
if (info.audio_streams == null) {
actionBarHandler.showAudioAction(false);
} else {
actionBarHandler.setOnPlayAudioListener(new ActionBarHandler.OnActionListener() {
@Override
public void onActionSelected(int selectedStreamId) {
if (isLoading.get()) return;
boolean useExternalAudioPlayer = PreferenceManager.getDefaultSharedPreferences(VideoItemDetailActivity.this)
.getBoolean(VideoItemDetailActivity.this.getString(R.string.use_external_audio_player_key), false);
Intent intent;
AudioStream audioStream =
info.audio_streams.get(getPreferredAudioStreamId(info));
if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 18) {
//internal music player: explicit intent
if (!BackgroundPlayer.isRunning && streamThumbnail != null) {
ActivityCommunicator.getCommunicator()
.backgroundPlayerThumbnail = streamThumbnail;
intent = new Intent(VideoItemDetailActivity.this, BackgroundPlayer.class);
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(audioStream.url),
MediaFormat.getMimeById(audioStream.format));
intent.putExtra(BackgroundPlayer.TITLE, info.title);
intent.putExtra(BackgroundPlayer.WEB_URL, info.webpage_url);
intent.putExtra(BackgroundPlayer.SERVICE_ID, serviceId);
intent.putExtra(BackgroundPlayer.CHANNEL_NAME, info.uploader);
VideoItemDetailActivity.this.startService(intent);
}
} else {
intent = new Intent();
try {
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(audioStream.url),
MediaFormat.getMimeById(audioStream.format));
intent.putExtra(Intent.EXTRA_TITLE, info.title);
intent.putExtra("title", info.title);
// HERE !!!
VideoItemDetailActivity.this.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
AlertDialog.Builder builder = new AlertDialog.Builder(VideoItemDetailActivity.this);
builder.setMessage(R.string.no_player_found)
.setPositiveButton(R.string.install, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(VideoItemDetailActivity.this.getString(R.string.fdroid_vlc_url)));
VideoItemDetailActivity.this.startActivity(intent);
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.i(TAG, "You unlocked a secret unicorn.");
}
});
builder.create().show();
Log.e(TAG, "Either no Streaming player for audio was installed, or something important crashed:");
e.printStackTrace();
}
}
}
});
}
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
private void handleIntent(Intent intent) {
if (intent == null) return;
serviceId = intent.getIntExtra(NavStack.SERVICE_ID, 0);
videoUrl = intent.getStringExtra(NavStack.URL);
autoPlayEnabled = intent.getBooleanExtra(AUTO_PLAY, false);
selectVideo(videoUrl, serviceId);
}
private void selectVideo(String url, int serviceId) {
if (curExtractorThread != null && curExtractorThread.isRunning()) curExtractorThread.cancel();
animateView(contentRootLayout, false, 200, null);
thumbnailPlayButton.setVisibility(View.GONE);
loadingProgressBar.setVisibility(View.VISIBLE);
imageLoader.cancelDisplayTask(thumbnailImageView);
imageLoader.cancelDisplayTask(uploaderThumb);
thumbnailImageView.setImageDrawable(null);
curExtractorThread = StreamExtractorWorker.startExtractorThread(serviceId, url, this, this);
isLoading.set(true);
}
public void playVideo(StreamInfo info) {
// ----------- THE MAGIC MOMENT ---------------
VideoStream selectedVideoStream = info.video_streams.get(actionBarHandler.getSelectedVideoStream());
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(this.getString(R.string.use_external_video_player_key), false)) {
// External Player
Intent intent = new Intent();
try {
intent.setAction(Intent.ACTION_VIEW)
.setDataAndType(Uri.parse(selectedVideoStream.url), MediaFormat.getMimeById(selectedVideoStream.format))
.putExtra(Intent.EXTRA_TITLE, info.title)
.putExtra("title", info.title);
this.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.no_player_found)
.setPositiveButton(R.string.install, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent()
.setAction(Intent.ACTION_VIEW)
.setData(Uri.parse(getString(R.string.fdroid_vlc_url)));
startActivity(intent);
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.create().show();
}
} else {
Intent intent;
boolean useOldPlayer = PreferenceManager
.getDefaultSharedPreferences(this)
.getBoolean(getString(R.string.use_old_player_key), false)
|| (Build.VERSION.SDK_INT < 16);
if (!useOldPlayer) {
// ExoPlayer
if (streamThumbnail != null) ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail = streamThumbnail;
intent = new Intent(this, ExoPlayerActivity.class)
.putExtra(AbstractPlayer.VIDEO_TITLE, info.title)
.putExtra(AbstractPlayer.VIDEO_URL, info.webpage_url)
.putExtra(AbstractPlayer.CHANNEL_NAME, info.uploader)
.putExtra(AbstractPlayer.INDEX_SEL_VIDEO_STREAM, actionBarHandler.getSelectedVideoStream())
.putExtra(AbstractPlayer.VIDEO_STREAMS_LIST, new ArrayList<>(info.video_streams));
if (info.start_position > 0) intent.putExtra(AbstractPlayer.START_POSITION, info.start_position * 1000);
} else {
// Internal Player
intent = new Intent(this, PlayVideoActivity.class)
.putExtra(PlayVideoActivity.VIDEO_TITLE, info.title)
.putExtra(PlayVideoActivity.STREAM_URL, selectedVideoStream.url)
.putExtra(PlayVideoActivity.VIDEO_URL, info.webpage_url)
.putExtra(PlayVideoActivity.START_POSITION, info.start_position);
}
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
private int getPreferredAudioStreamId(final StreamInfo info) {
String preferredFormatString = PreferenceManager.getDefaultSharedPreferences(this)
.getString(getString(R.string.default_audio_format_key), "webm");
int preferredFormat = MediaFormat.WEBMA.id;
switch (preferredFormatString) {
case "webm":
preferredFormat = MediaFormat.WEBMA.id;
break;
case "m4a":
preferredFormat = MediaFormat.M4A.id;
break;
default:
break;
}
for (int i = 0; i < info.audio_streams.size(); i++) {
if (info.audio_streams.get(i).format == preferredFormat) {
return i;
}
}
//todo: make this a proper error
Log.e(TAG, "FAILED to set audioStream value!");
return 0;
}
private View getSeparatorView() {
View separator = new View(this);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1);
int m8 = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, getResources().getDisplayMetrics());
int m5 = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics());
params.setMargins(m8, m5, m8, m5);
separator.setLayoutParams(params);
TypedValue typedValue = new TypedValue();
getTheme().resolveAttribute(R.attr.separatorColor, typedValue, true);
separator.setBackgroundColor(typedValue.data);
return separator;
}
private void setHeightThumbnail() {
boolean isPortrait = getResources().getDisplayMetrics().heightPixels > getResources().getDisplayMetrics().widthPixels;
int height = isPortrait ? (int) (getResources().getDisplayMetrics().widthPixels / (16.0f / 9.0f))
: (int) (getResources().getDisplayMetrics().heightPixels / 2f);
thumbnailImageView.setScaleType(isPortrait ? ImageView.ScaleType.CENTER_CROP : ImageView.ScaleType.FIT_CENTER);
thumbnailImageView.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, height));
thumbnailImageView.setMinimumHeight(height);
thumbnailBackgroundButton.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, height));
thumbnailBackgroundButton.setMinimumHeight(height);
}
/**
* Animate the view
*
* @param view view that will be animated
* @param enterOrExit true to enter, false to exit
* @param duration how long the animation will take, in milliseconds
* @param execOnEnd runnable that will be executed when the animation ends
*/
public void animateView(final View view, final boolean enterOrExit, long duration, final Runnable execOnEnd) {
if (view.getVisibility() == View.VISIBLE && enterOrExit) {
view.animate().setListener(null).cancel();
view.setVisibility(View.VISIBLE);
view.setAlpha(1f);
if (execOnEnd != null) execOnEnd.run();
return;
} else if ((view.getVisibility() == View.GONE || view.getVisibility() == View.INVISIBLE) && !enterOrExit) {
view.animate().setListener(null).cancel();
view.setVisibility(View.GONE);
view.setAlpha(0f);
if (execOnEnd != null) execOnEnd.run();
return;
}
view.animate().setListener(null).cancel();
view.setVisibility(View.VISIBLE);
if (enterOrExit) {
view.animate().alpha(1f).setDuration(duration)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (execOnEnd != null) execOnEnd.run();
}
}).start();
} else {
view.animate().alpha(0f)
.setDuration(duration)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
view.setVisibility(View.GONE);
if (execOnEnd != null) execOnEnd.run();
}
})
.start();
}
}
/*//////////////////////////////////////////////////////////////////////////
// OnStreamInfoReceivedListener callbacks
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onReceive(StreamInfo info) {
currentStreamInfo = info;
loadingProgressBar.setVisibility(View.GONE);
thumbnailPlayButton.setVisibility(View.VISIBLE);
relatedStreamRootLayout.setVisibility(showRelatedStreams ? View.VISIBLE : View.GONE);
parallaxScrollRootView.scrollTo(0, 0);
// Since newpipe is designed to work even if certain information is not available,
// the UI has to react on missing information.
videoTitleTextView.setText(info.title);
if (!info.uploader.isEmpty()) uploaderTextView.setText(info.uploader);
uploaderTextView.setVisibility(!info.uploader.isEmpty() ? View.VISIBLE : View.GONE);
uploaderButton.setVisibility(!info.channel_url.isEmpty() ? View.VISIBLE : View.GONE);
uploaderThumb.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.buddy));
if (info.view_count >= 0) videoCountView.setText(Localization.localizeViewCount(info.view_count, this));
videoCountView.setVisibility(info.view_count >= 0 ? View.VISIBLE : View.GONE);
if (info.dislike_count == -1 && info.like_count == -1) {
thumbsDownImageView.setVisibility(View.VISIBLE);
thumbsUpImageView.setVisibility(View.VISIBLE);
thumbsUpTextView.setVisibility(View.GONE);
thumbsDownTextView.setVisibility(View.GONE);
thumbsDisabledTextView.setVisibility(View.VISIBLE);
} else {
thumbsDisabledTextView.setVisibility(View.GONE);
if (info.dislike_count >= 0) thumbsDownTextView.setText(Localization.localizeNumber(info.dislike_count, this));
thumbsDownTextView.setVisibility(info.dislike_count >= 0 ? View.VISIBLE : View.GONE);
thumbsDownImageView.setVisibility(info.dislike_count >= 0 ? View.VISIBLE : View.GONE);
if (info.like_count >= 0) thumbsUpTextView.setText(Localization.localizeNumber(info.like_count, this));
thumbsUpTextView.setVisibility(info.like_count >= 0 ? View.VISIBLE : View.GONE);
thumbsUpImageView.setVisibility(info.like_count >= 0 ? View.VISIBLE : View.GONE);
}
if (!info.upload_date.isEmpty()) videoUploadDateView.setText(Localization.localizeDate(info.upload_date, this));
videoUploadDateView.setVisibility(!info.upload_date.isEmpty() ? View.VISIBLE : View.GONE);
if (!info.description.isEmpty()) videoDescriptionView.setText(
Build.VERSION.SDK_INT >= 24 ? Html.fromHtml(info.description, 0) : Html.fromHtml(info.description)
);
videoDescriptionView.setVisibility(!info.description.isEmpty() ? View.VISIBLE : View.GONE);
videoDescriptionRootLayout.setVisibility(View.GONE);
videoTitleToggleArrow.setImageResource(R.drawable.arrow_down);
setupActionBarHandler(info);
initRelatedVideos(info);
initThumbnailViews(info);
animateView(contentRootLayout, true, 200, null);
isLoading.set(false);
if (autoPlayEnabled) playVideo(info);
}
@Override
public void onError(int messageId) {
Toast.makeText(this, messageId, Toast.LENGTH_LONG).show();
loadingProgressBar.setVisibility(View.GONE);
thumbnailImageView.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.not_available_monkey));
}
@Override
public void onReCaptchaException() {
Toast.makeText(this, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
// Starting ReCaptcha Challenge Activity
startActivityForResult(new Intent(this, ReCaptchaActivity.class), ReCaptchaActivity.RECAPTCHA_REQUEST);
}
@Override
public void onBlockedByGemaError() {
loadingProgressBar.setVisibility(View.GONE);
thumbnailImageView.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.gruese_die_gema));
thumbnailBackgroundButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(getString(R.string.c3s_url)));
startActivity(intent);
}
});
Toast.makeText(this, R.string.blocked_by_gema, Toast.LENGTH_LONG).show();
}
@Override
public void onContentErrorWithMessage(int messageId) {
loadingProgressBar.setVisibility(View.GONE);
thumbnailImageView.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.not_available_monkey));
Toast.makeText(this, messageId, Toast.LENGTH_LONG).show();
}
@Override
public void onContentError() {
loadingProgressBar.setVisibility(View.GONE);
thumbnailImageView.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.not_available_monkey));
Toast.makeText(this, R.string.content_not_available, Toast.LENGTH_LONG).show();
}
}

View File

@@ -1,876 +0,0 @@
package org.schabi.newpipe.detail;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.Fragment;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.RecyclerView;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import org.schabi.newpipe.ActivityCommunicator;
import org.schabi.newpipe.ImageErrorLoadingListener;
import org.schabi.newpipe.Localization;
import org.schabi.newpipe.R;
import org.schabi.newpipe.ReCaptchaActivity;
import org.schabi.newpipe.download.DownloadDialog;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.stream_info.AudioStream;
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
import org.schabi.newpipe.extractor.stream_info.VideoStream;
import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.player.BackgroundPlayer;
import org.schabi.newpipe.player.ExoPlayerActivity;
import org.schabi.newpipe.player.PlayVideoActivity;
import org.schabi.newpipe.player.PopupVideoPlayer;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.util.NavStack;
import org.schabi.newpipe.util.PermissionHelper;
import java.util.Vector;
import static android.app.Activity.RESULT_OK;
import static org.schabi.newpipe.ReCaptchaActivity.RECAPTCHA_REQUEST;
/**
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
* VideoItemDetailFragment.java is part of NewPipe.
*
* NewPipe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NewPipe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public class VideoItemDetailFragment extends Fragment {
private static final String TAG = VideoItemDetailFragment.class.toString();
private static final String KORE_PACKET = "org.xbmc.kore";
/**
* The fragment argument representing the item ID that this fragment
* represents.
*/
public static final String AUTO_PLAY = "auto_play";
private AppCompatActivity activity;
private ActionBarHandler actionBarHandler;
private ProgressBar progressBar;
private int streamingServiceId = -1;
private boolean autoPlayEnabled;
private boolean showNextStreamItem;
private View thumbnailWindowLayout;
//this only remains due to downwards compatibility
private FloatingActionButton playVideoButton;
private final Point initialThumbnailPos = new Point(0, 0);
private View rootView = null;
private Bitmap streamThumbnail = null;
private ImageLoader imageLoader = ImageLoader.getInstance();
private DisplayImageOptions displayImageOptions =
new DisplayImageOptions.Builder().cacheInMemory(true).build();
private InfoItemBuilder infoItemBuilder = null;
public interface OnInvokeCreateOptionsMenuListener {
void createOptionsMenu();
}
private OnInvokeCreateOptionsMenuListener onInvokeCreateOptionsMenuListener;
private void updateInfo(final StreamInfo info) {
Activity a = getActivity();
RelativeLayout textContentLayout =
(RelativeLayout) activity.findViewById(R.id.detail_text_content_layout);
final TextView videoTitleView =
(TextView) activity.findViewById(R.id.detail_video_title_view);
TextView uploaderView = (TextView) activity.findViewById(R.id.detail_uploader_view);
TextView viewCountView = (TextView) activity.findViewById(R.id.detail_view_count_view);
TextView thumbsUpView = (TextView) activity.findViewById(R.id.detail_thumbs_up_count_view);
TextView thumbsDownView =
(TextView) activity.findViewById(R.id.detail_thumbs_down_count_view);
TextView uploadDateView = (TextView) activity.findViewById(R.id.detail_upload_date_view);
TextView descriptionView = (TextView) activity.findViewById(R.id.detail_description_view);
RecyclerView nextStreamView =
(RecyclerView) activity.findViewById(R.id.detail_next_stream_content);
RelativeLayout nextVideoRootFrame =
(RelativeLayout) activity.findViewById(R.id.detail_next_stream_root_layout);
TextView similarTitle = (TextView) activity.findViewById(R.id.detail_similar_title);
Button backgroundButton = (Button)
activity.findViewById(R.id.detail_stream_thumbnail_window_background_button);
View thumbnailView = activity.findViewById(R.id.detail_thumbnail_view);
View topView = activity.findViewById(R.id.detailTopView);
Button channelButton = (Button) activity.findViewById(R.id.channel_button);
// prevents a crash if the activity/fragment was already left when the response came
if(channelButton != null) {
progressBar.setVisibility(View.GONE);
if (info.next_video != null) {
// todo: activate this function or remove it
nextStreamView.setVisibility(View.GONE);
} else {
nextStreamView.setVisibility(View.GONE);
activity.findViewById(R.id.detail_similar_title).setVisibility(View.GONE);
}
textContentLayout.setVisibility(View.VISIBLE);
if (android.os.Build.VERSION.SDK_INT < 18) {
playVideoButton.setVisibility(View.VISIBLE);
} else {
ImageView playArrowView = (ImageView) activity.findViewById(R.id.play_arrow_view);
playArrowView.setVisibility(View.VISIBLE);
}
if (!showNextStreamItem) {
nextVideoRootFrame.setVisibility(View.GONE);
similarTitle.setVisibility(View.GONE);
}
videoTitleView.setText(info.title);
topView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == android.view.MotionEvent.ACTION_UP) {
ImageView arrow = (ImageView) activity.findViewById(R.id.toggle_description_view);
View extra = activity.findViewById(R.id.detailExtraView);
if (extra.getVisibility() == View.VISIBLE) {
extra.setVisibility(View.GONE);
arrow.setImageResource(R.drawable.arrow_down);
} else {
extra.setVisibility(View.VISIBLE);
arrow.setImageResource(R.drawable.arrow_up);
}
}
return true;
}
});
// Since newpipe is designed to work even if certain information is not available,
// the UI has to react on missing information.
videoTitleView.setText(info.title);
if (!info.uploader.isEmpty()) {
uploaderView.setText(info.uploader);
} else {
activity.findViewById(R.id.detail_uploader_view).setVisibility(View.GONE);
}
if (info.view_count >= 0) {
viewCountView.setText(Localization.localizeViewCount(info.view_count, a));
} else {
viewCountView.setVisibility(View.GONE);
}
if (info.dislike_count >= 0) {
thumbsDownView.setText(Localization.localizeNumber(info.dislike_count, a));
} else {
thumbsDownView.setVisibility(View.INVISIBLE);
activity.findViewById(R.id.detail_thumbs_down_count_view).setVisibility(View.GONE);
}
if (info.like_count >= 0) {
thumbsUpView.setText(Localization.localizeNumber(info.like_count, a));
} else {
thumbsUpView.setVisibility(View.GONE);
activity.findViewById(R.id.detail_thumbs_up_img_view).setVisibility(View.GONE);
thumbsDownView.setVisibility(View.GONE);
activity.findViewById(R.id.detail_thumbs_down_img_view).setVisibility(View.GONE);
}
if (!info.upload_date.isEmpty()) {
uploadDateView.setText(Localization.localizeDate(info.upload_date, a));
} else {
uploadDateView.setVisibility(View.GONE);
}
if (!info.description.isEmpty()) {
descriptionView.setText(Html.fromHtml(info.description));
} else {
descriptionView.setVisibility(View.GONE);
}
descriptionView.setMovementMethod(LinkMovementMethod.getInstance());
// parse streams
Vector<VideoStream> streamsToUse = new Vector<>();
for (VideoStream i : info.video_streams) {
if (useStream(i, streamsToUse)) {
streamsToUse.add(i);
}
}
textContentLayout.setVisibility(View.VISIBLE);
if (info.next_video == null) {
activity.findViewById(R.id.detail_next_stream_title).setVisibility(View.GONE);
}
if (info.related_streams != null && !info.related_streams.isEmpty()) {
initSimilarVideos(info);
} else {
activity.findViewById(R.id.detail_similar_title).setVisibility(View.GONE);
activity.findViewById(R.id.similar_streams_view).setVisibility(View.GONE);
}
setupActionBarHandler(info);
if (autoPlayEnabled) {
playVideo(info);
}
if (android.os.Build.VERSION.SDK_INT < 18) {
playVideoButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
playVideo(info);
}
});
}
backgroundButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
playVideo(info);
}
});
//todo: make backgroundButton handle this
thumbnailView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
playVideo(info);
}
});
if (info.channel_url != null && info.channel_url != "") {
channelButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
NavStack.getInstance()
.openChannelActivity(getActivity(), info.channel_url, info.service_id);
}
});
} else {
channelButton.setVisibility(Button.GONE);
}
initThumbnailViews(info);
}
}
private void initThumbnailViews(final StreamInfo info) {
ImageView videoThumbnailView = (ImageView) activity.findViewById(R.id.detail_thumbnail_view);
ImageView uploaderThumb
= (ImageView) activity.findViewById(R.id.detail_uploader_thumbnail_view);
if (info.thumbnail_url != null && !info.thumbnail_url.isEmpty()) {
imageLoader.displayImage(info.thumbnail_url, videoThumbnailView,
displayImageOptions, new ImageLoadingListener() {
@Override
public void onLoadingStarted(String imageUri, View view) {
}
@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
ErrorActivity.reportError(getActivity(),
failReason.getCause(), null, rootView,
ErrorActivity.ErrorInfo.make(ErrorActivity.LOAD_IMAGE,
NewPipe.getNameOfService(info.service_id), imageUri,
R.string.could_not_load_thumbnails));
}
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
streamThumbnail = loadedImage;
if (streamThumbnail != null) {
// TODO: Change the thumbnail implementation
// When the thumbnail is not loaded yet, it not passes to the service in time
// so, I can notify the service through a broadcast, but the problem is
// when I click in another video, another thumbnail will be load, and will
// notify again, so I send the videoUrl and compare with the service's url
ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail = streamThumbnail;
Intent intent = new Intent(PopupVideoPlayer.InternalListener.ACTION_UPDATE_THUMB);
intent.putExtra(PopupVideoPlayer.VIDEO_URL, info.webpage_url);
getContext().sendBroadcast(intent);
}
}
@Override
public void onLoadingCancelled(String imageUri, View view) {
}
});
} else {
videoThumbnailView.setImageResource(R.drawable.dummy_thumbnail_dark);
}
if (info.uploader_thumbnail_url != null && !info.uploader_thumbnail_url.isEmpty()) {
imageLoader.displayImage(info.uploader_thumbnail_url,
uploaderThumb, displayImageOptions,
new ImageErrorLoadingListener(activity, rootView, info.service_id));
}
}
private void setupActionBarHandler(final StreamInfo info) {
actionBarHandler.setupStreamList(info.video_streams);
actionBarHandler.setOnShareListener(new ActionBarHandler.OnActionListener() {
@Override
public void onActionSelected(int selectedStreamId) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_TEXT, info.webpage_url);
intent.setType("text/plain");
activity.startActivity(Intent.createChooser(intent, activity.getString(R.string.share_dialog_title)));
}
});
actionBarHandler.setOnOpenInBrowserListener(new ActionBarHandler.OnActionListener() {
@Override
public void onActionSelected(int selectedStreamId) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(info.webpage_url));
activity.startActivity(Intent.createChooser(intent, activity.getString(R.string.choose_browser)));
}
});
actionBarHandler.setOnOpenInPopupListener(new ActionBarHandler.OnActionListener() {
@Override
public void onActionSelected(int selectedStreamId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& !PermissionHelper.checkSystemAlertWindowPermission(activity)) {
Toast.makeText(activity, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
return;
}
if (streamThumbnail != null)
ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail = streamThumbnail;
VideoStream selectedVideoStream = info.video_streams.get(selectedStreamId);
Intent i = new Intent(activity, PopupVideoPlayer.class);
Toast.makeText(activity, "Starting in popup mode", Toast.LENGTH_SHORT).show();
i.putExtra(PopupVideoPlayer.VIDEO_TITLE, info.title)
.putExtra(PopupVideoPlayer.STREAM_URL, selectedVideoStream.url)
.putExtra(PopupVideoPlayer.CHANNEL_NAME, info.uploader)
.putExtra(PopupVideoPlayer.VIDEO_URL, info.webpage_url);
activity.startService(i);
}
});
actionBarHandler.setOnPlayWithKodiListener(new ActionBarHandler.OnActionListener() {
@Override
public void onActionSelected(int selectedStreamId) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setPackage(KORE_PACKET);
intent.setData(Uri.parse(info.webpage_url.replace("https", "http")));
activity.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage(R.string.kore_not_found)
.setPositiveButton(R.string.install, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(activity.getString(R.string.fdroid_kore_url)));
activity.startActivity(intent);
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.create().show();
}
}
});
actionBarHandler.setOnDownloadListener(new ActionBarHandler.OnActionListener() {
@Override
public void onActionSelected(int selectedStreamId) {
if(!PermissionHelper.checkStoragePermissions(getActivity())) {
return;
}
try {
Bundle args = new Bundle();
// Sometimes it may be that some information is not available due to changes fo the
// website which was crawled. Then the ui has to understand this and act right.
if (info.audio_streams != null) {
AudioStream audioStream =
info.audio_streams.get(getPreferredAudioStreamId(info));
String audioSuffix = "." + MediaFormat.getSuffixById(audioStream.format);
args.putString(DownloadDialog.AUDIO_URL, audioStream.url);
args.putString(DownloadDialog.FILE_SUFFIX_AUDIO, audioSuffix);
}
if (info.video_streams != null) {
VideoStream selectedStreamItem = info.video_streams.get(selectedStreamId);
String videoSuffix = "." + MediaFormat.getSuffixById(selectedStreamItem.format);
args.putString(DownloadDialog.FILE_SUFFIX_VIDEO, videoSuffix);
args.putString(DownloadDialog.VIDEO_URL, selectedStreamItem.url);
}
args.putString(DownloadDialog.TITLE, info.title);
DownloadDialog downloadDialog = DownloadDialog.newInstance(args);
downloadDialog.show(activity.getSupportFragmentManager(), "downloadDialog");
} catch (Exception e) {
Toast.makeText(VideoItemDetailFragment.this.getActivity(),
R.string.could_not_setup_download_menu, Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
});
if (info.audio_streams == null) {
actionBarHandler.showAudioAction(false);
} else {
actionBarHandler.setOnPlayAudioListener(new ActionBarHandler.OnActionListener() {
@Override
public void onActionSelected(int selectedStreamId) {
boolean useExternalAudioPlayer = PreferenceManager.getDefaultSharedPreferences(activity)
.getBoolean(activity.getString(R.string.use_external_audio_player_key), false);
Intent intent;
AudioStream audioStream =
info.audio_streams.get(getPreferredAudioStreamId(info));
if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 18) {
//internal music player: explicit intent
if (!BackgroundPlayer.isRunning && streamThumbnail != null) {
ActivityCommunicator.getCommunicator()
.backgroundPlayerThumbnail = streamThumbnail;
intent = new Intent(activity, BackgroundPlayer.class);
intent.setAction(Intent.ACTION_VIEW);
Log.i(TAG, "audioStream is null:" + (audioStream == null));
Log.i(TAG, "audioStream.url is null:" + (audioStream.url == null));
intent.setDataAndType(Uri.parse(audioStream.url),
MediaFormat.getMimeById(audioStream.format));
intent.putExtra(BackgroundPlayer.TITLE, info.title);
intent.putExtra(BackgroundPlayer.WEB_URL, info.webpage_url);
intent.putExtra(BackgroundPlayer.SERVICE_ID, streamingServiceId);
intent.putExtra(BackgroundPlayer.CHANNEL_NAME, info.uploader);
activity.startService(intent);
}
} else {
intent = new Intent();
try {
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(audioStream.url),
MediaFormat.getMimeById(audioStream.format));
intent.putExtra(Intent.EXTRA_TITLE, info.title);
intent.putExtra("title", info.title);
// HERE !!!
activity.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage(R.string.no_player_found)
.setPositiveButton(R.string.install, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(activity.getString(R.string.fdroid_vlc_url)));
activity.startActivity(intent);
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.i(TAG, "You unlocked a secret unicorn.");
}
});
builder.create().show();
Log.e(TAG, "Either no Streaming player for audio was installed, or something important crashed:");
e.printStackTrace();
}
}
}
});
}
}
private int getPreferredAudioStreamId(final StreamInfo info) {
String preferredFormatString = PreferenceManager.getDefaultSharedPreferences(getActivity())
.getString(activity.getString(R.string.default_audio_format_key), "webm");
int preferredFormat = MediaFormat.WEBMA.id;
switch(preferredFormatString) {
case "webm":
preferredFormat = MediaFormat.WEBMA.id;
break;
case "m4a":
preferredFormat = MediaFormat.M4A.id;
break;
default:
break;
}
for(int i = 0; i < info.audio_streams.size(); i++) {
if(info.audio_streams.get(i).format == preferredFormat) {
return i;
}
}
//todo: make this a proper error
Log.e(TAG, "FAILED to set audioStream value!");
return 0;
}
private void initSimilarVideos(final StreamInfo info) {
LinearLayout similarLayout = (LinearLayout) activity.findViewById(R.id.similar_streams_view);
for (final InfoItem item : info.related_streams) {
similarLayout.addView(infoItemBuilder.buildView(similarLayout, item));
}
infoItemBuilder.setOnStreamInfoItemSelectedListener(
new InfoItemBuilder.OnInfoItemSelectedListener() {
@Override
public void selected(String url, int serviceId) {
NavStack.getInstance()
.openDetailActivity(getContext(), url, serviceId);
}
});
}
private void onErrorBlockedByGema() {
Button backgroundButton = (Button)
activity.findViewById(R.id.detail_stream_thumbnail_window_background_button);
ImageView thumbnailView = (ImageView) activity.findViewById(R.id.detail_thumbnail_view);
progressBar.setVisibility(View.GONE);
thumbnailView.setImageBitmap(BitmapFactory.decodeResource(
getResources(), R.drawable.gruese_die_gema));
backgroundButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(activity.getString(R.string.c3s_url)));
activity.startActivity(intent);
}
});
Toast.makeText(VideoItemDetailFragment.this.getActivity(),
R.string.blocked_by_gema, Toast.LENGTH_LONG).show();
}
private void onNotSpecifiedContentError() {
ImageView thumbnailView = (ImageView) activity.findViewById(R.id.detail_thumbnail_view);
progressBar.setVisibility(View.GONE);
thumbnailView.setImageBitmap(BitmapFactory.decodeResource(
getResources(), R.drawable.not_available_monkey));
Toast.makeText(activity, R.string.content_not_available, Toast.LENGTH_LONG)
.show();
}
private void onNotSpecifiedContentErrorWithMessage(int resourceId) {
ImageView thumbnailView = (ImageView) activity.findViewById(R.id.detail_thumbnail_view);
progressBar.setVisibility(View.GONE);
thumbnailView.setImageBitmap(BitmapFactory.decodeResource(
getResources(), R.drawable.not_available_monkey));
Toast.makeText(activity, resourceId, Toast.LENGTH_LONG)
.show();
}
private boolean useStream(VideoStream stream, Vector<VideoStream> streams) {
for(VideoStream i : streams) {
if(i.resolution.equals(stream.resolution)) {
return false;
}
}
return true;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activity = (AppCompatActivity) getActivity();
showNextStreamItem = PreferenceManager.getDefaultSharedPreferences(getActivity())
.getBoolean(activity.getString(R.string.show_next_video_key), true);
StreamInfoWorker siw = StreamInfoWorker.getInstance();
siw.setOnStreamInfoReceivedListener(new StreamInfoWorker.OnStreamInfoReceivedListener() {
@Override
public void onReceive(StreamInfo info) {
updateInfo(info);
}
@Override
public void onError(int messageId) {
postNewErrorToast(messageId);
}
@Override
public void onReCaptchaException() {
Toast.makeText(getActivity(), R.string.recaptcha_request_toast,
Toast.LENGTH_LONG).show();
// Starting ReCaptcha Challenge Activity
startActivityForResult(
new Intent(getActivity(), ReCaptchaActivity.class),
RECAPTCHA_REQUEST);
}
@Override
public void onBlockedByGemaError() {
onErrorBlockedByGema();
}
@Override
public void onContentErrorWithMessage(int messageId) {
onNotSpecifiedContentErrorWithMessage(messageId);
}
@Override
public void onContentError() {
onNotSpecifiedContentError();
}
});
setHasOptionsMenu(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_videoitem_detail, container, false);
progressBar = (ProgressBar) rootView.findViewById(R.id.detail_progress_bar);
actionBarHandler = new ActionBarHandler(activity);
actionBarHandler.setupNavMenu(activity);
if(onInvokeCreateOptionsMenuListener != null) {
onInvokeCreateOptionsMenuListener.createOptionsMenu();
}
return rootView;
}
@Override
public void onStart() {
super.onStart();
Activity a = getActivity();
infoItemBuilder = new InfoItemBuilder(a, a.findViewById(android.R.id.content));
if (android.os.Build.VERSION.SDK_INT < 18) {
playVideoButton = (FloatingActionButton) a.findViewById(R.id.play_video_button);
}
thumbnailWindowLayout = a.findViewById(R.id.detail_stream_thumbnail_window_layout);
Button backgroundButton = (Button)
a.findViewById(R.id.detail_stream_thumbnail_window_background_button);
// Sometimes when this fragment is not visible it still gets initiated
// then we must not try to access objects of this fragment.
// Otherwise the applications would crash.
if(backgroundButton != null) {
streamingServiceId = getArguments().getInt(NavStack.SERVICE_ID);
String videoUrl = getArguments().getString(NavStack.URL);
StreamInfoWorker siw = StreamInfoWorker.getInstance();
siw.search(streamingServiceId, videoUrl, getActivity());
autoPlayEnabled = getArguments().getBoolean(AUTO_PLAY);
if(Build.VERSION.SDK_INT >= 18) {
ImageView thumbnailView = (ImageView) activity.findViewById(R.id.detail_thumbnail_view);
thumbnailView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
// This is used to synchronize the thumbnailWindowButton and the playVideoButton
// inside the ScrollView with the actual size of the thumbnail.
//todo: onLayoutChage sometimes not triggered
// background buttons area seem to overlap the thumbnail view
// So although you just clicked slightly beneath the thumbnail the action still
// gets triggered.
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
RelativeLayout.LayoutParams newWindowLayoutParams =
(RelativeLayout.LayoutParams) thumbnailWindowLayout.getLayoutParams();
newWindowLayoutParams.height = bottom - top;
thumbnailWindowLayout.setLayoutParams(newWindowLayoutParams);
//noinspection SuspiciousNameCombination
initialThumbnailPos.set(top, left);
}
});
}
}
}
public void playVideo(final StreamInfo info) {
// ----------- THE MAGIC MOMENT ---------------
VideoStream selectedVideoStream =
info.video_streams.get(actionBarHandler.getSelectedVideoStream());
if (PreferenceManager.getDefaultSharedPreferences(activity)
.getBoolean(activity.getString(R.string.use_external_video_player_key), false)) {
// External Player
Intent intent = new Intent();
try {
intent.setAction(Intent.ACTION_VIEW)
.setDataAndType(Uri.parse(selectedVideoStream.url),
MediaFormat.getMimeById(selectedVideoStream.format))
.putExtra(Intent.EXTRA_TITLE, info.title)
.putExtra("title", info.title);
activity.startActivity(intent); // HERE !!!
} catch (Exception e) {
e.printStackTrace();
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage(R.string.no_player_found)
.setPositiveButton(R.string.install, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent()
.setAction(Intent.ACTION_VIEW)
.setData(Uri.parse(activity.getString(R.string.fdroid_vlc_url)));
activity.startActivity(intent);
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.create().show();
}
} else {
if (PreferenceManager.getDefaultSharedPreferences(activity)
.getBoolean(activity.getString(R.string.use_exoplayer_key), false)) {
// TODO: Fix this mess
if (streamThumbnail != null)
ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail = streamThumbnail;
// exo player
if(info.dashMpdUrl != null && !info.dashMpdUrl.isEmpty()) {
// try dash
Intent intent = new Intent(activity, ExoPlayerActivity.class)
.setData(Uri.parse(info.dashMpdUrl));
//.putExtra(ExoPlayerActivity.CONTENT_TYPE_EXTRA, Util.TYPE_DASH);
startActivity(intent);
} else if((info.audio_streams != null && !info.audio_streams.isEmpty()) &&
(info.video_only_streams != null && !info.video_only_streams.isEmpty())) {
// try smooth streaming
} else {
//default streaming
Intent intent = new Intent(activity, ExoPlayerActivity.class)
.setDataAndType(Uri.parse(selectedVideoStream.url),
MediaFormat.getMimeById(selectedVideoStream.format))
.putExtra(ExoPlayerActivity.VIDEO_TITLE, info.title)
.putExtra(ExoPlayerActivity.CHANNEL_NAME, info.uploader);
//.putExtra(ExoPlayerActivity.CONTENT_TYPE_EXTRA, Util.TYPE_OTHER);
activity.startActivity(intent); // HERE !!!
}
//-------------
} else {
// Internal Player
Intent intent = new Intent(activity, PlayVideoActivity.class)
.putExtra(PlayVideoActivity.VIDEO_TITLE, info.title)
.putExtra(PlayVideoActivity.STREAM_URL, selectedVideoStream.url)
.putExtra(PlayVideoActivity.VIDEO_URL, info.webpage_url)
.putExtra(PlayVideoActivity.START_POSITION, info.start_position);
activity.startActivity(intent); //also HERE !!!
}
}
// --------------------------------------------
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
actionBarHandler.setupMenu(menu, inflater);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if(!actionBarHandler.onItemSelected(item)) {
return super.onOptionsItemSelected(item);
} else {
return true;
}
}
public void setOnInvokeCreateOptionsMenuListener(OnInvokeCreateOptionsMenuListener listener) {
this.onInvokeCreateOptionsMenuListener = listener;
}
private void postNewErrorToast(final int stringResource) {
Toast.makeText(VideoItemDetailFragment.this.getActivity(),
stringResource, Toast.LENGTH_LONG).show();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case RECAPTCHA_REQUEST:
if (resultCode == RESULT_OK) {
String videoUrl = getArguments().getString(NavStack.URL);
StreamInfoWorker siw = StreamInfoWorker.getInstance();
siw.search(streamingServiceId, videoUrl, getActivity());
} else {
Log.d(TAG, "ReCaptcha failed");
}
break;
default:
Log.e(TAG, "Request code from activity not supported [" + requestCode + "]");
break;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -26,7 +26,6 @@ import org.schabi.newpipe.ActivityCommunicator;
import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.R;
import org.schabi.newpipe.detail.VideoItemDetailActivity;
import org.schabi.newpipe.detail.VideoItemDetailFragment;
import org.schabi.newpipe.util.NavStack;
import java.io.IOException;
@@ -343,7 +342,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
/*
NotificationCompat.Action pauseButton = new NotificationCompat.Action.Builder
(R.drawable.ic_pause_white_24dp, "Pause", playPI).build();
(R.drawable.ic_pause_white, "Pause", playPI).build();
*/
PendingIntent playPI = PendingIntent.getBroadcast(owner, noteID,
@@ -465,7 +464,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
RemoteViews views = getContentView(), bigViews = getBigContentView();
int imageSrc;
if(isPlaying) {
imageSrc = R.drawable.ic_pause_white_24dp;
imageSrc = R.drawable.ic_pause_white;
} else {
imageSrc = R.drawable.ic_play_circle_filled_white_24dp;
}

View File

@@ -1,220 +1,574 @@
package org.schabi.newpipe.player;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.graphics.Color;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.PopupMenu;
import android.widget.SeekBar;
import com.devbrackets.android.exomedia.listener.OnCompletionListener;
import com.devbrackets.android.exomedia.listener.OnPreparedListener;
import com.devbrackets.android.exomedia.listener.VideoControlsVisibilityListener;
import com.devbrackets.android.exomedia.ui.widget.EMVideoView;
import com.devbrackets.android.exomedia.ui.widget.VideoControlsMobile;
import android.widget.TextView;
import android.widget.Toast;
import org.schabi.newpipe.R;
import org.schabi.newpipe.util.NavStack;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ThemeHelper;
public class ExoPlayerActivity extends Activity implements OnPreparedListener, OnCompletionListener {
private static final String TAG = "ExoPlayerActivity";
private static final boolean DEBUG = false;
private EMVideoView videoView;
private CustomVideoControls videoControls;
/**
* Activity Player implementing AbstractPlayer
*
* @author mauriciocolli
*/
public class ExoPlayerActivity extends Activity {
private static final String TAG = ".ExoPlayerActivity";
private static final boolean DEBUG = AbstractPlayer.DEBUG;
public static final String VIDEO_TITLE = "video_title";
public static final String CHANNEL_NAME = "channel_name";
private String videoTitle = "";
private volatile String channelName = "";
private int lastPosition;
private boolean isFinished;
private AudioManager audioManager;
private BroadcastReceiver broadcastReceiver;
private GestureDetector gestureDetector;
private final Runnable hideUiRunnable = new Runnable() {
@Override
public void run() {
hideSystemUi();
}
};
private boolean activityPaused;
private AbstractPlayerImpl playerImpl;
/*//////////////////////////////////////////////////////////////////////////
// Activity LifeCycle
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onCreate(Bundle savedInstanceState) {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
ThemeHelper.setTheme(this, false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) getWindow().setStatusBarColor(Color.BLACK);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
if (getIntent() == null) {
Toast.makeText(this, R.string.general_error, Toast.LENGTH_SHORT).show();
finish();
return;
}
setContentView(R.layout.activity_exo_player);
videoView = (EMVideoView) findViewById(R.id.emVideoView);
playerImpl = new AbstractPlayerImpl();
playerImpl.setup(findViewById(android.R.id.content));
initReceiver();
playerImpl.handleIntent(getIntent());
}
@Override
protected void onStart() {
super.onStart();
Intent intent = getIntent();
videoTitle = intent.getStringExtra(VIDEO_TITLE);
channelName = intent.getStringExtra(CHANNEL_NAME);
videoView.setOnPreparedListener(this);
videoView.setOnCompletionListener(this);
videoView.setVideoURI(intent.getData());
videoControls = new CustomVideoControls(this);
videoControls.setTitle(videoTitle);
videoControls.setSubTitle(channelName);
//We don't need these button until the playlist or queue is implemented
videoControls.setNextButtonRemoved(true);
videoControls.setPreviousButtonRemoved(true);
videoControls.setVisibilityListener(new VideoControlsVisibilityListener() {
@Override
public void onControlsShown() {
if (DEBUG) Log.d(TAG, "------------ onControlsShown() called");
showSystemUi();
}
@Override
public void onControlsHidden() {
if (DEBUG) Log.d(TAG, "------------ onControlsHidden() called");
hideSystemUi();
}
});
videoView.setControls(videoControls);
protected void onNewIntent(Intent intent) {
if (DEBUG) Log.d(TAG, "onNewIntent() called with: intent = [" + intent + "]");
super.onNewIntent(intent);
playerImpl.handleIntent(intent);
}
@Override
public void onPrepared() {
if (DEBUG) Log.d(TAG, "onPrepared() called");
videoView.start();
public void onBackPressed() {
if (DEBUG) Log.d(TAG, "onBackPressed() called");
super.onBackPressed();
if (playerImpl.isStartedFromNewPipe()) NavStack.getInstance().openDetailActivity(this, playerImpl.getVideoUrl(), 0);
if (playerImpl.isPlaying()) playerImpl.getPlayer().setPlayWhenReady(false);
}
@Override
public void onCompletion() {
if (DEBUG) Log.d(TAG, "onCompletion() called");
// videoView.getVideoControls().setButtonListener();
//videoView.restart();
videoControls.setRewindButtonRemoved(true);
videoControls.setFastForwardButtonRemoved(true);
isFinished = true;
videoControls.getSeekBar().setEnabled(false);
}
@Override
protected void onPause() {
super.onPause();
videoView.stopPlayback();
lastPosition = videoView.getCurrentPosition();
protected void onStop() {
super.onStop();
if (DEBUG) Log.d(TAG, "onStop() called");
activityPaused = true;
playerImpl.destroy();
playerImpl.setVideoStartPos((int) playerImpl.getPlayer().getCurrentPosition());
}
@Override
protected void onResume() {
super.onResume();
if (lastPosition > 0) videoView.seekTo(lastPosition);
if (DEBUG) Log.d(TAG, "onResume() called");
if (activityPaused) {
//playerImpl.getPlayer().setPlayWhenReady(true);
playerImpl.getPlayPauseButton().setImageResource(R.drawable.ic_play_arrow_white);
playerImpl.initPlayer();
playerImpl.playVideo(playerImpl.getSelectedStreamUri(), false);
activityPaused = false;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
videoView.stopPlayback();
if (DEBUG) Log.d(TAG, "onDestroy() called");
if (playerImpl != null) playerImpl.destroy();
if (broadcastReceiver != null) unregisterReceiver(broadcastReceiver);
}
/*//////////////////////////////////////////////////////////////////////////
// Init
//////////////////////////////////////////////////////////////////////////*/
private void initReceiver() {
if (DEBUG) Log.d(TAG, "initReceiver() called");
broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) Log.d(TAG, "onReceive() called with: context = [" + context + "], intent = [" + intent + "]");
switch (intent.getAction()) {
case AbstractPlayer.ACTION_UPDATE_THUMB:
playerImpl.onUpdateThumbnail(intent);
break;
}
}
};
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(AbstractPlayer.ACTION_UPDATE_THUMB);
registerReceiver(broadcastReceiver, intentFilter);
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
private void showSystemUi() {
if (DEBUG) Log.d(TAG, "showSystemUi() called");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
);
} else getWindow().getDecorView().setSystemUiVisibility(0);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().getDecorView().setSystemUiVisibility(0);
}
private void hideSystemUi() {
if (DEBUG) Log.d(TAG, "hideSystemUi() called");
if (android.os.Build.VERSION.SDK_INT >= 17) {
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
if (android.os.Build.VERSION.SDK_INT >= 16) {
int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) visibility |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
getWindow().getDecorView().setSystemUiVisibility(visibility);
}
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
private class CustomVideoControls extends VideoControlsMobile {
protected static final int FAST_FORWARD_REWIND_AMOUNT = 8000;
private void toggleOrientation() {
setRequestedOrientation(getResources().getDisplayMetrics().heightPixels > getResources().getDisplayMetrics().widthPixels
? ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
: ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
}
protected ImageButton fastForwardButton;
protected ImageButton rewindButton;
///////////////////////////////////////////////////////////////////////////
public CustomVideoControls(Context context) {
super(context);
@SuppressWarnings({"unused", "WeakerAccess"})
private class AbstractPlayerImpl extends AbstractPlayer {
private TextView titleTextView;
private TextView channelTextView;
private TextView volumeTextView;
private TextView brightnessTextView;
private ImageButton repeatButton;
private ImageButton screenRotationButton;
private ImageButton playPauseButton;
AbstractPlayerImpl() {
super("AbstractPlayerImpl" + ExoPlayerActivity.TAG, ExoPlayerActivity.this);
}
@Override
protected int getLayoutResource() {
return R.layout.exomedia_custom_controls;
public void initViews(View rootView) {
super.initViews(rootView);
this.titleTextView = (TextView) rootView.findViewById(R.id.titleTextView);
this.channelTextView = (TextView) rootView.findViewById(R.id.channelTextView);
this.volumeTextView = (TextView) rootView.findViewById(R.id.volumeTextView);
this.brightnessTextView = (TextView) rootView.findViewById(R.id.brightnessTextView);
this.repeatButton = (ImageButton) rootView.findViewById(R.id.repeatButton);
this.screenRotationButton = (ImageButton) rootView.findViewById(R.id.screenRotationButton);
this.playPauseButton = (ImageButton) rootView.findViewById(R.id.playPauseButton);
// Due to a bug on lower API, lets set the alpha instead of using a drawable
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) repeatButton.setImageAlpha(77);
else { //noinspection deprecation
repeatButton.setAlpha(77);
}
}
@Override
protected void retrieveViews() {
super.retrieveViews();
rewindButton = (ImageButton) findViewById(R.id.exomedia_controls_frewind_btn);
fastForwardButton = (ImageButton) findViewById(R.id.exomedia_controls_fforward_btn);
public void initListeners() {
super.initListeners();
MySimpleOnGestureListener listener = new MySimpleOnGestureListener();
gestureDetector = new GestureDetector(context, listener);
gestureDetector.setIsLongpressEnabled(false);
playerImpl.getRootView().setOnTouchListener(listener);
repeatButton.setOnClickListener(this);
playPauseButton.setOnClickListener(this);
screenRotationButton.setOnClickListener(this);
}
@Override
protected void registerListeners() {
super.registerListeners();
rewindButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
onRewindClicked();
}
});
fastForwardButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
onFastForwardClicked();
}
});
}
public boolean onFastForwardClicked() {
if (videoView == null) return false;
int newPosition = videoView.getCurrentPosition() + FAST_FORWARD_REWIND_AMOUNT;
if (newPosition > seekBar.getMax()) newPosition = seekBar.getMax();
performSeek(newPosition);
return true;
}
public boolean onRewindClicked() {
if (videoView == null) return false;
int newPosition = videoView.getCurrentPosition() - FAST_FORWARD_REWIND_AMOUNT;
if (newPosition < 0) newPosition = 0;
performSeek(newPosition);
return true;
public void handleIntent(Intent intent) {
super.handleIntent(intent);
titleTextView.setText(getVideoTitle());
channelTextView.setText(getChannelName());
}
@Override
public void setFastForwardButtonRemoved(boolean removed) {
fastForwardButton.setVisibility(removed ? View.GONE : View.VISIBLE);
public void playVideo(Uri videoURI, boolean autoPlay) {
super.playVideo(videoURI, autoPlay);
playPauseButton.setImageResource(autoPlay ? R.drawable.ic_pause_white : R.drawable.ic_play_arrow_white);
}
@Override
public void setRewindButtonRemoved(boolean removed) {
rewindButton.setVisibility(removed ? View.GONE : View.VISIBLE);
public void onFullScreenButtonClicked() {
if (DEBUG) Log.d(TAG, "onFullScreenButtonClicked() called");
if (playerImpl.getPlayer() == null) return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& !PermissionHelper.checkSystemAlertWindowPermission(ExoPlayerActivity.this)) {
Toast.makeText(ExoPlayerActivity.this, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
return;
}
Intent i = new Intent(ExoPlayerActivity.this, PopupVideoPlayer.class);
i.putExtra(AbstractPlayer.VIDEO_TITLE, getVideoTitle())
.putExtra(AbstractPlayer.CHANNEL_NAME, getChannelName())
.putExtra(AbstractPlayer.VIDEO_URL, getVideoUrl())
.putExtra(AbstractPlayer.INDEX_SEL_VIDEO_STREAM, getSelectedIndexStream())
.putExtra(AbstractPlayer.VIDEO_STREAMS_LIST, getVideoStreamsList())
.putExtra(AbstractPlayer.START_POSITION, ((int) getPlayer().getCurrentPosition()));
context.startService(i);
((View) getControlAnimationView().getParent()).setVisibility(View.GONE);
if (playerImpl.isPlaying()) playerImpl.getPlayer().setPlayWhenReady(false);
ExoPlayerActivity.this.finish();
}
@Override
protected void onPlayPauseClick() {
super.onPlayPauseClick();
if (videoView == null) return;
if (DEBUG) Log.d(TAG, "onPlayPauseClick() called" + videoView.getDuration() + " position= " + videoView.getCurrentPosition());
if (isFinished) {
videoView.restart();
setRewindButtonRemoved(false);
setFastForwardButtonRemoved(false);
isFinished = false;
seekBar.setEnabled(true);
@SuppressWarnings("deprecation")
public void onRepeatClicked() {
super.onRepeatClicked();
if (DEBUG) Log.d(TAG, "onRepeatClicked() called");
switch (getCurrentRepeatMode()) {
case REPEAT_DISABLED:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) repeatButton.setImageAlpha(77);
else repeatButton.setAlpha(77);
break;
case REPEAT_ONE:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) repeatButton.setImageAlpha(255);
else repeatButton.setAlpha(255);
break;
case REPEAT_ALL:
// Waiting :)
break;
}
}
private void performSeek(int newPosition) {
internalListener.onSeekEnded(newPosition);
@Override
public void onClick(View v) {
super.onClick(v);
if (v.getId() == repeatButton.getId()) onRepeatClicked();
else if (v.getId() == playPauseButton.getId()) onVideoPlayPause();
else if (v.getId() == screenRotationButton.getId()) onScreenRotationClicked();
if (getCurrentState() != STATE_COMPLETED) {
animateView(playerImpl.getControlsRoot(), true, 300, 0, new Runnable() {
@Override
public void run() {
if (getCurrentState() == STATE_PLAYING && !playerImpl.isQualityMenuVisible()) {
animateView(playerImpl.getControlsRoot(), false, 300, DEFAULT_CONTROLS_HIDE_TIME, true);
}
}
}, false);
}
}
public SeekBar getSeekBar() {
return seekBar;
private void onScreenRotationClicked() {
if (DEBUG) Log.d(TAG, "onScreenRotationClicked() called");
toggleOrientation();
}
@Override
public void onVideoPlayPause() {
super.onVideoPlayPause();
if (getPlayer().getPlayWhenReady()) {
animateView(playPauseButton, false, 80, 0, new Runnable() {
@Override
public void run() {
playPauseButton.setImageResource(R.drawable.ic_pause_white);
animateView(playPauseButton, true, 200, 0);
}
});
} else {
animateView(playPauseButton, false, 80, 0, new Runnable() {
@Override
public void run() {
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white);
animateView(playPauseButton, true, 200, 0);
}
});
}
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
super.onStopTrackingTouch(seekBar);
if (playerImpl.wasPlaying()) {
hideSystemUi();
playerImpl.getControlsRoot().setVisibility(View.GONE);
}
}
@Override
public void onDismiss(PopupMenu menu) {
super.onDismiss(menu);
if (isPlaying()) animateView(getControlsRoot(), false, 500, 0, true);
}
@Override
public void onError() {
Toast.makeText(context, "Failed to play this video", Toast.LENGTH_SHORT).show();
finish();
}
/*//////////////////////////////////////////////////////////////////////////
// States
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onLoading() {
super.onLoading();
playPauseButton.setImageResource(R.drawable.ic_pause_white);
}
@Override
public void onPaused() {
super.onPaused();
animateView(playPauseButton, true, 100, 0);
showSystemUi();
}
@Override
public void onPausedSeek() {
super.onPausedSeek();
animateView(playPauseButton, false, 100, 0);
}
@Override
public void onPlaying() {
super.onPlaying();
animateView(playPauseButton, true, 500, 0);
}
@Override
public void onCompleted() {
if (getCurrentRepeatMode() == RepeatMode.REPEAT_ONE) {
playPauseButton.setImageResource(R.drawable.ic_pause_white);
} else {
showSystemUi();
animateView(playPauseButton, false, 0, 0, new Runnable() {
@Override
public void run() {
playPauseButton.setImageResource(R.drawable.ic_replay_white);
animateView(playPauseButton, true, 300, 0);
}
});
}
super.onCompleted();
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
@Override
public void animateView(View view, boolean enterOrExit, long duration, long delay, final Runnable execOnEnd, boolean hideUi) {
//if (execOnEnd == null) playerImpl.setDefaultAnimationEnd(hideUiRunnable);
if (hideUi && execOnEnd != null) {
Runnable combinedRunnable = new Runnable() {
@Override
public void run() {
execOnEnd.run();
hideUiRunnable.run();
}
};
super.animateView(view, enterOrExit, duration, delay, combinedRunnable, true);
} else super.animateView(view, enterOrExit, duration, delay, hideUi ? hideUiRunnable : execOnEnd, hideUi);
}
///////////////////////////////////////////////////////////////////////////
// Getters
///////////////////////////////////////////////////////////////////////////
public TextView getTitleTextView() {
return titleTextView;
}
public TextView getChannelTextView() {
return channelTextView;
}
public TextView getVolumeTextView() {
return volumeTextView;
}
public TextView getBrightnessTextView() {
return brightnessTextView;
}
public ImageButton getRepeatButton() {
return repeatButton;
}
public ImageButton getPlayPauseButton() {
return playPauseButton;
}
}
}
private class MySimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener {
private boolean isMoving;
@Override
public boolean onDoubleTap(MotionEvent e) {
if (DEBUG) Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY());
if (!playerImpl.isPlaying()) return false;
if (e.getX() > playerImpl.getRootView().getWidth() / 2) playerImpl.onFastForward();
else playerImpl.onFastRewind();
return true;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
if (DEBUG) Log.d(TAG, "onSingleTapConfirmed() called with: e = [" + e + "]");
if (playerImpl.getCurrentState() != StateInterface.STATE_PLAYING) return true;
if (playerImpl.isControlsVisible()) playerImpl.animateView(playerImpl.getControlsRoot(), false, 150, 0, true);
else {
playerImpl.animateView(playerImpl.getControlsRoot(), true, 500, 0, new Runnable() {
@Override
public void run() {
playerImpl.animateView(playerImpl.getControlsRoot(), false, 300, AbstractPlayer.DEFAULT_CONTROLS_HIDE_TIME, true);
}
});
showSystemUi();
}
return true;
}
private final float stepsBrightness = 21, stepBrightness = (1f / stepsBrightness), minBrightness = .01f;
private float currentBrightness = .5f;
private int currentVolume, maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
private final String brightnessUnicode = new String(Character.toChars(0x2600));
// private final String volumeUnicode = new String(Character.toChars(0x1F50A));
private final String volumeUnicode = new String(Character.toChars(0x1F508));
private final int MOVEMENT_THRESHOLD = 40;
private final int eventsThreshold = 3;
private boolean triggered = false;
private int eventsNum;
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
//noinspection PointlessBooleanExpression
if (DEBUG && false) Log.d(TAG, "ExoPlayerActivity.onScroll = " +
", e1.getRaw = [" + e1.getRawX() + ", " + e1.getRawY() + "]" +
", e2.getRaw = [" + e2.getRawX() + ", " + e2.getRawY() + "]" +
", distanceXy = [" + distanceX + ", " + distanceY + "]");
float abs = Math.abs(e2.getY() - e1.getY());
if (!triggered) {
triggered = abs > MOVEMENT_THRESHOLD;
return false;
}
if (eventsNum++ % eventsThreshold != 0 || playerImpl.getCurrentState() == StateInterface.STATE_COMPLETED) return false;
isMoving = true;
// boolean up = !((e2.getY() - e1.getY()) > 0) && distanceY > 0; // Android's origin point is on top
boolean up = distanceY > 0;
if (e1.getX() > playerImpl.getRootView().getWidth() / 2) {
currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) + (up ? 1 : -1);
if (currentVolume >= maxVolume) currentVolume = maxVolume;
if (currentVolume <= 0) currentVolume = 0;
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, currentVolume, 0);
if (DEBUG) Log.d(TAG, "onScroll().volumeControl, currentVolume = " + currentVolume);
playerImpl.getVolumeTextView().setText(volumeUnicode + " " + Math.round((((float) currentVolume) / maxVolume) * 100) + "%");
if (playerImpl.getVolumeTextView().getVisibility() != View.VISIBLE) playerImpl.animateView(playerImpl.getVolumeTextView(), true, 200, 0);
if (playerImpl.getBrightnessTextView().getVisibility() == View.VISIBLE) playerImpl.getBrightnessTextView().setVisibility(View.GONE);
} else {
WindowManager.LayoutParams lp = getWindow().getAttributes();
currentBrightness += up ? stepBrightness : -stepBrightness;
if (currentBrightness >= 1f) currentBrightness = 1f;
if (currentBrightness <= minBrightness) currentBrightness = minBrightness;
lp.screenBrightness = currentBrightness;
getWindow().setAttributes(lp);
if (DEBUG) Log.d(TAG, "onScroll().brightnessControl, currentBrightness = " + currentBrightness);
int brightnessNormalized = Math.round(currentBrightness * 100);
playerImpl.getBrightnessTextView().setText(brightnessUnicode + " " + (brightnessNormalized == 1 ? 0 : brightnessNormalized) + "%");
if (playerImpl.getBrightnessTextView().getVisibility() != View.VISIBLE) playerImpl.animateView(playerImpl.getBrightnessTextView(), true, 200, 0);
if (playerImpl.getVolumeTextView().getVisibility() == View.VISIBLE) playerImpl.getVolumeTextView().setVisibility(View.GONE);
}
return true;
}
private void onScrollEnd() {
if (DEBUG) Log.d(TAG, "onScrollEnd() called");
triggered = false;
eventsNum = 0;
/* if (playerImpl.getVolumeTextView().getVisibility() == View.VISIBLE) playerImpl.getVolumeTextView().setVisibility(View.GONE);
if (playerImpl.getBrightnessTextView().getVisibility() == View.VISIBLE) playerImpl.getBrightnessTextView().setVisibility(View.GONE);*/
if (playerImpl.getVolumeTextView().getVisibility() == View.VISIBLE) playerImpl.animateView(playerImpl.getVolumeTextView(), false, 200, 200);
if (playerImpl.getBrightnessTextView().getVisibility() == View.VISIBLE) playerImpl.animateView(playerImpl.getBrightnessTextView(), false, 200, 200);
if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == StateInterface.STATE_PLAYING) {
playerImpl.animateView(playerImpl.getControlsRoot(), false, 300, AbstractPlayer.DEFAULT_CONTROLS_HIDE_TIME);
}
}
@Override
public boolean onTouch(View v, MotionEvent event) {
gestureDetector.onTouchEvent(event);
if (event.getAction() == MotionEvent.ACTION_UP && isMoving) {
isMoving = false;
onScrollEnd();
}
return true;
}
}
}

View File

@@ -5,9 +5,8 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.media.MediaPlayer;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -28,7 +27,6 @@ import android.widget.MediaController;
import android.widget.ProgressBar;
import android.widget.VideoView;
import org.schabi.newpipe.App;
import org.schabi.newpipe.R;
/**

View File

@@ -1,8 +1,9 @@
package org.schabi.newpipe.player.popup;
package org.schabi.newpipe.player;
public interface StateInterface {
int STATE_LOADING = 123;
int STATE_PLAYING = 125;
int STATE_PLAYING = 124;
int STATE_BUFFERING = 125;
int STATE_PAUSED = 126;
int STATE_PAUSED_SEEK = 127;
int STATE_COMPLETED = 128;
@@ -11,6 +12,7 @@ public interface StateInterface {
void onLoading();
void onPlaying();
void onBuffering();
void onPaused();
void onPausedSeek();
void onCompleted();

View File

@@ -1,93 +0,0 @@
package org.schabi.newpipe.player.popup;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.os.Build;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import com.devbrackets.android.exomedia.ui.widget.EMVideoView;
import org.schabi.newpipe.R;
public class PopupViewHolder {
private View rootView;
private EMVideoView videoView;
private View loadingPanel;
private ImageView endScreen;
private ImageView controlAnimationView;
private LinearLayout controlsRoot;
private SeekBar playbackSeekBar;
private TextView playbackCurrentTime;
private TextView playbackEndTime;
public PopupViewHolder(View rootView) {
if (rootView == null) return;
this.rootView = rootView;
this.videoView = (EMVideoView) rootView.findViewById(R.id.popupVideoView);
this.loadingPanel = rootView.findViewById(R.id.loadingPanel);
this.endScreen = (ImageView) rootView.findViewById(R.id.endScreen);
this.controlAnimationView = (ImageView) rootView.findViewById(R.id.controlAnimationView);
this.controlsRoot = (LinearLayout) rootView.findViewById(R.id.playbackControlRoot);
this.playbackSeekBar = (SeekBar) rootView.findViewById(R.id.playbackSeekBar);
this.playbackCurrentTime = (TextView) rootView.findViewById(R.id.playbackCurrentTime);
this.playbackEndTime = (TextView) rootView.findViewById(R.id.playbackEndTime);
doModifications();
}
private void doModifications() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) playbackSeekBar.getThumb().setColorFilter(Color.RED, PorterDuff.Mode.SRC_IN);
playbackSeekBar.getProgressDrawable().setColorFilter(Color.RED, PorterDuff.Mode.MULTIPLY);
}
public boolean isControlsVisible() {
return controlsRoot != null && controlsRoot.getVisibility() == View.VISIBLE;
}
public boolean isVisible(View view) {
return view != null && view.getVisibility() == View.VISIBLE;
}
///////////////////////////////////////////////////////////////////////////
// GETTERS
///////////////////////////////////////////////////////////////////////////
public View getRootView() {
return rootView;
}
public EMVideoView getVideoView() {
return videoView;
}
public View getLoadingPanel() {
return loadingPanel;
}
public ImageView getEndScreen() {
return endScreen;
}
public ImageView getControlAnimationView() {
return controlAnimationView;
}
public LinearLayout getControlsRoot() {
return controlsRoot;
}
public SeekBar getPlaybackSeekBar() {
return playbackSeekBar;
}
public TextView getPlaybackCurrentTime() {
return playbackCurrentTime;
}
public TextView getPlaybackEndTime() {
return playbackEndTime;
}
}

View File

@@ -19,17 +19,14 @@ import android.view.inputmethod.InputMethodManager;
import android.widget.ProgressBar;
import android.widget.Toast;
import org.schabi.newpipe.ChannelActivity;
import org.schabi.newpipe.R;
import org.schabi.newpipe.ReCaptchaActivity;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.search.SearchEngine;
import org.schabi.newpipe.extractor.search.SearchResult;
import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.R;
import org.schabi.newpipe.detail.VideoItemDetailActivity;
import org.schabi.newpipe.detail.VideoItemDetailFragment;
import org.schabi.newpipe.info_list.InfoListAdapter;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.util.NavStack;
import java.util.EnumSet;

View File

@@ -22,6 +22,7 @@ import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R;
import java.util.ArrayList;
import java.util.List;
import info.guardianproject.netcipher.proxy.OrbotHelper;
@@ -52,6 +53,7 @@ public class SettingsFragment extends PreferenceFragment
SharedPreferences.OnSharedPreferenceChangeListener prefListener;
// get keys
String DEFAULT_RESOLUTION_PREFERENCE;
String PREFERRED_VIDEO_FORMAT_PREFERENCE;
String DEFAULT_AUDIO_FORMAT_PREFERENCE;
String SEARCH_LANGUAGE_PREFERENCE;
String DOWNLOAD_PATH_PREFERENCE;
@@ -59,6 +61,7 @@ public class SettingsFragment extends PreferenceFragment
String USE_TOR_KEY;
String THEME;
private ListPreference defaultResolutionPreference;
private ListPreference preferredVideoFormatPreference;
private ListPreference defaultAudioFormatPreference;
private ListPreference searchLanguagePreference;
private Preference downloadPathPreference;
@@ -77,6 +80,7 @@ public class SettingsFragment extends PreferenceFragment
// get keys
DEFAULT_RESOLUTION_PREFERENCE = getString(R.string.default_resolution_key);
PREFERRED_VIDEO_FORMAT_PREFERENCE = getString(R.string.preferred_video_format_key);
DEFAULT_AUDIO_FORMAT_PREFERENCE = getString(R.string.default_audio_format_key);
SEARCH_LANGUAGE_PREFERENCE = getString(R.string.search_language_key);
DOWNLOAD_PATH_PREFERENCE = getString(R.string.download_path_key);
@@ -87,6 +91,8 @@ public class SettingsFragment extends PreferenceFragment
// get pref objects
defaultResolutionPreference =
(ListPreference) findPreference(DEFAULT_RESOLUTION_PREFERENCE);
preferredVideoFormatPreference =
(ListPreference) findPreference(PREFERRED_VIDEO_FORMAT_PREFERENCE);
defaultAudioFormatPreference =
(ListPreference) findPreference(DEFAULT_AUDIO_FORMAT_PREFERENCE);
searchLanguagePreference =
@@ -254,6 +260,9 @@ public class SettingsFragment extends PreferenceFragment
defaultResolutionPreference.setSummary(
defaultPreferences.getString(DEFAULT_RESOLUTION_PREFERENCE,
getString(R.string.default_resolution_value)));
preferredVideoFormatPreference.setSummary(
defaultPreferences.getString(PREFERRED_VIDEO_FORMAT_PREFERENCE,
getString(R.string.preferred_video_format_default)));
defaultAudioFormatPreference.setSummary(
defaultPreferences.getString(DEFAULT_AUDIO_FORMAT_PREFERENCE,
getString(R.string.default_audio_format_value)));

View File

@@ -5,13 +5,10 @@ import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.NavUtils;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import org.schabi.newpipe.ChannelActivity;
import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.detail.VideoItemDetailActivity;
import org.schabi.newpipe.detail.VideoItemDetailFragment;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;

View File

@@ -7,18 +7,21 @@ import org.schabi.newpipe.R;
public class ThemeHelper {
public static void setTheme(Context context, boolean mode) {
// mode is true for normal theme, false for no action bar theme.
/**
* Apply the selected theme (on NewPipe settings) in the context
*
* @param context context that the theme will be applied
* @param useActionbarTheme whether to use an action bar theme or not
*/
public static void setTheme(Context context, boolean useActionbarTheme) {
String themeKey = context.getString(R.string.theme_key);
//String lightTheme = context.getResources().getString(R.string.light_theme_title);
String darkTheme = context.getResources().getString(R.string.dark_theme_title);
String blackTheme = context.getResources().getString(R.string.black_theme_title);
String sp = PreferenceManager.getDefaultSharedPreferences(context)
.getString(themeKey, context.getResources().getString(R.string.light_theme_title));
if (mode) {
if (useActionbarTheme) {
if (sp.equals(darkTheme)) context.setTheme(R.style.DarkTheme);
else if (sp.equals(blackTheme)) context.setTheme(R.style.BlackTheme);
else context.setTheme(R.style.AppTheme);

View File

Before

Width:  |  Height:  |  Size: 347 B

After

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 B

View File

Before

Width:  |  Height:  |  Size: 92 B

After

Width:  |  Height:  |  Size: 92 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 B

View File

Before

Width:  |  Height:  |  Size: 283 B

After

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 976 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 858 B

View File

Before

Width:  |  Height:  |  Size: 257 B

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 B

View File

Before

Width:  |  Height:  |  Size: 90 B

After

Width:  |  Height:  |  Size: 90 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 B

View File

Before

Width:  |  Height:  |  Size: 220 B

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 639 B

View File

Before

Width:  |  Height:  |  Size: 436 B

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 B

View File

Before

Width:  |  Height:  |  Size: 94 B

After

Width:  |  Height:  |  Size: 94 B

View File

Before

Width:  |  Height:  |  Size: 343 B

After

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 B

View File

Before

Width:  |  Height:  |  Size: 461 B

After

Width:  |  Height:  |  Size: 461 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 707 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 B

View File

Before

Width:  |  Height:  |  Size: 605 B

After

Width:  |  Height:  |  Size: 605 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="#a0000000"
android:centerColor="#26000000"
android:startColor="#8c000000"
android:endColor="#00000000"
android:angle="90"
/>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="#8c000000"
android:endColor="#00000000"
android:angle="-90"
/>
</shape>

View File

@@ -1,279 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".detail.VideoItemDetailFragment"
android:textIsSelectable="true"
style="?android:attr/textAppearanceLarge"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/video_item_detail">
<ProgressBar android:id="@+id/detail_progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:indeterminate="true"/>
<com.nirhart.parallaxscroll.views.ParallaxScrollView
android:id="@+id/detail_main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible"
app:parallax_factor="1.9"
tools:ignore="UselessParent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:id="@+id/detail_stream_thumbnail_window_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground">
<ImageView android:id="@+id/detail_thumbnail_view"
android:contentDescription="@string/detail_thumbnail_view_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:background="@android:color/black"
android:src="@drawable/dummy_thumbnail_dark"/>
<ImageView android:id="@+id/play_arrow_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:layout_centerInParent="true"
android:src="@drawable/new_play_arrow"
android:visibility="invisible"/>
<Button
android:id="@+id/detail_stream_thumbnail_window_background_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"/>
</RelativeLayout>
<RelativeLayout android:id="@+id/detail_text_content_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/detail_stream_thumbnail_window_layout"
android:background="?android:windowBackground"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/detailTopView">
<TextView android:id="@+id/detail_video_title_view"
android:layout_width="0dp"
android:layout_weight=".7"
android:layout_height="wrap_content"
android:textSize="@dimen/video_item_detail_title_text_size"
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_marginTop="12dp"
android:layout_marginLeft="12dp"
android:layout_marginStart="12dp"
android:text="Title"/>
<ImageView
android:layout_width="15dp"
android:layout_height="30dp"
android:id="@+id/toggle_description_view"
android:src="@drawable/arrow_down"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:layout_marginRight="10dp"
android:layout_marginEnd="10dp"
android:layout_marginTop="8dp"/>
</LinearLayout>
<TextView android:id="@+id/detail_view_count_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/video_item_detail_views_text_size"
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_marginLeft="12dp"
android:layout_marginStart="12dp"
android:text="10,069,948 views"
android:layout_below="@id/detailTopView"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginTop="5dp" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/detail_view_count_view"
android:id="@+id/detailExtraView"
android:layout_marginLeft="12dp"
android:layout_marginStart="12dp"
android:layout_marginRight="12dp"
android:layout_marginEnd="12dp"
android:visibility="gone">
<TextView android:id="@+id/detail_upload_date_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/video_item_detail_upload_date_text_size"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Upload date"
android:layout_marginTop="3dp" />
<TextView android:id="@+id/detail_description_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/video_item_detail_description_text_size"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_below="@id/detail_upload_date_view"
android:text="Description............."
android:layout_marginTop="3dp" />
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/stream_info_layout"
android:layout_marginLeft="12dp"
android:layout_marginStart="12dp"
android:layout_below="@+id/detailExtraView"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginTop="5dp">
<ImageView android:id="@+id/detail_thumbs_up_img_view"
android:contentDescription="@string/detail_likes_img_view_description"
android:layout_width="@dimen/video_item_detail_like_image_width"
android:layout_height="@dimen/video_item_detail_like_image_height"
android:src="?attr/thumbs_up"/>
<TextView android:id="@+id/detail_thumbs_up_count_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/video_item_detail_likes_text_size"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="200" />
<ImageView android:id="@+id/detail_thumbs_down_img_view"
android:contentDescription="@string/detail_dislikes_img_view_description"
android:layout_width="@dimen/video_item_detail_like_image_width"
android:layout_height="@dimen/video_item_detail_like_image_height"
android:src="?attr/thumbs_down"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"/>
<TextView android:id="@+id/detail_thumbs_down_count_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/video_item_detail_likes_text_size"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="100" />
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/detail_uploader_frame"
android:layout_below="@id/stream_info_layout">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/detail_uploader_layout"
android:layout_marginTop="12dp">
<View
android:background="#000"
android:layout_width="match_parent"
android:layout_height="1px" />
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/detail_uploader_thumbnail_view"
android:contentDescription="@string/detail_uploader_thumbnail_view_description"
android:layout_width="@dimen/video_item_detail_uploader_image_size"
android:layout_height="@dimen/video_item_detail_uploader_image_size"
android:src="@drawable/buddy"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"/>
<TextView android:id="@+id/detail_uploader_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:textSize="@dimen/video_item_detail_uploader_text_size"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Uploader"
android:layout_centerVertical="true"
android:layout_toRightOf="@+id/detail_uploader_thumbnail_view"
android:layout_toEndOf="@+id/detail_uploader_thumbnail_view"
android:layout_marginLeft="15dp"
android:layout_marginStart="28dp" />
<View
android:background="#000"
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_below="@id/detail_uploader_thumbnail_view"/>
</RelativeLayout>
<Button
android:layout_marginTop="13dp"
android:id="@+id/channel_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"/>
</FrameLayout>
<RelativeLayout android:id="@+id/detail_next_stream_root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal|bottom"
android:layout_below="@+id/detail_uploader_frame"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp">
<TextView
android:id="@+id/detail_next_stream_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:textSize="@dimen/video_item_detail_next_text_size"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/next_video_title"
android:textAllCaps="true" />
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/detail_next_stream_content"/>
<TextView android:id="@+id/detail_similar_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:textSize="@dimen/video_item_detail_next_text_size"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/similar_videos_btn_text"
android:layout_below="@id/detail_next_stream_content"
android:textAllCaps="true" />
<LinearLayout
android:id="@+id/similar_streams_view"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/detail_similar_title">
</LinearLayout>
</RelativeLayout>
</RelativeLayout>
</RelativeLayout>
</com.nirhart.parallaxscroll.views.ParallaxScrollView>
</RelativeLayout>

View File

@@ -1,15 +1,318 @@
<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root"
android:focusable="true"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
android:gravity="center"
android:keepScreenOn="true">
<com.devbrackets.android.exomedia.ui.widget.EMVideoView
android:id="@+id/emVideoView"
<com.google.android.exoplayer2.ui.AspectRatioFrameLayout
android:id="@+id/aspectRatioLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:layout_height="match_parent"
android:layout_gravity="center">
</RelativeLayout>
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"/>
<View
android:id="@+id/surfaceForeground"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"/>
</com.google.android.exoplayer2.ui.AspectRatioFrameLayout>
<ImageView
android:id="@+id/endScreen"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:scaleType="centerInside"
android:visibility="gone"
tools:background="@android:color/white"
tools:ignore="ContentDescription"
tools:visibility="visible"/>
<RelativeLayout
android:id="@+id/playbackControlRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#64000000"
android:visibility="gone"
tools:visibility="visible">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<RelativeLayout
android:id="@+id/topControls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:background="@drawable/player_top_controls_bg"
android:gravity="top"
android:paddingBottom="70dp"
android:paddingLeft="2dp"
android:paddingRight="10dp"
android:paddingTop="10dp"
tools:ignore="RtlHardcoded">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_toLeftOf="@+id/qualityTextView"
android:gravity="top"
android:orientation="vertical"
android:paddingLeft="8dp"
android:paddingRight="8dp"
tools:ignore="RtlHardcoded">
<TextView
android:id="@+id/titleTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@android:color/white"
android:textSize="15sp"
android:textStyle="bold"
tools:ignore="RtlHardcoded"
tools:text="The Video Title LONG very LONG"/>
<TextView
android:id="@+id/channelTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@android:color/white"
android:textSize="12sp"
tools:text="The Video Artist LONG very LONG very Long"/>
</LinearLayout>
<TextView
android:id="@+id/qualityTextView"
android:layout_width="wrap_content"
android:layout_height="35dp"
android:layout_marginLeft="2dp"
android:layout_marginRight="2dp"
android:layout_toLeftOf="@+id/screenRotationButton"
android:gravity="center"
android:minWidth="50dp"
android:text="720p"
android:textColor="@android:color/white"
android:textStyle="bold"
tools:ignore="HardcodedText,RtlHardcoded"/>
<ImageButton
android:id="@+id/screenRotationButton"
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_marginLeft="2dp"
android:layout_marginRight="2dp"
android:layout_toLeftOf="@+id/repeatButton"
android:background="#00ffffff"
android:clickable="true"
android:padding="8dp"
android:scaleType="fitXY"
android:src="@drawable/ic_screen_rotation_white"
tools:ignore="ContentDescription,RtlHardcoded"/>
<ImageButton
android:id="@+id/repeatButton"
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_marginLeft="2dp"
android:layout_marginRight="2dp"
android:layout_toLeftOf="@+id/fullScreenButton"
android:background="#00ffffff"
android:clickable="true"
android:padding="5dp"
android:scaleType="fitXY"
android:src="@drawable/ic_repeat_white"
tools:ignore="ContentDescription,RtlHardcoded"/>
<ImageButton
android:id="@+id/fullScreenButton"
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_alignParentRight="true"
android:layout_marginLeft="4dp"
android:background="#00ffffff"
android:clickable="true"
android:scaleType="fitXY"
android:src="@drawable/ic_fullscreen_exit_white"
tools:ignore="ContentDescription,RtlHardcoded"/>
</RelativeLayout>
<LinearLayout
android:id="@+id/bottomControls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="@drawable/player_controls_bg"
android:gravity="center"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<TextView
android:id="@+id/playbackCurrentTime"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:minHeight="40dp"
android:text="-:--:--"
android:textColor="@android:color/white"
tools:ignore="HardcodedText"
tools:text="1:06:29"/>
<android.support.v7.widget.AppCompatSeekBar
android:id="@+id/playbackSeekBar"
style="@style/Widget.AppCompat.SeekBar"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:paddingBottom="4dp"
android:paddingTop="8dp"
tools:progress="25"
tools:secondaryProgress="50"/>
<TextView
android:id="@+id/playbackEndTime"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text="-:--:--"
android:textColor="@android:color/white"
tools:ignore="HardcodedText"
tools:text="1:23:49"/>
</LinearLayout>
</RelativeLayout>
<ImageButton
android:id="@+id/playPauseButton"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerInParent="true"
android:background="#00000000"
android:scaleType="fitXY"
android:src="@drawable/ic_pause_white"
tools:ignore="ContentDescription"/>
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/controlAnimationView"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/ic_action_av_fast_rewind"
android:visibility="gone"
tools:ignore="ContentDescription"
tools:visibility="visible"/>
</LinearLayout>
<RelativeLayout
android:id="@+id/loadingPanel"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
tools:visibility="gone">
<ProgressBar
android:id="@+id/progressBarLoadingPanel"
style="?android:attr/progressBarStyleLargeInverse"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:indeterminate="true"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
tools:ignore="RtlHardcoded">
<TextView
android:id="@+id/volumeTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerInParent="true"
android:layout_marginLeft="20dp"
android:background="#64000000"
android:paddingBottom="10dp"
android:paddingLeft="30dp"
android:paddingRight="30dp"
android:paddingTop="10dp"
android:textColor="@android:color/white"
android:textSize="35sp"
android:visibility="gone"
tools:ignore="RtlHardcoded"
tools:text="Volume 0"
tools:visibility="visible"/>
<TextView
android:id="@+id/brightnessTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerInParent="true"
android:layout_marginRight="20dp"
android:background="#64000000"
android:paddingBottom="10dp"
android:paddingLeft="30dp"
android:paddingRight="30dp"
android:paddingTop="10dp"
android:textColor="@android:color/white"
android:textSize="35sp"
android:visibility="gone"
tools:ignore="RtlHardcoded"
tools:text="Brightness 0"
tools:visibility="visible"/>
<TextView
android:id="@+id/currentDisplaySeek"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginBottom="58dp"
android:background="#64000000"
android:paddingBottom="10dp"
android:paddingLeft="30dp"
android:paddingRight="30dp"
android:paddingTop="10dp"
android:textColor="@android:color/white"
android:textSize="26sp"
android:textStyle="bold"
android:visibility="gone"
tools:ignore="RtlHardcoded"
tools:text="1:06:29"
tools:visibility="visible"/>
</RelativeLayout>
</FrameLayout>

View File

@@ -1,4 +1,325 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/videoitem_detail_container"
android:layout_width="match_parent" android:layout_height="match_parent"
tools:context=".detail.VideoItemDetailActivity" tools:ignore="MergeRootFrame" />
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/video_item_detail"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.nirhart.parallaxscroll.views.ParallaxScrollView
android:id="@+id/detail_main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible"
app:parallax_factor="1.9">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:id="@+id/detail_thumbnail_root_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/black">
<ImageView
android:id="@+id/detail_thumbnail_image_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:background="@android:color/transparent"
android:contentDescription="@string/detail_thumbnail_view_description"
android:scaleType="centerCrop"
tools:ignore="RtlHardcoded"
tools:layout_height="200dp"
tools:src="@drawable/dummy_thumbnail"/>
<ImageView
android:id="@+id/detail_thumbnail_play_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@android:color/transparent"
android:src="@drawable/new_play_arrow"
android:visibility="invisible"
tools:ignore="ContentDescription"
tools:visibility="visible"/>
<Button
android:id="@+id/detail_stream_thumbnail_background_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/detail_content_root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/detail_thumbnail_root_layout"
android:background="?android:windowBackground"
android:visibility="gone"
tools:visibility="visible">
<RelativeLayout
android:id="@+id/detail_title_root_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:paddingTop="12dp">
<TextView
android:id="@+id/detail_video_title_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/detail_toggle_description_view"
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="1"
android:paddingBottom="2dp"
android:paddingTop="6dp"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_detail_title_text_size"
tools:ignore="RtlHardcoded"
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed a ultricies ex. Integer sit amet sodales risus. Duis non mi et urna pretium bibendum. Nunc eleifend est quis ipsum porttitor egestas. Sed facilisis, nisl quis eleifend pellentesque, orci metus egestas dolor, at accumsan eros metus quis libero."/>
<ImageView
android:id="@+id/detail_toggle_description_view"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="5dp"
android:layout_marginTop="6dp"
android:src="@drawable/arrow_down"
tools:ignore="ContentDescription,RtlHardcoded"/>
</RelativeLayout>
<TextView
android:id="@+id/detail_view_count_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@id/detail_title_root_layout"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginTop="5dp"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_detail_views_text_size"
tools:ignore="RtlHardcoded"
tools:text="10,069,948 views"/>
<RelativeLayout
android:id="@+id/detail_description_root_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/detail_view_count_view"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginTop="5dp"
android:visibility="gone"
tools:visibility="visible">
<TextView
android:id="@+id/detail_upload_date_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="@dimen/video_item_detail_upload_date_text_size"
android:textStyle="bold"
tools:text="Upload date"/>
<TextView
android:id="@+id/detail_description_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/detail_upload_date_view"
android:layout_marginTop="3dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="@dimen/video_item_detail_description_text_size"
tools:text="Description Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed a ultricies ex. Integer sit amet sodales risus. Duis non mi et urna pretium bibendum."/>
</RelativeLayout>
<LinearLayout
android:id="@+id/detail_thumbs_root_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_below="@id/detail_description_root_layout"
android:layout_marginLeft="12dp"
android:layout_marginStart="12dp"
android:layout_marginTop="5dp"
android:gravity="center_vertical"
android:minHeight="30dp">
<ImageView
android:id="@+id/detail_thumbs_up_img_view"
android:layout_width="@dimen/video_item_detail_like_image_width"
android:layout_height="@dimen/video_item_detail_like_image_height"
android:contentDescription="@string/detail_likes_img_view_description"
android:src="?attr/thumbs_up"/>
<TextView
android:id="@+id/detail_thumbs_up_count_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="5dp"
android:gravity="left|center_vertical"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="@dimen/video_item_detail_likes_text_size"
tools:ignore="RtlHardcoded"
tools:text="200"/>
<ImageView
android:id="@+id/detail_thumbs_down_img_view"
android:layout_width="@dimen/video_item_detail_like_image_width"
android:layout_height="@dimen/video_item_detail_like_image_height"
android:layout_marginLeft="15dp"
android:contentDescription="@string/detail_dislikes_img_view_description"
android:src="?attr/thumbs_down"
tools:ignore="RtlHardcoded"/>
<TextView
android:id="@+id/detail_thumbs_down_count_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="5dp"
android:gravity="left|center_vertical"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="@dimen/video_item_detail_likes_text_size"
tools:ignore="RtlHardcoded"
tools:text="100"/>
<TextView
android:id="@+id/detail_thumbs_disabled_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="15dp"
android:gravity="left|center_vertical"
android:text="@string/disabled"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_detail_likes_text_size"
android:textStyle="bold"
android:visibility="gone"
tools:ignore="RtlHardcoded"
tools:visibility="visible"/>
</LinearLayout>
<FrameLayout
android:id="@+id/detail_uploader_root_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/detail_thumbs_root_layout">
<RelativeLayout
android:id="@+id/detail_uploader_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginTop="8dp">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/detail_uploader_thumbnail_view"
android:layout_width="@dimen/video_item_detail_uploader_image_size"
android:layout_height="@dimen/video_item_detail_uploader_image_size"
android:layout_alignParentLeft="true"
android:contentDescription="@string/detail_uploader_thumbnail_view_description"
android:src="@drawable/buddy"
tools:ignore="RtlHardcoded"/>
<TextView
android:id="@+id/detail_uploader_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="15dp"
android:layout_toEndOf="@+id/detail_uploader_thumbnail_view"
android:layout_toRightOf="@+id/detail_uploader_thumbnail_view"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_detail_uploader_text_size"
android:textStyle="bold"
tools:ignore="RtlHardcoded"
tools:text="Uploader"/>
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_gravity="bottom"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:background="?attr/separatorColor"/>
<Button
android:id="@+id/detail_uploader_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"
tools:visibility="gone"/>
</FrameLayout>
<RelativeLayout
android:id="@+id/detail_related_streams_root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/detail_uploader_root_layout"
android:layout_centerHorizontal="true"
android:layout_gravity="center_horizontal|bottom"
android:layout_marginTop="14dp">
<TextView
android:id="@+id/detail_next_stream_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginLeft="12dp"
android:text="@string/next_video_title"
android:textAllCaps="true"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="@dimen/video_item_detail_next_text_size"
tools:ignore="RtlHardcoded"/>
<LinearLayout
android:id="@+id/detail_related_streams_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/detail_next_stream_title"
android:layout_marginTop="2dp"
android:orientation="vertical"
tools:minHeight="100dp">
</LinearLayout>
</RelativeLayout>
</RelativeLayout>
<ProgressBar
android:id="@+id/detail_loading_progress_bar"
style="@style/Widget.AppCompat.ProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/detail_thumbnail_root_layout"
android:layout_centerHorizontal="true"
android:layout_marginTop="30dp"
android:indeterminate="true"/>
</RelativeLayout>
</com.nirhart.parallaxscroll.views.ParallaxScrollView>
</RelativeLayout>

View File

@@ -1,91 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout android:id="@+id/item_main_layout"
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_height="wrap_content">
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:orientation="vertical">
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:id="@+id/itemThumbnailViewContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="12dp">
android:layout_marginRight="@dimen/video_item_search_image_right_margin"
tools:ignore="RtlHardcoded">
<RelativeLayout android:id="@+id/itemThumbnailViewContainer"
android:layout_marginRight="@dimen/video_item_search_image_right_margin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:ignore="RtlHardcoded">
<de.hdodenhof.circleimageview.CircleImageView android:id="@+id/itemThumbnailView"
android:contentDescription="@string/list_thumbnail_view_description"
android:layout_width="@dimen/video_item_search_thumbnail_image_width"
android:layout_height="@dimen/video_item_search_thumbnail_image_height"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:src="@drawable/buddy_channel_item"/>
</RelativeLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/itemThumbnailView"
android:layout_width="@dimen/video_item_search_thumbnail_image_width"
android:layout_height="@dimen/video_item_search_thumbnail_image_height"
android:layout_toRightOf="@id/itemThumbnailViewContainer"
tools:ignore="RtlHardcoded">
<TextView android:id="@+id/itemChannelTitleView"
android:layout_weight="1"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/channel_item_detail_title_text_size"/>
<TextView android:id="@+id/itemChannelDescriptionView"
android:layout_weight="2"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="@dimen/video_item_search_uploader_text_size"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView android:id="@+id/itemSubscriberCountView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="@dimen/video_item_search_upload_date_text_size"
android:text="1000 subs"/>
<TextView android:id="@+id/itemVideoCountView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="@dimen/video_item_search_upload_date_text_size"
android:text="1000 vids"/>
</LinearLayout>
</LinearLayout>
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:contentDescription="@string/list_thumbnail_view_description"
android:src="@drawable/buddy_channel_item"/>
</RelativeLayout>
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/video_item_search_thumbnail_image_height"
android:layout_toRightOf="@id/itemThumbnailViewContainer"
android:orientation="vertical"
tools:ignore="RtlHardcoded">
<TextView
android:id="@+id/itemChannelTitleView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/channel_item_detail_title_text_size"
tools:text="Channel Title, Lorem ipsum"/>
<TextView
android:id="@+id/itemChannelDescriptionView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/itemSubscriberCountView"
android:layout_below="@+id/itemChannelTitleView"
android:ellipsize="end"
android:maxLines="2"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="@dimen/video_item_search_uploader_text_size"
tools:text="Channel description, Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blanditLorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blanditLorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blandit"/>
<TextView
android:id="@+id/itemSubscriberCountView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:maxLines="1"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="@dimen/video_item_search_upload_date_text_size"
tools:text="10M subscribers • "/>
<TextView
android:id="@+id/itemVideoCountView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_toRightOf="@+id/itemSubscriberCountView"
android:maxLines="1"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="@dimen/video_item_search_upload_date_text_size"
tools:text="1000 videos"/>
</RelativeLayout>
</RelativeLayout>
<Button
android:id="@+id/item_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"/>
android:background="?attr/selectableItemBackground"
tools:visibility="gone"/>
</FrameLayout>

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="org.schabi.newpipe.ChannelActivity"
tools:showIn="@layout/activity_channel">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin"
android:text="@string/large_text" />
</android.support.v4.widget.NestedScrollView>

View File

@@ -1,180 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<com.devbrackets.android.exomedia.ui.widget.FitsSystemWindowRelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/exomedia_controls_text_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:background="@drawable/exomedia_default_controls_text_background"
android:orientation="vertical"
android:paddingBottom="16dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp"
android:visibility="gone"
tools:visibility="visible">
<TextView
android:id="@+id/exomedia_controls_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:textSize="16sp"
android:textStyle="bold"
tools:text="The Video Title"/>
<TextView
android:id="@+id/exomedia_controls_sub_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:textSize="14sp"
tools:text="The Video Album"/>
<TextView
android:id="@+id/exomedia_controls_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:textSize="14sp"
tools:text="The Video Artist"/>
</LinearLayout>
<LinearLayout
android:id="@+id/exomedia_controls_interactive_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:background="@drawable/exomedia_default_controls_interactive_background"
android:orientation="vertical"
android:paddingBottom="16dp"
android:paddingTop="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:orientation="horizontal">
<TextView
android:id="@+id/exomedia_controls_current_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="2dp"
android:paddingRight="2dp"
android:textColor="@android:color/white"
tools:ignore="RtlHardcoded"
tools:text="1:06:29"/>
<SeekBar
android:id="@+id/exomedia_controls_video_seek"
style="?android:seekBarStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
tools:ignore="RtlHardcoded"/>
<TextView
android:id="@+id/exomedia_controls_end_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="2dp"
android:paddingRight="2dp"
android:textColor="@android:color/white"
tools:ignore="RtlHardcoded"
tools:text="1:23:49"/>
<LinearLayout
android:id="@+id/exomedia_controls_extra_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:ignore="RtlHardcoded,UselessLeaf"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:gravity="center"
android:orientation="horizontal">
<ImageButton
android:id="@+id/exomedia_controls_previous_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="24dp"
android:background="@android:color/transparent"
android:minHeight="24dp"
android:minWidth="24dp"
app:srcCompat="@drawable/exomedia_ic_skip_previous_white"
tools:ignore="ContentDescription,RtlHardcoded"
tools:visibility="visible"/>
<ImageButton
android:id="@+id/exomedia_controls_frewind_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="24dp"
android:background="@android:color/transparent"
android:minHeight="24dp"
android:minWidth="24dp"
android:src="@drawable/exomedia_ic_rewind_white"
tools:ignore="ContentDescription,RtlHardcoded"
tools:visibility="visible"/>
<ImageButton
android:id="@+id/exomedia_controls_play_pause_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:minHeight="24dp"
android:minWidth="24dp"
app:srcCompat="@drawable/exomedia_ic_play_arrow_white"
tools:ignore="ContentDescription"/>
<ImageButton
android:id="@+id/exomedia_controls_fforward_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="24dp"
android:background="@android:color/transparent"
android:minHeight="24dp"
android:minWidth="24dp"
android:src="@drawable/exomedia_ic_fast_forward_white"
tools:ignore="ContentDescription,RtlHardcoded"
tools:visibility="visible"/>
<ImageButton
android:id="@+id/exomedia_controls_next_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="24dp"
android:background="@android:color/transparent"
android:minHeight="24dp"
android:minWidth="24dp"
app:srcCompat="@drawable/exomedia_ic_skip_next_white"
tools:ignore="ContentDescription,RtlHardcoded"
tools:visibility="visible"/>
</LinearLayout>
</LinearLayout>
<ProgressBar
android:id="@+id/exomedia_controls_video_loading"
style="?android:progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone"/>
</com.devbrackets.android.exomedia.ui.widget.FitsSystemWindowRelativeLayout>

View File

@@ -1,284 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".detail.VideoItemDetailFragment"
android:textIsSelectable="true"
style="?android:attr/textAppearanceLarge"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/video_item_detail">
<com.nirhart.parallaxscroll.views.ParallaxScrollView
android:id="@+id/detail_main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible"
app:parallax_factor="1.9"
tools:ignore="UselessParent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:id="@+id/detail_stream_thumbnail_window_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground">
<ImageView android:id="@+id/detail_thumbnail_view"
android:contentDescription="@string/detail_thumbnail_view_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:background="@android:color/black"
android:src="@drawable/dummy_thumbnail_dark"/>
<ProgressBar android:id="@+id/detail_progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:indeterminate="true"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/play_video_button"
android:visibility="invisible"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
app:backgroundTint="@color/light_youtube_primary_color"
android:src="@drawable/ic_play_arrow_black"
android:layout_margin="@dimen/video_item_detail_play_fab_margin"/>
<Button
android:id="@+id/detail_stream_thumbnail_window_background_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"/>
</RelativeLayout>
<RelativeLayout android:id="@+id/detail_text_content_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/detail_stream_thumbnail_window_layout"
android:background="?android:windowBackground"
android:visibility="visible">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/detailTopView">
<TextView android:id="@+id/detail_video_title_view"
android:layout_width="0dp"
android:layout_weight=".7"
android:layout_height="wrap_content"
android:textSize="@dimen/video_item_detail_title_text_size"
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_marginTop="12dp"
android:layout_marginLeft="12dp"
android:layout_marginStart="12dp"
android:text="Title"/>
<ImageView
android:layout_width="15dp"
android:layout_height="30dp"
android:id="@+id/toggle_description_view"
android:src="@drawable/arrow_down"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:layout_marginRight="10dp"
android:layout_marginEnd="10dp"
android:layout_marginTop="8dp"/>
</LinearLayout>
<TextView android:id="@+id/detail_view_count_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/video_item_detail_views_text_size"
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_marginLeft="12dp"
android:layout_marginStart="12dp"
android:text="10,069,948 views"
android:layout_below="@id/detailTopView"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginTop="5dp" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/detail_view_count_view"
android:id="@+id/detailExtraView"
android:layout_marginLeft="12dp"
android:layout_marginStart="12dp"
android:layout_marginRight="12dp"
android:layout_marginEnd="12dp"
android:visibility="gone">
<TextView android:id="@+id/detail_upload_date_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/video_item_detail_upload_date_text_size"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Upload date"
android:layout_marginTop="3dp" />
<TextView android:id="@+id/detail_description_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/video_item_detail_description_text_size"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_below="@id/detail_upload_date_view"
android:text="Description............."
android:layout_marginTop="3dp" />
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/stream_info_layout"
android:layout_marginLeft="12dp"
android:layout_marginStart="12dp"
android:layout_below="@+id/detailExtraView"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginTop="5dp">
<ImageView android:id="@+id/detail_thumbs_up_img_view"
android:contentDescription="@string/detail_likes_img_view_description"
android:layout_width="@dimen/video_item_detail_like_image_width"
android:layout_height="@dimen/video_item_detail_like_image_height"
android:src="?attr/thumbs_up" />
<TextView android:id="@+id/detail_thumbs_up_count_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/video_item_detail_likes_text_size"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="200" />
<ImageView android:id="@+id/detail_thumbs_down_img_view"
android:contentDescription="@string/detail_dislikes_img_view_description"
android:layout_width="@dimen/video_item_detail_like_image_width"
android:layout_height="@dimen/video_item_detail_like_image_height"
android:src="?attr/thumbs_down"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"/>
<TextView android:id="@+id/detail_thumbs_down_count_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/video_item_detail_likes_text_size"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="100" />
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/detail_uploader_frame"
android:layout_below="@id/stream_info_layout">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/detail_uploader_layout"
android:layout_marginTop="12dp">
<View
android:background="#000"
android:layout_width="match_parent"
android:layout_height="1px" />
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/detail_uploader_thumbnail_view"
android:contentDescription="@string/detail_uploader_thumbnail_view_description"
android:layout_width="@dimen/video_item_detail_uploader_image_size"
android:layout_height="@dimen/video_item_detail_uploader_image_size"
android:src="@drawable/buddy"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"/>
<TextView android:id="@+id/detail_uploader_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:textSize="@dimen/video_item_detail_uploader_text_size"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Uploader"
android:layout_centerVertical="true"
android:layout_toRightOf="@+id/detail_uploader_thumbnail_view"
android:layout_toEndOf="@+id/detail_uploader_thumbnail_view"
android:layout_marginLeft="15dp"
android:layout_marginStart="28dp" />
<View
android:background="#000"
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_toRightOf="@+id/detail_uploader_thumbnail_view"
android:layout_toEndOf="@+id/detail_uploader_thumbnail_view"
android:layout_below="@id/detail_uploader_thumbnail_view"/>
</RelativeLayout>
<Button
android:layout_marginTop="13dp"
android:id="@+id/channel_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"/>
</FrameLayout>
<RelativeLayout android:id="@+id/detail_next_stream_root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal|bottom"
android:layout_below="@+id/detail_uploader_frame"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp">
<TextView
android:id="@+id/detail_next_stream_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:textSize="@dimen/video_item_detail_next_text_size"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/next_video_title"
android:textAllCaps="true" />
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/detail_next_stream_content"/>
<TextView android:id="@+id/detail_similar_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:textSize="@dimen/video_item_detail_next_text_size"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/similar_videos_btn_text"
android:layout_below="@id/detail_next_stream_content"
android:textAllCaps="true" />
<LinearLayout
android:id="@+id/similar_streams_view"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/detail_similar_title">
</LinearLayout>
</RelativeLayout>
</RelativeLayout>
</RelativeLayout>
</com.nirhart.parallaxscroll.views.ParallaxScrollView>
</FrameLayout>

View File

@@ -65,7 +65,7 @@
android:background="#00ffffff"
android:clickable="true"
android:scaleType="fitXY"
android:src="@drawable/ic_pause_white_24dp" />
android:src="@drawable/ic_pause_white" />
<ImageButton
android:id="@+id/notificationStop"
@@ -75,7 +75,7 @@
android:background="#00ffffff"
android:clickable="true"
android:scaleType="fitXY"
android:src="@drawable/ic_close_white_24dp" />
android:src="@drawable/ic_close_white" />
</LinearLayout>

View File

@@ -58,7 +58,7 @@
android:background="#00ffffff"
android:clickable="true"
android:scaleType="fitXY"
android:src="@drawable/ic_close_white_24dp" />
android:src="@drawable/ic_close_white" />
<RelativeLayout
@@ -77,7 +77,7 @@
android:background="#00ffffff"
android:clickable="true"
android:scaleType="fitXY"
android:src="@drawable/ic_pause_white_24dp"
android:src="@drawable/ic_pause_white"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" />

View File

@@ -4,18 +4,29 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
android:gravity="center">
<RelativeLayout
android:id="@+id/blackBackground"
<com.google.android.exoplayer2.ui.AspectRatioFrameLayout
android:id="@+id/aspectRatioLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"/>
android:layout_gravity="center">
<com.devbrackets.android.exomedia.ui.widget.EMVideoView
android:id="@+id/popupVideoView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"/>
<View
android:id="@+id/surfaceForeground"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"/>
</com.google.android.exoplayer2.ui.AspectRatioFrameLayout>
<ImageView
@@ -24,72 +35,142 @@
android:layout_height="match_parent"
android:background="@color/black"
android:visibility="gone"
tools:visibility="visible"
tools:background="@android:color/white"
tools:ignore="ContentDescription"/>
tools:ignore="ContentDescription"
tools:visibility="visible"/>
<RelativeLayout
android:id="@+id/playbackControlRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#32000000"
android:visibility="gone"
tools:visibility="visible">
<RelativeLayout
android:id="@+id/topControls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:background="@drawable/player_top_controls_bg"
android:paddingBottom="20dp"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:paddingTop="4dp">
<TextView
android:id="@+id/qualityTextView"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_alignParentLeft="true"
android:padding="5dp"
android:gravity="center"
android:text="720p"
android:textColor="@android:color/white"
android:textStyle="bold"
tools:ignore="HardcodedText,RtlHardcoded,RtlSymmetry"/>
<ImageButton
android:id="@+id/fullScreenButton"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_alignParentRight="true"
android:background="#00ffffff"
android:clickable="true"
android:scaleType="fitCenter"
android:src="@drawable/ic_fullscreen_white"
tools:ignore="ContentDescription,RtlHardcoded"/>
</RelativeLayout>
<!--Shadow Bottom Control-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="@drawable/player_controls_bg"
android:orientation="horizontal"
android:paddingTop="50dp"/>
<LinearLayout
android:id="@+id/bottomControls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingBottom="2dp"
android:paddingLeft="8dp"
android:paddingRight="8dp">
<TextView
android:id="@+id/playbackCurrentTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:text="-:--:--"
android:textColor="@android:color/white"
tools:ignore="HardcodedText,RtlHardcoded,RtlSymmetry"
tools:text="1:06:29"/>
<android.support.v7.widget.AppCompatSeekBar
android:id="@+id/playbackSeekBar"
style="@style/Widget.AppCompat.SeekBar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
tools:progress="25"
tools:secondaryProgress="50"/>
<TextView
android:id="@+id/playbackEndTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:text="-:--:--"
android:textColor="@android:color/white"
tools:ignore="HardcodedText,RtlHardcoded,RtlSymmetry"
tools:text="1:23:49"/>
</LinearLayout>
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:weightSum="2">
android:orientation="horizontal"
android:weightSum="5">
<!--tools:visibility="gone">-->
<ImageView
android:id="@+id/controlAnimationView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:src="@drawable/ic_action_av_fast_rewind"
android:visibility="gone"
tools:visibility="visible"
tools:ignore="ContentDescription"/>
tools:ignore="ContentDescription"
tools:visibility="visible"/>
</LinearLayout>
<LinearLayout
android:id="@+id/playbackControlRoot"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_gravity="bottom"
<TextView
android:id="@+id/currentDisplaySeek"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="#64000000"
android:paddingBottom="5dp"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:gravity="bottom|center"
android:orientation="horizontal"
android:background="@drawable/popup_controls_bg"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:paddingTop="5dp"
android:textColor="@android:color/white"
android:textSize="18sp"
android:textStyle="bold"
android:visibility="gone"
tools:visibility="visible">
<TextView
android:id="@+id/playbackCurrentTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="2dp"
android:paddingRight="2dp"
android:textColor="@android:color/white"
tools:ignore="HardcodedText"
android:text="-:--:--"/>
<!--style="?android:attr/progressBarStyleHorizontal"-->
<SeekBar
android:id="@+id/playbackSeekBar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:max="100"
android:layout_weight="1"
android:progress="0"/>
<TextView
android:id="@+id/playbackEndTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="2dp"
android:paddingRight="2dp"
android:textColor="@android:color/white"
tools:ignore="HardcodedText"
android:text="-:--:--"/>
</LinearLayout>
tools:ignore="RtlHardcoded"
tools:text="1:06:29"
tools:visibility="visible"/>
<RelativeLayout
android:id="@+id/loadingPanel"
@@ -102,6 +183,7 @@
tools:visibility="gone">
<ProgressBar
android:id="@+id/progressBarLoadingPanel"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

View File

@@ -33,7 +33,7 @@
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:maxLines="1"
tools:text="title"/>
tools:text="a long, long, long, long, long title"/>
<TextView
android:id="@+id/notificationArtist"
@@ -42,28 +42,38 @@
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:maxLines="1"
tools:text="artist"/>
tools:text="a long, long artist"/>
</LinearLayout>
<ImageButton
android:id="@+id/notificationPlayPause"
android:id="@+id/notificationRepeat"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_margin="5dp"
android:background="#00ffffff"
android:layout_height="match_parent"
android:background="#00000000"
android:clickable="true"
android:scaleType="fitXY"
android:src="@drawable/ic_pause_white_24dp"
android:padding="5dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_repeat_white"
tools:ignore="ContentDescription"/>
<ImageButton
android:id="@+id/notificationPlayPause"
android:layout_width="45dp"
android:layout_height="match_parent"
android:background="#00000000"
android:clickable="true"
android:src="@drawable/ic_pause_white"
tools:ignore="ContentDescription"/>
<ImageButton
android:id="@+id/notificationStop"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_margin="5dp"
android:background="#00ffffff"
android:layout_marginLeft="5dp"
android:background="#00000000"
android:clickable="true"
android:scaleType="fitXY"
android:src="@drawable/ic_close_white_24dp"
tools:ignore="ContentDescription"/>
android:padding="5dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_close_white"
tools:ignore="ContentDescription,RtlHardcoded"/>
</LinearLayout>

View File

@@ -1,108 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
android:id="@+id/item_main_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_main_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:orientation="vertical">
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:id="@+id/itemThumbnailViewContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="12dp">
android:layout_marginRight="@dimen/video_item_search_image_right_margin"
tools:ignore="RtlHardcoded">
<RelativeLayout android:id="@+id/itemThumbnailViewContainer"
android:layout_marginRight="@dimen/video_item_search_image_right_margin"
<ImageView
android:id="@+id/itemThumbnailView"
android:layout_width="@dimen/video_item_search_thumbnail_image_width"
android:layout_height="@dimen/video_item_search_thumbnail_image_height"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:contentDescription="@string/list_thumbnail_view_description"
android:scaleType="centerCrop"
android:src="@drawable/dummy_thumbnail"/>
<TextView
android:id="@+id/itemDurationView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:ignore="RtlHardcoded">
<ImageView android:id="@+id/itemThumbnailView"
android:contentDescription="@string/list_thumbnail_view_description"
android:layout_width="@dimen/video_item_search_thumbnail_image_width"
android:layout_height="@dimen/video_item_search_thumbnail_image_height"
android:scaleType="centerCrop"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:src="@drawable/dummy_thumbnail"/>
<TextView android:id="@+id/itemDurationView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/itemThumbnailView"
android:layout_alignRight="@id/itemThumbnailView"
android:layout_alignEnd="@id/itemThumbnailView"
android:layout_marginRight="@dimen/video_item_search_duration_margin"
android:layout_marginEnd="@dimen/video_item_search_duration_margin"
android:layout_marginBottom="@dimen/video_item_search_duration_margin"
android:paddingTop="@dimen/video_item_search_duration_vertical_padding"
android:paddingBottom="@dimen/video_item_search_duration_vertical_padding"
android:paddingRight="@dimen/video_item_search_duration_horizontal_padding"
android:paddingLeft="@dimen/video_item_search_duration_horizontal_padding"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="@dimen/video_item_search_duration_text_size"
android:background="@color/duration_background_color"
android:textColor="@color/duration_text_color"/>
</RelativeLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="@dimen/video_item_search_thumbnail_image_height"
android:layout_toRightOf="@id/itemThumbnailViewContainer"
tools:ignore="RtlHardcoded">
<TextView android:id="@+id/itemVideoTitleView"
android:layout_weight="1"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_search_title_text_size"/>
<TextView android:id="@+id/itemUploaderView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="@dimen/video_item_search_uploader_text_size"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView android:id="@+id/itemUploadDateView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="@dimen/video_item_search_upload_date_text_size"/>
<TextView android:id="@+id/itemViewCountView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="@dimen/video_item_search_upload_date_text_size"/>
</LinearLayout>
</LinearLayout>
android:layout_alignBottom="@id/itemThumbnailView"
android:layout_alignRight="@id/itemThumbnailView"
android:layout_marginBottom="@dimen/video_item_search_duration_margin"
android:layout_marginRight="@dimen/video_item_search_duration_margin"
android:background="@color/duration_background_color"
android:paddingBottom="@dimen/video_item_search_duration_vertical_padding"
android:paddingLeft="@dimen/video_item_search_duration_horizontal_padding"
android:paddingRight="@dimen/video_item_search_duration_horizontal_padding"
android:paddingTop="@dimen/video_item_search_duration_vertical_padding"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@color/duration_text_color"
android:textSize="@dimen/video_item_search_duration_text_size"
tools:text="1:09:10"/>
</RelativeLayout>
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/video_item_search_thumbnail_image_height"
android:layout_toRightOf="@id/itemThumbnailViewContainer"
android:orientation="vertical"
tools:ignore="RtlHardcoded">
<TextView
android:id="@+id/itemVideoTitleView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/itemUploaderView"
android:layout_alignParentTop="true"
android:ellipsize="end"
android:maxLines="2"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_search_title_text_size"
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blanditLorem ipsumLorem ipsumLorem ipsumLorem ipsumLorem ipsumLorem ipsumLorem ipsum"/>
<TextView
android:id="@+id/itemUploaderView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/itemUploadDateView"
android:maxLines="1"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="@dimen/video_item_search_uploader_text_size"
tools:text="Uploader"/>
<TextView
android:id="@+id/itemUploadDateView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:maxLines="1"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="@dimen/video_item_search_upload_date_text_size"
tools:text="2 years ago • "/>
<TextView
android:id="@+id/itemViewCountView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_toRightOf="@+id/itemUploadDateView"
android:maxLines="1"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="@dimen/video_item_search_upload_date_text_size"
tools:text="10M views"/>
</RelativeLayout>
</RelativeLayout>
<Button
android:id="@+id/item_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"/>
android:background="?attr/selectableItemBackground"
tools:visibility="gone"/>
</FrameLayout>

View File

@@ -5,5 +5,5 @@
<item android:id="@+id/menu_item_screen_rotation"
android:title="@string/screen_rotation"
app:showAsAction="always"
android:icon="@drawable/ic_screen_rotation_white_24dp"/>
android:icon="@drawable/ic_screen_rotation_white"/>
</menu>

View File

@@ -1,27 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="abc_action_bar_home_description">Navigate home</string>
<string name="abc_action_bar_home_description_format">%1$s, %2$s</string>
<string name="abc_action_bar_home_subtitle_description_format">%1$s, %2$s, %3$s</string>
<string name="abc_action_bar_up_description">Navigate up</string>
<string name="abc_action_menu_overflow_description">More options</string>
<string name="abc_action_mode_done">Done</string>
<string name="abc_activity_chooser_view_see_all">See all</string>
<string name="abc_activitychooserview_choose_application">Choose an app</string>
<string name="abc_capital_off">OFF</string>
<string name="abc_capital_on">ON</string>
<string name="abc_search_hint">Search…</string>
<string name="abc_searchview_description_clear">Clear query</string>
<string name="abc_searchview_description_query">Search query</string>
<string name="abc_searchview_description_search">Search</string>
<string name="abc_searchview_description_submit">Submit query</string>
<string name="abc_searchview_description_voice">Voice search</string>
<string name="abc_shareactionprovider_share_with">Share with</string>
<string name="abc_shareactionprovider_share_with_application">Share with %s</string>
<string name="abc_toolbar_collapse_description">Collapse</string>
<string name="status_bar_notification_info_overflow">999+</string>
<string name="autoplay_through_intent_summary">"بدء تشغيل الفيديو تلقائيًا عندما يتم فتحه من تطبيق أخر."</string>
<string name="autoplay_through_intent_title">التشغيل التلقائي</string>
<string name="background_player_name">مشغل NewPipe في الخلفية</string>
<string name="background_player_playing_toast">جاري التشغيل في الخلفية</string>
<string name="cancel">إلغاء</string>
@@ -87,31 +65,4 @@
<string name="general_error">خطأ</string>
<string name="parsing_error">لا يمكن تحليل الموقع.</string>
<string name="youtube_signature_decryption_error">لا يمكن فك تشفير توقيع رابط الفيديو.</string>
<string name="app_name">NewPipe</string>
<string name="appbar_scrolling_view_behavior">android.support.design.widget.AppBarLayout$ScrollingViewBehavior</string>
<string name="autoplay_through_intent_key">autoplay_through_intent</string>
<string name="background_player_time_text">%1$s - NewPipe</string>
<string name="c3s_url">https://www.c3s.cc/</string>
<string name="character_counter_pattern">%1$d / %2$d</string>
<string name="default_audio_format_key">default_audio_format</string>
<string name="default_audio_format_value">m4a</string>
<string name="default_language_value">en</string>
<string name="default_resolution_key">default_resolution_preference</string>
<string name="default_resolution_value">360p</string>
<string name="default_theme_value">0</string>
<string name="download_path_audio_key">download_path_audio</string>
<string name="download_path_key">download_path</string>
<string name="fdroid_kore_url">https://f-droid.org/repository/browse/?fdfilter=Kore&amp;fdid=org.xbmc.kore</string>
<string name="fdroid_vlc_url">https://f-droid.org/repository/browse/?fdfilter=vlc&amp;fdid=org.videolan.vlc</string>
<string name="search_language_key">search_language</string>
<string name="settings_category_appearance">settings_category_appearance</string>
<string name="settings_category_other">settings_category_other</string>
<string name="settings_category_video_audio">settings_category_video_audio</string>
<string name="show_next_video_key">show_next_video</string>
<string name="show_play_with_kodi_key">show_play_with_kodi</string>
<string name="theme_key">الثيمات</string>
<string name="title_videoitem_detail">NewPipe</string>
<string name="use_external_audio_player_key">use_external_audio_player</string>
<string name="use_external_video_player_key">use_external_video_player</string>
<string name="use_tor_key">use_tor</string>
</resources>

View File

@@ -22,8 +22,6 @@
<string name="download_path_audio_dialog_title">Zadejte umístění pro stažené audio soubory.</string>
<string name="download_path_audio_title">Umístění pro stažené audio</string>
<string name="autoplay_through_intent_title">Automatické přehrávání skrze Intent</string>
<string name="autoplay_through_intent_summary">Automaticky přehrávat video, jestliže je volané z jiné aplikace.</string>
<string name="default_resolution_title">Výchozí rozlišení</string>
<string name="play_with_kodi_title">Přehrát pomocí Kodi</string>
<string name="kore_not_found">Aplikace Kore nenalezena. Nainstalovat Kore?</string>
@@ -159,146 +157,30 @@
<string name="title_activity_channel">Aktivita kanálu</string>
<string name="user_report">Hlášení uživatele</string>
<string name="large_text">"
Material is the metaphor.
A material metaphor is the unifying theory of a rationalized space and a system of motion.
The material is grounded in tactile reality, inspired by the study of paper and ink, yet
technologically advanced and open to imagination and magic.
Surfaces and edges of the material provide visual cues that are grounded in reality. The
use of familiar tactile attributes helps users quickly understand affordances. Yet the
flexibility of the material creates new affordances that supercede those in the physical
world, without breaking the rules of physics.
The fundamentals of light, surface, and movement are key to conveying how objects move,
interact, and exist in space and in relation to each other. Realistic lighting shows
seams, divides space, and indicates moving parts.
Bold, graphic, intentional.
The foundational elements of print based design typography, grids, space, scale, color,
and use of imagery guide visual treatments. These elements do far more than please the
eye. They create hierarchy, meaning, and focus. Deliberate color choices, edge to edge
imagery, large scale typography, and intentional white space create a bold and graphic
interface that immerse the user in the experience.
An emphasis on user actions makes core functionality immediately apparent and provides
waypoints for the user.
Motion provides meaning.
Motion respects and reinforces the user as the prime mover. Primary user actions are
inflection points that initiate motion, transforming the whole design.
All action takes place in a single environment. Objects are presented to the user without
breaking the continuity of experience even as they transform and reorganize.
Motion is meaningful and appropriate, serving to focus attention and maintain continuity.
Feedback is subtle yet clear. Transitions are efficient yet coherent.
3D world.
The material environment is a 3D space, which means all objects have x, y, and z
dimensions. The z-axis is perpendicularly aligned to the plane of the display, with the
positive z-axis extending towards the viewer. Every sheet of material occupies a single
position along the z-axis and has a standard 1dp thickness.
On the web, the z-axis is used for layering and not for perspective. The 3D world is
emulated by manipulating the y-axis.
Light and shadow.
Within the material environment, virtual lights illuminate the scene. Key lights create
directional shadows, while ambient light creates soft shadows from all angles.
Shadows in the material environment are cast by these two light sources. In Android
development, shadows occur when light sources are blocked by sheets of material at
various positions along the z-axis. On the web, shadows are depicted by manipulating the
y-axis only. The following example shows the card with a height of 6dp.
Resting elevation.
All material objects, regardless of size, have a resting elevation, or default elevation
that does not change. If an object changes elevation, it should return to its resting
elevation as soon as possible.
Component elevations.
The resting elevation for a component type is consistent across apps (e.g., FAB elevation
does not vary from 6dp in one app to 16dp in another app).
Components may have different resting elevations across platforms, depending on the depth
of the environment (e.g., TV has a greater depth than mobile or desktop).
Responsive elevation and dynamic elevation offsets.
Some component types have responsive elevation, meaning they change elevation in response
to user input (e.g., normal, focused, and pressed) or system events. These elevation
changes are consistently implemented using dynamic elevation offsets.
Dynamic elevation offsets are the goal elevation that a component moves towards, relative
to the components resting state. They ensure that elevation changes are consistent
across actions and component types. For example, all components that lift on press have
the same elevation change relative to their resting elevation.
Once the input event is completed or cancelled, the component will return to its resting
elevation.
Avoiding elevation interference.
Components with responsive elevations may encounter other components as they move between
their resting elevations and dynamic elevation offsets. Because material cannot pass
through other material, components avoid interfering with one another any number of ways,
whether on a per component basis or using the entire app layout.
On a component level, components can move or be removed before they cause interference.
For example, a floating action button (FAB) can disappear or move off screen before a
user picks up a card, or it can move if a snackbar appears.
On the layout level, design your app layout to minimize opportunities for interference.
For example, position the FAB to one side of stream of a cards so the FAB wont interfere
when a user tries to pick up one of cards.
"</string>
<string name="logging_verbose">Podrobné</string>
<string name="error_querying_decoders">Nelze zjistit dekodéry zařízení</string>
<string name="error_instantiating_decoder">Nelze doložit dekodér <xliff:g id="decoder_name">
%1$s</xliff:g></string>
<string name="info_labels">Co:\\nŽádost:\\nJazyk obsahu:\\nSlužba:\\nČas GMT:\\nBalíček:\\nVerze:\\nVerze OS:\\nGlobální rozsah IP:</string>
<string name="all">Vše</string>
<string name="channel">Kanál</string>
<string name="yes">Ano</string>
<string name="later">Později</string>
<string name="videos">videa</string>
<string name="subscriber">odběratel</string>
<string name="subscribe">Odebírat</string>
<string name="views">zobrazení</string>
<string name="short_thousand">k</string>
<string name="restart_title">Restart</string>
<string name="msg_restart">Pro aplikaci tématu je potřeba restartovat aplikaci.
Chcete restartovat ihned?</string>
<string name="open_in_popup_mode">Otevřít ve vyskakovacím okně</string>
<string name="short_million">M</string>
<string name="msg_popup_permission">Toto oprávnění je vyžadováno pro
otevření ve vyskakovacím okně</string>
</resources>

View File

@@ -16,16 +16,12 @@
<string name="choose_browser">Browser</string>
<string name="screen_rotation">Rotation</string>
<string name="settings_activity_title">Einstellungen</string>
<string name="useExternalPlayerTitle">Externen Player benutzen</string>
<string name="download_path_title">Downloadverzeichnis für Videos</string>
<string name="download_path_summary">Verzeichnis in dem heruntergeladene Videos gespeichert werden.</string>
<string name="download_path_dialog_title">Downloadverzeichnis für Videos eingeben</string>
<string name="autoplay_through_intent_title">Automatisches Abspielen durch Intent</string>
<string name="autoplay_through_intent_summary">Startet ein Video automatisch wenn es von einer anderen App aufgerufen wurde.</string>
<string name="default_resolution_title">Standardauflösung</string>
<string name="play_with_kodi_title">Mit Kodi abspielen</string>
<string name="kore_not_found">Kore App wurde nicht gefunden. Möchten sie Kore jetzt installieren?</string>
<string name="installeKore">Kore installieren</string>
<string name="show_play_with_kodi_title">Zeige \"Mit Kodi abspielen\" Option</string>
<string name="show_play_with_kodi_summary">Zeigt eine Option an, über die man Videos mit dem Kodi Mediacenter abspielen kann.</string>
<string name="play_audio">Audio</string>
@@ -139,7 +135,7 @@
<string name="switch_mode">Zwischen Liste und Gitter umschalten</string>
<string name="videos">Videos</string>
<string name="subscriber">Abonenten</string>
<string name="subscriber">Abonnenten</string>
<string name="views">Aufrufe</string>
<string name="short_thousand">Tsd.</string>
<string name="short_million">Mio.</string>
@@ -169,143 +165,6 @@
<string name="msg_url_malform">Beschädigte URL oder Internet nicht erreichbar</string>
<string name="msg_fetch_filename">Vorgeschlagener Dateiname</string>
<string name="title_activity_channel">Kanalaktivität</string>
<string name="large_text">"
Material is the metaphor.
A material metaphor is the unifying theory of a rationalized space and a system of motion.
The material is grounded in tactile reality, inspired by the study of paper and ink, yet
technologically advanced and open to imagination and magic.
Surfaces and edges of the material provide visual cues that are grounded in reality. The
use of familiar tactile attributes helps users quickly understand affordances. Yet the
flexibility of the material creates new affordances that supercede those in the physical
world, without breaking the rules of physics.
The fundamentals of light, surface, and movement are key to conveying how objects move,
interact, and exist in space and in relation to each other. Realistic lighting shows
seams, divides space, and indicates moving parts.
Bold, graphic, intentional.
The foundational elements of print based design typography, grids, space, scale, color,
and use of imagery guide visual treatments. These elements do far more than please the
eye. They create hierarchy, meaning, and focus. Deliberate color choices, edge to edge
imagery, large scale typography, and intentional white space create a bold and graphic
interface that immerse the user in the experience.
An emphasis on user actions makes core functionality immediately apparent and provides
waypoints for the user.
Motion provides meaning.
Motion respects and reinforces the user as the prime mover. Primary user actions are
inflection points that initiate motion, transforming the whole design.
All action takes place in a single environment. Objects are presented to the user without
breaking the continuity of experience even as they transform and reorganize.
Motion is meaningful and appropriate, serving to focus attention and maintain continuity.
Feedback is subtle yet clear. Transitions are efficient yet coherent.
3D world.
The material environment is a 3D space, which means all objects have x, y, and z
dimensions. The z-axis is perpendicularly aligned to the plane of the display, with the
positive z-axis extending towards the viewer. Every sheet of material occupies a single
position along the z-axis and has a standard 1dp thickness.
On the web, the z-axis is used for layering and not for perspective. The 3D world is
emulated by manipulating the y-axis.
Light and shadow.
Within the material environment, virtual lights illuminate the scene. Key lights create
directional shadows, while ambient light creates soft shadows from all angles.
Shadows in the material environment are cast by these two light sources. In Android
development, shadows occur when light sources are blocked by sheets of material at
various positions along the z-axis. On the web, shadows are depicted by manipulating the
y-axis only. The following example shows the card with a height of 6dp.
Resting elevation.
All material objects, regardless of size, have a resting elevation, or default elevation
that does not change. If an object changes elevation, it should return to its resting
elevation as soon as possible.
Component elevations.
The resting elevation for a component type is consistent across apps (e.g., FAB elevation
does not vary from 6dp in one app to 16dp in another app).
Components may have different resting elevations across platforms, depending on the depth
of the environment (e.g., TV has a greater depth than mobile or desktop).
Responsive elevation and dynamic elevation offsets.
Some component types have responsive elevation, meaning they change elevation in response
to user input (e.g., normal, focused, and pressed) or system events. These elevation
changes are consistently implemented using dynamic elevation offsets.
Dynamic elevation offsets are the goal elevation that a component moves towards, relative
to the components resting state. They ensure that elevation changes are consistent
across actions and component types. For example, all components that lift on press have
the same elevation change relative to their resting elevation.
Once the input event is completed or cancelled, the component will return to its resting
elevation.
Avoiding elevation interference.
Components with responsive elevations may encounter other components as they move between
their resting elevations and dynamic elevation offsets. Because material cannot pass
through other material, components avoid interfering with one another any number of ways,
whether on a per component basis or using the entire app layout.
On a component level, components can move or be removed before they cause interference.
For example, a floating action button (FAB) can disappear or move off screen before a
user picks up a card, or it can move if a snackbar appears.
On the layout level, design your app layout to minimize opportunities for interference.
For example, position the FAB to one side of stream of a cards so the FAB wont interfere
when a user tries to pick up one of cards.
"</string>
<string name="reCaptchaActivity">reCAPTCHA</string>
<string name="black_theme_title">Schwarz</string>
@@ -319,4 +178,12 @@
<string name="channel">Kanal</string>
<string name="restart_title">Neu starten</string>
<string name="msg_restart">Um die Änderung des Aussehens zu ändern, müssen Sie die App neu starten.
Möchten Sie jetzt neu starten?</string>
<string name="subscribe">Abonnieren</string>
<string name="disabled">Deaktiviert</string>
<string name="use_old_player_title">Benutze den alten Player</string>
</resources>

View File

@@ -28,8 +28,6 @@
<string name="download_path_audio_summary">Διαδρομή για αποθήκευση αρχείων ήχου</string>
<string name="download_path_audio_dialog_title">Εισάγετε διαδρομή για λήψη αρχείων ήχου.</string>
<string name="autoplay_through_intent_title">Αυτόματη αναπαραγωγή μέσω Intent</string>
<string name="autoplay_through_intent_summary">Αυτόματη αναπαραγωγή video όταν καλείται από άλλη εφαρμογή.</string>
<string name="default_resolution_title">Προεπιλεγμένη ανάλυση</string>
<string name="play_with_kodi_title">Αναπαραγωγή με το Kodi</string>
<string name="kore_not_found">Η εφαρμογή Kore δεν βρέθηκε. Εγκατάσταση;</string>

View File

@@ -48,7 +48,6 @@
<string name="detail_likes_img_view_description">Ŝatoj</string>
<string name="detail_dislikes_img_view_description">Malŝatoj</string>
<string name="use_tor_title">Uzi la programon Tor</string>
<string name="autoplay_through_intent_title">Ludi aŭtomate per Intent</string>
<string name="no_player_found">Neniu elsendlflua ludilo trovita. Ĉu instali la aplikaĵon VLC?</string>
<string name="kore_not_found">La aplikaĵo Kore ne estas trovita. Ĉu instali la aplikaĵon Kore?</string>
<string name="show_next_and_similar_title">Montri la sekvan videon kaj similajn videojn</string>
@@ -67,7 +66,6 @@
<string name="show_play_with_kodi_summary">Montri opcion por ludi videon per la aplikaĵo Kodi.</string>
<string name="download_path_summary">Dosierujo por konservi elŝutitajn videojn.</string>
<string name="download_path_audio_summary">Dosierujo por konservi elŝutitan muzikon</string>
<string name="autoplay_through_intent_summary">Ludi videon aŭtomate kiam ĝi estas vokita de alia aplikaĵo.</string>
<string name="download_path_dialog_title">Elektu lokon por konservi elŝutitajn videojn</string>
<string name="download_path_audio_dialog_title">Elektu lokon por konservi elŝutitan muzikon.</string>

View File

@@ -16,16 +16,12 @@
<string name="choose_browser">Seleccionar navegador</string>
<string name="screen_rotation">rotación</string>
<string name="settings_activity_title">Ajustes</string>
<string name="useExternalPlayerTitle">Usar reproductor externo</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_dialog_title">Introducir directorio de descargas para vídeos</string>
<string name="autoplay_through_intent_title">Intentar reproducción automática</string>
<string name="autoplay_through_intent_summary">Reproducir vídeos automáticamente cuando se llamen desde otra aplicación.</string>
<string name="default_resolution_title">Resolución por defecto</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="installeKore">Instalar Kore</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="play_audio">Audio</string>
@@ -155,147 +151,10 @@
<string name="could_not_load_image">No se pudo cargar la imagen</string>
<string name="app_ui_crash">La interfaz de la app dejó de funcionar</string>
<string name="info_labels">Qué ocurrió:\\nSolicitud:\\nIdioma del contenido:\\nServicio:\\nHora GMT:\\nPaquete:\\nVersión:\\nVersión del S.O:\\nRango global de la IP:</string>
<string name="info_labels">Lo sucedido:\\nSolicitud:\\nIdioma del contenido:\\nServicio:\\nHora GMT:\\nPaquete:\\nVersión:\\nVersión del S.O:\\nRango global de la IP:</string>
<string name="title_activity_channel">Actividad del canal</string>
<string name="action_settings">Ajustes</string>
<string name="large_text">"
Material is the metaphor.
A material metaphor is the unifying theory of a rationalized space and a system of motion.
The material is grounded in tactile reality, inspired by the study of paper and ink, yet
technologically advanced and open to imagination and magic.
Surfaces and edges of the material provide visual cues that are grounded in reality. The
use of familiar tactile attributes helps users quickly understand affordances. Yet the
flexibility of the material creates new affordances that supercede those in the physical
world, without breaking the rules of physics.
The fundamentals of light, surface, and movement are key to conveying how objects move,
interact, and exist in space and in relation to each other. Realistic lighting shows
seams, divides space, and indicates moving parts.
Bold, graphic, intentional.
The foundational elements of print based design typography, grids, space, scale, color,
and use of imagery guide visual treatments. These elements do far more than please the
eye. They create hierarchy, meaning, and focus. Deliberate color choices, edge to edge
imagery, large scale typography, and intentional white space create a bold and graphic
interface that immerse the user in the experience.
An emphasis on user actions makes core functionality immediately apparent and provides
waypoints for the user.
Motion provides meaning.
Motion respects and reinforces the user as the prime mover. Primary user actions are
inflection points that initiate motion, transforming the whole design.
All action takes place in a single environment. Objects are presented to the user without
breaking the continuity of experience even as they transform and reorganize.
Motion is meaningful and appropriate, serving to focus attention and maintain continuity.
Feedback is subtle yet clear. Transitions are efficient yet coherent.
3D world.
The material environment is a 3D space, which means all objects have x, y, and z
dimensions. The z-axis is perpendicularly aligned to the plane of the display, with the
positive z-axis extending towards the viewer. Every sheet of material occupies a single
position along the z-axis and has a standard 1dp thickness.
On the web, the z-axis is used for layering and not for perspective. The 3D world is
emulated by manipulating the y-axis.
Light and shadow.
Within the material environment, virtual lights illuminate the scene. Key lights create
directional shadows, while ambient light creates soft shadows from all angles.
Shadows in the material environment are cast by these two light sources. In Android
development, shadows occur when light sources are blocked by sheets of material at
various positions along the z-axis. On the web, shadows are depicted by manipulating the
y-axis only. The following example shows the card with a height of 6dp.
Resting elevation.
All material objects, regardless of size, have a resting elevation, or default elevation
that does not change. If an object changes elevation, it should return to its resting
elevation as soon as possible.
Component elevations.
The resting elevation for a component type is consistent across apps (e.g., FAB elevation
does not vary from 6dp in one app to 16dp in another app).
Components may have different resting elevations across platforms, depending on the depth
of the environment (e.g., TV has a greater depth than mobile or desktop).
Responsive elevation and dynamic elevation offsets.
Some component types have responsive elevation, meaning they change elevation in response
to user input (e.g., normal, focused, and pressed) or system events. These elevation
changes are consistently implemented using dynamic elevation offsets.
Dynamic elevation offsets are the goal elevation that a component moves towards, relative
to the components resting state. They ensure that elevation changes are consistent
across actions and component types. For example, all components that lift on press have
the same elevation change relative to their resting elevation.
Once the input event is completed or cancelled, the component will return to its resting
elevation.
Avoiding elevation interference.
Components with responsive elevations may encounter other components as they move between
their resting elevations and dynamic elevation offsets. Because material cannot pass
through other material, components avoid interfering with one another any number of ways,
whether on a per component basis or using the entire app layout.
On a component level, components can move or be removed before they cause interference.
For example, a floating action button (FAB) can disappear or move off screen before a
user picks up a card, or it can move if a snackbar appears.
On the layout level, design your app layout to minimize opportunities for interference.
For example, position the FAB to one side of stream of a cards so the FAB wont interfere
when a user tries to pick up one of cards.
"</string>
<string name="black_theme_title">Oscuro</string>
<string name="all">Todo</string>
@@ -317,4 +176,16 @@
¿Quieres reiniciar ahora?</string>
<string name="reCaptchaActivity">reCAPTCHA</string>
<string name="open_in_popup_mode">Abrir en modo popup</string>
<string name="msg_popup_permission">Este permiso es necesario para
abrir en modo popup</string>
<string name="reCaptcha_title">Desafío reCAPTCHA</string>
<string name="recaptcha_request_toast">Desafío reCAPTCHA requerido</string>
<string name="popup_mode_share_menu_title">Modo popup de NewPipe</string>
<string name="popup_playing_toast">Reproduciendo en modo popup</string>
<string name="use_old_player_title">Usar reproductor antiguo</string>
<string name="use_old_player_summary">Versión antigua en reproductor Mediaframework.</string>
</resources>

View File

@@ -19,7 +19,6 @@
<string name="default_resolution_title">Lehenetsitako bereizmena</string>
<string name="play_with_kodi_title">Kodirekin erreproduzitu</string>
<string name="kore_not_found">Kore aplikazioa ez da aurkitu. Kore beharrezkoa da Kodi multimedia zentroarekin bideoak erreproduzitzeko.</string>
<string name="installeKore">Kore instalatu</string>
<string name="show_play_with_kodi_title">\"Kodirekin erreproduzitu\" aukera erakutsi</string>
<string name="show_play_with_kodi_summary">Kodi multimedia zentroarekin bideoa erreproduzitzeko aukera erakusten du.</string>
<string name="play_audio">Audioa</string>
@@ -48,8 +47,6 @@
<string name="search_page">"Orrialdea bilatu: "</string>
<string name="use_external_video_player_title">Kanpoko bideo erreproduzitzailea erabili</string>
<string name="use_external_audio_player_title">Kanpoko audio erreproduzitzailea erabili</string>
<string name="autoplay_through_intent_title">Intent bidez automatikoki erreproduzitu</string>
<string name="autoplay_through_intent_summary">Bideoa automatikoki hasten du beste aplikazio batetik deitu denean.</string>
<string name="background_player_playing_toast">Atzeko planoan erreproduzitzen</string>
<string name="main_bg_subtitle">Ukitu bilaketa hasteko</string>
<string name="download_path_audio_title">Audioa deskargatzeko kokapena</string>

View File

@@ -1,6 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="autoplay_through_intent_summary">Lire automatiquement une vidéo lorsquelle a été appelée depuis une autre application.</string>
<string name="cancel">Annuler</string>
<string name="choose_browser">Choisir un navigateur </string>
<string name="default_resolution_title">Définition par défaut</string>
@@ -10,11 +9,9 @@
<string name="download_path_dialog_title">Entrer le chemin de téléchargement des vidéos</string>
<string name="download_path_summary">Chemin de stockage des vidéos téléchargées.</string>
<string name="install">Installer</string>
<string name="installeKore">Installer Kore</string>
<string name="kore_not_found">Lapplication Kore est introuvable. Installer Kore ?</string>
<string name="no_player_found">Aucun lecteur de flux réseau trouvé. Installer VLC ?</string>
<string name="open_in_browser">Ouvrir dans le navigateur</string>
<string name="autoplay_through_intent_title">Lecture automatique via Intent</string>
<string name="play_with_kodi_title">Lire avec Kodi</string>
<string name="screen_rotation">rotation</string>
<string name="search">Rechercher</string>
@@ -26,7 +23,6 @@
<string name="show_play_with_kodi_title">Afficher loption «Lire avec Kodi»</string>
<string name="settings_activity_title">Paramètres</string>
<string name="upload_date_text">Ajoutée le %1$s</string>
<string name="useExternalPlayerTitle">Utiliser un lecteur externe</string>
<string name="view_count_text">%1$s vues</string>
<string name="play_audio">Audio</string>
<string name="default_audio_format_title">Format audio par défaut</string>
@@ -64,7 +60,7 @@
<string name="download_path_audio_title">Chemin de téléchargement de l\'audio</string>
<string name="download_path_audio_summary">Chemin de stockage des fichiers audio téléchargés</string>
<string name="download_path_audio_dialog_title">Entrer le chemin de téléchargement des fichiers audio.</string>
<string name="download_path_audio_dialog_title">Entrez le chemin de téléchargement des fichiers audio.</string>
<string name="err_dir_create">Impossible de créer le répertoire de téléchargement « %1$s»</string>
<string name="info_dir_created">Répertoire de téléchargement « %1$s» créé</string>
@@ -100,12 +96,12 @@
<string name="logging_normal">Normal</string>
<string name="retry">Réessayer</string>
<string name="error_drm_unsupported_scheme">Cet appareil ne supporte pas le système DRM requis</string>
<string name="error_drm_unknown">Une erreur DRM s\'est produite</string>
<string name="error_drm_unknown">Une erreur DRM inconnue s\'est produite</string>
<string name="storage_permission_denied">Autorisation d\'accès au stockage refusée</string>
<string name="use_exoplayer_title">Utiliser ExoPlayer</string>
<string name="use_exoplayer_summary">Expérimental</string>
<string name="main_bg_subtitle">Faites une recherche pour commencer</string>
<string name="autoplay_by_calling_app_title">Lecture automatique quand ouvert depuis une autre application</string>
<string name="autoplay_by_calling_app_title">Lecture automatique lors de l\'ouverture depuis une autre application</string>
<string name="video_is_age_restricted">Cette vidéo est soumise à une restriction d\'âge. Autoriser le contenu restreint dans les paramètres avant.</string>
<string name="info_searched_lbl">Recherché :</string>
<string name="user_report">Rapport utilisateur</string>
@@ -152,7 +148,7 @@
<string name="no_available_dir">Sélectionner un répertoire de téléchargement disponible.</string>
<string name="could_not_load_image">Limage ne peut pas être chargée</string>
<string name="app_ui_crash">Lappli / linterface utilisateur a crashé</string>
<string name="app_ui_crash">Lappli/linterface utilisateur a crashé</string>
<string name="title_activity_channel">Activité de la chaine</string>
<string name="action_settings">Paramètres</string>
@@ -168,4 +164,26 @@
<string name="reCaptcha_title">Test reCAPTCHA</string>
<string name="recaptcha_request_toast">Test reCAPTCHA demandé</string>
<string name="open_in_popup_mode">Ouvrir dans une fenêtre popup</string>
<string name="popup_mode_share_menu_title">Mode Popup de Newpipe</string>
<string name="popup_playing_toast">Lecture en mode popup</string>
<string name="yes">Oui</string>
<string name="later">Plus tard</string>
<string name="disabled">Désactivé</string>
<string name="info_labels">Quoi :\\nRequête :\\nLang. du contenu:\\nService :\\nHeure GMT :\\nPaquage :\\nVersion :\\nVersion de l\'OS:\\nGamme globale d\'IPs :</string>
<string name="use_old_player_title">Utiliser l\'ancien lecteur</string>
<string name="use_old_player_summary">Ancien build dans le lecteur Mediaframework.</string>
<string name="subscribe">S\'abonner</string>
<string name="short_thousand">K</string>
<string name="short_million">M</string>
<string name="restart_title">Redémarrer</string>
<string name="msg_restart">Vous devez redémarrer l\'application pour appliquer le thème.
Voulez-vous redémarrer maintenant ?</string>
<string name="msg_popup_permission">Cette permission est nécessaire
pour ouvrir en mode popup</string>
</resources>

View File

@@ -16,16 +16,12 @@
<string name="choose_browser">Válasszon böngészőt</string>
<string name="screen_rotation">forgatás</string>
<string name="settings_activity_title">Beállítások</string>
<string name="useExternalPlayerTitle">Külső lejátszó használata</string>
<string name="download_path_title">Videófájlok letöltési helye</string>
<string name="download_path_summary">Útvonal a letöltött videók tárolásához.</string>
<string name="download_path_dialog_title">Adja meg a videófájlok letöltési helyét</string>
<string name="autoplay_through_intent_title">Automatikus lejátszás Intent-en keresztül</string>
<string name="autoplay_through_intent_summary">Videó automatikus lejátszása külső alkalmazással való megnyitás esetén.</string>
<string name="default_resolution_title">Alapértelmezett felbontás</string>
<string name="play_with_kodi_title">Lejátszás Kodi-val</string>
<string name="kore_not_found">A Kore alkalmazás nem található. Feltelepíti a Kore lejátszót?</string>
<string name="installeKore">Kore telepítése</string>
<string name="show_play_with_kodi_title">\"Lejátszás Kodi-val\" opció mutatása</string>
<string name="show_play_with_kodi_summary">Mutat egy opciót a videók Kodi médiaközponttal való lejátszására.</string>
<string name="play_audio">Hang</string>

View File

@@ -151,143 +151,6 @@
<string name="user_report">Laporan pengguna</string>
<string name="msg_threads">Thread</string>
<string name="large_text">"
Material is the metaphor.
A material metaphor is the unifying theory of a rationalized space and a system of motion.
The material is grounded in tactile reality, inspired by the study of paper and ink, yet
technologically advanced and open to imagination and magic.
Surfaces and edges of the material provide visual cues that are grounded in reality. The
use of familiar tactile attributes helps users quickly understand affordances. Yet the
flexibility of the material creates new affordances that supercede those in the physical
world, without breaking the rules of physics.
The fundamentals of light, surface, and movement are key to conveying how objects move,
interact, and exist in space and in relation to each other. Realistic lighting shows
seams, divides space, and indicates moving parts.
Bold, graphic, intentional.
The foundational elements of print based design typography, grids, space, scale, color,
and use of imagery guide visual treatments. These elements do far more than please the
eye. They create hierarchy, meaning, and focus. Deliberate color choices, edge to edge
imagery, large scale typography, and intentional white space create a bold and graphic
interface that immerse the user in the experience.
An emphasis on user actions makes core functionality immediately apparent and provides
waypoints for the user.
Motion provides meaning.
Motion respects and reinforces the user as the prime mover. Primary user actions are
inflection points that initiate motion, transforming the whole design.
All action takes place in a single environment. Objects are presented to the user without
breaking the continuity of experience even as they transform and reorganize.
Motion is meaningful and appropriate, serving to focus attention and maintain continuity.
Feedback is subtle yet clear. Transitions are efficient yet coherent.
3D world.
The material environment is a 3D space, which means all objects have x, y, and z
dimensions. The z-axis is perpendicularly aligned to the plane of the display, with the
positive z-axis extending towards the viewer. Every sheet of material occupies a single
position along the z-axis and has a standard 1dp thickness.
On the web, the z-axis is used for layering and not for perspective. The 3D world is
emulated by manipulating the y-axis.
Light and shadow.
Within the material environment, virtual lights illuminate the scene. Key lights create
directional shadows, while ambient light creates soft shadows from all angles.
Shadows in the material environment are cast by these two light sources. In Android
development, shadows occur when light sources are blocked by sheets of material at
various positions along the z-axis. On the web, shadows are depicted by manipulating the
y-axis only. The following example shows the card with a height of 6dp.
Resting elevation.
All material objects, regardless of size, have a resting elevation, or default elevation
that does not change. If an object changes elevation, it should return to its resting
elevation as soon as possible.
Component elevations.
The resting elevation for a component type is consistent across apps (e.g., FAB elevation
does not vary from 6dp in one app to 16dp in another app).
Components may have different resting elevations across platforms, depending on the depth
of the environment (e.g., TV has a greater depth than mobile or desktop).
Responsive elevation and dynamic elevation offsets.
Some component types have responsive elevation, meaning they change elevation in response
to user input (e.g., normal, focused, and pressed) or system events. These elevation
changes are consistently implemented using dynamic elevation offsets.
Dynamic elevation offsets are the goal elevation that a component moves towards, relative
to the components resting state. They ensure that elevation changes are consistent
across actions and component types. For example, all components that lift on press have
the same elevation change relative to their resting elevation.
Once the input event is completed or cancelled, the component will return to its resting
elevation.
Avoiding elevation interference.
Components with responsive elevations may encounter other components as they move between
their resting elevations and dynamic elevation offsets. Because material cannot pass
through other material, components avoid interfering with one another any number of ways,
whether on a per component basis or using the entire app layout.
On a component level, components can move or be removed before they cause interference.
For example, a floating action button (FAB) can disappear or move off screen before a
user picks up a card, or it can move if a snackbar appears.
On the layout level, design your app layout to minimize opportunities for interference.
For example, position the FAB to one side of stream of a cards so the FAB wont interfere
when a user tries to pick up one of cards.
"</string>
<string name="reCaptchaActivity">reCAPTCHA</string>
<string name="reCaptcha_title">Rintangan reCAPTCHA</string>
@@ -315,4 +178,15 @@
Apakah Anda ingin memulai ulang sekarang?</string>
<string name="open_in_popup_mode">Buka di mode popup</string>
<string name="msg_popup_permission">Izin ini dibutuhkan untuk
membuka di mode popup</string>
<string name="popup_mode_share_menu_title">Mode Popup NewPipe</string>
<string name="popup_playing_toast">Memutar dalam mode popup</string>
<string name="use_old_player_title">Gunakan pemutar lama</string>
<string name="use_old_player_summary">Versi lama dalam pemutar Mediaframework.</string>
<string name="disabled">Dinonaktifkan</string>
</resources>

View File

@@ -16,16 +16,12 @@
<string name="choose_browser">Scegli browser</string>
<string name="screen_rotation">rotazione</string>
<string name="settings_activity_title">Impostazioni</string>
<string name="useExternalPlayerTitle">Usa un riproduttore video esterno</string>
<string name="download_path_title">Cartella dei video scaricati</string>
<string name="download_path_summary">Percorso dove memorizzare i video scaricati.</string>
<string name="download_path_dialog_title">Inserisci il percorso per i download</string>
<string name="autoplay_through_intent_title">Auto riproduzione attraverso internet</string>
<string name="autoplay_through_intent_summary">Avvia automaticamente un video quando è stato chiamato da un\'altra applicazione.</string>
<string name="default_resolution_title">Risoluzione predefinita</string>
<string name="play_with_kodi_title">Riproduci con Kodi</string>
<string name="kore_not_found">L\'applicazione Kore non è stata trovata. Kore è necessario per riprodurre video con Kodi media center. Vorresti installarlo?</string>
<string name="installeKore">Installa Kore</string>
<string name="show_play_with_kodi_title">Mostra l\'opzione \"Riproduci con Kodi\"</string>
<string name="show_play_with_kodi_summary">Mostra un opzione per riprodurre un video attraverso Kodi media center.</string>
<string name="play_audio">Audio</string>
@@ -39,8 +35,6 @@
<string name="similar_videos_btn_text">Video simili</string>
<string name="search_language_title">Lingua preferita per i contenuti</string>
<string name="settings_category_video_audio_title">Video e Audio</string>
<string name="settingsCategoryVideoInfoTittle">INFO</string>
<string name="settingsCategoryEtcTitle">ETC</string>
<string name="list_thumbnail_view_description">Anteprima video</string>
<string name="detail_thumbnail_view_description">Anteprima video</string>
@@ -128,7 +122,7 @@
<string name="storage_permission_denied">È stato negato il permesso di accedere all\'archiviazione di massa</string>
<string name="use_exoplayer_title">Usa ExoPlayer</string>
<string name="use_exoplayer_summary">Sperimentale</string>
<string name="downloads">download</string>
<string name="downloads">Download</string>
<string name="downloads_title">Download</string>
<string name="settings_title">Impostazioni</string>
<string name="error_report_title">Segnalazione errori</string>
@@ -153,7 +147,7 @@
<string name="msg_exists">File già esistente</string>
<string name="msg_url_malform">URL errato od errore di rete</string>
<string name="msg_running">Download di NewPipe</string>
<string name="msg_running_detail">Fai tap per i dettagli</string>
<string name="msg_running_detail">Tocca per dettagli</string>
<string name="msg_wait">Attendi…</string>
<string name="msg_copied">Copiato negli appunti.</string>
<string name="no_available_dir">Seleziona una cartella disponibile in cui salvare i download.</string>
@@ -164,143 +158,6 @@
<string name="action_settings">Impostazioni</string>
<string name="title_activity_channel">ChannelActivity</string>
<string name="large_text">"
Material is the metaphor.
A material metaphor is the unifying theory of a rationalized space and a system of motion.
The material is grounded in tactile reality, inspired by the study of paper and ink, yet
technologically advanced and open to imagination and magic.
Surfaces and edges of the material provide visual cues that are grounded in reality. The
use of familiar tactile attributes helps users quickly understand affordances. Yet the
flexibility of the material creates new affordances that supercede those in the physical
world, without breaking the rules of physics.
The fundamentals of light, surface, and movement are key to conveying how objects move,
interact, and exist in space and in relation to each other. Realistic lighting shows
seams, divides space, and indicates moving parts.
Bold, graphic, intentional.
The foundational elements of print based design typography, grids, space, scale, color,
and use of imagery guide visual treatments. These elements do far more than please the
eye. They create hierarchy, meaning, and focus. Deliberate color choices, edge to edge
imagery, large scale typography, and intentional white space create a bold and graphic
interface that immerse the user in the experience.
An emphasis on user actions makes core functionality immediately apparent and provides
waypoints for the user.
Motion provides meaning.
Motion respects and reinforces the user as the prime mover. Primary user actions are
inflection points that initiate motion, transforming the whole design.
All action takes place in a single environment. Objects are presented to the user without
breaking the continuity of experience even as they transform and reorganize.
Motion is meaningful and appropriate, serving to focus attention and maintain continuity.
Feedback is subtle yet clear. Transitions are efficient yet coherent.
3D world.
The material environment is a 3D space, which means all objects have x, y, and z
dimensions. The z-axis is perpendicularly aligned to the plane of the display, with the
positive z-axis extending towards the viewer. Every sheet of material occupies a single
position along the z-axis and has a standard 1dp thickness.
On the web, the z-axis is used for layering and not for perspective. The 3D world is
emulated by manipulating the y-axis.
Light and shadow.
Within the material environment, virtual lights illuminate the scene. Key lights create
directional shadows, while ambient light creates soft shadows from all angles.
Shadows in the material environment are cast by these two light sources. In Android
development, shadows occur when light sources are blocked by sheets of material at
various positions along the z-axis. On the web, shadows are depicted by manipulating the
y-axis only. The following example shows the card with a height of 6dp.
Resting elevation.
All material objects, regardless of size, have a resting elevation, or default elevation
that does not change. If an object changes elevation, it should return to its resting
elevation as soon as possible.
Component elevations.
The resting elevation for a component type is consistent across apps (e.g., FAB elevation
does not vary from 6dp in one app to 16dp in another app).
Components may have different resting elevations across platforms, depending on the depth
of the environment (e.g., TV has a greater depth than mobile or desktop).
Responsive elevation and dynamic elevation offsets.
Some component types have responsive elevation, meaning they change elevation in response
to user input (e.g., normal, focused, and pressed) or system events. These elevation
changes are consistently implemented using dynamic elevation offsets.
Dynamic elevation offsets are the goal elevation that a component moves towards, relative
to the components resting state. They ensure that elevation changes are consistent
across actions and component types. For example, all components that lift on press have
the same elevation change relative to their resting elevation.
Once the input event is completed or cancelled, the component will return to its resting
elevation.
Avoiding elevation interference.
Components with responsive elevations may encounter other components as they move between
their resting elevations and dynamic elevation offsets. Because material cannot pass
through other material, components avoid interfering with one another any number of ways,
whether on a per component basis or using the entire app layout.
On a component level, components can move or be removed before they cause interference.
For example, a floating action button (FAB) can disappear or move off screen before a
user picks up a card, or it can move if a snackbar appears.
On the layout level, design your app layout to minimize opportunities for interference.
For example, position the FAB to one side of stream of a cards so the FAB wont interfere
when a user tries to pick up one of cards.
"</string>
<string name="reCaptchaActivity">reCAPTCHA</string>
<string name="reCaptcha_title">Sfida reCAPTCHA</string>
@@ -328,4 +185,11 @@
Vuoi riavviare l\'app ora?</string>
<string name="open_in_popup_mode">Aprire in modo popup</string>
<string name="popup_mode_share_menu_title">Modo popup NewPipe</string>
<string name="popup_playing_toast">Riproduzione in modo popup</string>
<string name="disabled">Disattivato</string>
<string name="use_old_player_title">Usare anziane riproduttore</string>
</resources>

View File

@@ -15,16 +15,12 @@
<string name="choose_browser">ブラウザーを選択</string>
<string name="screen_rotation">回転</string>
<string name="settings_activity_title">設定</string>
<string name="useExternalPlayerTitle">外部プレーヤーを使用する</string>
<string name="download_path_title">動画を保存する場所</string>
<string name="download_path_summary">動画を保存する位置</string>
<string name="download_path_dialog_title">動画を保存する場所を入力して下さい</string>
<string name="autoplay_through_intent_title">インテントで自動再生</string>
<string name="autoplay_through_intent_summary">他のアプリケーションから呼び出されたとき、自動的に動画再生を開始します。</string>
<string name="default_resolution_title">基本解像度</string>
<string name="play_with_kodi_title">Kodi で再生</string>
<string name="kore_not_found">Koreが見つかりません。Kore を入手しますか。</string>
<string name="installeKore">Kore をインストール</string>
<string name="show_play_with_kodi_title">\"Kodi で再生\" 設定を表示</string>
<string name="show_play_with_kodi_summary">Kodi メディアセンター経由で動画を再生するための設定を表示します</string>
<string name="play_audio">音楽</string>
@@ -162,143 +158,6 @@
<string name="app_ui_crash">アプリ/UI がクラッシュしました</string>
<string name="info_labels">何:\\n提案:\\nコンテンツ言語:\\nサービス:\\nGMT 時間:\\nパッケージ:\\nバージョン:\\nOS バージョン:\\nグローバル IP 範囲:</string>
<string name="large_text">"
Material is the metaphor.
A material metaphor is the unifying theory of a rationalized space and a system of motion.
The material is grounded in tactile reality, inspired by the study of paper and ink, yet
technologically advanced and open to imagination and magic.
Surfaces and edges of the material provide visual cues that are grounded in reality. The
use of familiar tactile attributes helps users quickly understand affordances. Yet the
flexibility of the material creates new affordances that supercede those in the physical
world, without breaking the rules of physics.
The fundamentals of light, surface, and movement are key to conveying how objects move,
interact, and exist in space and in relation to each other. Realistic lighting shows
seams, divides space, and indicates moving parts.
Bold, graphic, intentional.
The foundational elements of print based design typography, grids, space, scale, color,
and use of imagery guide visual treatments. These elements do far more than please the
eye. They create hierarchy, meaning, and focus. Deliberate color choices, edge to edge
imagery, large scale typography, and intentional white space create a bold and graphic
interface that immerse the user in the experience.
An emphasis on user actions makes core functionality immediately apparent and provides
waypoints for the user.
Motion provides meaning.
Motion respects and reinforces the user as the prime mover. Primary user actions are
inflection points that initiate motion, transforming the whole design.
All action takes place in a single environment. Objects are presented to the user without
breaking the continuity of experience even as they transform and reorganize.
Motion is meaningful and appropriate, serving to focus attention and maintain continuity.
Feedback is subtle yet clear. Transitions are efficient yet coherent.
3D world.
The material environment is a 3D space, which means all objects have x, y, and z
dimensions. The z-axis is perpendicularly aligned to the plane of the display, with the
positive z-axis extending towards the viewer. Every sheet of material occupies a single
position along the z-axis and has a standard 1dp thickness.
On the web, the z-axis is used for layering and not for perspective. The 3D world is
emulated by manipulating the y-axis.
Light and shadow.
Within the material environment, virtual lights illuminate the scene. Key lights create
directional shadows, while ambient light creates soft shadows from all angles.
Shadows in the material environment are cast by these two light sources. In Android
development, shadows occur when light sources are blocked by sheets of material at
various positions along the z-axis. On the web, shadows are depicted by manipulating the
y-axis only. The following example shows the card with a height of 6dp.
Resting elevation.
All material objects, regardless of size, have a resting elevation, or default elevation
that does not change. If an object changes elevation, it should return to its resting
elevation as soon as possible.
Component elevations.
The resting elevation for a component type is consistent across apps (e.g., FAB elevation
does not vary from 6dp in one app to 16dp in another app).
Components may have different resting elevations across platforms, depending on the depth
of the environment (e.g., TV has a greater depth than mobile or desktop).
Responsive elevation and dynamic elevation offsets.
Some component types have responsive elevation, meaning they change elevation in response
to user input (e.g., normal, focused, and pressed) or system events. These elevation
changes are consistently implemented using dynamic elevation offsets.
Dynamic elevation offsets are the goal elevation that a component moves towards, relative
to the components resting state. They ensure that elevation changes are consistent
across actions and component types. For example, all components that lift on press have
the same elevation change relative to their resting elevation.
Once the input event is completed or cancelled, the component will return to its resting
elevation.
Avoiding elevation interference.
Components with responsive elevations may encounter other components as they move between
their resting elevations and dynamic elevation offsets. Because material cannot pass
through other material, components avoid interfering with one another any number of ways,
whether on a per component basis or using the entire app layout.
On a component level, components can move or be removed before they cause interference.
For example, a floating action button (FAB) can disappear or move off screen before a
user picks up a card, or it can move if a snackbar appears.
On the layout level, design your app layout to minimize opportunities for interference.
For example, position the FAB to one side of stream of a cards so the FAB wont interfere
when a user tries to pick up one of cards.
"</string>
<string name="reCaptchaActivity">reCAPTCHA</string>
<string name="reCaptcha_title">reCAPTCHA の要求</string>
@@ -326,4 +185,15 @@
今すぐ再起動しますか?</string>
<string name="open_in_popup_mode">ポップアップモードで開く</string>
<string name="msg_popup_permission">ポップアップモードで開くには
このアクセス許可が必要です</string>
<string name="popup_mode_share_menu_title">NewPipe ポップアップモード</string>
<string name="popup_playing_toast">ポップアップモードで再生中</string>
<string name="use_old_player_title">古いプレーヤーを使用する</string>
<string name="use_old_player_summary">Mediaframework プレーヤーの古いビルド。</string>
<string name="disabled">無効</string>
</resources>

View File

@@ -16,16 +16,12 @@
<string name="choose_browser">브라우저 선택</string>
<string name="screen_rotation">회전</string>
<string name="settings_activity_title">설정</string>
<string name="useExternalPlayerTitle">외부 플레이어 사용</string>
<string name="download_path_title">비디오 다운로드 위치</string>
<string name="download_path_summary">다운로드된 비디오가 저장될 경로를 선택하세요.</string>
<string name="download_path_dialog_title">비디오 다운로드 경로 입력</string>
<string name="autoplay_through_intent_title">인텐트로 경유할 경우 자동 재생</string>
<string name="autoplay_through_intent_summary">다른 앱으로부터 호출되었을 경우에 비디오를 자동으로 재생합니다.</string>
<string name="default_resolution_title">기본 해상도</string>
<string name="play_with_kodi_title">Kodi로 재생</string>
<string name="kore_not_found">Kore 앱이 발견되지 않았습니다. Kore를 설치할까요?</string>
<string name="installeKore">Kore 설치</string>
<string name="show_play_with_kodi_title">\"Kodi로 재생\" 옵션 표시</string>
<string name="show_play_with_kodi_summary">비디오를 Kodi media center를 사용해 재생하는 옵션을 표시합니다.</string>
<string name="play_audio">오디오</string>
@@ -39,8 +35,6 @@
<string name="similar_videos_btn_text">유사한 비디오</string>
<string name="search_language_title">선호하는 컨텐츠 언어</string>
<string name="settings_category_video_audio_title">비디오 &amp; 오디오</string>
<string name="settingsCategoryVideoInfoTittle">정보</string>
<string name="settingsCategoryEtcTitle">기타</string>
<string name="list_thumbnail_view_description">비디오 미리보기 썸네일</string>
<string name="detail_thumbnail_view_description">비디오 미리보기 썸네일</string>

View File

@@ -3,14 +3,15 @@
<!-- Video Item Search View Dimensions-->
<!-- Text Size -->
<dimen name="channel_item_detail_title_text_size">33sp</dimen>
<dimen name="video_item_search_title_text_size">22sp</dimen>
<dimen name="video_item_search_duration_text_size">16sp</dimen>
<dimen name="video_item_search_uploader_text_size">18sp</dimen>
<dimen name="video_item_search_upload_date_text_size">18sp</dimen>
<dimen name="channel_item_detail_title_text_size">18sp</dimen>
<dimen name="video_item_search_title_text_size">16sp</dimen>
<dimen name="video_item_search_duration_text_size">12sp</dimen>
<dimen name="video_item_search_uploader_text_size">14sp</dimen>
<dimen name="video_item_search_upload_date_text_size">14sp</dimen>
<!-- Elements Size -->
<dimen name="video_item_search_thumbnail_image_width">210dp</dimen>
<dimen name="video_item_search_thumbnail_image_height">130dp</dimen>
<!-- 16 / 9 ratio-->
<dimen name="video_item_search_thumbnail_image_width">142dp</dimen>
<dimen name="video_item_search_thumbnail_image_height">80dp</dimen>
<!-- Paddings & Margins -->
<dimen name="video_item_search_card_vertical_margin">5dp</dimen>
<dimen name="video_item_search_card_horizontal_margin">10dp</dimen>
@@ -24,17 +25,15 @@
<!-- Video Item Detail View Dimensions-->
<!-- Text Size -->
<dimen name="video_item_detail_title_text_size">24sp</dimen>
<dimen name="video_item_detail_views_text_size">18sp</dimen>
<dimen name="video_item_detail_likes_text_size">16sp</dimen>
<dimen name="video_item_detail_uploader_text_size">18sp</dimen>
<dimen name="video_item_detail_upload_date_text_size">18sp</dimen>
<dimen name="video_item_detail_description_text_size">18sp</dimen>
<dimen name="video_item_detail_next_text_size">20sp</dimen>
<dimen name="video_item_detail_similar_text_size">20sp</dimen>
<dimen name="video_item_detail_title_text_size">20sp</dimen>
<dimen name="video_item_detail_views_text_size">14sp</dimen>
<dimen name="video_item_detail_likes_text_size">13sp</dimen>
<dimen name="video_item_detail_uploader_text_size">16sp</dimen>
<dimen name="video_item_detail_upload_date_text_size">14sp</dimen>
<dimen name="video_item_detail_description_text_size">14sp</dimen>
<dimen name="video_item_detail_next_text_size">17sp</dimen>
<!-- Elements Size -->
<dimen name="video_item_detail_thumbnail_image_height">240dp</dimen>
<dimen name="video_item_detail_uploader_image_size">100dp</dimen>
<dimen name="video_item_detail_uploader_image_size">70dp</dimen>
<dimen name="video_item_detail_like_image_height">20sp</dimen>
<dimen name="video_item_detail_like_image_width">20sp</dimen>
<!-- Paddings & Margins -->

View File

@@ -28,7 +28,6 @@
<string name="download_path_audio_summary">Sti å lagre nedlastet lyd i.</string>
<string name="download_path_audio_dialog_title">Skriv inn nedlastingssti for lydfiler.</string>
<string name="autoplay_through_intent_summary">Automatisk avspilling av video når det blir forespurt fra et annet program.</string>
<string name="default_resolution_title">Forvalgt oppløsning</string>
<string name="play_with_kodi_title">Spill av med Kodi</string>
<string name="kore_not_found">Kore-programmet ble ikke funnet. Installer Kore?</string>
@@ -65,7 +64,6 @@
<string name="err_dir_create">Kan ikke opprette nedlastingsmappe \'%1$s\'</string>
<string name="info_dir_created">Opprettet nedlastingsmappen \'%1$s\'</string>
<string name="autoplay_through_intent_title">Automatisk avspilling med Intent</string>
<string name="main_bg_subtitle">Trykk for å komme i gang</string>
<string name="autoplay_by_calling_app_title">Automatisk avspilling når forespurt av et annet program</string>
<string name="autoplay_by_calling_app_summary">Automatisk avspilling av video når NewPipe blir forespurt av et annet program.</string>

View File

@@ -16,16 +16,12 @@
<string name="choose_browser">Kies een browser</string>
<string name="screen_rotation">rotatie</string>
<string name="settings_activity_title">Instellingen</string>
<string name="useExternalPlayerTitle">Gebruik externe speler</string>
<string name="download_path_title">Downloadlocatie voor video\'s</string>
<string name="download_path_summary">Locatie om gedownloade video\'s in op te slaan.</string>
<string name="download_path_dialog_title">Voer downloadlocatie in voor video\'s</string>
<string name="autoplay_through_intent_title">Speel automatisch via Intent</string>
<string name="autoplay_through_intent_summary">Speel een video automatisch af indien geopend vanuit een andere app.</string>
<string name="default_resolution_title">Standaardresolutie</string>
<string name="play_with_kodi_title">Afspelen met Kodi</string>
<string name="kore_not_found">Kore-app niet gevonden. Kore installeren?</string>
<string name="installeKore">Installeer Kore</string>
<string name="show_play_with_kodi_title">Toon \"Afspelen met Kodi\"-optie</string>
<string name="show_play_with_kodi_summary">Toont een optie om een video op een Kodi media center af te spelen.</string>
<string name="play_audio">Audio</string>
@@ -157,147 +153,36 @@
<string name="title_activity_channel">ChannelActivity</string>
<string name="action_settings">Instellingen</string>
<string name="large_text">"
Material is the metaphor.
A material metaphor is the unifying theory of a rationalized space and a system of motion.
The material is grounded in tactile reality, inspired by the study of paper and ink, yet
technologically advanced and open to imagination and magic.
Surfaces and edges of the material provide visual cues that are grounded in reality. The
use of familiar tactile attributes helps users quickly understand affordances. Yet the
flexibility of the material creates new affordances that supercede those in the physical
world, without breaking the rules of physics.
The fundamentals of light, surface, and movement are key to conveying how objects move,
interact, and exist in space and in relation to each other. Realistic lighting shows
seams, divides space, and indicates moving parts.
Bold, graphic, intentional.
The foundational elements of print based design typography, grids, space, scale, color,
and use of imagery guide visual treatments. These elements do far more than please the
eye. They create hierarchy, meaning, and focus. Deliberate color choices, edge to edge
imagery, large scale typography, and intentional white space create a bold and graphic
interface that immerse the user in the experience.
An emphasis on user actions makes core functionality immediately apparent and provides
waypoints for the user.
Motion provides meaning.
Motion respects and reinforces the user as the prime mover. Primary user actions are
inflection points that initiate motion, transforming the whole design.
All action takes place in a single environment. Objects are presented to the user without
breaking the continuity of experience even as they transform and reorganize.
Motion is meaningful and appropriate, serving to focus attention and maintain continuity.
Feedback is subtle yet clear. Transitions are efficient yet coherent.
3D world.
The material environment is a 3D space, which means all objects have x, y, and z
dimensions. The z-axis is perpendicularly aligned to the plane of the display, with the
positive z-axis extending towards the viewer. Every sheet of material occupies a single
position along the z-axis and has a standard 1dp thickness.
On the web, the z-axis is used for layering and not for perspective. The 3D world is
emulated by manipulating the y-axis.
Light and shadow.
Within the material environment, virtual lights illuminate the scene. Key lights create
directional shadows, while ambient light creates soft shadows from all angles.
Shadows in the material environment are cast by these two light sources. In Android
development, shadows occur when light sources are blocked by sheets of material at
various positions along the z-axis. On the web, shadows are depicted by manipulating the
y-axis only. The following example shows the card with a height of 6dp.
Resting elevation.
All material objects, regardless of size, have a resting elevation, or default elevation
that does not change. If an object changes elevation, it should return to its resting
elevation as soon as possible.
Component elevations.
The resting elevation for a component type is consistent across apps (e.g., FAB elevation
does not vary from 6dp in one app to 16dp in another app).
Components may have different resting elevations across platforms, depending on the depth
of the environment (e.g., TV has a greater depth than mobile or desktop).
Responsive elevation and dynamic elevation offsets.
Some component types have responsive elevation, meaning they change elevation in response
to user input (e.g., normal, focused, and pressed) or system events. These elevation
changes are consistently implemented using dynamic elevation offsets.
Dynamic elevation offsets are the goal elevation that a component moves towards, relative
to the components resting state. They ensure that elevation changes are consistent
across actions and component types. For example, all components that lift on press have
the same elevation change relative to their resting elevation.
Once the input event is completed or cancelled, the component will return to its resting
elevation.
Avoiding elevation interference.
Components with responsive elevations may encounter other components as they move between
their resting elevations and dynamic elevation offsets. Because material cannot pass
through other material, components avoid interfering with one another any number of ways,
whether on a per component basis or using the entire app layout.
On a component level, components can move or be removed before they cause interference.
For example, a floating action button (FAB) can disappear or move off screen before a
user picks up a card, or it can move if a snackbar appears.
On the layout level, design your app layout to minimize opportunities for interference.
For example, position the FAB to one side of stream of a cards so the FAB wont interfere
when a user tries to pick up one of cards.
"</string>
<string name="black_theme_title">Zwart</string>
<string name="reCaptchaActivity">reCAPTCHA</string>
<string name="reCaptcha_title">reCAPTCHA-uitdaging</string>
<string name="recaptcha_request_toast">reCAPTCHA-uitdaging gevraagd</string>
<string name="open_in_popup_mode">Openen in pop-upmodus</string>
<string name="all">Alles</string>
<string name="channel">Kanaal</string>
<string name="yes">Ja</string>
<string name="later">Later</string>
<string name="videos">video\'s</string>
<string name="subscriber">abonneren</string>
<string name="subscribe">Abonneren</string>
<string name="views">weergaven</string>
<string name="short_thousand">K</string>
<string name="short_million">M</string>
<string name="short_billion">B</string>
<string name="restart_title">Herstarten</string>
<string name="msg_restart">Je moet de applicatie herstarten om het thema toe te passen.
Wil je nu herstarten?</string>
<string name="msg_popup_permission">Deze toestemming is vereist om
te openen in pop-upmodus</string>
<string name="popup_mode_share_menu_title">NewPipe-pop-upmodus</string>
<string name="popup_playing_toast">Speelt af in pop-upmodus</string>
<string name="use_old_player_title">Gebruik oude speler</string>
<string name="use_old_player_summary">Oude build in Mediaframework-speler.</string>
</resources>

View File

@@ -2,7 +2,7 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="view_count_text">%1$s visualizações</string>
<string name="upload_date_text">Publicado em %1$s</string>
<string name="no_player_found">Reprodutor não disponível. Instalar o VLC?</string>
<string name="no_player_found">Reprodutor não disponível. Deseja instalar o VLC?</string>
<string name="install">Instalar</string>
<string name="cancel">Cancelar</string>
<string name="open_in_browser">Abrir no navegador</string>
@@ -21,12 +21,9 @@
<string name="download_path_title">Descarga de vídeos</string>
<string name="download_path_summary">Local para guardar os vídeos descarregados.</string>
<string name="download_path_dialog_title">Digite o caminho para os vídeos</string>
<string name="autoplay_through_intent_title">Reproduzir através de Intent</string>
<string name="autoplay_through_intent_summary">Reproduzir automaticamente o vídeo se invocado por outra aplicação.</string>
<string name="default_resolution_title">Resolução padrão</string>
<string name="play_with_kodi_title">Reproduzir com Kodi</string>
<string name="kore_not_found">Aplicação não encontrada. Instalar o Kore?</string>
<string name="installeKore">Instalar o Kore</string>
<string name="show_play_with_kodi_title">Mostrar opção \"Reproduzir com Kodi\"</string>
<string name="show_play_with_kodi_summary">Mostra uma opção para reproduzir o vídeo com o Kodi.</string>
<string name="play_audio">Áudio</string>
@@ -40,8 +37,6 @@
<string name="similar_videos_btn_text">Vídeos similares</string>
<string name="search_language_title">Idioma preferencial do conteúdo</string>
<string name="settings_category_video_audio_title">Vídeo e áudio</string>
<string name="settingsCategoryVideoInfoTittle">Informações</string>
<string name="settingsCategoryEtcTitle">Outras</string>
<string name="list_thumbnail_view_description">Miniatura de vídeos</string>
<string name="detail_thumbnail_view_description">Miniatura de vídeos</string>
@@ -56,7 +51,7 @@
<string name="settings_category_appearance_title">Aparência</string>
<string name="settings_category_other_title">Outra</string>
<string name="background_player_playing_toast">A reproduzir em segundo plano</string>
<string name="background_player_playing_toast">Reprodução em segundo plano</string>
<string name="play_btn_text">Reproduzir</string>
<string name="network_error">Erro de rede</string>
@@ -131,7 +126,7 @@
<string name="pause">Pausa</string>
<string name="view">Ver</string>
<string name="delete">Apagar</string>
<string name="checksum">Soma de verificação</string>
<string name="checksum">Checksum</string>
<string name="add">Nova missão</string>
<string name="switch_mode">Alternar entre lista e grelha</string>
@@ -141,158 +136,53 @@
<string name="msg_error">Erro</string>
<string name="msg_server_unsupported">Servidor não suportado</string>
<string name="msg_exists">Ficheiro já existe</string>
<string name="msg_url_malform">URL deformado ou internet não disponível</string>
<string name="msg_url_malform">URL inválido ou Internet não disponível</string>
<string name="msg_running_detail">Toque para detalhes</string>
<string name="msg_wait">Por favor aguarde...</string>
<string name="msg_wait">Por favor aguarde</string>
<string name="msg_copied">Copiado para a área de transferência.</string>
<string name="no_available_dir">Por favor selecione um diretório disponível.</string>
<string name="finish">OK</string>
<string name="msg_url">URL da descarga</string>
<string name="msg_threads">Tópicos</string>
<string name="msg_threads">Processos</string>
<string name="msg_fetch_filename">Obter nome de ficheiro</string>
<string name="msg_running">Descarga NewPipe</string>
<string name="could_not_load_image">Não foi possível carregar a imagem</string>
<string name="action_settings">Configurações</string>
<string name="action_settings">Definições</string>
<string name="app_ui_crash">Aplicação encerrada</string>
<string name="info_labels">O quê:\\nPedido:\\nIdioma do conteúdo:\\nServiço:\\nHora GMT:\\nPacote:\\nVersão:\\nVersão do SO:\\nIP global:</string>
<string name="title_activity_channel">Atividade do canal</string>
<string name="large_text">"
Material is the metaphor.
A material metaphor is the unifying theory of a rationalized space and a system of motion.
The material is grounded in tactile reality, inspired by the study of paper and ink, yet
technologically advanced and open to imagination and magic.
Surfaces and edges of the material provide visual cues that are grounded in reality. The
use of familiar tactile attributes helps users quickly understand affordances. Yet the
flexibility of the material creates new affordances that supercede those in the physical
world, without breaking the rules of physics.
The fundamentals of light, surface, and movement are key to conveying how objects move,
interact, and exist in space and in relation to each other. Realistic lighting shows
seams, divides space, and indicates moving parts.
Bold, graphic, intentional.
The foundational elements of print based design typography, grids, space, scale, color,
and use of imagery guide visual treatments. These elements do far more than please the
eye. They create hierarchy, meaning, and focus. Deliberate color choices, edge to edge
imagery, large scale typography, and intentional white space create a bold and graphic
interface that immerse the user in the experience.
An emphasis on user actions makes core functionality immediately apparent and provides
waypoints for the user.
Motion provides meaning.
Motion respects and reinforces the user as the prime mover. Primary user actions are
inflection points that initiate motion, transforming the whole design.
All action takes place in a single environment. Objects are presented to the user without
breaking the continuity of experience even as they transform and reorganize.
Motion is meaningful and appropriate, serving to focus attention and maintain continuity.
Feedback is subtle yet clear. Transitions are efficient yet coherent.
3D world.
The material environment is a 3D space, which means all objects have x, y, and z
dimensions. The z-axis is perpendicularly aligned to the plane of the display, with the
positive z-axis extending towards the viewer. Every sheet of material occupies a single
position along the z-axis and has a standard 1dp thickness.
On the web, the z-axis is used for layering and not for perspective. The 3D world is
emulated by manipulating the y-axis.
Light and shadow.
Within the material environment, virtual lights illuminate the scene. Key lights create
directional shadows, while ambient light creates soft shadows from all angles.
Shadows in the material environment are cast by these two light sources. In Android
development, shadows occur when light sources are blocked by sheets of material at
various positions along the z-axis. On the web, shadows are depicted by manipulating the
y-axis only. The following example shows the card with a height of 6dp.
Resting elevation.
All material objects, regardless of size, have a resting elevation, or default elevation
that does not change. If an object changes elevation, it should return to its resting
elevation as soon as possible.
Component elevations.
The resting elevation for a component type is consistent across apps (e.g., FAB elevation
does not vary from 6dp in one app to 16dp in another app).
Components may have different resting elevations across platforms, depending on the depth
of the environment (e.g., TV has a greater depth than mobile or desktop).
Responsive elevation and dynamic elevation offsets.
Some component types have responsive elevation, meaning they change elevation in response
to user input (e.g., normal, focused, and pressed) or system events. These elevation
changes are consistently implemented using dynamic elevation offsets.
Dynamic elevation offsets are the goal elevation that a component moves towards, relative
to the components resting state. They ensure that elevation changes are consistent
across actions and component types. For example, all components that lift on press have
the same elevation change relative to their resting elevation.
Once the input event is completed or cancelled, the component will return to its resting
elevation.
Avoiding elevation interference.
Components with responsive elevations may encounter other components as they move between
their resting elevations and dynamic elevation offsets. Because material cannot pass
through other material, components avoid interfering with one another any number of ways,
whether on a per component basis or using the entire app layout.
On a component level, components can move or be removed before they cause interference.
For example, a floating action button (FAB) can disappear or move off screen before a
user picks up a card, or it can move if a snackbar appears.
On the layout level, design your app layout to minimize opportunities for interference.
For example, position the FAB to one side of stream of a cards so the FAB wont interfere
when a user tries to pick up one of cards.
"</string>
<string name="open_in_popup_mode">Abrir no modo “popup“</string>
<string name="black_theme_title">Preto</string>
<string name="all">Tudo</string>
<string name="channel">Canais</string>
<string name="yes">Sim</string>
<string name="later">Depois</string>
<string name="videos">vídeos</string>
<string name="subscriber">subscritor</string>
<string name="subscribe">Subscrever</string>
<string name="views">visualizações</string>
<string name="short_thousand">K</string>
<string name="short_million">M</string>
<string name="short_billion">MM</string>
<string name="restart_title">Reiniciar</string>
<string name="msg_restart">Tem que reiniciar a aplicação para aplicar o tema.
Deseja reiniciar agora?</string>
<string name="msg_popup_permission">Permissão necessário para
o modo “popup“</string>
<string name="reCaptchaActivity">reCAPTCHA</string>
<string name="reCaptcha_title">Desafio reCAPTCHA</string>
<string name="recaptcha_request_toast">Desafio reCAPTCHA solicitado</string>
<string name="popup_mode_share_menu_title">Modo popup de NewPipe</string>
<string name="popup_playing_toast">Jogando em modo de popup</string>
<string name="use_old_player_title">Use jogador velho</string>
<string name="use_old_player_summary">Versão antiga no jogador Mediaframework.</string>
</resources>

View File

@@ -28,8 +28,6 @@
<string name="download_path_audio_summary">Locul în care se vor stoca fișierele audio descărcate</string>
<string name="download_path_audio_dialog_title">Introduceți calea de descărcare pentru fișierele audio.</string>
<string name="autoplay_through_intent_title">Redare automată intenționată</string>
<string name="autoplay_through_intent_summary">Redați automat un videoclip atunci când este chemat din altă aplicație.</string>
<string name="default_resolution_title">Rezoluție implicită</string>
<string name="play_with_kodi_title">Redați folosind Kodi</string>
<string name="kore_not_found">Aplicația Kore nu a fost găsită. Instalați Kore?</string>

Some files were not shown because too many files have changed in this diff Show More