Compare commits
163 Commits
v0.11.1
...
v0.11.6-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f65f2da890 | ||
|
|
30ab58c33d | ||
|
|
87ba5a7eb6 | ||
|
|
6e8593af91 | ||
|
|
0ab1d3fc40 | ||
|
|
f22d13e695 | ||
|
|
cdde61a460 | ||
|
|
989ce126f1 | ||
|
|
28618e822e | ||
|
|
6772381afc | ||
|
|
75b45beabc | ||
|
|
56d53e9b01 | ||
|
|
3a8b04e2d1 | ||
|
|
1ce7d66fb1 | ||
|
|
7b5a9b69fe | ||
|
|
837b22ccac | ||
|
|
7146719393 | ||
|
|
71ee604c69 | ||
|
|
9945a5b813 | ||
|
|
7254387042 | ||
|
|
3a7f2a94a6 | ||
|
|
6bea4aa96b | ||
|
|
fa262bbceb | ||
|
|
3139fe0170 | ||
|
|
ef6c5de65b | ||
|
|
07799563b5 | ||
|
|
b1de4b7bd6 | ||
|
|
2b8ae9a5ea | ||
|
|
7c52d3ec5d | ||
|
|
aefaa7619e | ||
|
|
ca202290bf | ||
|
|
cbdbc4cba2 | ||
|
|
8abf904a78 | ||
|
|
ecf7969c46 | ||
|
|
a473e3d623 | ||
|
|
1f8e90858e | ||
|
|
2c2edca8fa | ||
|
|
50e86ff1ca | ||
|
|
02ecc5011a | ||
|
|
6e666a018b | ||
|
|
66fbb2ce1e | ||
|
|
b00722ec0a | ||
|
|
77b1413319 | ||
|
|
87b8d60c9d | ||
|
|
db5203e1ff | ||
|
|
ea022670c4 | ||
|
|
54b009cc49 | ||
|
|
80c3acace9 | ||
|
|
cea9428b47 | ||
|
|
f8ffbfabbe | ||
|
|
836a1e652b | ||
|
|
d8544e0b84 | ||
|
|
4817d7fddc | ||
|
|
e6a385a85e | ||
|
|
3634f68364 | ||
|
|
f17ffa94fe | ||
|
|
d52bcd46a1 | ||
|
|
7e58b0b6fe | ||
|
|
f451f1f65d | ||
|
|
4d12e71fba | ||
|
|
66fde7a212 | ||
|
|
86eccf219d | ||
|
|
677865f347 | ||
|
|
eb4b3810e9 | ||
|
|
5f26501ddf | ||
|
|
b33a72f864 | ||
|
|
675f43b968 | ||
|
|
9f117a2e59 | ||
|
|
a0844229a3 | ||
|
|
114fcc144c | ||
|
|
43372ff648 | ||
|
|
b8ebbc5404 | ||
|
|
28309f82f3 | ||
|
|
c70fa391b6 | ||
|
|
caab589dce | ||
|
|
20370054e7 | ||
| f1882cb1e1 | |||
|
|
2ed149852d | ||
|
|
ac7226a0df | ||
|
|
5705650ca8 | ||
|
|
60855ca7c5 | ||
|
|
b8d8d181f3 | ||
|
|
6309160fc6 | ||
|
|
fc0e6ed273 | ||
|
|
ada0cee656 | ||
|
|
6dde524d2c | ||
|
|
6a631e1915 | ||
|
|
bb6fa343cf | ||
|
|
7557acde6c | ||
|
|
25ed8952f9 | ||
|
|
b93d94b0bd | ||
|
|
33d75fd2fb | ||
|
|
28a9855fd2 | ||
|
|
9aad07621c | ||
|
|
384cde6eaa | ||
|
|
5ae98661ad | ||
|
|
8f4d9ceca9 | ||
|
|
83d9a1233e | ||
|
|
522a287d79 | ||
|
|
e052d4660d | ||
|
|
39b0b2f032 | ||
|
|
0223d6d200 | ||
|
|
808ce72078 | ||
|
|
3a84c47176 | ||
|
|
20c2426128 | ||
|
|
bf11d4c9fa | ||
|
|
783c0f79d7 | ||
|
|
98b94bd9c4 | ||
|
|
ff0178f965 | ||
|
|
7f88c3d0a9 | ||
|
|
11e8e38f2c | ||
|
|
50c5314eaf | ||
|
|
a7a76d4f58 | ||
|
|
4df4f68fe1 | ||
|
|
7db8d37137 | ||
|
|
b7503a7d81 | ||
|
|
9f5d4034e3 | ||
|
|
3c941f6c4b | ||
|
|
ec8fff421a | ||
|
|
a47a0b5432 | ||
|
|
96ba46f21d | ||
|
|
90f48d5817 | ||
|
|
7288dd097a | ||
|
|
0119d62a35 | ||
|
|
4d6ab73fa9 | ||
| f58c95840a | |||
|
|
ecf24f81ec | ||
|
|
dd3306a940 | ||
|
|
bbb2b98f27 | ||
|
|
bc7332780d | ||
|
|
6f3bc3ac8f | ||
|
|
cd04d869b7 | ||
|
|
909e15cbdd | ||
|
|
33fa30ab78 | ||
|
|
44933ac17a | ||
|
|
bb2af96deb | ||
|
|
1fe6da14ea | ||
|
|
b7b9653c21 | ||
|
|
8adc5918f8 | ||
|
|
0db593b1bb | ||
|
|
a0c9dbeb78 | ||
|
|
61d5546d89 | ||
|
|
f97b7c943b | ||
|
|
97549b633b | ||
|
|
1949e4a9d4 | ||
|
|
ecb5f7a5ba | ||
|
|
6021f72cf0 | ||
|
|
c00ef74f96 | ||
|
|
962e070150 | ||
|
|
221cbf5e07 | ||
|
|
91ff301d53 | ||
|
|
f5f8371865 | ||
|
|
1191455d37 | ||
|
|
011e151c91 | ||
|
|
54aa40eac1 | ||
|
|
621a1909ec | ||
|
|
c485e7e167 | ||
|
|
71a8361580 | ||
|
|
3d1cc348c8 | ||
|
|
25db3c2940 | ||
|
|
442290d7f0 | ||
|
|
a6eb871f5e | ||
|
|
b500c3f526 |
1
.gitignore
vendored
@@ -10,3 +10,4 @@
|
||||
gradle.properties
|
||||
*~
|
||||
.weblate
|
||||
*.class
|
||||
|
||||
@@ -10,6 +10,8 @@ android:
|
||||
# The SDK version used to compile NewPipe
|
||||
- android-27
|
||||
|
||||
before_install:
|
||||
- yes | sdkmanager "platforms;android-27"
|
||||
script: ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleDebug lintDebug testDebugUnitTest
|
||||
|
||||
licenses:
|
||||
|
||||
170
CheckTranslations.java
Normal file
@@ -0,0 +1,170 @@
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.regex.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public final class CheckTranslations {
|
||||
|
||||
private static boolean debug = false;
|
||||
private static boolean plurals = false;
|
||||
private static boolean empty = false;
|
||||
private static boolean remove = false;
|
||||
private static int checks = 0;
|
||||
private static int matches = 0;
|
||||
private static int changes = 0;
|
||||
private static Pattern p, pb, pe, e, o;
|
||||
|
||||
/**
|
||||
* Search translated strings.xml files for empty item / plural tags
|
||||
* and remove them.
|
||||
* @param args directories which contain string.xml files (in any subdirectory)
|
||||
* -e option to find all empty string tags
|
||||
* -p option to find all empty plurals and item tags
|
||||
* -r option to remove all occurrences from the files
|
||||
* -d option to see more details
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
if (args.length < 1 || (args[0].equals("-d") && args.length < 2)) {
|
||||
System.out.println("Not enough arguments");
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
switch (args[i]) {
|
||||
case "-d":
|
||||
debug = true;
|
||||
break;
|
||||
case "-p":
|
||||
plurals = true;
|
||||
break;
|
||||
case "-e":
|
||||
empty = true;
|
||||
break;
|
||||
case "-r":
|
||||
remove = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!plurals && !empty) {
|
||||
plurals = true;
|
||||
empty = true;
|
||||
}
|
||||
|
||||
p = Pattern.compile("(<item quantity=\")(zero|one|two|three|few|many|other)(\"></item>|\"/>)");
|
||||
pb = Pattern.compile("(<plurals[\\sa-zA-Z=\"]*>)");
|
||||
pe = Pattern.compile("(</plurals>)");
|
||||
e = Pattern.compile("(<string[\\sa-z_\\\"=]*)((><\\/string>|\\/>){1})");
|
||||
o = Pattern.compile("(<item quantity=\"other\">)[^</>]*(<\\/item>)");
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
if (!args[i].equals("-d") && !args[i].equals("-p") && !args[i].equals("-e") && !args[i].equals("-r")) {
|
||||
File f = new File(args[i]);
|
||||
if (f.exists() && !f.isDirectory()) {
|
||||
checkFile(f);
|
||||
} else if (f.isDirectory()) {
|
||||
checkFiles(f.listFiles());
|
||||
} else {
|
||||
System.out.println("'" + args[i] + "' does not exist!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(checks + " files were checked.");
|
||||
System.out.println(matches + " corrupt lines detected.");
|
||||
if (remove) {
|
||||
System.out.println(matches + " corrupt lines removed and " + changes + " lines fixed.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void checkFiles(File[] f) {
|
||||
for (int i = 0; i < f.length; i++) {
|
||||
if (f[i].exists() && !f[i].isDirectory()) {
|
||||
if (f[i].toString().contains("strings.xml")) {
|
||||
checkFile(f[i]);
|
||||
}
|
||||
} else if (f[i].isDirectory()) {
|
||||
checkFiles(f[i].listFiles());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkFile(File f) {
|
||||
// Do not check our original English strings to cause no unwanted changes
|
||||
// Btw. there should not be empty plural/item tags
|
||||
if (f.toString().contains("values/strings.xml")) {
|
||||
return;
|
||||
}
|
||||
if (debug) System.out.println("Checking " + f.toString());
|
||||
checks++;
|
||||
|
||||
|
||||
List<String> lines = new ArrayList<String>();
|
||||
boolean checkFailed = false;
|
||||
boolean otherDetected = false;
|
||||
boolean inPlurals = false;
|
||||
try (BufferedReader br = new BufferedReader(new FileReader(f))) {
|
||||
String line;
|
||||
int ln = 0;
|
||||
while ((line = br.readLine()) != null) {
|
||||
ln++;
|
||||
if (plurals && p.matcher(line).find()) {
|
||||
matches++;
|
||||
if (debug) System.out.println(" Line " + ln + " was " + ((remove) ? "removed" : "detected") + ": '" + line + "'");
|
||||
checkFailed = true;
|
||||
} else if (empty && e.matcher(line).find()) {
|
||||
matches++;
|
||||
checkFailed = true;
|
||||
if (debug) System.out.println(" Line " + ln + " was " + ((remove) ? "removed" : "detected") + ": '" + line + "'");
|
||||
} else {
|
||||
if (remove) lines.add(line);
|
||||
}
|
||||
}
|
||||
br.close();
|
||||
int pluralsLine = 0;
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
if (o.matcher(lines.get(i)).find()) {
|
||||
otherDetected = true;
|
||||
}
|
||||
if (plurals && pb.matcher(lines.get(i)).find()) {
|
||||
inPlurals = true;
|
||||
pluralsLine = i;
|
||||
} else if (plurals && pe.matcher(lines.get(i)).find()) {
|
||||
inPlurals = false;
|
||||
if (!otherDetected) {
|
||||
boolean b = false;
|
||||
check: for(int j = pluralsLine; j < i; j++) {
|
||||
if (lines.get(j).contains("many")) {
|
||||
b = true;
|
||||
pluralsLine = j;
|
||||
break check;
|
||||
}
|
||||
}
|
||||
if (remove && b) {
|
||||
if (debug) System.out.println(" Line " + (pluralsLine + 1) + " was " + ((remove) ? "changed" : "detected") + ": '" + lines.get(pluralsLine) + "'");
|
||||
lines.set(pluralsLine, lines.get(pluralsLine).replace("many", "other"));
|
||||
changes++;
|
||||
checkFailed = true;
|
||||
} else if (debug) {
|
||||
if (debug) System.out.println(" WARNING: Line " + (i + 1) + " - No <item quantity=\"other\"> found!");
|
||||
}
|
||||
}
|
||||
otherDetected = false;
|
||||
}
|
||||
|
||||
}
|
||||
if (remove && checkFailed) {
|
||||
Files.write(f.toPath(), lines, Charset.forName("UTF-8"));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.out.println(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
</p>
|
||||
<hr />
|
||||
<p align="center"><a href="#screenshots">Screenshots</a> • <a href="#description">Description</a> • <a href="#features">Features</a> • <a href="#contribution">Contribution</a> • <a href="#donate">Donate</a> • <a href="#license">License</a></p>
|
||||
<p align="center"><a href="https://newpipe.schabi.org">Website</a> • <a href="https://newpipe.schabi.org/blog/">Blog</a> • <a href="https://newpipe.schabi.org/press/">Press</a></p>
|
||||
<hr />
|
||||
WARNING: PUTTING NEWPIPE OR ANY FORK OF IT INTO GOOGLE PLAYSTORE VIOLATES THEIR TERMS OF CONDITIONS.
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ android {
|
||||
applicationId "org.schabi.newpipe"
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 27
|
||||
versionCode 42
|
||||
versionName "0.11.1"
|
||||
versionCode 47
|
||||
versionName "0.11.6"
|
||||
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
@@ -42,26 +42,29 @@ android {
|
||||
abortOnError false
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
supportLibVersion = '27.0.2'
|
||||
}
|
||||
dependencies {
|
||||
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2') {
|
||||
exclude module: 'support-annotations'
|
||||
}
|
||||
|
||||
implementation 'com.github.TeamNewPipe:NewPipeExtractor:17ce9f537e8df'
|
||||
implementation 'com.github.TeamNewPipe:NewPipeExtractor:7fd21ec08581d'
|
||||
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation 'org.mockito:mockito-core:1.10.19'
|
||||
|
||||
implementation 'com.android.support:appcompat-v7:27.0.1'
|
||||
implementation 'com.android.support:support-v4:27.0.1'
|
||||
implementation 'com.android.support:design:27.0.1'
|
||||
implementation 'com.android.support:recyclerview-v7:27.0.1'
|
||||
implementation 'com.android.support:preference-v14:27.0.1'
|
||||
implementation "com.android.support:appcompat-v7:$supportLibVersion"
|
||||
implementation "com.android.support:support-v4:$supportLibVersion"
|
||||
implementation "com.android.support:design:$supportLibVersion"
|
||||
implementation "com.android.support:recyclerview-v7:$supportLibVersion"
|
||||
implementation "com.android.support:preference-v14:$supportLibVersion"
|
||||
|
||||
implementation 'com.google.code.gson:gson:2.8.2'
|
||||
implementation 'ch.acra:acra:4.9.2'
|
||||
@@ -76,7 +79,7 @@ dependencies {
|
||||
debugImplementation 'com.facebook.stetho:stetho-urlconnection:1.5.0'
|
||||
debugImplementation 'com.android.support:multidex:1.0.2'
|
||||
|
||||
implementation 'io.reactivex.rxjava2:rxjava:2.1.6'
|
||||
implementation 'io.reactivex.rxjava2:rxjava:2.1.7'
|
||||
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
|
||||
implementation 'com.jakewharton.rxbinding2:rxbinding:2.0.0'
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:logo="@mipmap/ic_launcher"
|
||||
android:theme="@style/DarkTheme"
|
||||
android:theme="@style/OpeningTheme"
|
||||
tools:ignore="AllowBackup">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
@@ -31,7 +31,7 @@
|
||||
<activity
|
||||
android:name=".player.old.PlayVideoActivity"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||
android:theme="@style/VideoPlayerTheme"
|
||||
android:theme="@style/OldVideoPlayerTheme"
|
||||
tools:ignore="UnusedAttribute"/>
|
||||
|
||||
<service
|
||||
@@ -56,8 +56,7 @@
|
||||
android:name=".player.MainVideoPlayer"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/PlayerTheme"/>
|
||||
android:launchMode="singleTask"/>
|
||||
|
||||
<activity
|
||||
android:name=".settings.SettingsActivity"
|
||||
@@ -124,7 +123,7 @@
|
||||
<activity
|
||||
android:name=".RouterActivity"
|
||||
android:taskAffinity=""
|
||||
android:theme="@android:style/Theme.NoDisplay">
|
||||
android:theme="@style/RouterActivityThemeDark">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>
|
||||
@@ -175,17 +174,21 @@
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
|
||||
<data android:mimeType="text/plain"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".RouterPlayerActivity$FetcherService"
|
||||
android:exported="false"/>
|
||||
|
||||
<activity
|
||||
android:name=".RouterPopupActivity"
|
||||
android:label="@string/popup_mode_share_menu_title"
|
||||
android:name=".RouterPlayerActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:label="@string/preferred_player_share_menu_title"
|
||||
android:taskAffinity=""
|
||||
android:theme="@style/PopupPermissionsTheme">
|
||||
android:theme="@style/RouterActivityThemeDark">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>
|
||||
@@ -204,6 +207,11 @@
|
||||
<data android:pathPrefix="/embed/"/>
|
||||
<data android:pathPrefix="/watch"/>
|
||||
<data android:pathPrefix="/attribution_link"/>
|
||||
<!-- channel prefix -->
|
||||
<data android:pathPrefix="/channel/"/>
|
||||
<data android:pathPrefix="/user/"/>
|
||||
<!-- playlist prefix -->
|
||||
<data android:pathPrefix="/playlist"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package org.schabi.newpipe;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.Application;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -116,7 +119,6 @@ public class App extends Application {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void initACRA() {
|
||||
try {
|
||||
final ACRAConfiguration acraConfig = new ConfigurationBuilder(this)
|
||||
@@ -149,4 +151,5 @@ public class App extends Application {
|
||||
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
mNotificationManager.createNotificationChannel(mChannel);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -77,17 +77,6 @@ public abstract class BaseFragment extends Fragment {
|
||||
protected void initListeners() {
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected final int resolveResourceIdFromAttr(@AttrRes int attr) {
|
||||
TypedArray a = activity.getTheme().obtainStyledAttributes(new int[]{attr});
|
||||
int attributeResourceId = a.getResourceId(0, 0);
|
||||
a.recycle();
|
||||
return attributeResourceId;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// DisplayImageOptions default configurations
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@@ -28,8 +28,12 @@ import android.os.Looper;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.NavigationView;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.view.GravityCompat;
|
||||
import android.support.v4.widget.DrawerLayout;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.ActionBarDrawerToggle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
@@ -50,11 +54,13 @@ import org.schabi.newpipe.extractor.stream.AudioStream;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||
import org.schabi.newpipe.fragments.BackPressable;
|
||||
import org.schabi.newpipe.fragments.MainFragment;
|
||||
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
|
||||
import org.schabi.newpipe.fragments.list.search.SearchFragment;
|
||||
import org.schabi.newpipe.history.HistoryListener;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.ServiceHelper;
|
||||
import org.schabi.newpipe.util.StateSaver;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
|
||||
@@ -67,8 +73,10 @@ import io.reactivex.subjects.PublishSubject;
|
||||
|
||||
public class MainActivity extends AppCompatActivity implements HistoryListener {
|
||||
private static final String TAG = "MainActivity";
|
||||
public static final boolean DEBUG = false;
|
||||
public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
private ActionBarDrawerToggle toggle = null;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Activity's LifeCycle
|
||||
@@ -77,7 +85,10 @@ public class MainActivity extends AppCompatActivity implements HistoryListener {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
|
||||
ThemeHelper.setTheme(this);
|
||||
|
||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
ThemeHelper.setTheme(this, ServiceHelper.getSelectedServiceId(this));
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
@@ -85,13 +96,53 @@ public class MainActivity extends AppCompatActivity implements HistoryListener {
|
||||
initFragments();
|
||||
}
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
||||
setSupportActionBar(findViewById(R.id.toolbar));
|
||||
setupDrawer();
|
||||
initHistory();
|
||||
}
|
||||
|
||||
private void setupDrawer() {
|
||||
final Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
final DrawerLayout drawer = findViewById(R.id.drawer_layout);
|
||||
final NavigationView drawerItems = findViewById(R.id.navigation);
|
||||
|
||||
//drawerItems.setItemIconTintList(null); // Set null to use the original icon
|
||||
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(true);
|
||||
|
||||
if (!BuildConfig.BUILD_TYPE.equals("release")) {
|
||||
toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.drawer_open, R.string.drawer_close);
|
||||
toggle.syncState();
|
||||
drawer.addDrawerListener(toggle);
|
||||
drawer.addDrawerListener(new DrawerLayout.SimpleDrawerListener() {
|
||||
private int lastService;
|
||||
|
||||
@Override
|
||||
public void onDrawerOpened(View drawerView) {
|
||||
lastService = ServiceHelper.getSelectedServiceId(MainActivity.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawerClosed(View drawerView) {
|
||||
if (lastService != ServiceHelper.getSelectedServiceId(MainActivity.this)) {
|
||||
new Handler(Looper.getMainLooper()).post(MainActivity.this::recreate);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
drawerItems.setNavigationItemSelectedListener(item -> {
|
||||
if (item.getGroupId() == R.id.menu_services_group) {
|
||||
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(false);
|
||||
ServiceHelper.setSelectedServiceId(this, item.getTitle().toString());
|
||||
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(true);
|
||||
}
|
||||
drawer.closeDrawers();
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
@@ -112,20 +163,14 @@ public class MainActivity extends AppCompatActivity implements HistoryListener {
|
||||
sharedPreferences.edit().putBoolean(Constants.KEY_THEME_CHANGE, false).apply();
|
||||
// https://stackoverflow.com/questions/10844112/runtimeexception-performing-pause-of-activity-that-is-not-resumed
|
||||
// Briefly, let the activity resume properly posting the recreate call to end of the message queue
|
||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
MainActivity.this.recreate();
|
||||
}
|
||||
});
|
||||
new Handler(Looper.getMainLooper()).post(MainActivity.this::recreate);
|
||||
}
|
||||
|
||||
if(sharedPreferences.getBoolean(Constants.KEY_MAIN_PAGE_CHANGE, false)) {
|
||||
if (sharedPreferences.getBoolean(Constants.KEY_MAIN_PAGE_CHANGE, false)) {
|
||||
if (DEBUG) Log.d(TAG, "main page has changed, recreating main fragment...");
|
||||
sharedPreferences.edit().putBoolean(Constants.KEY_MAIN_PAGE_CHANGE, false).apply();
|
||||
NavigationHelper.openMainActivity(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -159,6 +204,38 @@ public class MainActivity extends AppCompatActivity implements HistoryListener {
|
||||
} else super.onBackPressed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement the following diagram behavior for the up button:
|
||||
* <pre>
|
||||
* +---------------+
|
||||
* | Main Screen +----+
|
||||
* +-------+-------+ |
|
||||
* | |
|
||||
* ▲ Up | Search Button
|
||||
* | |
|
||||
* +----+-----+ |
|
||||
* +------------+ Search |◄-----+
|
||||
* | +----+-----+
|
||||
* | Open |
|
||||
* | something ▲ Up
|
||||
* | |
|
||||
* | +------------+-------------+
|
||||
* | | |
|
||||
* | | Video <-> Channel |
|
||||
* +---►| Channel <-> Playlist |
|
||||
* | Video <-> .... |
|
||||
* | |
|
||||
* +--------------------------+
|
||||
* </pre>
|
||||
*/
|
||||
private void onHomeButtonPressed() {
|
||||
// If search fragment wasn't found in the backstack...
|
||||
if (!NavigationHelper.tryGotoSearchFragment(getSupportFragmentManager())) {
|
||||
// ...go to the main fragment
|
||||
NavigationHelper.gotoMainFragment(getSupportFragmentManager());
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Menu
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
@@ -184,6 +261,9 @@ public class MainActivity extends AppCompatActivity implements HistoryListener {
|
||||
if (actionBar != null) {
|
||||
actionBar.setDisplayHomeAsUpEnabled(false);
|
||||
}
|
||||
|
||||
updateDrawerNavigation();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -194,7 +274,7 @@ public class MainActivity extends AppCompatActivity implements HistoryListener {
|
||||
|
||||
switch (id) {
|
||||
case android.R.id.home:
|
||||
NavigationHelper.gotoMainFragment(getSupportFragmentManager());
|
||||
onHomeButtonPressed();
|
||||
return true;
|
||||
case R.id.action_settings:
|
||||
NavigationHelper.openSettings(this);
|
||||
@@ -228,6 +308,27 @@ public class MainActivity extends AppCompatActivity implements HistoryListener {
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void updateDrawerNavigation() {
|
||||
if (getSupportActionBar() == null) return;
|
||||
|
||||
final Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
final DrawerLayout drawer = findViewById(R.id.drawer_layout);
|
||||
|
||||
final Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
|
||||
if (fragment instanceof MainFragment) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
|
||||
if (toggle != null) {
|
||||
toggle.syncState();
|
||||
toolbar.setNavigationOnClickListener(v -> drawer.openDrawer(GravityCompat.START));
|
||||
drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNDEFINED);
|
||||
}
|
||||
} else {
|
||||
drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
toolbar.setNavigationOnClickListener(v -> onHomeButtonPressed());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleIntent(Intent intent) {
|
||||
if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]");
|
||||
|
||||
|
||||
@@ -3,14 +3,25 @@ package org.schabi.newpipe;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
import icepick.Icepick;
|
||||
import icepick.State;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
/*
|
||||
* Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org>
|
||||
* RouterActivity.java is part of NewPipe.
|
||||
*
|
||||
@@ -34,23 +45,71 @@ import java.util.HashSet;
|
||||
*/
|
||||
public class RouterActivity extends AppCompatActivity {
|
||||
|
||||
@State
|
||||
protected String currentUrl;
|
||||
protected CompositeDisposable disposables = new CompositeDisposable();
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
Icepick.restoreInstanceState(this, savedInstanceState);
|
||||
|
||||
String videoUrl = getUrl(getIntent());
|
||||
handleUrl(videoUrl);
|
||||
if (TextUtils.isEmpty(currentUrl)) {
|
||||
currentUrl = getUrl(getIntent());
|
||||
|
||||
if (TextUtils.isEmpty(currentUrl)) {
|
||||
Toast.makeText(this, R.string.invalid_url_toast, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
Icepick.saveInstanceState(this, outState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
handleUrl(currentUrl);
|
||||
}
|
||||
|
||||
protected void handleUrl(String url) {
|
||||
boolean success = NavigationHelper.openByLink(this, url);
|
||||
if (!success) {
|
||||
disposables.add(Observable
|
||||
.fromCallable(() -> NavigationHelper.getIntentByLink(this, url))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(intent -> {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
startActivity(intent);
|
||||
|
||||
finish();
|
||||
}, this::handleError)
|
||||
);
|
||||
}
|
||||
|
||||
protected void handleError(Throwable error) {
|
||||
error.printStackTrace();
|
||||
|
||||
if (error instanceof ExtractionException) {
|
||||
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
ExtractorHelper.handleGeneralException(this, -1, null, error, UserAction.SOMETHING_ELSE, null);
|
||||
}
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
disposables.clear();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
@@ -71,7 +130,8 @@ public class RouterActivity extends AppCompatActivity {
|
||||
} else if (intent.getStringExtra(Intent.EXTRA_TEXT) != null) {
|
||||
//this means that vidoe was called through share menu
|
||||
String extraText = intent.getStringExtra(Intent.EXTRA_TEXT);
|
||||
videoUrl = getUris(extraText)[0];
|
||||
final String[] uris = getUris(extraText);
|
||||
videoUrl = uris.length > 0 ? uris[0] : null;
|
||||
}
|
||||
|
||||
return videoUrl;
|
||||
|
||||
413
app/src/main/java/org/schabi/newpipe/RouterPlayerActivity.java
Normal file
@@ -0,0 +1,413 @@
|
||||
package org.schabi.newpipe;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.os.PersistableBundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.text.TextUtils;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.RadioGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.schabi.newpipe.extractor.Info;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.ServiceList;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.StreamingService.LinkType;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.player.helper.PlayerHelper;
|
||||
import org.schabi.newpipe.playlist.ChannelPlayQueue;
|
||||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.playlist.PlaylistPlayQueue;
|
||||
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.PermissionHelper;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
|
||||
import icepick.State;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.functions.Consumer;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
import static org.schabi.newpipe.util.ThemeHelper.resolveResourceIdFromAttr;
|
||||
|
||||
/**
|
||||
* Get the url from the intent and open it in the chosen preferred player
|
||||
*/
|
||||
public class RouterPlayerActivity extends RouterActivity {
|
||||
|
||||
@State
|
||||
protected int currentServiceId = -1;
|
||||
private StreamingService currentService;
|
||||
@State
|
||||
protected LinkType currentLinkType;
|
||||
@State
|
||||
protected int selectedRadioPosition = -1;
|
||||
protected int selectedPreviously = -1;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
|
||||
super.onCreate(savedInstanceState, persistentState);
|
||||
setTheme(ThemeHelper.isLightThemeSelected(this) ? R.style.RouterActivityThemeLight : R.style.RouterActivityThemeDark);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleUrl(String url) {
|
||||
disposables.add(Observable
|
||||
.fromCallable(() -> {
|
||||
if (currentServiceId == -1) {
|
||||
currentService = NewPipe.getServiceByUrl(url);
|
||||
currentServiceId = currentService.getServiceId();
|
||||
currentLinkType = currentService.getLinkTypeByUrl(url);
|
||||
currentUrl = NavigationHelper.getCleanUrl(currentService, url, currentLinkType);
|
||||
} else {
|
||||
currentService = NewPipe.getService(currentServiceId);
|
||||
}
|
||||
|
||||
return currentLinkType != LinkType.NONE;
|
||||
})
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(result -> {
|
||||
if (result) {
|
||||
onSuccess();
|
||||
} else {
|
||||
onError();
|
||||
}
|
||||
}, this::handleError));
|
||||
}
|
||||
|
||||
protected void onError() {
|
||||
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
}
|
||||
|
||||
protected void onSuccess() {
|
||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
boolean isExtVideoEnabled = preferences.getBoolean(getString(R.string.use_external_video_player_key), false);
|
||||
boolean isExtAudioEnabled = preferences.getBoolean(getString(R.string.use_external_audio_player_key), false);
|
||||
|
||||
if ((isExtAudioEnabled || isExtVideoEnabled) && currentLinkType != LinkType.STREAM) {
|
||||
Toast.makeText(this, R.string.external_player_unsupported_link_type, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Add some sort of "capabilities" field to services (audio only, video and audio, etc.)
|
||||
if (currentService == ServiceList.SoundCloud.getService()) {
|
||||
handleChoice(getString(R.string.background_player_key));
|
||||
return;
|
||||
}
|
||||
|
||||
final String playerChoiceKey = preferences.getString(getString(R.string.preferred_player_key), getString(R.string.preferred_player_default));
|
||||
final String alwaysAskKey = getString(R.string.always_ask_player_key);
|
||||
|
||||
if (playerChoiceKey.equals(alwaysAskKey)) {
|
||||
showDialog();
|
||||
} else {
|
||||
handleChoice(playerChoiceKey);
|
||||
}
|
||||
}
|
||||
|
||||
private void showDialog() {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
final ContextThemeWrapper themeWrapper = new ContextThemeWrapper(this,
|
||||
ThemeHelper.isLightThemeSelected(this) ? R.style.LightTheme : R.style.DarkTheme);
|
||||
|
||||
LayoutInflater inflater = LayoutInflater.from(themeWrapper);
|
||||
final LinearLayout rootLayout = (LinearLayout) inflater.inflate(R.layout.preferred_player_dialog_view, null, false);
|
||||
final RadioGroup radioGroup = rootLayout.findViewById(android.R.id.list);
|
||||
|
||||
final AdapterChoiceItem[] choices = {
|
||||
new AdapterChoiceItem(getString(R.string.video_player_key), getString(R.string.video_player),
|
||||
resolveResourceIdFromAttr(themeWrapper, R.attr.play)),
|
||||
new AdapterChoiceItem(getString(R.string.background_player_key), getString(R.string.background_player),
|
||||
resolveResourceIdFromAttr(themeWrapper, R.attr.audio)),
|
||||
new AdapterChoiceItem(getString(R.string.popup_player_key), getString(R.string.popup_player),
|
||||
resolveResourceIdFromAttr(themeWrapper, R.attr.popup))
|
||||
};
|
||||
|
||||
final DialogInterface.OnClickListener dialogButtonsClickListener = (dialog, which) -> {
|
||||
final int indexOfChild = radioGroup.indexOfChild(radioGroup.findViewById(radioGroup.getCheckedRadioButtonId()));
|
||||
final AdapterChoiceItem choice = choices[indexOfChild];
|
||||
|
||||
handleChoice(choice.key);
|
||||
|
||||
if (which == DialogInterface.BUTTON_POSITIVE) {
|
||||
preferences.edit().putString(getString(R.string.preferred_player_key), choice.key).apply();
|
||||
}
|
||||
};
|
||||
|
||||
final AlertDialog alertDialog = new AlertDialog.Builder(themeWrapper)
|
||||
.setTitle(R.string.preferred_player_share_menu_title)
|
||||
.setView(radioGroup)
|
||||
.setCancelable(true)
|
||||
.setNegativeButton(R.string.just_once, dialogButtonsClickListener)
|
||||
.setPositiveButton(R.string.always, dialogButtonsClickListener)
|
||||
.setOnDismissListener((dialog) -> finish())
|
||||
.create();
|
||||
|
||||
alertDialog.setOnShowListener(dialog -> {
|
||||
setDialogButtonsState(alertDialog, radioGroup.getCheckedRadioButtonId() != -1);
|
||||
});
|
||||
|
||||
radioGroup.setOnCheckedChangeListener((group, checkedId) -> setDialogButtonsState(alertDialog, true));
|
||||
final View.OnClickListener radioButtonsClickListener = v -> {
|
||||
final int indexOfChild = radioGroup.indexOfChild(v);
|
||||
if (indexOfChild == -1) return;
|
||||
|
||||
selectedPreviously = selectedRadioPosition;
|
||||
selectedRadioPosition = indexOfChild;
|
||||
|
||||
if (selectedPreviously == selectedRadioPosition) {
|
||||
handleChoice(choices[selectedRadioPosition].key);
|
||||
}
|
||||
};
|
||||
|
||||
int id = 12345;
|
||||
for (AdapterChoiceItem item : choices) {
|
||||
final RadioButton radioButton = (RadioButton) inflater.inflate(R.layout.list_radio_icon_item, null);
|
||||
radioButton.setText(item.description);
|
||||
radioButton.setCompoundDrawablesWithIntrinsicBounds(item.icon, 0, 0, 0);
|
||||
radioButton.setChecked(false);
|
||||
radioButton.setId(id++);
|
||||
radioButton.setLayoutParams(new RadioGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
radioButton.setOnClickListener(radioButtonsClickListener);
|
||||
radioGroup.addView(radioButton);
|
||||
}
|
||||
|
||||
if (selectedRadioPosition == -1) {
|
||||
final String lastSelectedPlayer = preferences.getString(getString(R.string.preferred_player_last_selected_key), null);
|
||||
if (!TextUtils.isEmpty(lastSelectedPlayer)) {
|
||||
for (int i = 0; i < choices.length; i++) {
|
||||
AdapterChoiceItem c = choices[i];
|
||||
if (lastSelectedPlayer.equals(c.key)) {
|
||||
selectedRadioPosition = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectedRadioPosition = Math.min(Math.max(-1, selectedRadioPosition), choices.length - 1);
|
||||
if (selectedRadioPosition != -1) {
|
||||
((RadioButton) radioGroup.getChildAt(selectedRadioPosition)).setChecked(true);
|
||||
}
|
||||
selectedPreviously = selectedRadioPosition;
|
||||
|
||||
alertDialog.show();
|
||||
}
|
||||
|
||||
private void setDialogButtonsState(AlertDialog dialog, boolean state) {
|
||||
final Button negativeButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
|
||||
final Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
|
||||
if (negativeButton == null || positiveButton == null) return;
|
||||
|
||||
negativeButton.setEnabled(state);
|
||||
positiveButton.setEnabled(state);
|
||||
}
|
||||
|
||||
private void handleChoice(final String playerChoiceKey) {
|
||||
if (Arrays.asList(getResources().getStringArray(R.array.preferred_player_values_list)).contains(playerChoiceKey)) {
|
||||
PreferenceManager.getDefaultSharedPreferences(this).edit()
|
||||
.putString(getString(R.string.preferred_player_last_selected_key), playerChoiceKey).apply();
|
||||
}
|
||||
|
||||
if (playerChoiceKey.equals(getString(R.string.popup_player_key)) && !PermissionHelper.isPopupEnabled(this)) {
|
||||
PermissionHelper.showPopupEnablementToast(this);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
final Intent intent = new Intent(this, FetcherService.class);
|
||||
intent.putExtra(FetcherService.KEY_CHOICE, new Choice(currentService.getServiceId(), currentLinkType, currentUrl, playerChoiceKey));
|
||||
startService(intent);
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
private static class AdapterChoiceItem {
|
||||
final String description, key;
|
||||
@DrawableRes
|
||||
final int icon;
|
||||
|
||||
AdapterChoiceItem(String key, String description, int icon) {
|
||||
this.description = description;
|
||||
this.key = key;
|
||||
this.icon = icon;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Choice implements Serializable {
|
||||
final int serviceId;
|
||||
final String url, playerChoice;
|
||||
final LinkType linkType;
|
||||
|
||||
Choice(int serviceId, LinkType linkType, String url, String playerChoice) {
|
||||
this.serviceId = serviceId;
|
||||
this.linkType = linkType;
|
||||
this.url = url;
|
||||
this.playerChoice = playerChoice;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return serviceId + ":" + url + " > " + linkType + " ::: " + playerChoice;
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Service Fetcher
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public static class FetcherService extends IntentService {
|
||||
|
||||
private static final int ID = 456;
|
||||
public static final String KEY_CHOICE = "key_choice";
|
||||
private Disposable fetcher;
|
||||
|
||||
public FetcherService() {
|
||||
super(FetcherService.class.getSimpleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
startForeground(ID, createNotification().build());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(@Nullable Intent intent) {
|
||||
if (intent == null) return;
|
||||
|
||||
final Serializable serializable = intent.getSerializableExtra(KEY_CHOICE);
|
||||
if (!(serializable instanceof Choice)) return;
|
||||
Choice playerChoice = (Choice) serializable;
|
||||
handleChoice(playerChoice);
|
||||
}
|
||||
|
||||
public void handleChoice(Choice choice) {
|
||||
Single<? extends Info> single = null;
|
||||
UserAction userAction = UserAction.SOMETHING_ELSE;
|
||||
|
||||
switch (choice.linkType) {
|
||||
case STREAM:
|
||||
single = ExtractorHelper.getStreamInfo(choice.serviceId, choice.url, false);
|
||||
userAction = UserAction.REQUESTED_STREAM;
|
||||
break;
|
||||
case CHANNEL:
|
||||
single = ExtractorHelper.getChannelInfo(choice.serviceId, choice.url, false);
|
||||
userAction = UserAction.REQUESTED_CHANNEL;
|
||||
break;
|
||||
case PLAYLIST:
|
||||
single = ExtractorHelper.getPlaylistInfo(choice.serviceId, choice.url, false);
|
||||
userAction = UserAction.REQUESTED_PLAYLIST;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (single != null) {
|
||||
final UserAction finalUserAction = userAction;
|
||||
final Consumer<Info> resultHandler = getResultHandler(choice);
|
||||
fetcher = single
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(info -> {
|
||||
resultHandler.accept(info);
|
||||
if (fetcher != null) fetcher.dispose();
|
||||
}, throwable -> ExtractorHelper.handleGeneralException(this,
|
||||
choice.serviceId, choice.url, throwable, finalUserAction, ", opened with " + choice.playerChoice));
|
||||
}
|
||||
}
|
||||
|
||||
public Consumer<Info> getResultHandler(Choice choice) {
|
||||
return info -> {
|
||||
final String videoPlayerKey = getString(R.string.video_player_key);
|
||||
final String backgroundPlayerKey = getString(R.string.background_player_key);
|
||||
final String popupPlayerKey = getString(R.string.popup_player_key);
|
||||
|
||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
boolean isExtVideoEnabled = preferences.getBoolean(getString(R.string.use_external_video_player_key), false);
|
||||
boolean isExtAudioEnabled = preferences.getBoolean(getString(R.string.use_external_audio_player_key), false);
|
||||
boolean useOldVideoPlayer = PlayerHelper.isUsingOldPlayer(this);
|
||||
|
||||
PlayQueue playQueue;
|
||||
String playerChoice = choice.playerChoice;
|
||||
|
||||
if (info instanceof StreamInfo) {
|
||||
if (playerChoice.equals(backgroundPlayerKey) && isExtAudioEnabled) {
|
||||
NavigationHelper.playOnExternalAudioPlayer(this, (StreamInfo) info);
|
||||
|
||||
} else if (playerChoice.equals(videoPlayerKey) && isExtVideoEnabled) {
|
||||
NavigationHelper.playOnExternalVideoPlayer(this, (StreamInfo) info);
|
||||
|
||||
} else if (playerChoice.equals(videoPlayerKey) && useOldVideoPlayer) {
|
||||
NavigationHelper.playOnOldVideoPlayer(this, (StreamInfo) info);
|
||||
|
||||
} else {
|
||||
playQueue = new SinglePlayQueue((StreamInfo) info);
|
||||
|
||||
if (playerChoice.equals(videoPlayerKey)) {
|
||||
NavigationHelper.playOnMainPlayer(this, playQueue);
|
||||
} else if (playerChoice.equals(backgroundPlayerKey)) {
|
||||
NavigationHelper.enqueueOnBackgroundPlayer(this, playQueue, true);
|
||||
} else if (playerChoice.equals(popupPlayerKey)) {
|
||||
NavigationHelper.enqueueOnPopupPlayer(this, playQueue, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (info instanceof ChannelInfo || info instanceof PlaylistInfo) {
|
||||
playQueue = info instanceof ChannelInfo ? new ChannelPlayQueue((ChannelInfo) info) : new PlaylistPlayQueue((PlaylistInfo) info);
|
||||
|
||||
if (playerChoice.equals(videoPlayerKey)) {
|
||||
NavigationHelper.playOnMainPlayer(this, playQueue);
|
||||
} else if (playerChoice.equals(backgroundPlayerKey)) {
|
||||
NavigationHelper.playOnBackgroundPlayer(this, playQueue);
|
||||
} else if (playerChoice.equals(popupPlayerKey)) {
|
||||
NavigationHelper.playOnPopupPlayer(this, playQueue);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
stopForeground(true);
|
||||
if (fetcher != null) fetcher.dispose();
|
||||
}
|
||||
|
||||
private NotificationCompat.Builder createNotification() {
|
||||
return new NotificationCompat.Builder(this, getString(R.string.notification_channel_id))
|
||||
.setOngoing(true)
|
||||
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setContentTitle(getString(R.string.preferred_player_fetcher_notification_title))
|
||||
.setContentText(getString(R.string.preferred_player_fetcher_notification_message));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package org.schabi.newpipe;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.player.PopupVideoPlayer;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
import org.schabi.newpipe.util.PermissionHelper;
|
||||
|
||||
/**
|
||||
* Get the url from the intent and open a popup player
|
||||
*/
|
||||
public class RouterPopupActivity extends RouterActivity {
|
||||
|
||||
@Override
|
||||
protected void handleUrl(String url) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||
&& !PermissionHelper.checkSystemAlertWindowPermission(this)) {
|
||||
Toast.makeText(this, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
StreamingService service;
|
||||
try {
|
||||
service = NewPipe.getServiceByUrl(url);
|
||||
} catch (ExtractionException e) {
|
||||
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
Intent callIntent = new Intent(this, PopupVideoPlayer.class);
|
||||
switch (service.getLinkTypeByUrl(url)) {
|
||||
case STREAM:
|
||||
break;
|
||||
default:
|
||||
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
callIntent.putExtra(Constants.KEY_URL, url);
|
||||
callIntent.putExtra(Constants.KEY_SERVICE_ID, service.getServiceId());
|
||||
startService(callIntent);
|
||||
|
||||
finish();
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ public class WatchHistoryEntry extends HistoryEntry {
|
||||
}
|
||||
|
||||
public WatchHistoryEntry(StreamInfo streamInfo) {
|
||||
this(new Date(), streamInfo.service_id, streamInfo.name, streamInfo.url,
|
||||
this(new Date(), streamInfo.getServiceId(), streamInfo.getName(), streamInfo.getUrl(),
|
||||
streamInfo.id, streamInfo.thumbnail_url, streamInfo.uploader_name, streamInfo.duration);
|
||||
}
|
||||
|
||||
|
||||
@@ -108,8 +108,8 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
nameEditText = view.findViewById(R.id.file_name);
|
||||
nameEditText.setText(FilenameUtils.createFilename(getContext(), currentInfo.name));
|
||||
selectedAudioIndex = ListHelper.getDefaultAudioFormat(getContext(), currentInfo.audio_streams);
|
||||
nameEditText.setText(FilenameUtils.createFilename(getContext(), currentInfo.getName()));
|
||||
selectedAudioIndex = ListHelper.getDefaultAudioFormat(getContext(), currentInfo.getAudioStreams());
|
||||
|
||||
streamsSpinner = view.findViewById(R.id.quality_spinner);
|
||||
streamsSpinner.setOnItemSelectedListener(this);
|
||||
@@ -183,7 +183,7 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||
String[] items = new String[audioStreams.size()];
|
||||
for (int i = 0; i < audioStreams.size(); i++) {
|
||||
AudioStream audioStream = audioStreams.get(i);
|
||||
items[i] = MediaFormat.getNameById(audioStream.format) + " " + audioStream.average_bitrate + "kbps";
|
||||
items[i] = audioStream.getFormat().getName() + " " + audioStream.getAverageBitrate() + "kbps";
|
||||
}
|
||||
|
||||
ArrayAdapter<String> itemAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, items);
|
||||
@@ -242,7 +242,7 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||
RadioButton audioButton = view.findViewById(R.id.audio_button);
|
||||
RadioButton videoButton = view.findViewById(R.id.video_button);
|
||||
|
||||
if (currentInfo.audio_streams == null || currentInfo.audio_streams.size() == 0) {
|
||||
if (currentInfo.getAudioStreams() == null || currentInfo.getAudioStreams().size() == 0) {
|
||||
audioButton.setVisibility(View.GONE);
|
||||
videoButton.setChecked(true);
|
||||
} else if (sortedStreamVideosList == null || sortedStreamVideosList.size() == 0) {
|
||||
@@ -256,14 +256,18 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||
String url, location;
|
||||
|
||||
String fileName = nameEditText.getText().toString().trim();
|
||||
if (fileName.isEmpty()) fileName = FilenameUtils.createFilename(getContext(), currentInfo.name);
|
||||
if (fileName.isEmpty()) fileName = FilenameUtils.createFilename(getContext(), currentInfo.getName());
|
||||
|
||||
boolean isAudio = radioVideoAudioGroup.getCheckedRadioButtonId() == R.id.audio_button;
|
||||
url = isAudio ? currentInfo.audio_streams.get(selectedAudioIndex).url : sortedStreamVideosList.get(selectedVideoIndex).url;
|
||||
location = isAudio ? NewPipeSettings.getAudioDownloadPath(getContext()) : NewPipeSettings.getVideoDownloadPath(getContext());
|
||||
|
||||
if (isAudio) fileName += "." + MediaFormat.getSuffixById(currentInfo.audio_streams.get(selectedAudioIndex).format);
|
||||
else fileName += "." + MediaFormat.getSuffixById(sortedStreamVideosList.get(selectedVideoIndex).format);
|
||||
if (isAudio) {
|
||||
url = currentInfo.getAudioStreams().get(selectedAudioIndex).getUrl();
|
||||
location = NewPipeSettings.getAudioDownloadPath(getContext());
|
||||
fileName += "." + currentInfo.getAudioStreams().get(selectedAudioIndex).getFormat().getSuffix();
|
||||
} else {
|
||||
url = sortedStreamVideosList.get(selectedVideoIndex).getUrl();
|
||||
location = NewPipeSettings.getVideoDownloadPath(getContext());
|
||||
fileName += "." + sortedStreamVideosList.get(selectedVideoIndex).getFormat().getSuffix();
|
||||
}
|
||||
|
||||
DownloadManagerService.startMission(getContext(), url, location, fileName, isAudio, threadsSeekBar.getProgress() + 1);
|
||||
getDialog().dismiss();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.schabi.newpipe.fragments;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.StringRes;
|
||||
@@ -219,6 +220,7 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
|
||||
|
||||
if (serviceName == null) serviceName = "none";
|
||||
if (request == null) request = "none";
|
||||
|
||||
ErrorActivity.reportError(getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(userAction, serviceName, request, errorId));
|
||||
}
|
||||
|
||||
@@ -239,4 +241,28 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
|
||||
|
||||
ErrorActivity.reportError(getContext(), exception, MainActivity.class, rootView, ErrorActivity.ErrorInfo.make(userAction, serviceName, request, errorId));
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public void setTitle(String title) {
|
||||
if (DEBUG) Log.d(TAG, "setTitle() called with: title = [" + title + "]");
|
||||
if (activity != null && activity.getSupportActionBar() != null) {
|
||||
activity.getSupportActionBar().setTitle(title);
|
||||
}
|
||||
}
|
||||
|
||||
protected void openUrlInBrowser(String url) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
startActivity(Intent.createChooser(intent, activity.getString(R.string.share_dialog_title)));
|
||||
}
|
||||
|
||||
protected void shareUrl(String subject, String url) {
|
||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
intent.setType("text/plain");
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
|
||||
intent.putExtra(Intent.EXTRA_TEXT, url);
|
||||
startActivity(Intent.createChooser(intent, getString(R.string.share_dialog_title)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.schabi.newpipe.fragments;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.TabLayout;
|
||||
import android.support.v4.app.Fragment;
|
||||
@@ -22,6 +23,7 @@ import android.view.ViewGroup;
|
||||
import org.schabi.newpipe.BaseFragment;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.ServiceList;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.kiosk.KioskList;
|
||||
import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
|
||||
@@ -30,30 +32,25 @@ import org.schabi.newpipe.fragments.list.kiosk.KioskFragment;
|
||||
import org.schabi.newpipe.fragments.subscription.SubscriptionFragment;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
import org.schabi.newpipe.util.KioskTranslator;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.ServiceHelper;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class MainFragment extends BaseFragment implements TabLayout.OnTabSelectedListener {
|
||||
private ViewPager viewPager;
|
||||
private boolean showBlankTab = false;
|
||||
|
||||
private static final int FALLBACK_SERVICE_ID = 0; // Youtbe
|
||||
private static final String FALLBACK_CHANNEL_URL =
|
||||
"https://www.youtube.com/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ";
|
||||
private static final String FALLBACK_CHANNEL_NAME = "Music";
|
||||
private static final String FALLBACK_KIOSK_ID = "Trending";
|
||||
|
||||
public int currentServiceId = -1;
|
||||
private ViewPager viewPager;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Konst
|
||||
// Constants
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private static final int KIOSK_MENU_OFFSETT = 2000;
|
||||
private static final int FALLBACK_SERVICE_ID = ServiceList.YouTube.getId();
|
||||
private static final String FALLBACK_CHANNEL_URL = "https://www.youtube.com/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ";
|
||||
private static final String FALLBACK_CHANNEL_NAME = "Music";
|
||||
private static final String FALLBACK_KIOSK_ID = "Trending";
|
||||
private static final int KIOSK_MENU_OFFSET = 2000;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Fragment's LifeCycle
|
||||
@@ -66,9 +63,8 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
currentServiceId = Integer.parseInt(PreferenceManager.getDefaultSharedPreferences(getActivity())
|
||||
.getString(getString(R.string.current_service_key), "0"));
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
currentServiceId = ServiceHelper.getSelectedServiceId(activity);
|
||||
return inflater.inflate(R.layout.fragment_main, container, false);
|
||||
}
|
||||
|
||||
@@ -86,27 +82,15 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
||||
|
||||
tabLayout.setupWithViewPager(viewPager);
|
||||
|
||||
if(ThemeHelper.isLightThemeSelected(getActivity())) {
|
||||
tabLayout.setBackgroundColor(getResources().getColor(R.color.light_youtube_primary_color));
|
||||
}
|
||||
int channelIcon = ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.ic_channel);
|
||||
int whatsHotIcon = ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.ic_hot);
|
||||
|
||||
if(PreferenceManager.getDefaultSharedPreferences(getActivity())
|
||||
.getString(getString(R.string.main_page_content_key), getString(R.string.blank_page_key))
|
||||
.equals(getString(R.string.subscription_page_key))) {
|
||||
if(ThemeHelper.isLightThemeSelected(getActivity())) {
|
||||
tabLayout.getTabAt(0).setIcon(R.drawable.ic_channel_black_24dp);
|
||||
} else{
|
||||
tabLayout.getTabAt(0).setIcon(R.drawable.ic_channel_white_24dp);
|
||||
}
|
||||
} else {
|
||||
if(ThemeHelper.isLightThemeSelected(getActivity())) {
|
||||
tabLayout.getTabAt(0).setIcon(R.drawable.ic_whatshot_black_24dp);
|
||||
tabLayout.getTabAt(1).setIcon(R.drawable.ic_channel_black_24dp);
|
||||
} else {
|
||||
tabLayout.getTabAt(0).setIcon(R.drawable.ic_whatshot_white_24dp);
|
||||
tabLayout.getTabAt(1).setIcon(R.drawable.ic_channel_white_24dp);
|
||||
}
|
||||
}
|
||||
if (isSubscriptionsPageOnlySelected()) {
|
||||
tabLayout.getTabAt(0).setIcon(channelIcon);
|
||||
} else {
|
||||
tabLayout.getTabAt(0).setIcon(whatsHotIcon);
|
||||
tabLayout.getTabAt(1).setIcon(channelIcon);
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
@@ -139,7 +123,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_search:
|
||||
NavigationHelper.openSearchFragment(getFragmentManager(), 0, "");
|
||||
NavigationHelper.openSearchFragment(getFragmentManager(), ServiceHelper.getSelectedServiceId(activity), "");
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
@@ -164,11 +148,6 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
||||
|
||||
private class PagerAdapter extends FragmentPagerAdapter {
|
||||
|
||||
private int[] tabTitles = new int[]{
|
||||
R.string.tab_main,
|
||||
R.string.tab_subscriptions
|
||||
};
|
||||
|
||||
PagerAdapter(FragmentManager fm) {
|
||||
super(fm);
|
||||
}
|
||||
@@ -177,13 +156,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
||||
public Fragment getItem(int position) {
|
||||
switch (position) {
|
||||
case 0:
|
||||
if(PreferenceManager.getDefaultSharedPreferences(getActivity())
|
||||
.getString(getString(R.string.main_page_content_key), getString(R.string.blank_page_key))
|
||||
.equals(getString(R.string.subscription_page_key))) {
|
||||
return new SubscriptionFragment();
|
||||
} else {
|
||||
return getMainPageFramgent();
|
||||
}
|
||||
return isSubscriptionsPageOnlySelected() ? new SubscriptionFragment() : getMainPageFragment();
|
||||
case 1:
|
||||
return new SubscriptionFragment();
|
||||
default:
|
||||
@@ -199,13 +172,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
if(PreferenceManager.getDefaultSharedPreferences(getActivity())
|
||||
.getString(getString(R.string.main_page_content_key), getString(R.string.blank_page_key))
|
||||
.equals(getString(R.string.subscription_page_key))) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
return isSubscriptionsPageOnlySelected() ? 1 : 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,28 +180,33 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
||||
// Main page content
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private Fragment getMainPageFramgent() {
|
||||
private boolean isSubscriptionsPageOnlySelected() {
|
||||
return PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
.getString(getString(R.string.main_page_content_key), getString(R.string.blank_page_key))
|
||||
.equals(getString(R.string.subscription_page_key));
|
||||
}
|
||||
|
||||
private Fragment getMainPageFragment() {
|
||||
try {
|
||||
SharedPreferences preferences =
|
||||
PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
final String setMainPage = preferences.getString(getString(R.string.main_page_content_key),
|
||||
getString(R.string.main_page_selectd_kiosk_id));
|
||||
if(setMainPage.equals(getString(R.string.blank_page_key))) {
|
||||
getString(R.string.main_page_selectd_kiosk_id));
|
||||
if (setMainPage.equals(getString(R.string.blank_page_key))) {
|
||||
return new BlankFragment();
|
||||
} else if(setMainPage.equals(getString(R.string.kiosk_page_key))) {
|
||||
} else if (setMainPage.equals(getString(R.string.kiosk_page_key))) {
|
||||
int serviceId = preferences.getInt(getString(R.string.main_page_selected_service),
|
||||
FALLBACK_SERVICE_ID);
|
||||
String kioskId = preferences.getString(getString(R.string.main_page_selectd_kiosk_id),
|
||||
FALLBACK_KIOSK_ID);
|
||||
KioskFragment fragment = KioskFragment.getInstance(serviceId, kioskId
|
||||
);
|
||||
KioskFragment fragment = KioskFragment.getInstance(serviceId, kioskId);
|
||||
fragment.useAsFrontPage(true);
|
||||
return fragment;
|
||||
} else if(setMainPage.equals(getString(R.string.feed_page_key))) {
|
||||
} else if (setMainPage.equals(getString(R.string.feed_page_key))) {
|
||||
FeedFragment fragment = new FeedFragment();
|
||||
fragment.useAsFrontPage(true);
|
||||
return fragment;
|
||||
} else if(setMainPage.equals(getString(R.string.channel_page_key))) {
|
||||
} else if (setMainPage.equals(getString(R.string.channel_page_key))) {
|
||||
int serviceId = preferences.getInt(getString(R.string.main_page_selected_service),
|
||||
FALLBACK_SERVICE_ID);
|
||||
String url = preferences.getString(getString(R.string.main_page_selected_channel_url),
|
||||
@@ -267,8 +239,8 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
||||
StreamingService service = NewPipe.getService(currentServiceId);
|
||||
KioskList kl = service.getKioskList();
|
||||
int i = 0;
|
||||
for(final String ks : kl.getAvailableKiosks()) {
|
||||
menu.add(0, KIOSK_MENU_OFFSETT + i, Menu.NONE,
|
||||
for (final String ks : kl.getAvailableKiosks()) {
|
||||
menu.add(0, KIOSK_MENU_OFFSET + i, Menu.NONE,
|
||||
KioskTranslator.getTranslatedKioskName(ks, getContext()))
|
||||
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
|
||||
@@ -60,7 +60,7 @@ public class SpinnerToolbarAdapter extends BaseAdapter {
|
||||
ImageView woSoundIcon = convertView.findViewById(R.id.wo_sound_icon);
|
||||
TextView text = convertView.findViewById(android.R.id.text1);
|
||||
VideoStream item = (VideoStream) getItem(position);
|
||||
text.setText(MediaFormat.getNameById(item.format) + " " + item.resolution);
|
||||
text.setText(item.getFormat().getName() + " " + item.getResolution());
|
||||
|
||||
int visibility = !showIconNoAudio ? View.GONE
|
||||
: item.isVideoOnly ? View.VISIBLE
|
||||
|
||||
@@ -4,7 +4,8 @@ import java.io.Serializable;
|
||||
|
||||
class StackItem implements Serializable {
|
||||
private int serviceId;
|
||||
private String title, url;
|
||||
private String title;
|
||||
private String url;
|
||||
|
||||
StackItem(int serviceId, String url, String title) {
|
||||
this.serviceId = serviceId;
|
||||
|
||||
@@ -24,7 +24,6 @@ import android.text.util.Linkify;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
@@ -36,7 +35,6 @@ import android.widget.FrameLayout;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
@@ -50,7 +48,6 @@ 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.exceptions.ContentNotAvailableException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
@@ -79,6 +76,7 @@ import org.schabi.newpipe.util.ListHelper;
|
||||
import org.schabi.newpipe.util.Localization;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.PermissionHelper;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
@@ -283,7 +281,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
|
||||
// Check if the next video label and video is visible,
|
||||
// if it is, include the two elements in the next check
|
||||
int nextCount = currentInfo != null && currentInfo.next_video != null ? 2 : 0;
|
||||
int nextCount = currentInfo != null && currentInfo.getNextVideo() != null ? 2 : 0;
|
||||
if (relatedStreamsView != null && relatedStreamsView.getChildCount() > INITIAL_RELATED_VIDEOS + nextCount) {
|
||||
outState.putSerializable(WAS_RELATED_EXPANDED_KEY, true);
|
||||
}
|
||||
@@ -330,14 +328,22 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
openPopupPlayer(false);
|
||||
break;
|
||||
case R.id.detail_uploader_root_layout:
|
||||
if (currentInfo.uploader_url == null || currentInfo.uploader_url.isEmpty()) {
|
||||
if (TextUtils.isEmpty(currentInfo.getUploaderUrl())) {
|
||||
Log.w(TAG, "Can't open channel because we got no channel URL");
|
||||
} else {
|
||||
NavigationHelper.openChannelFragment(getFragmentManager(), currentInfo.service_id, currentInfo.uploader_url, currentInfo.uploader_name);
|
||||
NavigationHelper.openChannelFragment(
|
||||
getFragmentManager(),
|
||||
currentInfo.getServiceId(),
|
||||
currentInfo.getUploaderUrl(),
|
||||
currentInfo.getUploaderName());
|
||||
}
|
||||
break;
|
||||
case R.id.detail_thumbnail_root_layout:
|
||||
openVideoPlayer();
|
||||
if (currentInfo.video_streams.isEmpty() && currentInfo.video_only_streams.isEmpty()) {
|
||||
openBackgroundPlayer(false);
|
||||
} else {
|
||||
openVideoPlayer();
|
||||
}
|
||||
break;
|
||||
case R.id.detail_title_root_layout:
|
||||
toggleTitleAndDescription();
|
||||
@@ -380,22 +386,22 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
if (DEBUG) Log.d(TAG, "toggleExpandRelatedVideos() called with: info = [" + info + "]");
|
||||
if (!showRelatedStreams) return;
|
||||
|
||||
int nextCount = info.next_video != null ? 2 : 0;
|
||||
int nextCount = info.getNextVideo() != null ? 2 : 0;
|
||||
int initialCount = INITIAL_RELATED_VIDEOS + nextCount;
|
||||
|
||||
if (relatedStreamsView.getChildCount() > initialCount) {
|
||||
relatedStreamsView.removeViews(initialCount, relatedStreamsView.getChildCount() - (initialCount));
|
||||
relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, resolveResourceIdFromAttr(R.attr.expand)));
|
||||
relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.expand)));
|
||||
return;
|
||||
}
|
||||
|
||||
//Log.d(TAG, "toggleExpandRelatedVideos() called with: info = [" + info + "], from = [" + INITIAL_RELATED_VIDEOS + "]");
|
||||
for (int i = INITIAL_RELATED_VIDEOS; i < info.related_streams.size(); i++) {
|
||||
InfoItem item = info.related_streams.get(i);
|
||||
for (int i = INITIAL_RELATED_VIDEOS; i < info.getRelatedStreams().size(); i++) {
|
||||
InfoItem item = info.getRelatedStreams().get(i);
|
||||
//Log.d(TAG, "i = " + i);
|
||||
relatedStreamsView.addView(infoItemBuilder.buildView(relatedStreamsView, item));
|
||||
}
|
||||
relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, resolveResourceIdFromAttr(R.attr.collapse)));
|
||||
relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.collapse)));
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
@@ -459,7 +465,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
infoItemBuilder.setOnStreamSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener<StreamInfoItem>() {
|
||||
@Override
|
||||
public void selected(StreamInfoItem selectedItem) {
|
||||
selectAndLoadVideo(selectedItem.service_id, selectedItem.url, selectedItem.name);
|
||||
selectAndLoadVideo(selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -500,7 +506,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
|
||||
break;
|
||||
case 1:
|
||||
NavigationHelper.enqueueOnPopupPlayer(context, new SinglePlayQueue(item));
|
||||
NavigationHelper.enqueueOnPopupPlayer(getActivity(), new SinglePlayQueue(item));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -532,35 +538,35 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
|
||||
private void initThumbnailViews(StreamInfo info) {
|
||||
thumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark);
|
||||
if (info.thumbnail_url != null && !info.thumbnail_url.isEmpty()) {
|
||||
imageLoader.displayImage(info.thumbnail_url, thumbnailImageView, DISPLAY_THUMBNAIL_OPTIONS, new SimpleImageLoadingListener() {
|
||||
if (!TextUtils.isEmpty(info.getThumbnailUrl())) {
|
||||
imageLoader.displayImage(info.getThumbnailUrl(), thumbnailImageView, DISPLAY_THUMBNAIL_OPTIONS, new SimpleImageLoadingListener() {
|
||||
@Override
|
||||
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
||||
ErrorActivity.reportError(activity, failReason.getCause(), null, activity.findViewById(android.R.id.content), ErrorActivity.ErrorInfo.make(UserAction.LOAD_IMAGE, NewPipe.getNameOfService(currentInfo.service_id), imageUri, R.string.could_not_load_thumbnails));
|
||||
ErrorActivity.reportError(activity, failReason.getCause(), null, activity.findViewById(android.R.id.content), ErrorActivity.ErrorInfo.make(UserAction.LOAD_IMAGE, NewPipe.getNameOfService(currentInfo.getServiceId()), imageUri, R.string.could_not_load_thumbnails));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (info.uploader_avatar_url != null && !info.uploader_avatar_url.isEmpty()) {
|
||||
imageLoader.displayImage(info.uploader_avatar_url, uploaderThumb, DISPLAY_AVATAR_OPTIONS);
|
||||
|
||||
if (!TextUtils.isEmpty(info.getUploaderAvatarUrl())) {
|
||||
imageLoader.displayImage(info.getUploaderAvatarUrl(), uploaderThumb, DISPLAY_AVATAR_OPTIONS);
|
||||
}
|
||||
}
|
||||
|
||||
private void initRelatedVideos(StreamInfo info) {
|
||||
if (relatedStreamsView.getChildCount() > 0) relatedStreamsView.removeAllViews();
|
||||
|
||||
if (info.next_video != null && showRelatedStreams) {
|
||||
if (info.getNextVideo() != null && showRelatedStreams) {
|
||||
nextStreamTitle.setVisibility(View.VISIBLE);
|
||||
relatedStreamsView.addView(infoItemBuilder.buildView(relatedStreamsView, info.next_video));
|
||||
relatedStreamsView.addView(infoItemBuilder.buildView(relatedStreamsView, info.getNextVideo()));
|
||||
relatedStreamsView.addView(getSeparatorView());
|
||||
relatedStreamRootLayout.setVisibility(View.VISIBLE);
|
||||
} else nextStreamTitle.setVisibility(View.GONE);
|
||||
|
||||
if (info.related_streams != null && !info.related_streams.isEmpty() && showRelatedStreams) {
|
||||
//long first = System.nanoTime(), each;
|
||||
int to = info.related_streams.size() >= INITIAL_RELATED_VIDEOS ? INITIAL_RELATED_VIDEOS : info.related_streams.size();
|
||||
int to = info.getRelatedStreams().size() >= INITIAL_RELATED_VIDEOS ? INITIAL_RELATED_VIDEOS : info.getRelatedStreams().size();
|
||||
for (int i = 0; i < to; i++) {
|
||||
InfoItem item = info.related_streams.get(i);
|
||||
InfoItem item = info.getRelatedStreams().get(i);
|
||||
//each = System.nanoTime();
|
||||
relatedStreamsView.addView(infoItemBuilder.buildView(relatedStreamsView, item));
|
||||
//if (DEBUG) Log.d(TAG, "each took " + ((System.nanoTime() - each) / 1000000L) + "ms");
|
||||
@@ -570,9 +576,9 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
relatedStreamRootLayout.setVisibility(View.VISIBLE);
|
||||
relatedStreamExpandButton.setVisibility(View.VISIBLE);
|
||||
|
||||
relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, resolveResourceIdFromAttr(R.attr.expand)));
|
||||
relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.expand)));
|
||||
} else {
|
||||
if (info.next_video == null) relatedStreamRootLayout.setVisibility(View.GONE);
|
||||
if (info.getNextVideo() == null) relatedStreamRootLayout.setVisibility(View.GONE);
|
||||
relatedStreamExpandButton.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
@@ -616,26 +622,14 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
|
||||
private void setupActionBarHandler(final StreamInfo info) {
|
||||
if (DEBUG) Log.d(TAG, "setupActionBarHandler() called with: info = [" + info + "]");
|
||||
sortedStreamVideosList = new ArrayList<>(ListHelper.getSortedStreamVideosList(activity, info.video_streams, info.video_only_streams, false));
|
||||
sortedStreamVideosList = new ArrayList<>(ListHelper.getSortedStreamVideosList(activity, info.getVideoStreams(), info.getVideoOnlyStreams(), false));
|
||||
actionBarHandler.setupStreamList(sortedStreamVideosList, spinnerToolbar);
|
||||
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.url);
|
||||
intent.setType("text/plain");
|
||||
startActivity(Intent.createChooser(intent, activity.getString(R.string.share_dialog_title)));
|
||||
}
|
||||
});
|
||||
actionBarHandler.setOnShareListener(selectedStreamId -> shareUrl(info.name, info.url));
|
||||
|
||||
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.url));
|
||||
startActivity(Intent.createChooser(intent, activity.getString(R.string.choose_browser)));
|
||||
openUrlInBrowser(info.getUrl());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -643,7 +637,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
@Override
|
||||
public void onActionSelected(int selectedStreamId) {
|
||||
try {
|
||||
NavigationHelper.playWithKore(activity, Uri.parse(info.url.replace("https", "http")));
|
||||
NavigationHelper.playWithKore(activity, Uri.parse(info.getUrl().replace("https", "http")));
|
||||
if(activity instanceof HistoryListener) {
|
||||
((HistoryListener) activity).onVideoPlayed(info, null);
|
||||
}
|
||||
@@ -742,7 +736,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
public void prepareAndHandleInfo(final StreamInfo info, boolean scrollToTop) {
|
||||
if (DEBUG) Log.d(TAG, "prepareAndHandleInfo() called with: info = [" + info + "], scrollToTop = [" + scrollToTop + "]");
|
||||
|
||||
setInitialData(info.service_id, info.url, info.name);
|
||||
setInitialData(info.getServiceId(), info.getUrl(), info.getName());
|
||||
pushToStack(serviceId, url, name);
|
||||
showLoading();
|
||||
|
||||
@@ -798,7 +792,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void openBackgroundPlayer(final boolean append) {
|
||||
AudioStream audioStream = currentInfo.audio_streams.get(ListHelper.getDefaultAudioFormat(activity, currentInfo.audio_streams));
|
||||
AudioStream audioStream = currentInfo.getAudioStreams().get(ListHelper.getDefaultAudioFormat(activity, currentInfo.getAudioStreams()));
|
||||
|
||||
if (activity instanceof HistoryListener) {
|
||||
((HistoryListener) activity).onAudioPlayed(currentInfo, audioStream);
|
||||
@@ -810,16 +804,13 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 16) {
|
||||
openNormalBackgroundPlayer(append);
|
||||
} else {
|
||||
openExternalBackgroundPlayer(audioStream);
|
||||
NavigationHelper.playOnExternalPlayer(activity, currentInfo.getName(), currentInfo.getUploaderName(), audioStream);
|
||||
}
|
||||
}
|
||||
|
||||
private void openPopupPlayer(final boolean append) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !PermissionHelper.checkSystemAlertWindowPermission(activity)) {
|
||||
Toast toast = Toast.makeText(activity, R.string.msg_popup_permission, Toast.LENGTH_LONG);
|
||||
TextView messageView = toast.getView().findViewById(android.R.id.message);
|
||||
if (messageView != null) messageView.setGravity(Gravity.CENTER);
|
||||
toast.show();
|
||||
if (!PermissionHelper.isPopupEnabled(activity)) {
|
||||
PermissionHelper.showPopupEnablementToast(activity);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -847,13 +838,12 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
}
|
||||
|
||||
if (PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(this.getString(R.string.use_external_video_player_key), false)) {
|
||||
openExternalVideoPlayer(selectedVideoStream);
|
||||
NavigationHelper.playOnExternalPlayer(activity, currentInfo.getName(), currentInfo.getUploaderName(), selectedVideoStream);
|
||||
} else {
|
||||
openNormalPlayer(selectedVideoStream);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void openNormalBackgroundPlayer(final boolean append) {
|
||||
final PlayQueue itemQueue = new SinglePlayQueue(currentInfo);
|
||||
if (append) {
|
||||
@@ -863,85 +853,24 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
}
|
||||
}
|
||||
|
||||
private void openExternalBackgroundPlayer(AudioStream audioStream) {
|
||||
Intent intent;
|
||||
intent = new Intent();
|
||||
try {
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
intent.setDataAndType(Uri.parse(audioStream.url), MediaFormat.getMimeById(audioStream.format));
|
||||
intent.putExtra(Intent.EXTRA_TITLE, currentInfo.name);
|
||||
intent.putExtra("title", currentInfo.name);
|
||||
activity.startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
builder.setMessage(R.string.no_player_found)
|
||||
.setPositiveButton(R.string.install, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(activity.getString(R.string.fdroid_vlc_url)));
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Log.i(TAG, "You unlocked a secret unicorn.");
|
||||
}
|
||||
});
|
||||
builder.create().show();
|
||||
Log.e(TAG, "Either no Streaming player for audio was installed, or something important crashed:");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void openNormalPlayer(VideoStream selectedVideoStream) {
|
||||
Intent mIntent;
|
||||
boolean useOldPlayer = PlayerHelper.isUsingOldPlayer(activity) || (Build.VERSION.SDK_INT < 16);
|
||||
if (!useOldPlayer) {
|
||||
// ExoPlayer
|
||||
final PlayQueue playQueue = new SinglePlayQueue(currentInfo);
|
||||
mIntent = NavigationHelper.getPlayerIntent(activity, MainVideoPlayer.class, playQueue, getSelectedVideoStream().resolution);
|
||||
mIntent = NavigationHelper.getPlayerIntent(activity, MainVideoPlayer.class, playQueue, getSelectedVideoStream().getResolution());
|
||||
} else {
|
||||
// Internal Player
|
||||
mIntent = new Intent(activity, PlayVideoActivity.class)
|
||||
.putExtra(PlayVideoActivity.VIDEO_TITLE, currentInfo.name)
|
||||
.putExtra(PlayVideoActivity.STREAM_URL, selectedVideoStream.url)
|
||||
.putExtra(PlayVideoActivity.VIDEO_URL, currentInfo.url)
|
||||
.putExtra(PlayVideoActivity.START_POSITION, currentInfo.start_position);
|
||||
.putExtra(PlayVideoActivity.VIDEO_TITLE, currentInfo.getName())
|
||||
.putExtra(PlayVideoActivity.STREAM_URL, selectedVideoStream.getUrl())
|
||||
.putExtra(PlayVideoActivity.VIDEO_URL, currentInfo.getUrl())
|
||||
.putExtra(PlayVideoActivity.START_POSITION, currentInfo.getStartPosition());
|
||||
}
|
||||
startActivity(mIntent);
|
||||
}
|
||||
|
||||
private void openExternalVideoPlayer(VideoStream selectedVideoStream) {
|
||||
// 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, currentInfo.name)
|
||||
.putExtra("title", currentInfo.name);
|
||||
this.startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
builder.setMessage(R.string.no_player_found)
|
||||
.setPositiveButton(R.string.install, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Intent intent = new Intent()
|
||||
.setAction(Intent.ACTION_VIEW)
|
||||
.setData(Uri.parse(getString(R.string.fdroid_vlc_url)));
|
||||
startActivity(intent);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, null);
|
||||
builder.create().show();
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
@@ -1095,20 +1024,28 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
public void handleResult(@NonNull StreamInfo info) {
|
||||
super.handleResult(info);
|
||||
|
||||
setInitialData(info.service_id, info.url, info.name);
|
||||
setInitialData(info.getServiceId(), info.getUrl(), info.getName());
|
||||
pushToStack(serviceId, url, name);
|
||||
|
||||
animateView(thumbnailPlayButton, true, 200);
|
||||
videoTitleTextView.setText(name);
|
||||
|
||||
if (!TextUtils.isEmpty(info.uploader_name)) uploaderTextView.setText(info.uploader_name);
|
||||
uploaderTextView.setVisibility(!TextUtils.isEmpty(info.uploader_name) ? View.VISIBLE : View.GONE);
|
||||
if (!TextUtils.isEmpty(info.getUploaderName())) {
|
||||
uploaderTextView.setText(info.getUploaderName());
|
||||
uploaderTextView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
uploaderTextView.setVisibility(View.GONE);
|
||||
}
|
||||
uploaderThumb.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.buddy));
|
||||
|
||||
if (info.view_count >= 0) videoCountView.setText(Localization.localizeViewCount(activity, info.view_count));
|
||||
videoCountView.setVisibility(info.view_count >= 0 ? View.VISIBLE : View.GONE);
|
||||
if (info.getViewCount() >= 0) {
|
||||
videoCountView.setText(Localization.localizeViewCount(activity, info.getViewCount()));
|
||||
videoCountView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
videoCountView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (info.dislike_count == -1 && info.like_count == -1) {
|
||||
if (info.getDislikeCount() == -1 && info.getLikeCount() == -1) {
|
||||
thumbsDownImageView.setVisibility(View.VISIBLE);
|
||||
thumbsUpImageView.setVisibility(View.VISIBLE);
|
||||
thumbsUpTextView.setVisibility(View.GONE);
|
||||
@@ -1116,14 +1053,23 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
|
||||
thumbsDisabledTextView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
if (info.dislike_count >= 0) thumbsDownTextView.setText(Localization.shortCount(activity, info.dislike_count));
|
||||
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.shortCount(activity, info.like_count));
|
||||
thumbsUpTextView.setVisibility(info.like_count >= 0 ? View.VISIBLE : View.GONE);
|
||||
thumbsUpImageView.setVisibility(info.like_count >= 0 ? View.VISIBLE : View.GONE);
|
||||
if (info.getDislikeCount() >= 0) {
|
||||
thumbsDownTextView.setText(Localization.shortCount(activity, info.getDislikeCount()));
|
||||
thumbsDownTextView.setVisibility(View.VISIBLE);
|
||||
thumbsDownImageView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
thumbsDownTextView.setVisibility(View.GONE);
|
||||
thumbsDownImageView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (info.getLikeCount() >= 0) {
|
||||
thumbsUpTextView.setText(Localization.shortCount(activity, info.getLikeCount()));
|
||||
thumbsUpTextView.setVisibility(View.VISIBLE);
|
||||
thumbsUpImageView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
thumbsUpTextView.setVisibility(View.GONE);
|
||||
thumbsUpImageView.setVisibility(View.GONE);
|
||||
}
|
||||
thumbsDisabledTextView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@@ -1132,10 +1078,10 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
videoTitleToggleArrow.setImageResource(R.drawable.arrow_down);
|
||||
videoDescriptionView.setVisibility(View.GONE);
|
||||
videoDescriptionRootLayout.setVisibility(View.GONE);
|
||||
if (!TextUtils.isEmpty(info.upload_date)) {
|
||||
videoUploadDateView.setText(Localization.localizeDate(activity, info.upload_date));
|
||||
if (!TextUtils.isEmpty(info.getUploadDate())) {
|
||||
videoUploadDateView.setText(Localization.localizeDate(activity, info.getUploadDate()));
|
||||
}
|
||||
prepareDescription(info.description);
|
||||
prepareDescription(info.getDescription());
|
||||
|
||||
animateView(spinnerToolbar, true, 500);
|
||||
setupActionBarHandler(info);
|
||||
@@ -1145,10 +1091,17 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
toggleExpandRelatedVideos(currentInfo);
|
||||
wasRelatedStreamsExpanded = false;
|
||||
}
|
||||
setTitleToUrl(info.service_id, info.url, info.name);
|
||||
setTitleToUrl(info.getServiceId(), info.getUrl(), info.getName());
|
||||
|
||||
if (!info.errors.isEmpty()) {
|
||||
showSnackBarError(info.errors, UserAction.REQUESTED_STREAM, NewPipe.getNameOfService(info.service_id), info.url, 0);
|
||||
if (!info.getErrors().isEmpty()) {
|
||||
showSnackBarError(info.getErrors(), UserAction.REQUESTED_STREAM, NewPipe.getNameOfService(info.getServiceId()), info.getUrl(), 0);
|
||||
}
|
||||
|
||||
if (info.video_streams.isEmpty() && info.video_only_streams.isEmpty()) {
|
||||
detailControlsBackground.setVisibility(View.GONE);
|
||||
detailControlsPopup.setVisibility(View.GONE);
|
||||
spinnerToolbar.setVisibility(View.GONE);
|
||||
thumbnailPlayButton.setImageResource(R.drawable.ic_headset_white_24dp);
|
||||
}
|
||||
|
||||
if (autoPlayEnabled) {
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
package org.schabi.newpipe.fragments.list;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
@@ -24,6 +29,7 @@ import org.schabi.newpipe.info_list.InfoItemDialog;
|
||||
import org.schabi.newpipe.info_list.InfoListAdapter;
|
||||
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.PermissionHelper;
|
||||
import org.schabi.newpipe.util.StateSaver;
|
||||
|
||||
import java.util.List;
|
||||
@@ -140,7 +146,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
||||
onItemSelected(selectedItem);
|
||||
NavigationHelper.openVideoDetailFragment(
|
||||
useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(),
|
||||
selectedItem.service_id, selectedItem.url, selectedItem.name);
|
||||
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -155,7 +161,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
||||
onItemSelected(selectedItem);
|
||||
NavigationHelper.openChannelFragment(
|
||||
useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(),
|
||||
selectedItem.service_id, selectedItem.url, selectedItem.name);
|
||||
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -168,7 +174,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
||||
onItemSelected(selectedItem);
|
||||
NavigationHelper.openPlaylistFragment(
|
||||
useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(),
|
||||
selectedItem.service_id, selectedItem.url, selectedItem.name);
|
||||
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -192,6 +198,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
||||
|
||||
protected void showStreamDialog(final StreamInfoItem item) {
|
||||
final Context context = getContext();
|
||||
final Activity activity = getActivity();
|
||||
if (context == null || context.getResources() == null || getActivity() == null) return;
|
||||
|
||||
final String[] commands = new String[]{
|
||||
@@ -207,7 +214,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
||||
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
|
||||
break;
|
||||
case 1:
|
||||
NavigationHelper.enqueueOnPopupPlayer(context, new SinglePlayQueue(item));
|
||||
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -84,17 +84,6 @@ public abstract class BaseListInfoFragment<I extends ListInfo> extends BaseListF
|
||||
currentNextItemsUrl = (String) savedObjects.poll();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public void setTitle(String title) {
|
||||
Log.d(TAG, "setTitle() called with: title = [" + title + "]");
|
||||
if (activity.getSupportActionBar() != null) {
|
||||
activity.getSupportActionBar().setTitle(title);
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Load and handle
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
@@ -124,20 +113,12 @@ public abstract class BaseListInfoFragment<I extends ListInfo> extends BaseListF
|
||||
currentWorker = loadResult(forceLoad)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Consumer<I>() {
|
||||
@Override
|
||||
public void accept(@NonNull I result) throws Exception {
|
||||
isLoading.set(false);
|
||||
currentInfo = result;
|
||||
currentNextItemsUrl = result.next_streams_url;
|
||||
handleResult(result);
|
||||
}
|
||||
}, new Consumer<Throwable>() {
|
||||
@Override
|
||||
public void accept(@NonNull Throwable throwable) throws Exception {
|
||||
onError(throwable);
|
||||
}
|
||||
});
|
||||
.subscribe((@NonNull I result) -> {
|
||||
isLoading.set(false);
|
||||
currentInfo = result;
|
||||
currentNextItemsUrl = result.next_streams_url;
|
||||
handleResult(result);
|
||||
}, (@NonNull Throwable throwable) -> onError(throwable));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,18 +134,12 @@ public abstract class BaseListInfoFragment<I extends ListInfo> extends BaseListF
|
||||
currentWorker = loadMoreItemsLogic()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Consumer<ListExtractor.NextItemsResult>() {
|
||||
@Override
|
||||
public void accept(@io.reactivex.annotations.NonNull ListExtractor.NextItemsResult nextItemsResult) throws Exception {
|
||||
isLoading.set(false);
|
||||
handleNextItems(nextItemsResult);
|
||||
}
|
||||
}, new Consumer<Throwable>() {
|
||||
@Override
|
||||
public void accept(@io.reactivex.annotations.NonNull Throwable throwable) throws Exception {
|
||||
isLoading.set(false);
|
||||
onError(throwable);
|
||||
}
|
||||
.subscribe((@io.reactivex.annotations.NonNull ListExtractor.NextItemsResult nextItemsResult) -> {
|
||||
isLoading.set(false);
|
||||
handleNextItems(nextItemsResult);
|
||||
}, (@io.reactivex.annotations.NonNull Throwable throwable) -> {
|
||||
isLoading.set(false);
|
||||
onError(throwable);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -190,8 +165,8 @@ public abstract class BaseListInfoFragment<I extends ListInfo> extends BaseListF
|
||||
public void handleResult(@NonNull I result) {
|
||||
super.handleResult(result);
|
||||
|
||||
url = result.url;
|
||||
name = result.name;
|
||||
url = result.getUrl();
|
||||
name = result.getName();
|
||||
setTitle(name);
|
||||
|
||||
if (infoListAdapter.getItemsList().size() == 0) {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package org.schabi.newpipe.fragments.list.channel;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
@@ -12,7 +12,6 @@ import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
@@ -23,7 +22,6 @@ import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.jakewharton.rxbinding2.view.RxView;
|
||||
|
||||
@@ -45,7 +43,6 @@ import org.schabi.newpipe.util.AnimationUtils;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
import org.schabi.newpipe.util.Localization;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.PermissionHelper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -104,11 +101,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||
if(activity != null
|
||||
&& useAsFrontPage
|
||||
&& isVisibleToUser) {
|
||||
try {
|
||||
activity.getSupportActionBar().setTitle(currentInfo.name);
|
||||
} catch (Exception e) {
|
||||
onError(e);
|
||||
}
|
||||
setTitle(currentInfo != null ? currentInfo.getName() : name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,6 +146,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||
|
||||
@Override
|
||||
protected void showStreamDialog(final StreamInfoItem item) {
|
||||
final Activity activity = getActivity();
|
||||
final Context context = getContext();
|
||||
if (context == null || context.getResources() == null || getActivity() == null) return;
|
||||
|
||||
@@ -173,7 +167,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
|
||||
break;
|
||||
case 1:
|
||||
NavigationHelper.enqueueOnPopupPlayer(context, new SinglePlayQueue(item));
|
||||
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item));
|
||||
break;
|
||||
case 2:
|
||||
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index));
|
||||
@@ -182,7 +176,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index));
|
||||
break;
|
||||
case 4:
|
||||
NavigationHelper.playOnPopupPlayer(context, getPlayQueue(index));
|
||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -208,7 +202,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||
if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "], inflater = [" + inflater + "]");
|
||||
menuRssButton = menu.findItem(R.id.menu_item_rss);
|
||||
if (currentInfo != null) {
|
||||
menuRssButton.setVisible(!TextUtils.isEmpty(currentInfo.feed_url));
|
||||
menuRssButton.setVisible(!TextUtils.isEmpty(currentInfo.getFeedUrl()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -217,23 +211,11 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||
private void openRssFeed() {
|
||||
final ChannelInfo info = currentInfo;
|
||||
if(info != null) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(info.feed_url));
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(info.getFeedUrl()));
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
private void openChannelUriInBrowser() {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void shareChannelUri() {
|
||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
intent.setType("text/plain");
|
||||
intent.putExtra(Intent.EXTRA_TEXT, url);
|
||||
startActivity(Intent.createChooser(intent, getString(R.string.share_dialog_title)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
@@ -241,10 +223,10 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||
openRssFeed();
|
||||
break;
|
||||
case R.id.menu_item_openInBrowser:
|
||||
openChannelUriInBrowser();
|
||||
openUrlInBrowser(url);
|
||||
break;
|
||||
case R.id.menu_item_share: {
|
||||
shareChannelUri();
|
||||
shareUrl(name, url);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -264,12 +246,12 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||
@Override
|
||||
public void accept(Throwable throwable) throws Exception {
|
||||
animateView(headerSubscribeButton, false, 100);
|
||||
showSnackBarError(throwable, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(currentInfo.service_id), "Get subscription status", 0);
|
||||
showSnackBarError(throwable, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(currentInfo.getServiceId()), "Get subscription status", 0);
|
||||
}
|
||||
};
|
||||
|
||||
final Observable<List<SubscriptionEntity>> observable = subscriptionService.subscriptionTable()
|
||||
.getSubscription(info.service_id, info.url)
|
||||
.getSubscription(info.getServiceId(), info.getUrl())
|
||||
.toObservable();
|
||||
|
||||
disposables.add(observable
|
||||
@@ -315,14 +297,14 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||
final Action onComplete = new Action() {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
if (DEBUG) Log.d(TAG, "Updated subscription: " + info.url);
|
||||
if (DEBUG) Log.d(TAG, "Updated subscription: " + info.getUrl());
|
||||
}
|
||||
};
|
||||
|
||||
final Consumer<Throwable> onError = new Consumer<Throwable>() {
|
||||
@Override
|
||||
public void accept(@NonNull Throwable throwable) throws Exception {
|
||||
onUnrecoverableError(throwable, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(info.service_id), "Updating Subscription for " + info.url, R.string.subscription_update_failed);
|
||||
onUnrecoverableError(throwable, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(info.getServiceId()), "Updating Subscription for " + info.getUrl(), R.string.subscription_update_failed);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -343,7 +325,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||
final Consumer<Throwable> onError = new Consumer<Throwable>() {
|
||||
@Override
|
||||
public void accept(@NonNull Throwable throwable) throws Exception {
|
||||
onUnrecoverableError(throwable, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(currentInfo.service_id), "Subscription Change", R.string.subscription_change_failed);
|
||||
onUnrecoverableError(throwable, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(currentInfo.getServiceId()), "Subscription Change", R.string.subscription_change_failed);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -367,9 +349,9 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||
if (subscriptionEntities.isEmpty()) {
|
||||
if (DEBUG) Log.d(TAG, "No subscription to this channel!");
|
||||
SubscriptionEntity channel = new SubscriptionEntity();
|
||||
channel.setServiceId(info.service_id);
|
||||
channel.setUrl(info.url);
|
||||
channel.setData(info.name, info.avatar_url, info.description, info.subscriber_count);
|
||||
channel.setServiceId(info.getServiceId());
|
||||
channel.setUrl(info.getUrl());
|
||||
channel.setData(info.getName(), info.getAvatarUrl(), info.getDescription(), info.getSubscriberCount());
|
||||
subscribeButtonMonitor = monitorSubscribeButton(headerSubscribeButton, mapOnSubscribe(channel));
|
||||
} else {
|
||||
if (DEBUG) Log.d(TAG, "Found subscription to this channel!");
|
||||
@@ -440,16 +422,16 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||
imageLoader.displayImage(result.banner_url, headerChannelBanner, DISPLAY_BANNER_OPTIONS);
|
||||
imageLoader.displayImage(result.avatar_url, headerAvatarView, DISPLAY_AVATAR_OPTIONS);
|
||||
|
||||
if (result.subscriber_count != -1) {
|
||||
headerSubscribersTextView.setText(Localization.localizeSubscribersCount(activity, result.subscriber_count));
|
||||
if (result.getSubscriberCount() != -1) {
|
||||
headerSubscribersTextView.setText(Localization.localizeSubscribersCount(activity, result.getSubscriberCount()));
|
||||
headerSubscribersTextView.setVisibility(View.VISIBLE);
|
||||
} else headerSubscribersTextView.setVisibility(View.GONE);
|
||||
|
||||
if (menuRssButton != null) menuRssButton.setVisible(!TextUtils.isEmpty(result.feed_url));
|
||||
if (menuRssButton != null) menuRssButton.setVisible(!TextUtils.isEmpty(result.getFeedUrl()));
|
||||
playlistCtrl.setVisibility(View.VISIBLE);
|
||||
|
||||
if (!result.errors.isEmpty()) {
|
||||
showSnackBarError(result.errors, UserAction.REQUESTED_CHANNEL, NewPipe.getNameOfService(result.service_id), result.url, 0);
|
||||
showSnackBarError(result.errors, UserAction.REQUESTED_CHANNEL, NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
|
||||
}
|
||||
|
||||
if (disposables != null) disposables.clear();
|
||||
@@ -466,13 +448,6 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||
headerPopupButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !PermissionHelper.checkSystemAlertWindowPermission(activity)) {
|
||||
Toast toast = Toast.makeText(activity, R.string.msg_popup_permission, Toast.LENGTH_LONG);
|
||||
TextView messageView = toast.getView().findViewById(android.R.id.message);
|
||||
if (messageView != null) messageView.setGravity(Gravity.CENTER);
|
||||
toast.show();
|
||||
return;
|
||||
}
|
||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue());
|
||||
}
|
||||
});
|
||||
@@ -490,9 +465,9 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||
|
||||
private PlayQueue getPlayQueue(final int index) {
|
||||
return new ChannelPlayQueue(
|
||||
currentInfo.service_id,
|
||||
currentInfo.url,
|
||||
currentInfo.next_streams_url,
|
||||
currentInfo.getServiceId(),
|
||||
currentInfo.getUrl(),
|
||||
currentInfo.getNextStreamsUrl(),
|
||||
infoListAdapter.getItemsList(),
|
||||
index
|
||||
);
|
||||
@@ -502,8 +477,8 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||
public void handleNextItems(ListExtractor.NextItemsResult result) {
|
||||
super.handleNextItems(result);
|
||||
|
||||
if (!result.errors.isEmpty()) {
|
||||
showSnackBarError(result.errors, UserAction.REQUESTED_CHANNEL, NewPipe.getNameOfService(serviceId),
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(), UserAction.REQUESTED_CHANNEL, NewPipe.getNameOfService(serviceId),
|
||||
"Get next page of: " + url, R.string.general_error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,12 +297,12 @@ public class FeedFragment extends BaseListFragment<List<SubscriptionEntity>, Voi
|
||||
// Called only when response is non-empty
|
||||
@Override
|
||||
public void onSuccess(final ChannelInfo channelInfo) {
|
||||
if (infoListAdapter == null || channelInfo.related_streams.isEmpty()) {
|
||||
if (infoListAdapter == null || channelInfo.getRelatedStreams().isEmpty()) {
|
||||
onDone();
|
||||
return;
|
||||
}
|
||||
|
||||
final InfoItem item = channelInfo.related_streams.get(0);
|
||||
final InfoItem item = channelInfo.getRelatedStreams().get(0);
|
||||
// Keep requesting new items if the current one already exists
|
||||
boolean itemExists = doesItemExist(infoListAdapter.getItemsList(), item);
|
||||
if (!itemExists) {
|
||||
@@ -412,9 +412,9 @@ public class FeedFragment extends BaseListFragment<List<SubscriptionEntity>, Voi
|
||||
private boolean doesItemExist(final List<InfoItem> items, final InfoItem item) {
|
||||
for (final InfoItem existingItem : items) {
|
||||
if (existingItem.info_type == item.info_type &&
|
||||
existingItem.service_id == item.service_id &&
|
||||
existingItem.name.equals(item.name) &&
|
||||
existingItem.url.equals(item.url)) return true;
|
||||
existingItem.getServiceId() == item.getServiceId() &&
|
||||
existingItem.getName().equals(item.getName()) &&
|
||||
existingItem.getUrl().equals(item.getUrl())) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
@@ -56,15 +57,12 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
|
||||
|
||||
@State
|
||||
protected String kioskId = "";
|
||||
|
||||
protected String kioskTranslatedName;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Views
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private View headerRootLayout;
|
||||
private TextView headerTitleView;
|
||||
|
||||
public static KioskFragment getInstance(int serviceId)
|
||||
throws ExtractionException {
|
||||
return getInstance(serviceId, NewPipe.getService(serviceId)
|
||||
@@ -89,20 +87,30 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
|
||||
// LifeCycle
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
kioskTranslatedName = KioskTranslator.getTranslatedKioskName(kioskId, activity);
|
||||
name = kioskTranslatedName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
if(useAsFrontPage && isVisibleToUser) {
|
||||
if(useAsFrontPage && isVisibleToUser && activity != null) {
|
||||
try {
|
||||
activity.getSupportActionBar().setTitle(KioskTranslator.getTranslatedKioskName(kioskId, getActivity()));
|
||||
setTitle(kioskTranslatedName);
|
||||
} catch (Exception e) {
|
||||
onError(e);
|
||||
onUnrecoverableError(e, UserAction.UI_ERROR,
|
||||
"none",
|
||||
"none", R.string.app_ui_crash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_kiosk, container, false);
|
||||
}
|
||||
|
||||
@@ -127,14 +135,18 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
|
||||
public Single<KioskInfo> loadResult(boolean forceReload) {
|
||||
String contentCountry = PreferenceManager
|
||||
.getDefaultSharedPreferences(activity)
|
||||
.getString(getString(R.string.search_language_key),
|
||||
getString(R.string.default_language_value));
|
||||
.getString(getString(R.string.content_country_key),
|
||||
getString(R.string.default_country_value));
|
||||
return ExtractorHelper.getKioskInfo(serviceId, url, contentCountry, forceReload);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Single<ListExtractor.NextItemsResult> loadMoreItemsLogic() {
|
||||
return ExtractorHelper.getMoreKioskItems(serviceId, url, currentNextItemsUrl);
|
||||
String contentCountry = PreferenceManager
|
||||
.getDefaultSharedPreferences(activity)
|
||||
.getString(getString(R.string.content_country_key),
|
||||
getString(R.string.default_country_value));
|
||||
return ExtractorHelper.getMoreKioskItems(serviceId, url, currentNextItemsUrl, contentCountry);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
@@ -151,14 +163,13 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
|
||||
public void handleResult(@NonNull final KioskInfo result) {
|
||||
super.handleResult(result);
|
||||
|
||||
String title = KioskTranslator.getTranslatedKioskName(result.id, getActivity());
|
||||
ActionBar supportActionBar = activity.getSupportActionBar();
|
||||
supportActionBar.setTitle(title);
|
||||
name = kioskTranslatedName;
|
||||
setTitle(kioskTranslatedName);
|
||||
|
||||
if (!result.errors.isEmpty()) {
|
||||
showSnackBarError(result.errors,
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(),
|
||||
UserAction.REQUESTED_PLAYLIST,
|
||||
NewPipe.getNameOfService(result.service_id), result.url, 0);
|
||||
NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,8 +177,8 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
|
||||
public void handleNextItems(ListExtractor.NextItemsResult result) {
|
||||
super.handleNextItems(result);
|
||||
|
||||
if (!result.errors.isEmpty()) {
|
||||
showSnackBarError(result.errors,
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(),
|
||||
UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(serviceId)
|
||||
, "Get next page of: " + url, 0);
|
||||
}
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
package org.schabi.newpipe.fragments.list.playlist;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
@@ -32,7 +31,6 @@ import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.PermissionHelper;
|
||||
|
||||
import io.reactivex.Single;
|
||||
|
||||
@@ -98,16 +96,10 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||
infoListAdapter.useMiniItemVariants(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "], inflater = [" + inflater + "]");
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
inflater.inflate(R.menu.menu_playlist, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void showStreamDialog(final StreamInfoItem item) {
|
||||
final Context context = getContext();
|
||||
final Activity activity = getActivity();
|
||||
if (context == null || context.getResources() == null || getActivity() == null) return;
|
||||
|
||||
final String[] commands = new String[]{
|
||||
@@ -127,7 +119,7 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
|
||||
break;
|
||||
case 1:
|
||||
NavigationHelper.enqueueOnPopupPlayer(context, new SinglePlayQueue(item));
|
||||
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item));
|
||||
break;
|
||||
case 2:
|
||||
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index));
|
||||
@@ -136,7 +128,7 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index));
|
||||
break;
|
||||
case 4:
|
||||
NavigationHelper.playOnPopupPlayer(context, getPlayQueue(index));
|
||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -146,6 +138,14 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||
|
||||
new InfoItemDialog(getActivity(), item, commands, actions).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "], inflater = [" + inflater + "]");
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
inflater.inflate(R.menu.menu_playlist, menu);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Load and handle
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
@@ -160,6 +160,23 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||
return ExtractorHelper.getPlaylistInfo(serviceId, url, forceLoad);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_item_openInBrowser:
|
||||
openUrlInBrowser(url);
|
||||
break;
|
||||
case R.id.menu_item_share: {
|
||||
shareUrl(name, url);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Contract
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
@@ -181,13 +198,13 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||
animateView(headerRootLayout, true, 100);
|
||||
animateView(headerUploaderLayout, true, 300);
|
||||
headerUploaderLayout.setOnClickListener(null);
|
||||
if (!TextUtils.isEmpty(result.uploader_name)) {
|
||||
headerUploaderName.setText(result.uploader_name);
|
||||
if (!TextUtils.isEmpty(result.uploader_url)) {
|
||||
if (!TextUtils.isEmpty(result.getUploaderName())) {
|
||||
headerUploaderName.setText(result.getUploaderName());
|
||||
if (!TextUtils.isEmpty(result.getUploaderUrl())) {
|
||||
headerUploaderLayout.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
NavigationHelper.openChannelFragment(getFragmentManager(), result.service_id, result.uploader_url, result.uploader_name);
|
||||
NavigationHelper.openChannelFragment(getFragmentManager(), result.getServiceId(), result.getUploaderUrl(), result.getUploaderName());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -195,11 +212,11 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||
|
||||
playlistCtrl.setVisibility(View.VISIBLE);
|
||||
|
||||
imageLoader.displayImage(result.uploader_avatar_url, headerUploaderAvatar, DISPLAY_AVATAR_OPTIONS);
|
||||
imageLoader.displayImage(result.getUploaderAvatarUrl(), headerUploaderAvatar, DISPLAY_AVATAR_OPTIONS);
|
||||
headerStreamCount.setText(getResources().getQuantityString(R.plurals.videos, (int) result.stream_count, (int) result.stream_count));
|
||||
|
||||
if (!result.errors.isEmpty()) {
|
||||
showSnackBarError(result.errors, UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(result.service_id), result.url, 0);
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(), UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
|
||||
}
|
||||
|
||||
headerPlayAllButton.setOnClickListener(new View.OnClickListener() {
|
||||
@@ -211,13 +228,6 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||
headerPopupButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !PermissionHelper.checkSystemAlertWindowPermission(activity)) {
|
||||
Toast toast = Toast.makeText(activity, R.string.msg_popup_permission, Toast.LENGTH_LONG);
|
||||
TextView messageView = toast.getView().findViewById(android.R.id.message);
|
||||
if (messageView != null) messageView.setGravity(Gravity.CENTER);
|
||||
toast.show();
|
||||
return;
|
||||
}
|
||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue());
|
||||
}
|
||||
});
|
||||
@@ -235,9 +245,9 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||
|
||||
private PlayQueue getPlayQueue(final int index) {
|
||||
return new PlaylistPlayQueue(
|
||||
currentInfo.service_id,
|
||||
currentInfo.url,
|
||||
currentInfo.next_streams_url,
|
||||
currentInfo.getServiceId(),
|
||||
currentInfo.getUrl(),
|
||||
currentInfo.getNextStreamsUrl(),
|
||||
infoListAdapter.getItemsList(),
|
||||
index
|
||||
);
|
||||
@@ -247,8 +257,8 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||
public void handleNextItems(ListExtractor.NextItemsResult result) {
|
||||
super.handleNextItems(result);
|
||||
|
||||
if (!result.errors.isEmpty()) {
|
||||
showSnackBarError(result.errors, UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(serviceId)
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(), UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(serviceId)
|
||||
, "Get next page of: " + url, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor
|
||||
|
||||
private int currentPage = 0;
|
||||
private int currentNextPage = 0;
|
||||
private String searchLanguage;
|
||||
private String contentCountry;
|
||||
private boolean isSuggestionsEnabled = true;
|
||||
private boolean isSearchHistoryEnabled = true;
|
||||
|
||||
@@ -176,7 +176,7 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor
|
||||
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||
isSuggestionsEnabled = preferences.getBoolean(getString(R.string.show_search_suggestions_key), true);
|
||||
searchLanguage = preferences.getString(getString(R.string.search_language_key), getString(R.string.default_language_value));
|
||||
contentCountry = preferences.getString(getString(R.string.content_country_key), getString(R.string.default_country_value));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -619,7 +619,7 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor
|
||||
return local.materialize();
|
||||
}
|
||||
|
||||
final Observable<List<SuggestionItem>> network = ExtractorHelper.suggestionsFor(serviceId, query, searchLanguage).toObservable()
|
||||
final Observable<List<SuggestionItem>> network = ExtractorHelper.suggestionsFor(serviceId, query, contentCountry).toObservable()
|
||||
.map(new Function<List<String>, List<SuggestionItem>>() {
|
||||
@Override
|
||||
public List<SuggestionItem> apply(@io.reactivex.annotations.NonNull List<String> strings) throws Exception {
|
||||
@@ -731,7 +731,7 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor
|
||||
super.startLoading(forceLoad);
|
||||
if (disposables != null) disposables.clear();
|
||||
if (searchDisposable != null) searchDisposable.dispose();
|
||||
searchDisposable = ExtractorHelper.searchFor(serviceId, searchQuery, currentPage, searchLanguage, filter)
|
||||
searchDisposable = ExtractorHelper.searchFor(serviceId, searchQuery, currentPage, contentCountry, filter)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Consumer<SearchResult>() {
|
||||
@@ -755,7 +755,7 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor
|
||||
showListFooter(true);
|
||||
if (searchDisposable != null) searchDisposable.dispose();
|
||||
currentNextPage = currentPage + 1;
|
||||
searchDisposable = ExtractorHelper.getMoreSearchItems(serviceId, searchQuery, currentNextPage, searchLanguage, filter)
|
||||
searchDisposable = ExtractorHelper.getMoreSearchItems(serviceId, searchQuery, currentNextPage, contentCountry, filter)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Consumer<ListExtractor.NextItemsResult>() {
|
||||
@@ -861,8 +861,8 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor
|
||||
lastSearchedQuery = searchQuery;
|
||||
|
||||
if (infoListAdapter.getItemsList().size() == 0) {
|
||||
if (result.resultList.size() > 0) {
|
||||
infoListAdapter.addInfoItemList(result.resultList);
|
||||
if (!result.getResults().isEmpty()) {
|
||||
infoListAdapter.addInfoItemList(result.getResults());
|
||||
} else {
|
||||
infoListAdapter.clearStreamItemList();
|
||||
showEmptyState();
|
||||
@@ -876,11 +876,11 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor
|
||||
@Override
|
||||
public void handleNextItems(ListExtractor.NextItemsResult result) {
|
||||
showListFooter(false);
|
||||
currentPage = Integer.parseInt(result.nextItemsUrl);
|
||||
infoListAdapter.addInfoItemList(result.nextItemsList);
|
||||
currentPage = Integer.parseInt(result.getNextItemsUrl());
|
||||
infoListAdapter.addInfoItemList(result.getNextItemsList());
|
||||
|
||||
if (!result.errors.isEmpty()) {
|
||||
showSnackBarError(result.errors, UserAction.SEARCHED, NewPipe.getNameOfService(serviceId)
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(), UserAction.SEARCHED, NewPipe.getNameOfService(serviceId)
|
||||
, "\"" + searchQuery + "\" → page " + currentPage, 0);
|
||||
}
|
||||
super.handleNextItems(result);
|
||||
|
||||
@@ -129,7 +129,7 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
|
||||
@Override
|
||||
public void selected(ChannelInfoItem selectedItem) {
|
||||
// Requires the parent fragment to find holder for fragment replacement
|
||||
NavigationHelper.openChannelFragment(getParentFragment().getFragmentManager(), selectedItem.service_id, selectedItem.url, selectedItem.name);
|
||||
NavigationHelper.openChannelFragment(getParentFragment().getFragmentManager(), selectedItem.getServiceId(), selectedItem.url, selectedItem.getName());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ public class SubscriptionService {
|
||||
// Subscriber count changes very often, making this check almost unnecessary.
|
||||
// Consider removing it later.
|
||||
if (!isSubscriptionUpToDate(info, subscription)) {
|
||||
subscription.setData(info.name, info.avatar_url, info.description, info.subscriber_count);
|
||||
subscription.setData(info.getName(), info.getAvatarUrl(), info.getDescription(), info.getSubscriberCount());
|
||||
|
||||
return update(subscription);
|
||||
}
|
||||
@@ -122,7 +122,7 @@ public class SubscriptionService {
|
||||
}
|
||||
};
|
||||
|
||||
return subscriptionTable().getSubscription(info.service_id, info.url)
|
||||
return subscriptionTable().getSubscription(info.getServiceId(), info.getUrl())
|
||||
.firstOrError()
|
||||
.flatMapCompletable(update);
|
||||
}
|
||||
@@ -137,11 +137,11 @@ public class SubscriptionService {
|
||||
}
|
||||
|
||||
private boolean isSubscriptionUpToDate(final ChannelInfo info, final SubscriptionEntity entity) {
|
||||
return info.url.equals(entity.getUrl()) &&
|
||||
info.service_id == entity.getServiceId() &&
|
||||
info.name.equals(entity.getName()) &&
|
||||
info.avatar_url.equals(entity.getAvatarUrl()) &&
|
||||
info.description.equals(entity.getDescription()) &&
|
||||
info.subscriber_count == entity.getSubscriberCount();
|
||||
return info.getUrl().equals(entity.getUrl()) &&
|
||||
info.getServiceId() == entity.getServiceId() &&
|
||||
info.getName().equals(entity.getName()) &&
|
||||
info.getAvatarUrl().equals(entity.getAvatarUrl()) &&
|
||||
info.getDescription().equals(entity.getDescription()) &&
|
||||
info.getSubscriberCount() == entity.getSubscriberCount();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ public class InfoItemDialog {
|
||||
@NonNull final StreamInfoItem info,
|
||||
@NonNull final String[] commands,
|
||||
@NonNull final DialogInterface.OnClickListener actions) {
|
||||
this(activity, commands, actions, info.name, info.uploader_name);
|
||||
this(activity, commands, actions, info.getName(), info.uploader_name);
|
||||
}
|
||||
|
||||
public InfoItemDialog(@NonNull final Activity activity,
|
||||
|
||||
@@ -36,7 +36,7 @@ public class ChannelMiniInfoItemHolder extends InfoItemHolder {
|
||||
if (!(infoItem instanceof ChannelInfoItem)) return;
|
||||
final ChannelInfoItem item = (ChannelInfoItem) infoItem;
|
||||
|
||||
itemTitleView.setText(item.name);
|
||||
itemTitleView.setText(item.getName());
|
||||
itemAdditionalDetailView.setText(getDetailLine(item));
|
||||
|
||||
itemBuilder.getImageLoader()
|
||||
|
||||
@@ -32,7 +32,7 @@ public class PlaylistInfoItemHolder extends InfoItemHolder {
|
||||
if (!(infoItem instanceof PlaylistInfoItem)) return;
|
||||
final PlaylistInfoItem item = (PlaylistInfoItem) infoItem;
|
||||
|
||||
itemTitleView.setText(item.name);
|
||||
itemTitleView.setText(item.getName());
|
||||
itemStreamCountView.setText(item.stream_count + "");
|
||||
itemUploaderView.setText(item.uploader_name);
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
|
||||
if (!(infoItem instanceof StreamInfoItem)) return;
|
||||
final StreamInfoItem item = (StreamInfoItem) infoItem;
|
||||
|
||||
itemVideoTitleView.setText(item.name);
|
||||
itemVideoTitleView.setText(item.getName());
|
||||
itemUploaderView.setText(item.uploader_name);
|
||||
|
||||
if (item.duration > 0) {
|
||||
|
||||
@@ -65,7 +65,6 @@ public final class BackgroundPlayer extends Service {
|
||||
|
||||
public static final String ACTION_CLOSE = "org.schabi.newpipe.player.BackgroundPlayer.CLOSE";
|
||||
public static final String ACTION_PLAY_PAUSE = "org.schabi.newpipe.player.BackgroundPlayer.PLAY_PAUSE";
|
||||
public static final String ACTION_OPEN_CONTROLS = "org.schabi.newpipe.player.BackgroundPlayer.OPEN_CONTROLS";
|
||||
public static final String ACTION_REPEAT = "org.schabi.newpipe.player.BackgroundPlayer.REPEAT";
|
||||
public static final String ACTION_PLAY_NEXT = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_PLAY_NEXT";
|
||||
public static final String ACTION_PLAY_PREVIOUS = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_PLAY_PREVIOUS";
|
||||
@@ -177,7 +176,7 @@ public final class BackgroundPlayer extends Service {
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, getString(R.string.notification_channel_id))
|
||||
.setOngoing(true)
|
||||
.setSmallIcon(R.drawable.ic_play_circle_filled_white_24dp)
|
||||
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setCustomContentView(notRemoteView)
|
||||
.setCustomBigContentView(bigNotRemoteView);
|
||||
@@ -195,11 +194,14 @@ public final class BackgroundPlayer extends Service {
|
||||
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_PAUSE), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||
remoteViews.setOnClickPendingIntent(R.id.notificationStop,
|
||||
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_CLOSE), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||
remoteViews.setOnClickPendingIntent(R.id.notificationContent,
|
||||
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_OPEN_CONTROLS), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||
remoteViews.setOnClickPendingIntent(R.id.notificationRepeat,
|
||||
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_REPEAT), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||
|
||||
// Starts background player activity -- attempts to unlock lockscreen
|
||||
final Intent intent = NavigationHelper.getBackgroundPlayerActivityIntent(this);
|
||||
remoteViews.setOnClickPendingIntent(R.id.notificationContent,
|
||||
PendingIntent.getActivity(this, NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT));
|
||||
|
||||
if (basePlayerImpl.playQueue != null && basePlayerImpl.playQueue.size() > 1) {
|
||||
remoteViews.setInt(R.id.notificationFRewind, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_previous);
|
||||
remoteViews.setInt(R.id.notificationFForward, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_next);
|
||||
@@ -393,7 +395,7 @@ public final class BackgroundPlayer extends Service {
|
||||
if (index < 0 || index >= info.audio_streams.size()) return null;
|
||||
|
||||
final AudioStream audio = info.audio_streams.get(index);
|
||||
return buildMediaSource(audio.url, MediaFormat.getSuffixById(audio.format));
|
||||
return buildMediaSource(audio.getUrl(), MediaFormat.getSuffixById(audio.getFormatId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -453,7 +455,6 @@ public final class BackgroundPlayer extends Service {
|
||||
super.setupBroadcastReceiver(intentFilter);
|
||||
intentFilter.addAction(ACTION_CLOSE);
|
||||
intentFilter.addAction(ACTION_PLAY_PAUSE);
|
||||
intentFilter.addAction(ACTION_OPEN_CONTROLS);
|
||||
intentFilter.addAction(ACTION_REPEAT);
|
||||
intentFilter.addAction(ACTION_PLAY_PREVIOUS);
|
||||
intentFilter.addAction(ACTION_PLAY_NEXT);
|
||||
@@ -478,9 +479,6 @@ public final class BackgroundPlayer extends Service {
|
||||
case ACTION_PLAY_PAUSE:
|
||||
onVideoPlayPause();
|
||||
break;
|
||||
case ACTION_OPEN_CONTROLS:
|
||||
NavigationHelper.openBackgroundPlayerControl(getApplicationContext());
|
||||
break;
|
||||
case ACTION_REPEAT:
|
||||
onRepeatClicked();
|
||||
break;
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package org.schabi.newpipe.player;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.util.PermissionHelper;
|
||||
|
||||
import static org.schabi.newpipe.player.BackgroundPlayer.ACTION_CLOSE;
|
||||
|
||||
public final class BackgroundPlayerActivity extends ServicePlayerActivity {
|
||||
|
||||
@@ -36,4 +40,31 @@ public final class BackgroundPlayerActivity extends ServicePlayerActivity {
|
||||
((BackgroundPlayer.BasePlayerImpl) player).removeActivityListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlayerOptionMenuResource() {
|
||||
return R.menu.menu_play_queue_bg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPlayerOptionSelected(MenuItem item) {
|
||||
if (item.getItemId() == R.id.action_switch_popup) {
|
||||
|
||||
if (!PermissionHelper.isPopupEnabled(this)) {
|
||||
PermissionHelper.showPopupEnablementToast(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
this.player.setRecovery();
|
||||
getApplicationContext().sendBroadcast(getPlayerShutdownIntent());
|
||||
getApplicationContext().startService(getSwitchIntent(PopupVideoPlayer.class));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getPlayerShutdownIntent() {
|
||||
return new Intent(ACTION_CLOSE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +111,7 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||
public static final String PLAYBACK_QUALITY = "playback_quality";
|
||||
public static final String PLAY_QUEUE = "play_queue";
|
||||
public static final String APPEND_ONLY = "append_only";
|
||||
public static final String SELECT_ON_APPEND = "select_on_append";
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Playback
|
||||
@@ -218,7 +219,13 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||
|
||||
// Resolve append intents
|
||||
if (intent.getBooleanExtra(APPEND_ONLY, false) && playQueue != null) {
|
||||
int sizeBeforeAppend = playQueue.size();
|
||||
playQueue.append(queue.getStreams());
|
||||
|
||||
if (intent.getBooleanExtra(SELECT_ON_APPEND, false) && queue.getStreams().size() > 0) {
|
||||
playQueue.setIndex(sizeBeforeAppend);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
@@ -62,10 +61,9 @@ import org.schabi.newpipe.util.AnimationUtils;
|
||||
import org.schabi.newpipe.util.ListHelper;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.PermissionHelper;
|
||||
import org.schabi.newpipe.util.PopupMenuIconHacker;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
@@ -78,6 +76,7 @@ import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
public final class MainVideoPlayer extends Activity {
|
||||
private static final String TAG = ".MainVideoPlayer";
|
||||
private static final boolean DEBUG = BasePlayer.DEBUG;
|
||||
private static final String PLAYER_STATE_INTENT = "player_state_intent";
|
||||
|
||||
private GestureDetector gestureDetector;
|
||||
|
||||
@@ -99,19 +98,41 @@ public final class MainVideoPlayer extends Activity {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) getWindow().setStatusBarColor(Color.BLACK);
|
||||
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
||||
|
||||
if (getIntent() == null) {
|
||||
final Intent intent;
|
||||
if (savedInstanceState != null && savedInstanceState.getParcelable(PLAYER_STATE_INTENT) != null) {
|
||||
intent = savedInstanceState.getParcelable(PLAYER_STATE_INTENT);
|
||||
} else {
|
||||
intent = getIntent();
|
||||
}
|
||||
|
||||
if (intent == null) {
|
||||
Toast.makeText(this, R.string.general_error, Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
showSystemUi();
|
||||
setContentView(R.layout.activity_main_player);
|
||||
playerImpl = new VideoPlayerImpl(this);
|
||||
playerImpl.setup(findViewById(android.R.id.content));
|
||||
playerImpl.handleIntent(getIntent());
|
||||
playerImpl.handleIntent(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
if (this.playerImpl == null) return;
|
||||
|
||||
final Intent intent = NavigationHelper.getPlayerIntent(
|
||||
getApplicationContext(),
|
||||
this.getClass(),
|
||||
this.playerImpl.getPlayQueue(),
|
||||
this.playerImpl.getRepeatMode(),
|
||||
this.playerImpl.getPlaybackSpeed(),
|
||||
this.playerImpl.getPlaybackPitch(),
|
||||
this.playerImpl.getPlaybackQuality()
|
||||
);
|
||||
outState.putParcelable(PLAYER_STATE_INTENT, intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -303,7 +324,7 @@ public final class MainVideoPlayer extends Activity {
|
||||
this.playNextButton = rootView.findViewById(R.id.playNextButton);
|
||||
this.moreOptionsButton = rootView.findViewById(R.id.moreOptionsButton);
|
||||
this.moreOptionsPopupMenu = new PopupMenu(context, moreOptionsButton);
|
||||
this.moreOptionsPopupMenu.getMenuInflater().inflate(R.menu.menu_videooptions, moreOptionsPopupMenu.getMenu());
|
||||
buildMoreOptionsMenu();
|
||||
|
||||
titleTextView.setSelected(true);
|
||||
channelTextView.setSelected(true);
|
||||
@@ -356,7 +377,7 @@ public final class MainVideoPlayer extends Activity {
|
||||
titleTextView.setText(getVideoTitle());
|
||||
channelTextView.setText(getUploaderName());
|
||||
|
||||
playPauseButton.setImageResource(R.drawable.ic_pause_white);
|
||||
//playPauseButton.setImageResource(R.drawable.ic_pause_white);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -376,9 +397,8 @@ public final class MainVideoPlayer extends Activity {
|
||||
if (DEBUG) Log.d(TAG, "onFullScreenButtonClicked() called");
|
||||
if (simpleExoPlayer == null) return;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||
&& !PermissionHelper.checkSystemAlertWindowPermission(MainVideoPlayer.this)) {
|
||||
Toast.makeText(MainVideoPlayer.this, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
|
||||
if (!PermissionHelper.isPopupEnabled(context)) {
|
||||
PermissionHelper.showPopupEnablementToast(context);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -448,12 +468,9 @@ public final class MainVideoPlayer extends Activity {
|
||||
|
||||
if (getCurrentState() != STATE_COMPLETED) {
|
||||
getControlsVisibilityHandler().removeCallbacksAndMessages(null);
|
||||
animateView(getControlsRoot(), true, 300, 0, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (getCurrentState() == STATE_PLAYING && !isSomePopupMenuVisible()) {
|
||||
hideControls(300, DEFAULT_CONTROLS_HIDE_TIME);
|
||||
}
|
||||
animateView(getControlsRoot(), true, 300, 0, () -> {
|
||||
if (getCurrentState() == STATE_PLAYING && !isSomePopupMenuVisible()) {
|
||||
hideControls(300, DEFAULT_CONTROLS_HIDE_TIME);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -479,25 +496,7 @@ public final class MainVideoPlayer extends Activity {
|
||||
|
||||
private void onMoreOptionsClicked() {
|
||||
if (DEBUG) Log.d(TAG, "onMoreOptionsClicked() called");
|
||||
buildMoreOptionsMenu();
|
||||
|
||||
try {
|
||||
Field[] fields = moreOptionsPopupMenu.getClass().getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
if ("mPopup".equals(field.getName())) {
|
||||
field.setAccessible(true);
|
||||
Object menuPopupHelper = field.get(moreOptionsPopupMenu);
|
||||
Class<?> classPopupHelper = Class.forName(menuPopupHelper
|
||||
.getClass().getName());
|
||||
Method setForceIcons = classPopupHelper.getMethod(
|
||||
"setForceShowIcon", boolean.class);
|
||||
setForceIcons.invoke(menuPopupHelper, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
moreOptionsPopupMenu.show();
|
||||
isSomePopupMenuVisible = true;
|
||||
showControls(300);
|
||||
@@ -506,6 +505,7 @@ public final class MainVideoPlayer extends Activity {
|
||||
private void onScreenRotationClicked() {
|
||||
if (DEBUG) Log.d(TAG, "onScreenRotationClicked() called");
|
||||
toggleOrientation();
|
||||
showControlsThenHide();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -561,12 +561,9 @@ public final class MainVideoPlayer extends Activity {
|
||||
@Override
|
||||
public void onPlaying() {
|
||||
super.onPlaying();
|
||||
animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 80, 0, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
playPauseButton.setImageResource(R.drawable.ic_pause_white);
|
||||
animatePlayButtons(true, 200);
|
||||
}
|
||||
animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 80, 0, () -> {
|
||||
playPauseButton.setImageResource(R.drawable.ic_pause_white);
|
||||
animatePlayButtons(true, 200);
|
||||
});
|
||||
showSystemUi();
|
||||
getRootView().setKeepScreenOn(true);
|
||||
@@ -575,12 +572,9 @@ public final class MainVideoPlayer extends Activity {
|
||||
@Override
|
||||
public void onPaused() {
|
||||
super.onPaused();
|
||||
animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 80, 0, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white);
|
||||
animatePlayButtons(true, 200);
|
||||
}
|
||||
animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 80, 0, () -> {
|
||||
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white);
|
||||
animatePlayButtons(true, 200);
|
||||
});
|
||||
|
||||
showSystemUi();
|
||||
@@ -598,12 +592,9 @@ public final class MainVideoPlayer extends Activity {
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
showSystemUi();
|
||||
animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 0, 0, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
playPauseButton.setImageResource(R.drawable.ic_replay_white);
|
||||
animatePlayButtons(true, 300);
|
||||
}
|
||||
animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 0, 0, () -> {
|
||||
playPauseButton.setImageResource(R.drawable.ic_replay_white);
|
||||
animatePlayButtons(true, 300);
|
||||
});
|
||||
|
||||
getRootView().setKeepScreenOn(false);
|
||||
@@ -632,17 +623,10 @@ public final class MainVideoPlayer extends Activity {
|
||||
public void hideControls(final long duration, long delay) {
|
||||
if (DEBUG) Log.d(TAG, "hideControls() called with: delay = [" + delay + "]");
|
||||
getControlsVisibilityHandler().removeCallbacksAndMessages(null);
|
||||
getControlsVisibilityHandler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
animateView(getControlsRoot(), false, duration, 0, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
hideSystemUi();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, delay);
|
||||
getControlsVisibilityHandler().postDelayed(() ->
|
||||
animateView(getControlsRoot(), false, duration, 0, MainVideoPlayer.this::hideSystemUi),
|
||||
delay
|
||||
);
|
||||
}
|
||||
|
||||
private void updatePlaybackButtons() {
|
||||
@@ -654,24 +638,39 @@ public final class MainVideoPlayer extends Activity {
|
||||
}
|
||||
|
||||
private void buildMoreOptionsMenu() {
|
||||
if (moreOptionsPopupMenu == null) return;
|
||||
moreOptionsPopupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
switch (menuItem.getItemId()) {
|
||||
case R.id.toggleOrientation:
|
||||
onScreenRotationClicked();
|
||||
break;
|
||||
case R.id.switchPopup:
|
||||
onFullScreenButtonClicked();
|
||||
break;
|
||||
case R.id.switchBackground:
|
||||
onPlayBackgroundButtonClicked();
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
this.moreOptionsPopupMenu.getMenuInflater().inflate(R.menu.menu_videooptions,
|
||||
moreOptionsPopupMenu.getMenu());
|
||||
|
||||
moreOptionsPopupMenu.setOnMenuItemClickListener(menuItem -> {
|
||||
switch (menuItem.getItemId()) {
|
||||
case R.id.toggleOrientation:
|
||||
onScreenRotationClicked();
|
||||
break;
|
||||
case R.id.switchPopup:
|
||||
onFullScreenButtonClicked();
|
||||
break;
|
||||
case R.id.switchBackground:
|
||||
onPlayBackgroundButtonClicked();
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
try {
|
||||
PopupMenuIconHacker.setShowPopupIcon(moreOptionsPopupMenu);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// fix icon theme
|
||||
if(ThemeHelper.isLightThemeSelected(MainVideoPlayer.this)) {
|
||||
moreOptionsPopupMenu.getMenu()
|
||||
.findItem(R.id.toggleOrientation)
|
||||
.setIcon(R.drawable.ic_screen_rotation_black_24dp);
|
||||
moreOptionsPopupMenu.getMenu()
|
||||
.findItem(R.id.switchPopup)
|
||||
.setIcon(R.drawable.ic_fullscreen_exit_black_24dp);
|
||||
}
|
||||
}
|
||||
|
||||
private void buildQueue() {
|
||||
@@ -692,12 +691,7 @@ public final class MainVideoPlayer extends Activity {
|
||||
|
||||
playQueueAdapter.setSelectedListener(getOnSelectedListener());
|
||||
|
||||
itemsListCloseButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
onQueueClosed();
|
||||
}
|
||||
});
|
||||
itemsListCloseButton.setOnClickListener(view -> onQueueClosed());
|
||||
}
|
||||
|
||||
private OnScrollBelowItemsListener getQueueScrollListener() {
|
||||
|
||||
@@ -31,7 +31,6 @@ import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
@@ -44,48 +43,30 @@ import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.RemoteViews;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.exoplayer2.PlaybackParameters;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
|
||||
import org.schabi.newpipe.BuildConfig;
|
||||
import org.schabi.newpipe.MainActivity;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.ReCaptchaActivity;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
||||
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||
import org.schabi.newpipe.player.event.PlayerEventListener;
|
||||
import org.schabi.newpipe.player.helper.LockManager;
|
||||
import org.schabi.newpipe.player.helper.PlayerHelper;
|
||||
import org.schabi.newpipe.player.old.PlayVideoActivity;
|
||||
import org.schabi.newpipe.player.helper.LockManager;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
import org.schabi.newpipe.util.ListHelper;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.functions.Consumer;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.isUsingOldPlayer;
|
||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
|
||||
@@ -101,7 +82,6 @@ public final class PopupVideoPlayer extends Service {
|
||||
private static final int NOTIFICATION_ID = 40028922;
|
||||
public static final String ACTION_CLOSE = "org.schabi.newpipe.player.PopupVideoPlayer.CLOSE";
|
||||
public static final String ACTION_PLAY_PAUSE = "org.schabi.newpipe.player.PopupVideoPlayer.PLAY_PAUSE";
|
||||
public static final String ACTION_OPEN_CONTROLS = "org.schabi.newpipe.player.PopupVideoPlayer.OPEN_CONTROLS";
|
||||
public static final String ACTION_REPEAT = "org.schabi.newpipe.player.PopupVideoPlayer.REPEAT";
|
||||
|
||||
private static final String POPUP_SAVED_WIDTH = "popup_saved_width";
|
||||
@@ -126,8 +106,8 @@ public final class PopupVideoPlayer extends Service {
|
||||
private RemoteViews notRemoteView;
|
||||
|
||||
private VideoPlayerImpl playerImpl;
|
||||
private Disposable currentWorker;
|
||||
private LockManager lockManager;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Service-Activity Binder
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
@@ -158,31 +138,8 @@ public final class PopupVideoPlayer extends Service {
|
||||
if (playerImpl.getPlayer() == null) initPopup();
|
||||
if (!playerImpl.isPlaying()) playerImpl.getPlayer().setPlayWhenReady(true);
|
||||
|
||||
if (intent != null && intent.getStringExtra(Constants.KEY_URL) != null) {
|
||||
final int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
|
||||
final String url = intent.getStringExtra(Constants.KEY_URL);
|
||||
playerImpl.handleIntent(intent);
|
||||
|
||||
playerImpl.setStartedFromNewPipe(false);
|
||||
|
||||
final FetcherHandler fetcherRunnable = new FetcherHandler(this, serviceId, url);
|
||||
currentWorker = ExtractorHelper.getStreamInfo(serviceId,url,false)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Consumer<StreamInfo>() {
|
||||
@Override
|
||||
public void accept(@NonNull StreamInfo info) throws Exception {
|
||||
fetcherRunnable.onReceive(info);
|
||||
}
|
||||
}, new Consumer<Throwable>() {
|
||||
@Override
|
||||
public void accept(@NonNull Throwable throwable) throws Exception {
|
||||
fetcherRunnable.onError(throwable);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
playerImpl.setStartedFromNewPipe(true);
|
||||
playerImpl.handleIntent(intent);
|
||||
}
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@@ -266,16 +223,19 @@ public final class PopupVideoPlayer extends Service {
|
||||
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_PAUSE), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||
notRemoteView.setOnClickPendingIntent(R.id.notificationStop,
|
||||
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_CLOSE), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||
notRemoteView.setOnClickPendingIntent(R.id.notificationContent,
|
||||
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_OPEN_CONTROLS), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||
notRemoteView.setOnClickPendingIntent(R.id.notificationRepeat,
|
||||
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_REPEAT), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||
|
||||
// Starts popup player activity -- attempts to unlock lockscreen
|
||||
final Intent intent = NavigationHelper.getPopupPlayerActivityIntent(this);
|
||||
notRemoteView.setOnClickPendingIntent(R.id.notificationContent,
|
||||
PendingIntent.getActivity(this, NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT));
|
||||
|
||||
setRepeatModeRemote(notRemoteView, playerImpl.getRepeatMode());
|
||||
|
||||
return new NotificationCompat.Builder(this, getString(R.string.notification_channel_id))
|
||||
.setOngoing(true)
|
||||
.setSmallIcon(R.drawable.ic_play_arrow_white)
|
||||
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setContent(notRemoteView);
|
||||
}
|
||||
@@ -310,7 +270,6 @@ public final class PopupVideoPlayer extends Service {
|
||||
}
|
||||
if (lockManager != null) lockManager.releaseWifiAndCpu();
|
||||
if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID);
|
||||
if (currentWorker != null) currentWorker.dispose();
|
||||
mBinder = null;
|
||||
playerImpl = null;
|
||||
|
||||
@@ -401,6 +360,7 @@ public final class PopupVideoPlayer extends Service {
|
||||
|
||||
protected class VideoPlayerImpl extends VideoPlayer {
|
||||
private TextView resizingIndicator;
|
||||
private ImageButton fullScreenButton;
|
||||
|
||||
@Override
|
||||
public void handleIntent(Intent intent) {
|
||||
@@ -418,6 +378,8 @@ public final class PopupVideoPlayer extends Service {
|
||||
public void initViews(View rootView) {
|
||||
super.initViews(rootView);
|
||||
resizingIndicator = rootView.findViewById(R.id.resizing_indicator);
|
||||
fullScreenButton = rootView.findViewById(R.id.fullScreenButton);
|
||||
fullScreenButton.setOnClickListener(v -> onFullScreenButtonClicked());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -457,12 +419,11 @@ public final class PopupVideoPlayer extends Service {
|
||||
this.getPlaybackPitch(),
|
||||
this.getPlaybackQuality()
|
||||
);
|
||||
if (!isStartedFromNewPipe()) intent.putExtra(VideoPlayer.STARTED_FROM_NEWPIPE, false);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
} else {
|
||||
intent = new Intent(PopupVideoPlayer.this, PlayVideoActivity.class)
|
||||
.putExtra(PlayVideoActivity.VIDEO_TITLE, getVideoTitle())
|
||||
.putExtra(PlayVideoActivity.STREAM_URL, getSelectedVideoStream().url)
|
||||
.putExtra(PlayVideoActivity.STREAM_URL, getSelectedVideoStream().getUrl())
|
||||
.putExtra(PlayVideoActivity.VIDEO_URL, getVideoUrl())
|
||||
.putExtra(PlayVideoActivity.START_POSITION, Math.round(getPlayer().getCurrentPosition() / 1000f));
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
@@ -595,7 +556,6 @@ public final class PopupVideoPlayer extends Service {
|
||||
if (DEBUG) Log.d(TAG, "setupBroadcastReceiver() called with: intentFilter = [" + intentFilter + "]");
|
||||
intentFilter.addAction(ACTION_CLOSE);
|
||||
intentFilter.addAction(ACTION_PLAY_PAUSE);
|
||||
intentFilter.addAction(ACTION_OPEN_CONTROLS);
|
||||
intentFilter.addAction(ACTION_REPEAT);
|
||||
|
||||
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
|
||||
@@ -614,9 +574,6 @@ public final class PopupVideoPlayer extends Service {
|
||||
case ACTION_PLAY_PAUSE:
|
||||
onVideoPlayPause();
|
||||
break;
|
||||
case ACTION_OPEN_CONTROLS:
|
||||
NavigationHelper.openPopupPlayerControl(getApplicationContext());
|
||||
break;
|
||||
case ACTION_REPEAT:
|
||||
onRepeatClicked();
|
||||
break;
|
||||
@@ -871,69 +828,4 @@ public final class PopupVideoPlayer extends Service {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetcher handler used if open by a link out of NewPipe
|
||||
*/
|
||||
private class FetcherHandler {
|
||||
private final int serviceId;
|
||||
private final String url;
|
||||
|
||||
private final Context context;
|
||||
private final Handler mainHandler;
|
||||
|
||||
private FetcherHandler(Context context, int serviceId, String url) {
|
||||
this.mainHandler = new Handler(PopupVideoPlayer.this.getMainLooper());
|
||||
this.context = context;
|
||||
this.url = url;
|
||||
this.serviceId = serviceId;
|
||||
}
|
||||
|
||||
private void onReceive(final StreamInfo info) {
|
||||
mainHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final Intent intent = NavigationHelper.getPlayerIntent(getApplicationContext(),
|
||||
PopupVideoPlayer.class, new SinglePlayQueue(info));
|
||||
playerImpl.handleIntent(intent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onError(final Throwable exception) {
|
||||
if (DEBUG) Log.d(TAG, "onError() called with: exception = [" + exception + "]");
|
||||
exception.printStackTrace();
|
||||
mainHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (exception instanceof ReCaptchaException) {
|
||||
onReCaptchaException();
|
||||
} else if (exception instanceof IOException) {
|
||||
Toast.makeText(context, R.string.network_error, Toast.LENGTH_LONG).show();
|
||||
} else if (exception instanceof YoutubeStreamExtractor.GemaException) {
|
||||
Toast.makeText(context, R.string.blocked_by_gema, Toast.LENGTH_LONG).show();
|
||||
} else if (exception instanceof YoutubeStreamExtractor.LiveStreamException) {
|
||||
Toast.makeText(context, R.string.live_streams_not_supported, Toast.LENGTH_LONG).show();
|
||||
} else if (exception instanceof ContentNotAvailableException) {
|
||||
Toast.makeText(context, R.string.content_not_available, Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
int errorId = exception instanceof YoutubeStreamExtractor.DecryptException ? R.string.youtube_signature_decryption_error :
|
||||
exception instanceof ParsingException ? R.string.parsing_error : R.string.general_error;
|
||||
ErrorActivity.reportError(mainHandler, context, exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(UserAction.REQUESTED_STREAM, NewPipe.getNameOfService(serviceId), url, errorId));
|
||||
}
|
||||
}
|
||||
});
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
private void onReCaptchaException() {
|
||||
Toast.makeText(context, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
|
||||
// Starting ReCaptcha Challenge Activity
|
||||
Intent intent = new Intent(context, ReCaptchaActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(intent);
|
||||
stopSelf();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
package org.schabi.newpipe.player;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
|
||||
import static org.schabi.newpipe.player.PopupVideoPlayer.ACTION_CLOSE;
|
||||
|
||||
public final class PopupVideoPlayerActivity extends ServicePlayerActivity {
|
||||
|
||||
private static final String TAG = "PopupVideoPlayerActivity";
|
||||
@@ -36,4 +39,25 @@ public final class PopupVideoPlayerActivity extends ServicePlayerActivity {
|
||||
((PopupVideoPlayer.VideoPlayerImpl) player).removeActivityListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlayerOptionMenuResource() {
|
||||
return R.menu.menu_play_queue_popup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPlayerOptionSelected(MenuItem item) {
|
||||
if (item.getItemId() == R.id.action_switch_background) {
|
||||
this.player.setRecovery();
|
||||
getApplicationContext().sendBroadcast(getPlayerShutdownIntent());
|
||||
getApplicationContext().startService(getSwitchIntent(BackgroundPlayer.class));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getPlayerShutdownIntent() {
|
||||
return new Intent(ACTION_CLOSE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,11 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||
|
||||
public abstract void stopPlayerListener();
|
||||
|
||||
public abstract int getPlayerOptionMenuResource();
|
||||
|
||||
public abstract boolean onPlayerOptionSelected(MenuItem item);
|
||||
|
||||
public abstract Intent getPlayerShutdownIntent();
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Activity Lifecycle
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
@@ -134,6 +139,7 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.menu_play_queue, menu);
|
||||
getMenuInflater().inflate(getPlayerOptionMenuResource(), menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -153,8 +159,13 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||
case R.id.action_system_audio:
|
||||
startActivity(new Intent(Settings.ACTION_SOUND_SETTINGS));
|
||||
return true;
|
||||
case R.id.action_switch_main:
|
||||
this.player.setRecovery();
|
||||
getApplicationContext().sendBroadcast(getPlayerShutdownIntent());
|
||||
getApplicationContext().startActivity(getSwitchIntent(MainVideoPlayer.class));
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
return onPlayerOptionSelected(item) || super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -163,6 +174,17 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||
unbind();
|
||||
}
|
||||
|
||||
protected Intent getSwitchIntent(final Class clazz) {
|
||||
return NavigationHelper.getPlayerIntent(
|
||||
getApplicationContext(),
|
||||
clazz,
|
||||
this.player.getPlayQueue(),
|
||||
this.player.getRepeatMode(),
|
||||
this.player.getPlaybackSpeed(),
|
||||
this.player.getPlaybackPitch(),
|
||||
null
|
||||
).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Service Connection
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
@@ -288,14 +310,11 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||
final float playbackSpeed = BasePlayer.PLAYBACK_SPEEDS[i];
|
||||
final String formattedSpeed = formatSpeed(playbackSpeed);
|
||||
final MenuItem item = playbackSpeedPopupMenu.getMenu().add(PLAYBACK_SPEED_POPUP_MENU_GROUP_ID, i, Menu.NONE, formattedSpeed);
|
||||
item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
if (player == null) return false;
|
||||
item.setOnMenuItemClickListener(menuItem -> {
|
||||
if (player == null) return false;
|
||||
|
||||
player.setPlaybackSpeed(playbackSpeed);
|
||||
return true;
|
||||
}
|
||||
player.setPlaybackSpeed(playbackSpeed);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -308,14 +327,11 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||
final float playbackPitch = BasePlayer.PLAYBACK_PITCHES[i];
|
||||
final String formattedPitch = formatPitch(playbackPitch);
|
||||
final MenuItem item = playbackPitchPopupMenu.getMenu().add(PLAYBACK_PITCH_POPUP_MENU_GROUP_ID, i, Menu.NONE, formattedPitch);
|
||||
item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
if (player == null) return false;
|
||||
item.setOnMenuItemClickListener(menuItem -> {
|
||||
if (player == null) return false;
|
||||
|
||||
player.setPlaybackPitch(playbackPitch);
|
||||
return true;
|
||||
}
|
||||
player.setPlaybackPitch(playbackPitch);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -323,24 +339,18 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||
private void buildItemPopupMenu(final PlayQueueItem item, final View view) {
|
||||
final PopupMenu menu = new PopupMenu(this, view);
|
||||
final MenuItem remove = menu.getMenu().add(RECYCLER_ITEM_POPUP_MENU_GROUP_ID, 0, Menu.NONE, R.string.play_queue_remove);
|
||||
remove.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
if (player == null) return false;
|
||||
remove.setOnMenuItemClickListener(menuItem -> {
|
||||
if (player == null) return false;
|
||||
|
||||
final int index = player.getPlayQueue().indexOf(item);
|
||||
if (index != -1) player.getPlayQueue().remove(index);
|
||||
return true;
|
||||
}
|
||||
final int index = player.getPlayQueue().indexOf(item);
|
||||
if (index != -1) player.getPlayQueue().remove(index);
|
||||
return true;
|
||||
});
|
||||
|
||||
final MenuItem detail = menu.getMenu().add(RECYCLER_ITEM_POPUP_MENU_GROUP_ID, 1, Menu.NONE, R.string.play_queue_stream_detail);
|
||||
detail.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
onOpenDetail(item.getServiceId(), item.getUrl(), item.getTitle());
|
||||
return true;
|
||||
}
|
||||
detail.setOnMenuItemClickListener(menuItem -> {
|
||||
onOpenDetail(item.getServiceId(), item.getUrl(), item.getTitle());
|
||||
return true;
|
||||
});
|
||||
|
||||
menu.show();
|
||||
@@ -529,7 +539,7 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||
@Override
|
||||
public void onMetadataUpdate(StreamInfo info) {
|
||||
if (info != null) {
|
||||
metadataTitle.setText(info.name);
|
||||
metadataTitle.setText(info.getName());
|
||||
metadataArtist.setText(info.uploader_name);
|
||||
scrollToSelected();
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.ProgressBar;
|
||||
@@ -75,16 +74,16 @@ import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
* @author mauriciocolli
|
||||
*/
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.VideoListener, SeekBar.OnSeekBarChangeListener, View.OnClickListener, Player.EventListener, PopupMenu.OnMenuItemClickListener, PopupMenu.OnDismissListener {
|
||||
public abstract class VideoPlayer extends BasePlayer
|
||||
implements SimpleExoPlayer.VideoListener,
|
||||
SeekBar.OnSeekBarChangeListener,
|
||||
View.OnClickListener,
|
||||
Player.EventListener,
|
||||
PopupMenu.OnMenuItemClickListener,
|
||||
PopupMenu.OnDismissListener {
|
||||
public static final boolean DEBUG = BasePlayer.DEBUG;
|
||||
public final String TAG;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Intent
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public static final String STARTED_FROM_NEWPIPE = "started_from_newpipe";
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Player
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
@@ -96,7 +95,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||
|
||||
protected String playbackQuality;
|
||||
|
||||
private boolean startedFromNewPipe = true;
|
||||
protected boolean wasPlaying = false;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
@@ -282,12 +280,12 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||
if (index < 0 || index >= videos.size()) return null;
|
||||
final VideoStream video = videos.get(index);
|
||||
|
||||
final MediaSource streamSource = buildMediaSource(video.url, MediaFormat.getSuffixById(video.format));
|
||||
final MediaSource streamSource = buildMediaSource(video.getUrl(), MediaFormat.getSuffixById(video.format));
|
||||
final AudioStream audio = ListHelper.getHighestQualityAudio(info.audio_streams);
|
||||
if (!video.isVideoOnly || audio == null) return streamSource;
|
||||
|
||||
// Merge with audio stream in case if video does not contain audio
|
||||
final MediaSource audioSource = buildMediaSource(audio.url, MediaFormat.getSuffixById(audio.format));
|
||||
final MediaSource audioSource = buildMediaSource(audio.getUrl(), MediaFormat.getSuffixById(audio.format));
|
||||
return new MergingMediaSource(streamSource, audioSource);
|
||||
}
|
||||
|
||||
@@ -689,14 +687,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||
return availableStreams.get(selectedStreamIndex);
|
||||
}
|
||||
|
||||
public boolean isStartedFromNewPipe() {
|
||||
return startedFromNewPipe;
|
||||
}
|
||||
|
||||
public void setStartedFromNewPipe(boolean startedFromNewPipe) {
|
||||
this.startedFromNewPipe = startedFromNewPipe;
|
||||
}
|
||||
|
||||
public Handler getControlsVisibilityHandler() {
|
||||
return controlsVisibilityHandler;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ abstract class AbstractInfoPlayQueue<T extends ListInfo, U extends InfoItem> ext
|
||||
transient Disposable fetchReactor;
|
||||
|
||||
AbstractInfoPlayQueue(final U item) {
|
||||
this(item.service_id, item.url, null, Collections.<InfoItem>emptyList(), 0);
|
||||
this(item.getServiceId(), item.getUrl(), null, Collections.<InfoItem>emptyList(), 0);
|
||||
}
|
||||
|
||||
AbstractInfoPlayQueue(final int serviceId,
|
||||
|
||||
@@ -15,6 +15,10 @@ public final class ChannelPlayQueue extends AbstractInfoPlayQueue<ChannelInfo, C
|
||||
super(item);
|
||||
}
|
||||
|
||||
public ChannelPlayQueue(final ChannelInfo info) {
|
||||
this(info.getServiceId(), info.getUrl(), info.getNextStreamsUrl(), info.getRelatedStreams(), 0);
|
||||
}
|
||||
|
||||
public ChannelPlayQueue(final int serviceId,
|
||||
final String url,
|
||||
final String nextPageUrl,
|
||||
|
||||
@@ -30,12 +30,12 @@ public class PlayQueueItem implements Serializable {
|
||||
private transient Single<StreamInfo> stream;
|
||||
|
||||
PlayQueueItem(@NonNull final StreamInfo info) {
|
||||
this(info.name, info.url, info.service_id, info.duration, info.thumbnail_url, info.uploader_name);
|
||||
this(info.getName(), info.getUrl(), info.getServiceId(), info.duration, info.thumbnail_url, info.uploader_name);
|
||||
this.stream = Single.just(info);
|
||||
}
|
||||
|
||||
PlayQueueItem(@NonNull final StreamInfoItem item) {
|
||||
this(item.name, item.url, item.service_id, item.duration, item.thumbnail_url, item.uploader_name);
|
||||
this(item.getName(), item.getUrl(), item.getServiceId(), item.duration, item.thumbnail_url, item.uploader_name);
|
||||
}
|
||||
|
||||
private PlayQueueItem(final String name, final String url, final int serviceId,
|
||||
|
||||
@@ -15,6 +15,10 @@ public final class PlaylistPlayQueue extends AbstractInfoPlayQueue<PlaylistInfo,
|
||||
super(item);
|
||||
}
|
||||
|
||||
public PlaylistPlayQueue(final PlaylistInfo info) {
|
||||
this(info.getServiceId(), info.getUrl(), info.getNextStreamsUrl(), info.getRelatedStreams(), 0);
|
||||
}
|
||||
|
||||
public PlaylistPlayQueue(final int serviceId,
|
||||
final String url,
|
||||
final String nextPageUrl,
|
||||
|
||||
@@ -44,6 +44,7 @@ import java.io.StringWriter;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
import java.util.Vector;
|
||||
|
||||
@@ -144,12 +145,7 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
// async call
|
||||
public static void reportError(Handler handler, final Context context, final List<Throwable> el,
|
||||
final Class returnActivity, final View rootView, final ErrorInfo errorInfo) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
reportError(context, el, returnActivity, rootView, errorInfo);
|
||||
}
|
||||
});
|
||||
handler.post(() -> reportError(context, el, returnActivity, rootView, errorInfo));
|
||||
}
|
||||
|
||||
public static void reportError(final Context context, final CrashReportData report, final ErrorInfo errorInfo) {
|
||||
@@ -218,17 +214,13 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
addGuruMeditaion();
|
||||
currentTimeStamp = getCurrentTimeStamp();
|
||||
|
||||
reportButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
reportButton.setOnClickListener((View v) -> {
|
||||
Intent i = new Intent(Intent.ACTION_SENDTO);
|
||||
i.setData(Uri.parse("mailto:" + ERROR_EMAIL_ADDRESS))
|
||||
.putExtra(Intent.EXTRA_SUBJECT, ERROR_EMAIL_SUBJECT)
|
||||
.putExtra(Intent.EXTRA_TEXT, buildJson());
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_SENDTO);
|
||||
intent.setData(Uri.parse("mailto:" + ERROR_EMAIL_ADDRESS))
|
||||
.putExtra(Intent.EXTRA_SUBJECT, ERROR_EMAIL_SUBJECT)
|
||||
.putExtra(Intent.EXTRA_TEXT, buildJson());
|
||||
|
||||
startActivity(Intent.createChooser(intent, "Send Email"));
|
||||
}
|
||||
startActivity(Intent.createChooser(i, "Send Email"));
|
||||
});
|
||||
reportButton.setEnabled(false);
|
||||
|
||||
@@ -382,7 +374,7 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
|
||||
private String getContentLangString() {
|
||||
return PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.getString(this.getString(R.string.search_language_key), "none");
|
||||
.getString(this.getString(R.string.content_country_key), "none");
|
||||
}
|
||||
|
||||
private String getOsString() {
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
package org.schabi.newpipe.settings;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.preference.ListPreference;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
@@ -12,91 +17,208 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
import org.schabi.newpipe.util.FilePickerActivityHelper;
|
||||
import org.schabi.newpipe.util.KioskTranslator;
|
||||
import org.schabi.newpipe.util.ZipHelper;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class ContentSettingsFragment extends BasePreferenceFragment {
|
||||
|
||||
private static final int REQUEST_IMPORT_PATH = 8945;
|
||||
private static final int REQUEST_EXPORT_PATH = 30945;
|
||||
|
||||
private String homeDir;
|
||||
private File databasesDir;
|
||||
private File newpipe_db;
|
||||
private File newpipe_db_journal;
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
|
||||
homeDir = getActivity().getApplicationInfo().dataDir;
|
||||
databasesDir = new File(homeDir + "/databases");
|
||||
newpipe_db = new File(homeDir + "/databases/newpipe.db");
|
||||
newpipe_db_journal = new File(homeDir + "/databases/newpipe.db-journal");
|
||||
|
||||
addPreferencesFromResource(R.xml.content_settings);
|
||||
|
||||
final ListPreference mainPageContentPref = (ListPreference) findPreference(getString(R.string.main_page_content_key));
|
||||
mainPageContentPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValueO) {
|
||||
final String newValue = newValueO.toString();
|
||||
mainPageContentPref.setOnPreferenceChangeListener((Preference preference, Object newValueO) -> {
|
||||
final String newValue = newValueO.toString();
|
||||
|
||||
final String mainPrefOldValue =
|
||||
defaultPreferences.getString(getString(R.string.main_page_content_key), "blank_page");
|
||||
final String mainPrefOldSummary = getMainPagePrefSummery(mainPrefOldValue, mainPageContentPref);
|
||||
final String mainPrefOldValue =
|
||||
defaultPreferences.getString(getString(R.string.main_page_content_key), "blank_page");
|
||||
final String mainPrefOldSummary = getMainPagePrefSummery(mainPrefOldValue, mainPageContentPref);
|
||||
|
||||
if(newValue.equals(getString(R.string.kiosk_page_key))) {
|
||||
SelectKioskFragment selectKioskFragment = new SelectKioskFragment();
|
||||
selectKioskFragment.setOnSelectedLisener(new SelectKioskFragment.OnSelectedLisener() {
|
||||
@Override
|
||||
public void onKioskSelected(String kioskId, int service_id) {
|
||||
defaultPreferences.edit()
|
||||
.putInt(getString(R.string.main_page_selected_service), service_id).apply();
|
||||
defaultPreferences.edit()
|
||||
.putString(getString(R.string.main_page_selectd_kiosk_id), kioskId).apply();
|
||||
String serviceName = "";
|
||||
try {
|
||||
serviceName = NewPipe.getService(service_id).getServiceInfo().name;
|
||||
} catch (ExtractionException e) {
|
||||
onError(e);
|
||||
}
|
||||
String kioskName = KioskTranslator.getTranslatedKioskName(kioskId,
|
||||
getContext());
|
||||
|
||||
String summary =
|
||||
String.format(getString(R.string.service_kiosk_string),
|
||||
serviceName,
|
||||
kioskName);
|
||||
|
||||
mainPageContentPref.setSummary(summary);
|
||||
}
|
||||
});
|
||||
selectKioskFragment.setOnCancelListener(new SelectKioskFragment.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel() {
|
||||
mainPageContentPref.setSummary(mainPrefOldSummary);
|
||||
mainPageContentPref.setValue(mainPrefOldValue);
|
||||
}
|
||||
});
|
||||
selectKioskFragment.show(getFragmentManager(), "select_kiosk");
|
||||
} else if(newValue.equals(getString(R.string.channel_page_key))) {
|
||||
SelectChannelFragment selectChannelFragment = new SelectChannelFragment();
|
||||
selectChannelFragment.setOnSelectedLisener(new SelectChannelFragment.OnSelectedLisener() {
|
||||
@Override
|
||||
public void onChannelSelected(String url, String name, int service) {
|
||||
defaultPreferences.edit()
|
||||
.putInt(getString(R.string.main_page_selected_service), service).apply();
|
||||
defaultPreferences.edit()
|
||||
.putString(getString(R.string.main_page_selected_channel_url), url).apply();
|
||||
defaultPreferences.edit()
|
||||
.putString(getString(R.string.main_page_selected_channel_name), name).apply();
|
||||
|
||||
mainPageContentPref.setSummary(name);
|
||||
}
|
||||
});
|
||||
selectChannelFragment.setOnCancelListener(new SelectChannelFragment.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel() {
|
||||
mainPageContentPref.setSummary(mainPrefOldSummary);
|
||||
mainPageContentPref.setValue(mainPrefOldValue);
|
||||
}
|
||||
});
|
||||
selectChannelFragment.show(getFragmentManager(), "select_channel");
|
||||
} else {
|
||||
mainPageContentPref.setSummary(getMainPageSummeryByKey(newValue));
|
||||
}
|
||||
|
||||
defaultPreferences.edit().putBoolean(Constants.KEY_MAIN_PAGE_CHANGE, true).apply();
|
||||
|
||||
return true;
|
||||
if(newValue.equals(getString(R.string.kiosk_page_key))) {
|
||||
SelectKioskFragment selectKioskFragment = new SelectKioskFragment();
|
||||
selectKioskFragment.setOnSelectedLisener((String kioskId, int service_id) -> {
|
||||
defaultPreferences.edit()
|
||||
.putInt(getString(R.string.main_page_selected_service), service_id).apply();
|
||||
defaultPreferences.edit()
|
||||
.putString(getString(R.string.main_page_selectd_kiosk_id), kioskId).apply();
|
||||
String serviceName = "";
|
||||
try {
|
||||
serviceName = NewPipe.getService(service_id).getServiceInfo().name;
|
||||
} catch (ExtractionException e) {
|
||||
onError(e);
|
||||
}
|
||||
String kioskName = KioskTranslator.getTranslatedKioskName(kioskId,
|
||||
getContext());
|
||||
|
||||
String summary =
|
||||
String.format(getString(R.string.service_kiosk_string),
|
||||
serviceName,
|
||||
kioskName);
|
||||
|
||||
mainPageContentPref.setSummary(summary);
|
||||
});
|
||||
selectKioskFragment.setOnCancelListener(() -> {
|
||||
mainPageContentPref.setSummary(mainPrefOldSummary);
|
||||
mainPageContentPref.setValue(mainPrefOldValue);
|
||||
});
|
||||
selectKioskFragment.show(getFragmentManager(), "select_kiosk");
|
||||
} else if(newValue.equals(getString(R.string.channel_page_key))) {
|
||||
SelectChannelFragment selectChannelFragment = new SelectChannelFragment();
|
||||
selectChannelFragment.setOnSelectedLisener((String url, String name, int service) -> {
|
||||
defaultPreferences.edit()
|
||||
.putInt(getString(R.string.main_page_selected_service), service).apply();
|
||||
defaultPreferences.edit()
|
||||
.putString(getString(R.string.main_page_selected_channel_url), url).apply();
|
||||
defaultPreferences.edit()
|
||||
.putString(getString(R.string.main_page_selected_channel_name), name).apply();
|
||||
|
||||
mainPageContentPref.setSummary(name);
|
||||
});
|
||||
selectChannelFragment.setOnCancelListener(() -> {
|
||||
mainPageContentPref.setSummary(mainPrefOldSummary);
|
||||
mainPageContentPref.setValue(mainPrefOldValue);
|
||||
});
|
||||
selectChannelFragment.show(getFragmentManager(), "select_channel");
|
||||
} else {
|
||||
mainPageContentPref.setSummary(getMainPageSummeryByKey(newValue));
|
||||
}
|
||||
|
||||
defaultPreferences.edit().putBoolean(Constants.KEY_MAIN_PAGE_CHANGE, true).apply();
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
Preference importDataPreference = findPreference(getString(R.string.import_data));
|
||||
importDataPreference.setOnPreferenceClickListener((Preference p) -> {
|
||||
Intent i = new Intent(getActivity(), FilePickerActivityHelper.class)
|
||||
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_MULTIPLE, false)
|
||||
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_CREATE_DIR, false)
|
||||
.putExtra(FilePickerActivityHelper.EXTRA_MODE, FilePickerActivityHelper.MODE_FILE);
|
||||
startActivityForResult(i, REQUEST_IMPORT_PATH);
|
||||
return true;
|
||||
});
|
||||
|
||||
Preference exportDataPreference = findPreference(getString(R.string.export_data));
|
||||
exportDataPreference.setOnPreferenceClickListener((Preference p) -> {
|
||||
Intent i = new Intent(getActivity(), FilePickerActivityHelper.class)
|
||||
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_MULTIPLE, false)
|
||||
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_CREATE_DIR, true)
|
||||
.putExtra(FilePickerActivityHelper.EXTRA_MODE, FilePickerActivityHelper.MODE_DIR);
|
||||
startActivityForResult(i, REQUEST_EXPORT_PATH);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, @Nonnull Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onActivityResult() called with: requestCode = [" + requestCode + "], resultCode = [" + resultCode + "], data = [" + data + "]");
|
||||
}
|
||||
|
||||
if ((requestCode == REQUEST_IMPORT_PATH || requestCode == REQUEST_EXPORT_PATH)
|
||||
&& resultCode == Activity.RESULT_OK) {
|
||||
String path = data.getData().getPath();
|
||||
if (requestCode == REQUEST_EXPORT_PATH) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US);
|
||||
exportDatabase(path + "/NewPipeData-" + sdf.format(new Date()) + ".zip");
|
||||
} else {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setMessage(R.string.override_current_data)
|
||||
.setPositiveButton(android.R.string.ok,
|
||||
(DialogInterface d, int id) -> importDatabase(path))
|
||||
.setNegativeButton(android.R.string.cancel,
|
||||
(DialogInterface d, int id) -> d.cancel());
|
||||
builder.create().show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void exportDatabase(String path) {
|
||||
try {
|
||||
ZipOutputStream outZip = new ZipOutputStream(
|
||||
new BufferedOutputStream(
|
||||
new FileOutputStream(path)));
|
||||
ZipHelper.addFileToZip(outZip, newpipe_db.getPath(), "newpipe.db");
|
||||
ZipHelper.addFileToZip(outZip, newpipe_db_journal.getPath(), "newpipe.db-journal");
|
||||
|
||||
outZip.close();
|
||||
|
||||
Toast.makeText(getContext(), R.string.export_complete_toast, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
} catch (Exception e) {
|
||||
onError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void importDatabase(String filePath) {
|
||||
// check if file is supported
|
||||
ZipFile zipFile = null;
|
||||
try {
|
||||
zipFile = new ZipFile(filePath);
|
||||
} catch (IOException ioe) {
|
||||
Toast.makeText(getContext(), R.string.no_valid_zip_file, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
return;
|
||||
} finally {
|
||||
try {
|
||||
zipFile.close();
|
||||
} catch (Exception e){}
|
||||
}
|
||||
|
||||
try {
|
||||
ZipInputStream zipIn = new ZipInputStream(
|
||||
new BufferedInputStream(
|
||||
new FileInputStream(filePath)));
|
||||
|
||||
if (!databasesDir.exists() && !databasesDir.mkdir()) {
|
||||
throw new Exception("Could not create databases dir");
|
||||
}
|
||||
|
||||
if(!(ZipHelper.extractFileFromZip(zipIn, newpipe_db.getPath(), "newpipe.db")
|
||||
&& ZipHelper.extractFileFromZip(zipIn, newpipe_db_journal.getPath(), "newpipe.db-journal"))) {
|
||||
Toast.makeText(getContext(), R.string.could_not_import_all_files, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
|
||||
zipIn.close();
|
||||
|
||||
// restart app to properly load db
|
||||
//App.restart(getContext());
|
||||
System.exit(0);
|
||||
} catch (Exception e) {
|
||||
onError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -117,8 +239,8 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
|
||||
getString(R.string.main_page_selected_service), 0));
|
||||
|
||||
String kioskName = KioskTranslator.getTranslatedKioskName(
|
||||
defaultPreferences.getString(
|
||||
getString(R.string.main_page_selectd_kiosk_id), "Trending"),
|
||||
defaultPreferences.getString(
|
||||
getString(R.string.main_page_selectd_kiosk_id), "Trending"),
|
||||
getContext());
|
||||
|
||||
String summary =
|
||||
|
||||
@@ -46,7 +46,8 @@ public class DownloadSettingsFragment extends BasePreferenceFragment {
|
||||
Log.d(TAG, "onPreferenceTreeClick() called with: preference = [" + preference + "]");
|
||||
}
|
||||
|
||||
if (preference.getKey().equals(DOWNLOAD_PATH_PREFERENCE) || preference.getKey().equals(DOWNLOAD_PATH_AUDIO_PREFERENCE)) {
|
||||
if (preference.getKey().equals(DOWNLOAD_PATH_PREFERENCE)
|
||||
|| preference.getKey().equals(DOWNLOAD_PATH_AUDIO_PREFERENCE)) {
|
||||
Intent i = new Intent(getActivity(), FilePickerActivityHelper.class)
|
||||
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_MULTIPLE, false)
|
||||
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_CREATE_DIR, true)
|
||||
|
||||
@@ -14,21 +14,17 @@ import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.subscription.SubscriptionEntity;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.ServiceList;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.fragments.subscription.SubscriptionService;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.KioskTranslator;
|
||||
import org.schabi.newpipe.util.ServiceIconMapper;
|
||||
import org.schabi.newpipe.util.ServiceHelper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 09.10.17.
|
||||
* SelectKioskFragment.java is part of NewPipe.
|
||||
@@ -125,13 +121,15 @@ public class SelectKioskFragment extends DialogFragment {
|
||||
throws Exception {
|
||||
|
||||
for(StreamingService service : NewPipe.getServices()) {
|
||||
//TODO: Multi-service support
|
||||
if (service.getServiceId() != ServiceList.YouTube.getId()) continue;
|
||||
|
||||
for(String kioskId : service.getKioskList().getAvailableKiosks()) {
|
||||
String name = String.format(getString(R.string.service_kiosk_string),
|
||||
service.getServiceInfo().name,
|
||||
KioskTranslator.getTranslatedKioskName(kioskId, getContext()));
|
||||
kioskList.add(new Entry(
|
||||
//ServiceIconMapper.getIconResource(service.getServiceId()),
|
||||
ServiceIconMapper.getIconResource(-1),
|
||||
ServiceHelper.getIcon(service.getServiceId()),
|
||||
service.getServiceId(),
|
||||
kioskId,
|
||||
name));
|
||||
@@ -140,9 +138,7 @@ public class SelectKioskFragment extends DialogFragment {
|
||||
}
|
||||
|
||||
public int getItemCount() {
|
||||
//todo: uncommend this line on multyservice support
|
||||
//return kioskList.size();
|
||||
return 1;
|
||||
return kioskList.size();
|
||||
}
|
||||
|
||||
public SelectKioskItemHolder onCreateViewHolder(ViewGroup parent, int type) {
|
||||
|
||||
@@ -43,7 +43,8 @@ public class SettingsActivity extends AppCompatActivity implements BasePreferenc
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceBundle) {
|
||||
ThemeHelper.setTheme(this);
|
||||
setTheme(ThemeHelper.getSettingsThemeStyle(this));
|
||||
|
||||
super.onCreate(savedInstanceBundle);
|
||||
setContentView(R.layout.settings_layout);
|
||||
|
||||
@@ -72,7 +73,9 @@ public class SettingsActivity extends AppCompatActivity implements BasePreferenc
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
if (id == android.R.id.home) {
|
||||
finish();
|
||||
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
|
||||
finish();
|
||||
} else getSupportFragmentManager().popBackStack();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -19,29 +19,38 @@
|
||||
|
||||
package org.schabi.newpipe.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.schabi.newpipe.MainActivity;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.ReCaptchaActivity;
|
||||
import org.schabi.newpipe.extractor.Info;
|
||||
import org.schabi.newpipe.extractor.ListExtractor.NextItemsResult;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
||||
import org.schabi.newpipe.extractor.kiosk.KioskInfo;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||
import org.schabi.newpipe.extractor.search.SearchEngine;
|
||||
import org.schabi.newpipe.extractor.search.SearchResult;
|
||||
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.MaybeSource;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.annotations.NonNull;
|
||||
import io.reactivex.functions.Consumer;
|
||||
import io.reactivex.functions.Function;
|
||||
|
||||
public final class ExtractorHelper {
|
||||
private static final String TAG = ExtractorHelper.class.getSimpleName();
|
||||
@@ -57,104 +66,96 @@ public final class ExtractorHelper {
|
||||
}
|
||||
}
|
||||
|
||||
public static Single<SearchResult> searchFor(final int serviceId, final String query, final int pageNumber, final String searchLanguage, final SearchEngine.Filter filter) {
|
||||
public static Single<SearchResult> searchFor(final int serviceId,
|
||||
final String query,
|
||||
final int pageNumber,
|
||||
final String contentCountry,
|
||||
final SearchEngine.Filter filter) {
|
||||
checkServiceId(serviceId);
|
||||
return Single.fromCallable(new Callable<SearchResult>() {
|
||||
@Override
|
||||
public SearchResult call() throws Exception {
|
||||
return SearchResult.getSearchResult(NewPipe.getService(serviceId).getSearchEngine(),
|
||||
query, pageNumber, searchLanguage, filter);
|
||||
}
|
||||
});
|
||||
return Single.fromCallable(() ->
|
||||
SearchResult.getSearchResult(NewPipe.getService(serviceId).getSearchEngine(),
|
||||
query, pageNumber, contentCountry, filter)
|
||||
);
|
||||
}
|
||||
|
||||
public static Single<NextItemsResult> getMoreSearchItems(final int serviceId, final String query, final int nextPageNumber, final String searchLanguage, final SearchEngine.Filter filter) {
|
||||
public static Single<NextItemsResult> getMoreSearchItems(final int serviceId,
|
||||
final String query,
|
||||
final int nextPageNumber,
|
||||
final String searchLanguage,
|
||||
final SearchEngine.Filter filter) {
|
||||
checkServiceId(serviceId);
|
||||
return searchFor(serviceId, query, nextPageNumber, searchLanguage, filter)
|
||||
.map(new Function<SearchResult, NextItemsResult>() {
|
||||
@Override
|
||||
public NextItemsResult apply(@NonNull SearchResult searchResult) throws Exception {
|
||||
return new NextItemsResult(searchResult.resultList, nextPageNumber + "", searchResult.errors);
|
||||
}
|
||||
});
|
||||
.map((@NonNull SearchResult searchResult) ->
|
||||
new NextItemsResult(searchResult.resultList,
|
||||
nextPageNumber + "",
|
||||
searchResult.errors));
|
||||
}
|
||||
|
||||
public static Single<List<String>> suggestionsFor(final int serviceId, final String query, final String searchLanguage) {
|
||||
public static Single<List<String>> suggestionsFor(final int serviceId,
|
||||
final String query,
|
||||
final String contentCountry) {
|
||||
checkServiceId(serviceId);
|
||||
return Single.fromCallable(new Callable<List<String>>() {
|
||||
@Override
|
||||
public List<String> call() throws Exception {
|
||||
return NewPipe.getService(serviceId).getSuggestionExtractor().suggestionList(query, searchLanguage);
|
||||
}
|
||||
});
|
||||
return Single.fromCallable(() ->
|
||||
NewPipe.getService(serviceId)
|
||||
.getSuggestionExtractor()
|
||||
.suggestionList(query, contentCountry));
|
||||
}
|
||||
|
||||
public static Single<StreamInfo> getStreamInfo(final int serviceId, final String url, boolean forceLoad) {
|
||||
public static Single<StreamInfo> getStreamInfo(final int serviceId,
|
||||
final String url,
|
||||
boolean forceLoad) {
|
||||
checkServiceId(serviceId);
|
||||
return checkCache(forceLoad, serviceId, url, Single.fromCallable(new Callable<StreamInfo>() {
|
||||
@Override
|
||||
public StreamInfo call() throws Exception {
|
||||
return StreamInfo.getInfo(NewPipe.getService(serviceId), url);
|
||||
}
|
||||
}));
|
||||
return checkCache(forceLoad, serviceId, url, Single.fromCallable(() ->
|
||||
StreamInfo.getInfo(NewPipe.getService(serviceId), url)));
|
||||
}
|
||||
|
||||
public static Single<ChannelInfo> getChannelInfo(final int serviceId, final String url, boolean forceLoad) {
|
||||
public static Single<ChannelInfo> getChannelInfo(final int serviceId,
|
||||
final String url,
|
||||
boolean forceLoad) {
|
||||
checkServiceId(serviceId);
|
||||
return checkCache(forceLoad, serviceId, url, Single.fromCallable(new Callable<ChannelInfo>() {
|
||||
@Override
|
||||
public ChannelInfo call() throws Exception {
|
||||
return ChannelInfo.getInfo(NewPipe.getService(serviceId), url);
|
||||
}
|
||||
}));
|
||||
return checkCache(forceLoad, serviceId, url, Single.fromCallable(() ->
|
||||
ChannelInfo.getInfo(NewPipe.getService(serviceId), url)));
|
||||
}
|
||||
|
||||
public static Single<NextItemsResult> getMoreChannelItems(final int serviceId, final String url, final String nextStreamsUrl) {
|
||||
public static Single<NextItemsResult> getMoreChannelItems(final int serviceId,
|
||||
final String url,
|
||||
final String nextStreamsUrl) {
|
||||
checkServiceId(serviceId);
|
||||
return Single.fromCallable(new Callable<NextItemsResult>() {
|
||||
@Override
|
||||
public NextItemsResult call() throws Exception {
|
||||
return ChannelInfo.getMoreItems(NewPipe.getService(serviceId), url, nextStreamsUrl);
|
||||
}
|
||||
});
|
||||
return Single.fromCallable(() ->
|
||||
ChannelInfo.getMoreItems(NewPipe.getService(serviceId), url, nextStreamsUrl));
|
||||
}
|
||||
|
||||
public static Single<PlaylistInfo> getPlaylistInfo(final int serviceId, final String url, boolean forceLoad) {
|
||||
public static Single<PlaylistInfo> getPlaylistInfo(final int serviceId,
|
||||
final String url,
|
||||
boolean forceLoad) {
|
||||
checkServiceId(serviceId);
|
||||
return checkCache(forceLoad, serviceId, url, Single.fromCallable(new Callable<PlaylistInfo>() {
|
||||
@Override
|
||||
public PlaylistInfo call() throws Exception {
|
||||
return PlaylistInfo.getInfo(NewPipe.getService(serviceId), url);
|
||||
}
|
||||
}));
|
||||
return checkCache(forceLoad, serviceId, url, Single.fromCallable(() ->
|
||||
PlaylistInfo.getInfo(NewPipe.getService(serviceId), url)));
|
||||
}
|
||||
|
||||
public static Single<NextItemsResult> getMorePlaylistItems(final int serviceId, final String url, final String nextStreamsUrl) {
|
||||
public static Single<NextItemsResult> getMorePlaylistItems(final int serviceId,
|
||||
final String url,
|
||||
final String nextStreamsUrl) {
|
||||
checkServiceId(serviceId);
|
||||
return Single.fromCallable(new Callable<NextItemsResult>() {
|
||||
@Override
|
||||
public NextItemsResult call() throws Exception {
|
||||
return PlaylistInfo.getMoreItems(NewPipe.getService(serviceId), url, nextStreamsUrl);
|
||||
}
|
||||
});
|
||||
return Single.fromCallable(() ->
|
||||
PlaylistInfo.getMoreItems(NewPipe.getService(serviceId), url, nextStreamsUrl));
|
||||
}
|
||||
|
||||
public static Single<KioskInfo> getKioskInfo(final int serviceId, final String url, final String contentCountry, boolean forceLoad) {
|
||||
return checkCache(forceLoad, serviceId, url, Single.fromCallable(new Callable<KioskInfo>() {
|
||||
@Override
|
||||
public KioskInfo call() throws Exception {
|
||||
return KioskInfo.getInfo(NewPipe.getService(serviceId), url, contentCountry);
|
||||
}
|
||||
}));
|
||||
public static Single<KioskInfo> getKioskInfo(final int serviceId,
|
||||
final String url,
|
||||
final String contentCountry,
|
||||
boolean forceLoad) {
|
||||
return checkCache(forceLoad, serviceId, url, Single.fromCallable(() ->
|
||||
KioskInfo.getInfo(NewPipe.getService(serviceId), url, contentCountry)));
|
||||
}
|
||||
|
||||
public static Single<NextItemsResult> getMoreKioskItems(final int serviceId, final String url, final String nextStreamsUrl) {
|
||||
return Single.fromCallable(new Callable<NextItemsResult>() {
|
||||
@Override
|
||||
public NextItemsResult call() throws Exception {
|
||||
return KioskInfo.getMoreItems(NewPipe.getService(serviceId), url, nextStreamsUrl);
|
||||
}
|
||||
});
|
||||
public static Single<NextItemsResult> getMoreKioskItems(final int serviceId,
|
||||
final String url,
|
||||
final String nextStreamsUrl,
|
||||
final String contentCountry) {
|
||||
return Single.fromCallable(() ->
|
||||
KioskInfo.getMoreItems(NewPipe.getService(serviceId),
|
||||
url, nextStreamsUrl, contentCountry));
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
@@ -162,24 +163,24 @@ public final class ExtractorHelper {
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
/**
|
||||
* Check if we can load it from the cache (forceLoad parameter), if we can't, load from the network (Single loadFromNetwork)
|
||||
* Check if we can load it from the cache (forceLoad parameter), if we can't,
|
||||
* load from the network (Single loadFromNetwork)
|
||||
* and put the results in the cache.
|
||||
*/
|
||||
private static <I extends Info> Single<I> checkCache(boolean forceLoad, int serviceId, String url, Single<I> loadFromNetwork) {
|
||||
private static <I extends Info> Single<I> checkCache(boolean forceLoad,
|
||||
int serviceId,
|
||||
String url,
|
||||
Single<I> loadFromNetwork) {
|
||||
checkServiceId(serviceId);
|
||||
loadFromNetwork = loadFromNetwork.doOnSuccess(new Consumer<I>() {
|
||||
@Override
|
||||
public void accept(@NonNull I i) throws Exception {
|
||||
cache.putInfo(i);
|
||||
}
|
||||
});
|
||||
loadFromNetwork = loadFromNetwork.doOnSuccess((@NonNull I i) -> cache.putInfo(i));
|
||||
|
||||
Single<I> load;
|
||||
if (forceLoad) {
|
||||
cache.removeInfo(serviceId, url);
|
||||
load = loadFromNetwork;
|
||||
} else {
|
||||
load = Maybe.concat(ExtractorHelper.<I>loadFromCache(serviceId, url), loadFromNetwork.toMaybe())
|
||||
load = Maybe.concat(ExtractorHelper.<I>loadFromCache(serviceId, url),
|
||||
loadFromNetwork.toMaybe())
|
||||
.firstElement() //Take the first valid
|
||||
.toSingle();
|
||||
}
|
||||
@@ -192,9 +193,7 @@ public final class ExtractorHelper {
|
||||
*/
|
||||
public static <I extends Info> Maybe<I> loadFromCache(final int serviceId, final String url) {
|
||||
checkServiceId(serviceId);
|
||||
return Maybe.defer(new Callable<MaybeSource<? extends I>>() {
|
||||
@Override
|
||||
public MaybeSource<? extends I> call() throws Exception {
|
||||
return Maybe.defer(() -> {
|
||||
//noinspection unchecked
|
||||
I info = (I) cache.getFromKey(serviceId, url);
|
||||
if (MainActivity.DEBUG) Log.d(TAG, "loadFromCache() called, info > " + info);
|
||||
@@ -205,8 +204,38 @@ public final class ExtractorHelper {
|
||||
}
|
||||
|
||||
return Maybe.empty();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple and general error handler that show a Toast for known exceptions, and for others, opens the report error activity with the (optional) error message.
|
||||
*/
|
||||
public static void handleGeneralException(Context context, int serviceId, String url, Throwable exception, UserAction userAction, String optionalErrorMessage) {
|
||||
final Handler handler = new Handler(context.getMainLooper());
|
||||
|
||||
handler.post(() -> {
|
||||
if (exception instanceof ReCaptchaException) {
|
||||
Toast.makeText(context, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
|
||||
// Starting ReCaptcha Challenge Activity
|
||||
Intent intent = new Intent(context, ReCaptchaActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(intent);
|
||||
} else if (exception instanceof IOException) {
|
||||
Toast.makeText(context, R.string.network_error, Toast.LENGTH_LONG).show();
|
||||
} else if (exception instanceof YoutubeStreamExtractor.GemaException) {
|
||||
Toast.makeText(context, R.string.blocked_by_gema, Toast.LENGTH_LONG).show();
|
||||
} else if (exception instanceof YoutubeStreamExtractor.LiveStreamException) {
|
||||
Toast.makeText(context, R.string.live_streams_not_supported, Toast.LENGTH_LONG).show();
|
||||
} else if (exception instanceof ContentNotAvailableException) {
|
||||
Toast.makeText(context, R.string.content_not_available, Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
int errorId = exception instanceof YoutubeStreamExtractor.DecryptException ? R.string.youtube_signature_decryption_error :
|
||||
exception instanceof ParsingException ? R.string.parsing_error : R.string.general_error;
|
||||
ErrorActivity.reportError(handler, context, exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(userAction,
|
||||
serviceId == -1 ? "none" : NewPipe.getNameOfService(serviceId), url + (optionalErrorMessage == null ? "" : optionalErrorMessage), errorId));
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -214,21 +243,26 @@ public final class ExtractorHelper {
|
||||
*
|
||||
* @see Class#isAssignableFrom(Class)
|
||||
*/
|
||||
public static boolean hasAssignableCauseThrowable(Throwable throwable, Class<?>... causesToCheck) {
|
||||
public static boolean hasAssignableCauseThrowable(Throwable throwable,
|
||||
Class<?>... causesToCheck) {
|
||||
// Check if getCause is not the same as cause (the getCause is already the root),
|
||||
// as it will cause a infinite loop if it is
|
||||
Throwable cause, getCause = throwable;
|
||||
|
||||
// Check if throwable is a subclass of any of the filtered classes
|
||||
final Class throwableClass = throwable.getClass();
|
||||
for (Class<?> causesEl : causesToCheck) {
|
||||
if (throwable.getClass().isAssignableFrom(causesEl)) {
|
||||
if (causesEl.isAssignableFrom(throwableClass)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Iteratively checks if the root cause of the throwable is a subclass of the filtered class
|
||||
while ((cause = throwable.getCause()) != null && getCause != cause) {
|
||||
getCause = cause;
|
||||
final Class causeClass = cause.getClass();
|
||||
for (Class<?> causesEl : causesToCheck) {
|
||||
if (cause.getClass().isAssignableFrom(causesEl)) {
|
||||
if (causesEl.isAssignableFrom(causeClass)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -265,6 +299,8 @@ public final class ExtractorHelper {
|
||||
* Check if throwable have Interrupted* exception as one of its causes.
|
||||
*/
|
||||
public static boolean isInterruptedCaused(Throwable throwable) {
|
||||
return ExtractorHelper.hasExactCauseThrowable(throwable, InterruptedIOException.class, InterruptedException.class);
|
||||
return ExtractorHelper.hasExactCauseThrowable(throwable,
|
||||
InterruptedIOException.class,
|
||||
InterruptedException.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ public final class InfoCache {
|
||||
}
|
||||
|
||||
private static String keyOf(@NonNull final Info info) {
|
||||
return keyOf(info.service_id, info.url);
|
||||
return keyOf(info.getServiceId(), info.getUrl());
|
||||
}
|
||||
|
||||
private static String keyOf(final int serviceId, @NonNull final String url) {
|
||||
|
||||
@@ -95,7 +95,7 @@ public final class ListHelper {
|
||||
int highestQualityIndex = 0;
|
||||
if (audioStreams.size() > 1) for (int i = 1; i < audioStreams.size(); i++) {
|
||||
AudioStream audioStream = audioStreams.get(i);
|
||||
if (audioStream.average_bitrate >= audioStreams.get(highestQualityIndex).average_bitrate) highestQualityIndex = i;
|
||||
if (audioStream.getAverageBitrate() >= audioStreams.get(highestQualityIndex).getAverageBitrate()) highestQualityIndex = i;
|
||||
}
|
||||
return highestQualityIndex;
|
||||
}
|
||||
@@ -124,10 +124,10 @@ public final class ListHelper {
|
||||
int highestQualityIndex = -1;
|
||||
for (int i = 0; i < audioStreams.size(); i++) {
|
||||
AudioStream audioStream = audioStreams.get(i);
|
||||
if (highestQualityIndex == -1 && audioStream.format == defaultFormat.id) highestQualityIndex = i;
|
||||
if (highestQualityIndex == -1 && audioStream.getFormat() == defaultFormat) highestQualityIndex = i;
|
||||
|
||||
if (highestQualityIndex != -1 && audioStream.format == defaultFormat.id
|
||||
&& audioStream.average_bitrate > audioStreams.get(highestQualityIndex).average_bitrate) {
|
||||
if (highestQualityIndex != -1 && audioStream.getFormat() == defaultFormat
|
||||
&& audioStream.getAverageBitrate() > audioStreams.get(highestQualityIndex).getAverageBitrate()) {
|
||||
highestQualityIndex = i;
|
||||
}
|
||||
}
|
||||
@@ -171,23 +171,23 @@ public final class ListHelper {
|
||||
|
||||
if (videoOnlyStreams != null) {
|
||||
for (VideoStream stream : videoOnlyStreams) {
|
||||
if (!showHigherResolutions && HIGH_RESOLUTION_LIST.contains(stream.resolution)) continue;
|
||||
if (!showHigherResolutions && HIGH_RESOLUTION_LIST.contains(stream.getResolution())) continue;
|
||||
retList.add(stream);
|
||||
}
|
||||
}
|
||||
if (videoStreams != null) {
|
||||
for (VideoStream stream : videoStreams) {
|
||||
if (!showHigherResolutions && HIGH_RESOLUTION_LIST.contains(stream.resolution)) continue;
|
||||
if (!showHigherResolutions && HIGH_RESOLUTION_LIST.contains(stream.getResolution())) continue;
|
||||
retList.add(stream);
|
||||
}
|
||||
}
|
||||
|
||||
// Add all to the hashmap
|
||||
for (VideoStream videoStream : retList) hashMap.put(videoStream.resolution, videoStream);
|
||||
for (VideoStream videoStream : retList) hashMap.put(videoStream.getResolution(), videoStream);
|
||||
|
||||
// Override the values when the key == resolution, with the defaultFormat
|
||||
for (VideoStream videoStream : retList) {
|
||||
if (videoStream.format == defaultFormat.id) hashMap.put(videoStream.resolution, videoStream);
|
||||
if (videoStream.getFormat() == defaultFormat) hashMap.put(videoStream.getResolution(), videoStream);
|
||||
}
|
||||
|
||||
retList.clear();
|
||||
@@ -219,8 +219,8 @@ public final class ListHelper {
|
||||
Collections.sort(videoStreams, new Comparator<VideoStream>() {
|
||||
@Override
|
||||
public int compare(VideoStream o1, VideoStream o2) {
|
||||
int res1 = Integer.parseInt(o1.resolution.replace("0p60", "1").replaceAll("[^\\d.]", ""));
|
||||
int res2 = Integer.parseInt(o2.resolution.replace("0p60", "1").replaceAll("[^\\d.]", ""));
|
||||
int res1 = Integer.parseInt(o1.getResolution().replace("0p60", "1").replaceAll("[^\\d.]", ""));
|
||||
int res2 = Integer.parseInt(o2.getResolution().replace("0p60", "1").replaceAll("[^\\d.]", ""));
|
||||
|
||||
return ascendingOrder ? res1 - res2 : res2 - res1;
|
||||
}
|
||||
@@ -235,9 +235,9 @@ public final class ListHelper {
|
||||
int defaultStreamIndex = -1;
|
||||
for (int i = 0; i < videoStreams.size(); i++) {
|
||||
VideoStream stream = videoStreams.get(i);
|
||||
if (defaultStreamIndex == -1 && stream.resolution.equals(defaultResolution)) defaultStreamIndex = i;
|
||||
if (defaultStreamIndex == -1 && stream.getResolution().equals(defaultResolution)) defaultStreamIndex = i;
|
||||
|
||||
if (stream.format == defaultFormat.id && stream.resolution.equals(defaultResolution)) {
|
||||
if (stream.getFormat() == defaultFormat && stream.getResolution().equals(defaultResolution)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import android.os.Build;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
@@ -21,6 +23,10 @@ import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.ServiceList;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.stream.AudioStream;
|
||||
import org.schabi.newpipe.extractor.stream.Stream;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||
import org.schabi.newpipe.fragments.MainFragment;
|
||||
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
|
||||
import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
|
||||
@@ -36,16 +42,21 @@ import org.schabi.newpipe.player.MainVideoPlayer;
|
||||
import org.schabi.newpipe.player.PopupVideoPlayer;
|
||||
import org.schabi.newpipe.player.PopupVideoPlayerActivity;
|
||||
import org.schabi.newpipe.player.VideoPlayer;
|
||||
import org.schabi.newpipe.player.old.PlayVideoActivity;
|
||||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.settings.SettingsActivity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||
public class NavigationHelper {
|
||||
public static final String MAIN_FRAGMENT_TAG = "main_fragment_tag";
|
||||
public static final String SEARCH_FRAGMENT_TAG = "search_fragment_tag";
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Players
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public static Intent getPlayerIntent(final Context context,
|
||||
final Class targetClazz,
|
||||
final PlayQueue playQueue,
|
||||
@@ -65,9 +76,11 @@ public class NavigationHelper {
|
||||
|
||||
public static Intent getPlayerEnqueueIntent(final Context context,
|
||||
final Class targetClazz,
|
||||
final PlayQueue playQueue) {
|
||||
final PlayQueue playQueue,
|
||||
final boolean selectOnAppend) {
|
||||
return getPlayerIntent(context, targetClazz, playQueue)
|
||||
.putExtra(BasePlayer.APPEND_ONLY, true);
|
||||
.putExtra(BasePlayer.APPEND_ONLY, true)
|
||||
.putExtra(BasePlayer.SELECT_ON_APPEND, selectOnAppend);
|
||||
}
|
||||
|
||||
public static Intent getPlayerIntent(final Context context,
|
||||
@@ -84,10 +97,37 @@ public class NavigationHelper {
|
||||
}
|
||||
|
||||
public static void playOnMainPlayer(final Context context, final PlayQueue queue) {
|
||||
context.startActivity(getPlayerIntent(context, MainVideoPlayer.class, queue));
|
||||
final Intent playerIntent = getPlayerIntent(context, MainVideoPlayer.class, queue);
|
||||
playerIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(playerIntent);
|
||||
}
|
||||
|
||||
public static void playOnOldVideoPlayer(Context context, StreamInfo info) {
|
||||
ArrayList<VideoStream> videoStreamsList = new ArrayList<>(ListHelper.getSortedStreamVideosList(context, info.getVideoStreams(), null, false));
|
||||
int index = ListHelper.getDefaultResolutionIndex(context, videoStreamsList);
|
||||
|
||||
if (index == -1) {
|
||||
Toast.makeText(context, R.string.video_streams_empty, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
VideoStream videoStream = videoStreamsList.get(index);
|
||||
Intent intent = new Intent(context, PlayVideoActivity.class)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.putExtra(PlayVideoActivity.VIDEO_TITLE, info.getName())
|
||||
.putExtra(PlayVideoActivity.STREAM_URL, videoStream.getUrl())
|
||||
.putExtra(PlayVideoActivity.VIDEO_URL, info.getUrl())
|
||||
.putExtra(PlayVideoActivity.START_POSITION, info.getStartPosition());
|
||||
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
public static void playOnPopupPlayer(final Context context, final PlayQueue queue) {
|
||||
if (!PermissionHelper.isPopupEnabled(context)) {
|
||||
PermissionHelper.showPopupEnablementToast(context);
|
||||
return;
|
||||
}
|
||||
|
||||
Toast.makeText(context, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
|
||||
context.startService(getPlayerIntent(context, PopupVideoPlayer.class, queue));
|
||||
}
|
||||
@@ -98,14 +138,91 @@ public class NavigationHelper {
|
||||
}
|
||||
|
||||
public static void enqueueOnPopupPlayer(final Context context, final PlayQueue queue) {
|
||||
enqueueOnPopupPlayer(context, queue, false);
|
||||
}
|
||||
|
||||
public static void enqueueOnPopupPlayer(final Context context, final PlayQueue queue, boolean selectOnAppend) {
|
||||
if (!PermissionHelper.isPopupEnabled(context)) {
|
||||
PermissionHelper.showPopupEnablementToast(context);
|
||||
return;
|
||||
}
|
||||
|
||||
Toast.makeText(context, R.string.popup_playing_append, Toast.LENGTH_SHORT).show();
|
||||
context.startService(getPlayerEnqueueIntent(context, PopupVideoPlayer.class, queue));
|
||||
context.startService(getPlayerEnqueueIntent(context, PopupVideoPlayer.class, queue, selectOnAppend));
|
||||
}
|
||||
|
||||
public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue) {
|
||||
Toast.makeText(context, R.string.background_player_append, Toast.LENGTH_SHORT).show();
|
||||
context.startService(getPlayerEnqueueIntent(context, BackgroundPlayer.class, queue));
|
||||
enqueueOnBackgroundPlayer(context, queue, false);
|
||||
}
|
||||
|
||||
public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue, boolean selectOnAppend) {
|
||||
Toast.makeText(context, R.string.background_player_append, Toast.LENGTH_SHORT).show();
|
||||
context.startService(getPlayerEnqueueIntent(context, BackgroundPlayer.class, queue, selectOnAppend));
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// External Players
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public static void playOnExternalAudioPlayer(Context context, StreamInfo info) {
|
||||
final int index = ListHelper.getDefaultAudioFormat(context, info.getAudioStreams());
|
||||
|
||||
if (index == -1) {
|
||||
Toast.makeText(context, R.string.audio_streams_empty, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
AudioStream audioStream = info.getAudioStreams().get(index);
|
||||
playOnExternalPlayer(context, info.getName(), info.getUploaderName(), audioStream);
|
||||
}
|
||||
|
||||
public static void playOnExternalVideoPlayer(Context context, StreamInfo info) {
|
||||
ArrayList<VideoStream> videoStreamsList = new ArrayList<>(ListHelper.getSortedStreamVideosList(context, info.getVideoStreams(), null, false));
|
||||
int index = ListHelper.getDefaultResolutionIndex(context, videoStreamsList);
|
||||
|
||||
if (index == -1) {
|
||||
Toast.makeText(context, R.string.video_streams_empty, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
VideoStream videoStream = videoStreamsList.get(index);
|
||||
playOnExternalPlayer(context, info.getName(), info.getUploaderName(), videoStream);
|
||||
}
|
||||
|
||||
public static void playOnExternalPlayer(Context context, String name, String artist, Stream stream) {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
intent.setDataAndType(Uri.parse(stream.getUrl()), stream.getFormat().getMimeType());
|
||||
intent.putExtra(Intent.EXTRA_TITLE, name);
|
||||
intent.putExtra("title", name);
|
||||
intent.putExtra("artist", artist);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
resolveActivityOrAskToInstall(context, intent);
|
||||
}
|
||||
|
||||
public static void resolveActivityOrAskToInstall(Context context, Intent intent) {
|
||||
if (intent.resolveActivity(context.getPackageManager()) != null) {
|
||||
context.startActivity(intent);
|
||||
} else {
|
||||
if (context instanceof Activity) {
|
||||
new AlertDialog.Builder(context)
|
||||
.setMessage(R.string.no_player_found)
|
||||
.setPositiveButton(R.string.install, (dialog, which) -> {
|
||||
Intent i = new Intent();
|
||||
i.setAction(Intent.ACTION_VIEW);
|
||||
i.setData(Uri.parse(context.getString(R.string.fdroid_vlc_url)));
|
||||
context.startActivity(i);
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, (dialog, which) -> Log.i("NavigationHelper", "You unlocked a secret unicorn."))
|
||||
.show();
|
||||
//Log.e("NavigationHelper", "Either no Streaming player for audio was installed, or something important crashed:");
|
||||
} else {
|
||||
Toast.makeText(context, R.string.no_player_found_toast, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Through FragmentManager
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
@@ -128,11 +245,21 @@ public class NavigationHelper {
|
||||
.commit();
|
||||
}
|
||||
|
||||
public static boolean tryGotoSearchFragment(FragmentManager fragmentManager) {
|
||||
if (MainActivity.DEBUG) {
|
||||
for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) {
|
||||
Log.d("NavigationHelper", "tryGoToSearchFragment() [" + i + "] = [" + fragmentManager.getBackStackEntryAt(i) + "]");
|
||||
}
|
||||
}
|
||||
|
||||
return fragmentManager.popBackStackImmediate(SEARCH_FRAGMENT_TAG, 0);
|
||||
}
|
||||
|
||||
public static void openSearchFragment(FragmentManager fragmentManager, int serviceId, String query) {
|
||||
fragmentManager.beginTransaction()
|
||||
.setCustomAnimations(R.animator.custom_fade_in, R.animator.custom_fade_out, R.animator.custom_fade_in, R.animator.custom_fade_out)
|
||||
.replace(R.id.fragment_holder, SearchFragment.getInstance(serviceId, query))
|
||||
.addToBackStack(null)
|
||||
.addToBackStack(SEARCH_FRAGMENT_TAG)
|
||||
.commit();
|
||||
}
|
||||
|
||||
@@ -259,40 +386,26 @@ public class NavigationHelper {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void openBackgroundPlayerControl(final Context context) {
|
||||
openServicePlayerControl(context, BackgroundPlayerActivity.class);
|
||||
public static Intent getBackgroundPlayerActivityIntent(final Context context) {
|
||||
return getServicePlayerActivityIntent(context, BackgroundPlayerActivity.class);
|
||||
}
|
||||
|
||||
public static void openPopupPlayerControl(final Context context) {
|
||||
openServicePlayerControl(context, PopupVideoPlayerActivity.class);
|
||||
public static Intent getPopupPlayerActivityIntent(final Context context) {
|
||||
return getServicePlayerActivityIntent(context, PopupVideoPlayerActivity.class);
|
||||
}
|
||||
|
||||
private static void openServicePlayerControl(final Context context, final Class clazz) {
|
||||
final Intent intent = new Intent(context, clazz);
|
||||
private static Intent getServicePlayerActivityIntent(final Context context,
|
||||
final Class activityClass) {
|
||||
Intent intent = new Intent(context, activityClass);
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
}
|
||||
context.startActivity(intent);
|
||||
context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
|
||||
return intent;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Link handling
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public static boolean openByLink(Context context, String url) {
|
||||
Intent intentByLink;
|
||||
try {
|
||||
intentByLink = getIntentByLink(context, url);
|
||||
} catch (ExtractionException e) {
|
||||
return false;
|
||||
}
|
||||
intentByLink.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intentByLink.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
context.startActivity(intentByLink);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Intent getOpenIntent(Context context, String url, int serviceId, StreamingService.LinkType type) {
|
||||
Intent mIntent = new Intent(context, MainActivity.class);
|
||||
mIntent.putExtra(Constants.KEY_SERVICE_ID, serviceId);
|
||||
@@ -310,15 +423,14 @@ public class NavigationHelper {
|
||||
throw new ExtractionException("Service not supported at the moment");
|
||||
}
|
||||
|
||||
int serviceId = service.getServiceId();
|
||||
StreamingService.LinkType linkType = service.getLinkTypeByUrl(url);
|
||||
|
||||
if (linkType == StreamingService.LinkType.NONE) {
|
||||
throw new ExtractionException("Url not known to service. service=" + serviceId + " url=" + url);
|
||||
throw new ExtractionException("Url not known to service. service=" + service + " url=" + url);
|
||||
}
|
||||
|
||||
url = getCleanUrl(service, url, linkType);
|
||||
Intent rIntent = getOpenIntent(context, url, serviceId, linkType);
|
||||
Intent rIntent = getOpenIntent(context, url, service.getServiceId(), linkType);
|
||||
|
||||
switch (linkType) {
|
||||
case STREAM:
|
||||
@@ -330,7 +442,7 @@ public class NavigationHelper {
|
||||
return rIntent;
|
||||
}
|
||||
|
||||
private static String getCleanUrl(StreamingService service, String dirtyUrl, StreamingService.LinkType linkType) throws ExtractionException {
|
||||
public static String getCleanUrl(StreamingService service, String dirtyUrl, StreamingService.LinkType linkType) throws ExtractionException {
|
||||
switch (linkType) {
|
||||
case STREAM:
|
||||
return service.getStreamUrlIdHandler().cleanUrl(dirtyUrl);
|
||||
@@ -344,7 +456,6 @@ public class NavigationHelper {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private static Uri openMarketUrl(String packageName) {
|
||||
return Uri.parse("market://details")
|
||||
.buildUpon()
|
||||
|
||||
@@ -11,11 +11,15 @@ import android.provider.Settings;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.view.Gravity;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
|
||||
public class PermissionHelper {
|
||||
public static final int PERMISSION_WRITE_STORAGE = 778;
|
||||
public static final int PERMISSION_READ_STORAGE = 777;
|
||||
public static final int PERMISSION_SYSTEM_ALERT_WINDOW = 779;
|
||||
|
||||
|
||||
public static boolean checkStoragePermissions(Activity activity) {
|
||||
@@ -75,21 +79,31 @@ public class PermissionHelper {
|
||||
* In order to be able to draw over other apps, the permission android.permission.SYSTEM_ALERT_WINDOW have to be granted.
|
||||
* <p>
|
||||
* On < API 23 (MarshMallow) the permission was granted when the user installed the application (via AndroidManifest),
|
||||
* on > 23, however, it have to start a activity asking the user if he agree.
|
||||
* on > 23, however, it have to start a activity asking the user if he agrees.
|
||||
* <p>
|
||||
* This method just return if canDraw over other apps, if it doesn't, try to get the permission,
|
||||
* it does not get the result of the startActivityForResult, if the user accept, the next time that he tries to open
|
||||
* it will return true.
|
||||
* This method just return if the app has permission to draw over other apps, and if it doesn't, it will try to get the permission.
|
||||
*
|
||||
* @param activity context to startActivityForResult
|
||||
* @return returns {@link Settings#canDrawOverlays(Context)}
|
||||
**/
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public static boolean checkSystemAlertWindowPermission(Activity activity) {
|
||||
if (!Settings.canDrawOverlays(activity)) {
|
||||
Intent i = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + activity.getPackageName()));
|
||||
activity.startActivityForResult(i, PERMISSION_SYSTEM_ALERT_WINDOW);
|
||||
public static boolean checkSystemAlertWindowPermission(Context context) {
|
||||
if (!Settings.canDrawOverlays(context)) {
|
||||
Intent i = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + context.getPackageName()));
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(i);
|
||||
return false;
|
||||
}else return true;
|
||||
}
|
||||
|
||||
public static boolean isPopupEnabled(Context context) {
|
||||
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
|
||||
PermissionHelper.checkSystemAlertWindowPermission(context);
|
||||
}
|
||||
|
||||
public static void showPopupEnablementToast(Context context) {
|
||||
Toast toast = Toast.makeText(context, R.string.msg_popup_permission, Toast.LENGTH_LONG);
|
||||
TextView messageView = toast.getView().findViewById(android.R.id.message);
|
||||
if (messageView != null) messageView.setGravity(Gravity.CENTER);
|
||||
toast.show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.schabi.newpipe.util;
|
||||
|
||||
import android.widget.PopupMenu;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 20.01.18.
|
||||
* Copyright 2018 Christian Schabesberger <chris.schabesberger@mailbox.org>
|
||||
* PopupMenuIconHacker.java is part of NewPipe
|
||||
*
|
||||
* License: GPL-3.0+
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public class PopupMenuIconHacker {
|
||||
public static void setShowPopupIcon(PopupMenu menu) throws Exception {
|
||||
try {
|
||||
Field[] fields = menu.getClass().getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
if ("mPopup".equals(field.getName())) {
|
||||
field.setAccessible(true);
|
||||
Object menuPopupHelper = field.get(menu);
|
||||
Class<?> classPopupHelper = Class.forName(menuPopupHelper
|
||||
.getClass().getName());
|
||||
Method setForceIcons = classPopupHelper.getMethod(
|
||||
"setForceShowIcon", boolean.class);
|
||||
setForceIcons.invoke(menuPopupHelper, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new Exception("Could not make Popup menu show Icons", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
67
app/src/main/java/org/schabi/newpipe/util/ServiceHelper.java
Normal file
@@ -0,0 +1,67 @@
|
||||
package org.schabi.newpipe.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.DrawableRes;
|
||||
|
||||
import org.schabi.newpipe.BuildConfig;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.ServiceList;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
|
||||
public class ServiceHelper {
|
||||
private static final StreamingService DEFAULT_FALLBACK_SERVICE = ServiceList.YouTube.getService();
|
||||
|
||||
@DrawableRes
|
||||
public static int getIcon(int serviceId) {
|
||||
switch (serviceId) {
|
||||
case 0:
|
||||
return R.drawable.place_holder_youtube;
|
||||
case 1:
|
||||
return R.drawable.place_holder_circle;
|
||||
default:
|
||||
return R.drawable.service;
|
||||
}
|
||||
}
|
||||
|
||||
public static int getSelectedServiceId(Context context) {
|
||||
if (BuildConfig.BUILD_TYPE.equals("release")) return DEFAULT_FALLBACK_SERVICE.getServiceId();
|
||||
|
||||
final String serviceName = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.getString(context.getString(R.string.current_service_key), context.getString(R.string.default_service_value));
|
||||
|
||||
int serviceId;
|
||||
try {
|
||||
serviceId = NewPipe.getService(serviceName).getServiceId();
|
||||
} catch (ExtractionException e) {
|
||||
serviceId = DEFAULT_FALLBACK_SERVICE.getServiceId();
|
||||
}
|
||||
|
||||
return serviceId;
|
||||
}
|
||||
|
||||
public static void setSelectedServiceId(Context context, int serviceId) {
|
||||
String serviceName;
|
||||
try {
|
||||
serviceName = NewPipe.getService(serviceId).getServiceInfo().name;
|
||||
} catch (ExtractionException e) {
|
||||
serviceName = DEFAULT_FALLBACK_SERVICE.getServiceInfo().name;
|
||||
}
|
||||
|
||||
setSelectedServicePreferences(context, serviceName);
|
||||
}
|
||||
|
||||
public static void setSelectedServiceId(Context context, String serviceName) {
|
||||
int serviceId = NewPipe.getIdOfService(serviceName);
|
||||
if (serviceId == -1) serviceName = DEFAULT_FALLBACK_SERVICE.getServiceInfo().name;
|
||||
|
||||
setSelectedServicePreferences(context, serviceName);
|
||||
}
|
||||
|
||||
private static void setSelectedServicePreferences(Context context, String serviceName) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit().
|
||||
putString(context.getString(R.string.current_service_key), serviceName).apply();
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package org.schabi.newpipe.util;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
|
||||
/**
|
||||
* Created by Chrsitian Schabesberger on 09.10.17.
|
||||
* ServiceIconMapper.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 ServiceIconMapper {
|
||||
public static int getIconResource(int service_id) {
|
||||
switch(service_id) {
|
||||
case 0:
|
||||
return R.drawable.youtube;
|
||||
case 1:
|
||||
return R.drawable.soud_cloud;
|
||||
default:
|
||||
return R.drawable.service;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,38 @@
|
||||
package org.schabi.newpipe.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.AttrRes;
|
||||
import android.support.annotation.StyleRes;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
|
||||
public class ThemeHelper {
|
||||
|
||||
/**
|
||||
* Apply the selected theme (on NewPipe settings) in the context
|
||||
* with the default style (see {@link #setTheme(Context, int)}).
|
||||
*
|
||||
* @param context context that the theme will be applied
|
||||
*/
|
||||
public static void setTheme(Context context) {
|
||||
String lightTheme = context.getResources().getString(R.string.light_theme_key);
|
||||
String darkTheme = context.getResources().getString(R.string.dark_theme_key);
|
||||
String blackTheme = context.getResources().getString(R.string.black_theme_key);
|
||||
setTheme(context, -1);
|
||||
}
|
||||
|
||||
String selectedTheme = getSelectedTheme(context);
|
||||
|
||||
if (selectedTheme.equals(lightTheme)) context.setTheme(R.style.LightTheme);
|
||||
else if (selectedTheme.equals(blackTheme)) context.setTheme(R.style.BlackTheme);
|
||||
else if (selectedTheme.equals(darkTheme)) context.setTheme(R.style.DarkTheme);
|
||||
// Fallback
|
||||
else context.setTheme(R.style.DarkTheme);
|
||||
/**
|
||||
* Apply the selected theme (on NewPipe settings) in the context,
|
||||
* themed according with the styles defined for the service .
|
||||
*
|
||||
* @param context context that the theme will be applied
|
||||
* @param serviceId the theme will be styled to the service with this id,
|
||||
* pass -1 to get the default style
|
||||
*/
|
||||
public static void setTheme(Context context, int serviceId) {
|
||||
context.setTheme(getThemeForService(context, serviceId));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,9 +44,73 @@ public class ThemeHelper {
|
||||
return getSelectedTheme(context).equals(context.getResources().getString(R.string.light_theme_key));
|
||||
}
|
||||
|
||||
@StyleRes
|
||||
public static int getThemeForService(Context context, int serviceId) {
|
||||
String lightTheme = context.getResources().getString(R.string.light_theme_key);
|
||||
String darkTheme = context.getResources().getString(R.string.dark_theme_key);
|
||||
String blackTheme = context.getResources().getString(R.string.black_theme_key);
|
||||
|
||||
String selectedTheme = getSelectedTheme(context);
|
||||
|
||||
int defaultTheme = R.style.DarkTheme;
|
||||
if (selectedTheme.equals(lightTheme)) defaultTheme = R.style.LightTheme;
|
||||
else if (selectedTheme.equals(blackTheme)) defaultTheme = R.style.BlackTheme;
|
||||
else if (selectedTheme.equals(darkTheme)) defaultTheme = R.style.DarkTheme;
|
||||
|
||||
if (serviceId <= -1) {
|
||||
return defaultTheme;
|
||||
}
|
||||
|
||||
final StreamingService service;
|
||||
try {
|
||||
service = NewPipe.getService(serviceId);
|
||||
} catch (ExtractionException ignored) {
|
||||
return defaultTheme;
|
||||
}
|
||||
|
||||
String themeName = "DarkTheme";
|
||||
if (selectedTheme.equals(lightTheme)) themeName = "LightTheme";
|
||||
else if (selectedTheme.equals(blackTheme)) themeName = "BlackTheme";
|
||||
else if (selectedTheme.equals(darkTheme)) themeName = "DarkTheme";
|
||||
|
||||
themeName += "." + service.getServiceInfo().name;
|
||||
int resourceId = context.getResources().getIdentifier(themeName, "style", context.getPackageName());
|
||||
|
||||
if (resourceId > 0) {
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
return defaultTheme;
|
||||
}
|
||||
|
||||
public static String getSelectedTheme(Context context) {
|
||||
String themeKey = context.getString(R.string.theme_key);
|
||||
String defaultTheme = context.getResources().getString(R.string.default_theme_value);
|
||||
return PreferenceManager.getDefaultSharedPreferences(context).getString(themeKey, defaultTheme);
|
||||
}
|
||||
|
||||
@StyleRes
|
||||
public static int getSettingsThemeStyle(Context context) {
|
||||
String lightTheme = context.getResources().getString(R.string.light_theme_key);
|
||||
String darkTheme = context.getResources().getString(R.string.dark_theme_key);
|
||||
String blackTheme = context.getResources().getString(R.string.black_theme_key);
|
||||
|
||||
String selectedTheme = getSelectedTheme(context);
|
||||
|
||||
if (selectedTheme.equals(lightTheme)) return R.style.LightSettingsTheme;
|
||||
else if (selectedTheme.equals(blackTheme)) return R.style.BlackSettingsTheme;
|
||||
else if (selectedTheme.equals(darkTheme)) return R.style.DarkSettingsTheme;
|
||||
// Fallback
|
||||
else return R.style.DarkSettingsTheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a resource id from a resource styled according to the the context's theme.
|
||||
*/
|
||||
public static int resolveResourceIdFromAttr(Context context, @AttrRes int attr) {
|
||||
TypedArray a = context.getTheme().obtainStyledAttributes(new int[]{attr});
|
||||
int attributeResourceId = a.getResourceId(0, 0);
|
||||
a.recycle();
|
||||
return attributeResourceId;
|
||||
}
|
||||
}
|
||||
|
||||
94
app/src/main/java/org/schabi/newpipe/util/ZipHelper.java
Normal file
@@ -0,0 +1,94 @@
|
||||
package org.schabi.newpipe.util;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 28.01.18.
|
||||
* Copyright 2018 Christian Schabesberger <chris.schabesberger@mailbox.org>
|
||||
* ZipHelper.java is part of NewPipe
|
||||
*
|
||||
* License: GPL-3.0+
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public class ZipHelper {
|
||||
|
||||
private static final int BUFFER_SIZE = 2048;
|
||||
|
||||
/**
|
||||
* This function helps to create zip files.
|
||||
* Caution this will override the original file.
|
||||
* @param outZip The ZipOutputStream where the data should be stored in
|
||||
* @param file The path of the file that should be added to zip.
|
||||
* @param name The path of the file inside the zip.
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void addFileToZip(ZipOutputStream outZip, String file, String name) throws Exception {
|
||||
byte data[] = new byte[BUFFER_SIZE];
|
||||
FileInputStream fi = new FileInputStream(file);
|
||||
BufferedInputStream inputStream = new BufferedInputStream(fi, BUFFER_SIZE);
|
||||
ZipEntry entry = new ZipEntry(name);
|
||||
outZip.putNextEntry(entry);
|
||||
int count;
|
||||
while((count = inputStream.read(data, 0, BUFFER_SIZE)) != -1) {
|
||||
outZip.write(data, 0, count);
|
||||
}
|
||||
inputStream.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* This will extract data from Zipfiles.
|
||||
* Caution this will override the original file.
|
||||
* @param inZip The ZipOutputStream where the data is stored in
|
||||
* @param file The path of the file on the disk where the data should be extracted to.
|
||||
* @param name The path of the file inside the zip.
|
||||
* @return will return true if the file was found within the zip file
|
||||
* @throws Exception
|
||||
*/
|
||||
public static boolean extractFileFromZip(ZipInputStream inZip, String file, String name) throws Exception {
|
||||
byte data[] = new byte[BUFFER_SIZE];
|
||||
|
||||
boolean found = false;
|
||||
|
||||
ZipEntry ze;
|
||||
while((ze = inZip.getNextEntry()) != null) {
|
||||
if(ze.getName().equals(name)) {
|
||||
found = true;
|
||||
// delete old file first
|
||||
File oldFile = new File(file);
|
||||
if(oldFile.exists()) {
|
||||
if(!oldFile.delete()) {
|
||||
throw new Exception("Could not delete " + file);
|
||||
}
|
||||
}
|
||||
|
||||
FileOutputStream outFile = new FileOutputStream(file);
|
||||
int count = 0;
|
||||
while((count = inZip.read(data)) != -1) {
|
||||
outFile.write(data, 0, count);
|
||||
}
|
||||
|
||||
outFile.close();
|
||||
inZip.closeEntry();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
9
app/src/main/res/anim/switch_service_in.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:interpolator/decelerate_quint">
|
||||
|
||||
<alpha
|
||||
android:duration="150"
|
||||
android:fromAlpha="0.00"
|
||||
android:toAlpha="1.0"/>
|
||||
</set>
|
||||
9
app/src/main/res/anim/switch_service_out.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:interpolator/accelerate_quint">
|
||||
|
||||
<alpha
|
||||
android:duration="350"
|
||||
android:fromAlpha="1.0"
|
||||
android:toAlpha="0.00"/>
|
||||
</set>
|
||||
BIN
app/src/main/res/drawable-hdpi/ic_fullscreen_exit_black_24dp.png
Normal file
|
After Width: | Height: | Size: 103 B |
BIN
app/src/main/res/drawable-hdpi/ic_newpipe_triangle_white.png
Normal file
|
After Width: | Height: | Size: 570 B |
BIN
app/src/main/res/drawable-hdpi/ic_play_arrow_black_24dp.png
Normal file
|
After Width: | Height: | Size: 194 B |
BIN
app/src/main/res/drawable-hdpi/ic_play_arrow_white_24dp.png
Normal file
|
After Width: | Height: | Size: 195 B |
BIN
app/src/main/res/drawable-hdpi/ic_screen_rotation_black_24dp.png
Normal file
|
After Width: | Height: | Size: 478 B |
BIN
app/src/main/res/drawable-mdpi/ic_fullscreen_exit_black_24dp.png
Normal file
|
After Width: | Height: | Size: 93 B |
BIN
app/src/main/res/drawable-mdpi/ic_newpipe_triangle_white.png
Normal file
|
After Width: | Height: | Size: 328 B |
BIN
app/src/main/res/drawable-mdpi/ic_play_arrow_black_24dp.png
Normal file
|
After Width: | Height: | Size: 150 B |
BIN
app/src/main/res/drawable-mdpi/ic_play_arrow_white_24dp.png
Normal file
|
After Width: | Height: | Size: 157 B |
BIN
app/src/main/res/drawable-mdpi/ic_screen_rotation_black_24dp.png
Normal file
|
After Width: | Height: | Size: 321 B |
BIN
app/src/main/res/drawable-nodpi/place_holder_circle.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
app/src/main/res/drawable-nodpi/place_holder_youtube.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 111 B |
BIN
app/src/main/res/drawable-xhdpi/ic_newpipe_triangle_white.png
Normal file
|
After Width: | Height: | Size: 628 B |
BIN
app/src/main/res/drawable-xhdpi/ic_play_arrow_black_24dp.png
Normal file
|
After Width: | Height: | Size: 208 B |
BIN
app/src/main/res/drawable-xhdpi/ic_play_arrow_white_24dp.png
Normal file
|
After Width: | Height: | Size: 220 B |
|
After Width: | Height: | Size: 645 B |
|
After Width: | Height: | Size: 122 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_newpipe_triangle_white.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_play_arrow_black_24dp.png
Normal file
|
After Width: | Height: | Size: 265 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png
Normal file
|
After Width: | Height: | Size: 283 B |
|
After Width: | Height: | Size: 916 B |
|
After Width: | Height: | Size: 123 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_newpipe_triangle_white.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_play_arrow_black_24dp.png
Normal file
|
After Width: | Height: | Size: 320 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_play_arrow_white_24dp.png
Normal file
|
After Width: | Height: | Size: 343 B |
|
After Width: | Height: | Size: 1.2 KiB |
7
app/src/main/res/drawable/dark_checked_selector.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:enterFadeDuration="200" android:exitFadeDuration="200">
|
||||
<item android:drawable="@color/dark_ripple_color" android:state_checked="true" android:state_pressed="true" />
|
||||
<item android:drawable="@color/dark_ripple_color" android:state_pressed="true" />
|
||||
<item android:drawable="@color/dark_selected_color" android:state_checked="true" />
|
||||
<item android:drawable="@android:color/transparent" />
|
||||
</selector>
|
||||
7
app/src/main/res/drawable/light_checked_selector.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:enterFadeDuration="200" android:exitFadeDuration="200">
|
||||
<item android:drawable="@color/light_ripple_color" android:state_checked="true" android:state_pressed="true" />
|
||||
<item android:drawable="@color/light_ripple_color" android:state_pressed="true" />
|
||||
<item android:drawable="@color/light_selected_color" android:state_checked="true" />
|
||||
<item android:drawable="@android:color/transparent" />
|
||||
</selector>
|
||||
@@ -1,18 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge
|
||||
<android.support.v4.widget.DrawerLayout
|
||||
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/drawer_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context="org.schabi.newpipe.MainActivity">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/fragment_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="?attr/actionBarSize"/>
|
||||
android:orientation="vertical"
|
||||
tools:context="org.schabi.newpipe.MainActivity">
|
||||
<FrameLayout
|
||||
android:id="@+id/fragment_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="?attr/actionBarSize" />
|
||||
|
||||
<include layout="@layout/toolbar_layout"/>
|
||||
<include layout="@layout/toolbar_layout" />
|
||||
</FrameLayout>
|
||||
|
||||
</merge>
|
||||
<android.support.design.widget.NavigationView
|
||||
android:id="@+id/navigation"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start"
|
||||
app:menu="@menu/drawer_items" />
|
||||
</android.support.v4.widget.DrawerLayout>
|
||||
@@ -244,7 +244,7 @@
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:scaleType="fitXY"
|
||||
android:src="?attr/options"
|
||||
android:src="@drawable/ic_more_vert_white_24dp"
|
||||
tools:ignore="ContentDescription,RtlHardcoded"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
@@ -31,10 +31,14 @@
|
||||
android:layout_below="@+id/itemTitleView"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
android:marqueeRepeatLimit="marquee_forever"
|
||||
android:scrollHorizontally="true"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textSize="@dimen/video_item_search_uploader_text_size"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
tools:text="TYPE" />
|
||||
tools:text="UPLOADER" />
|
||||
</RelativeLayout>
|
||||
@@ -11,7 +11,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:background="@color/dark_youtube_primary_color"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:tabGravity="fill"/>
|
||||
|
||||
<android.support.v4.view.ViewPager
|
||||
|
||||
@@ -273,7 +273,7 @@
|
||||
android:paddingBottom="6dp"
|
||||
android:paddingTop="6dp"
|
||||
android:text="@string/controls_background_title"
|
||||
android:textSize="12sp"/>
|
||||
android:textSize="12sp" />
|
||||
</RelativeLayout>
|
||||
|
||||
<!--UPLOADER-->
|
||||
|
||||
19
app/src/main/res/layout/list_radio_icon_item.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RadioButton xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:button="@null"
|
||||
android:drawablePadding="24dp"
|
||||
android:ellipsize="marquee"
|
||||
android:gravity="center_vertical"
|
||||
android:maxLines="2"
|
||||
android:minHeight="?attr/listPreferredItemHeightSmall"
|
||||
android:paddingEnd="?attr/listPreferredItemPaddingRight"
|
||||
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
|
||||
android:paddingRight="?attr/listPreferredItemPaddingRight"
|
||||
android:paddingStart="?attr/listPreferredItemPaddingLeft"
|
||||
android:background="?attr/checked_selector_drawable"
|
||||
android:textColor="?attr/textColorAlertDialogListItem"
|
||||
tools:drawableLeft="?attr/play"
|
||||
tools:text="Lorem ipsum dolor sit amet" />
|
||||
@@ -11,8 +11,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:background="@color/background_notification_color"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="128dp"
|
||||
android:background="@color/background_notification_color"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
|
||||
@@ -92,6 +92,7 @@
|
||||
android:layout_alignParentRight="true"
|
||||
android:background="#00ffffff"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/ic_fullscreen_white"
|
||||
tools:ignore="ContentDescription,RtlHardcoded"/>
|
||||
|
||||