Compare commits
65 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f015349e8 | ||
|
|
a37802c2b9 | ||
|
|
4fa3baf5e1 | ||
|
|
ef8c2c81d5 | ||
|
|
8c80d8c457 | ||
|
|
1596872c54 | ||
|
|
da8873fa78 | ||
|
|
ecd8439b3f | ||
|
|
5d178532ac | ||
|
|
08e40a013d | ||
|
|
c136f7363c | ||
|
|
a60f10d739 | ||
|
|
ae46afcb42 | ||
|
|
bffb9f6800 | ||
|
|
79aa9ad04b | ||
| ff5714f04a | |||
|
|
ce499a9766 | ||
|
|
d3bb8b7651 | ||
|
|
6d9c23c4cb | ||
|
|
b95a9332a9 | ||
|
|
609715eb5c | ||
|
|
a1266c919c | ||
|
|
a1925a0302 | ||
|
|
a7a4c03372 | ||
|
|
37201600e0 | ||
|
|
a94f40ed62 | ||
|
|
0b08cf8c76 | ||
|
|
33e29be7db | ||
|
|
bd1c7851c7 | ||
|
|
82faea5965 | ||
|
|
0bbbfd3217 | ||
|
|
fb578ecda8 | ||
|
|
e804647a65 | ||
|
|
c3c3a94593 | ||
|
|
9d55569f80 | ||
|
|
5dd8271c15 | ||
|
|
7a4a54c3ea | ||
|
|
7c9078a625 | ||
|
|
71ae342f52 | ||
|
|
b43c56085d | ||
|
|
83d2ab95e0 | ||
|
|
20a8d7372c | ||
|
|
c36ba88db7 | ||
|
|
4aa23023ee | ||
|
|
8735cf931a | ||
|
|
2473069326 | ||
|
|
03bab57a97 | ||
|
|
e3baf69533 | ||
|
|
461f747af1 | ||
|
|
028872a7d8 | ||
|
|
a1483b6c55 | ||
|
|
e406ba094c | ||
|
|
c100d15ba8 | ||
|
|
b4ea592638 | ||
|
|
16b757d9a3 | ||
|
|
2aa801a392 | ||
|
|
b838344526 | ||
|
|
233a3df222 | ||
|
|
da6661b1ea | ||
|
|
c6e120fc51 | ||
|
|
c416a1254d | ||
|
|
2aa4f6ddda | ||
|
|
129597023d | ||
|
|
02aed86b7e | ||
|
|
e44f4b5823 |
22
.travis.yml
@@ -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:
|
||||
- '.+'
|
||||
|
||||
10
README.md
@@ -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
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
1104
app/src/main/java/org/schabi/newpipe/player/AbstractPlayer.java
Normal 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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
Before Width: | Height: | Size: 347 B After Width: | Height: | Size: 347 B |
|
Before Width: | Height: | Size: 221 B |
BIN
app/src/main/res/drawable-hdpi/ic_fullscreen_exit_white.png
Normal file
|
After Width: | Height: | Size: 105 B |
BIN
app/src/main/res/drawable-hdpi/ic_fullscreen_white.png
Normal file
|
After Width: | Height: | Size: 107 B |
|
Before Width: | Height: | Size: 92 B After Width: | Height: | Size: 92 B |
|
Before Width: | Height: | Size: 105 B |
|
Before Width: | Height: | Size: 283 B After Width: | Height: | Size: 283 B |
BIN
app/src/main/res/drawable-hdpi/ic_repeat_white.png
Normal file
|
After Width: | Height: | Size: 234 B |
BIN
app/src/main/res/drawable-hdpi/ic_screen_rotation_white.png
Normal file
|
After Width: | Height: | Size: 976 B |
|
Before Width: | Height: | Size: 858 B |
|
Before Width: | Height: | Size: 257 B After Width: | Height: | Size: 257 B |
|
Before Width: | Height: | Size: 175 B |
BIN
app/src/main/res/drawable-mdpi/ic_fullscreen_exit_white.png
Normal file
|
After Width: | Height: | Size: 101 B |
BIN
app/src/main/res/drawable-mdpi/ic_fullscreen_white.png
Normal file
|
After Width: | Height: | Size: 101 B |
|
Before Width: | Height: | Size: 90 B After Width: | Height: | Size: 90 B |
|
Before Width: | Height: | Size: 83 B |
|
Before Width: | Height: | Size: 220 B After Width: | Height: | Size: 220 B |
BIN
app/src/main/res/drawable-mdpi/ic_repeat_white.png
Normal file
|
After Width: | Height: | Size: 185 B |
BIN
app/src/main/res/drawable-mdpi/ic_screen_rotation_white.png
Normal file
|
After Width: | Height: | Size: 673 B |
|
Before Width: | Height: | Size: 639 B |
|
Before Width: | Height: | Size: 436 B After Width: | Height: | Size: 436 B |
BIN
app/src/main/res/drawable-xhdpi/ic_fullscreen_exit_white.png
Normal file
|
After Width: | Height: | Size: 106 B |
BIN
app/src/main/res/drawable-xhdpi/ic_fullscreen_white.png
Normal file
|
After Width: | Height: | Size: 109 B |
|
Before Width: | Height: | Size: 94 B After Width: | Height: | Size: 94 B |
|
Before Width: | Height: | Size: 343 B After Width: | Height: | Size: 343 B |
BIN
app/src/main/res/drawable-xhdpi/ic_repeat_white.png
Normal file
|
After Width: | Height: | Size: 258 B |
BIN
app/src/main/res/drawable-xhdpi/ic_screen_rotation_white.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_close_white.png
Normal file
|
After Width: | Height: | Size: 524 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_fullscreen_exit_white.png
Normal file
|
After Width: | Height: | Size: 123 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_fullscreen_white.png
Normal file
|
After Width: | Height: | Size: 123 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_pause_white.png
Normal file
|
After Width: | Height: | Size: 110 B |
|
Before Width: | Height: | Size: 461 B After Width: | Height: | Size: 461 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_repeat_white.png
Normal file
|
After Width: | Height: | Size: 356 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_screen_rotation_white.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_close_white.png
Normal file
|
After Width: | Height: | Size: 707 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_fullscreen_exit_white.png
Normal file
|
After Width: | Height: | Size: 125 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_fullscreen_white.png
Normal file
|
After Width: | Height: | Size: 124 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_pause_white.png
Normal file
|
After Width: | Height: | Size: 111 B |
|
Before Width: | Height: | Size: 605 B After Width: | Height: | Size: 605 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_repeat_white.png
Normal file
|
After Width: | Height: | Size: 403 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_screen_rotation_white.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
@@ -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"
|
||||
/>
|
||||
8
app/src/main/res/drawable/player_top_controls_bg.xml
Normal 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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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&fdid=org.xbmc.kore</string>
|
||||
<string name="fdroid_vlc_url">https://f-droid.org/repository/browse/?fdfilter=vlc&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>
|
||||
|
||||
@@ -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 component’s 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 won’t 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>
|
||||
|
||||
@@ -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 component’s 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 won’t 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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 component’s 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 won’t 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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 lorsqu’elle 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">L’application 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 l’option « 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">L’image ne peut pas être chargée</string>
|
||||
<string name="app_ui_crash">L’appli / l’interface utilisateur a crashé</string>
|
||||
<string name="app_ui_crash">L’appli/l’interface 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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 component’s 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 won’t 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>
|
||||
|
||||
@@ -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 component’s 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 won’t 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>
|
||||
|
||||
@@ -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 component’s 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 won’t 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>
|
||||
|
||||
@@ -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">비디오 & 오디오</string>
|
||||
<string name="settingsCategoryVideoInfoTittle">정보</string>
|
||||
<string name="settingsCategoryEtcTitle">기타</string>
|
||||
|
||||
<string name="list_thumbnail_view_description">비디오 미리보기 썸네일</string>
|
||||
<string name="detail_thumbnail_view_description">비디오 미리보기 썸네일</string>
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 component’s 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 won’t 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>
|
||||
|
||||
@@ -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 component’s 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 won’t 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>
|
||||
|
||||
@@ -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>
|
||||
|
||||