mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-10-07 11:42:15 +02:00
Compare commits
234 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
88b29cbbf9 | ||
![]() |
51d4d0d3dc | ||
![]() |
a482aa1e21 | ||
![]() |
040b38689d | ||
![]() |
8e8e53c4d5 | ||
![]() |
d717c6d2f6 | ||
![]() |
9587ce97a8 | ||
![]() |
aaaf573475 | ||
![]() |
6c6f322d90 | ||
![]() |
c03f0ed1fb | ||
![]() |
55287393be | ||
![]() |
9575f92165 | ||
![]() |
a5cbaad804 | ||
![]() |
f492414b5e | ||
![]() |
3e6ddf7176 | ||
![]() |
29ffb05653 | ||
![]() |
50c3ee2e9c | ||
![]() |
6292470677 | ||
![]() |
8d912f2673 | ||
![]() |
227d129c3a | ||
![]() |
d9eeb6afa0 | ||
![]() |
478aecb0f4 | ||
![]() |
abb82154c9 | ||
![]() |
03c8b6b5f5 | ||
![]() |
2281300165 | ||
![]() |
84068d101b | ||
![]() |
62020fa85b | ||
![]() |
3c8bf5ccc9 | ||
![]() |
71d5762be8 | ||
![]() |
65d5358366 | ||
![]() |
6ecdfaf19e | ||
![]() |
32bd6ae1ac | ||
![]() |
92231a1e26 | ||
![]() |
38ad4dc440 | ||
![]() |
6645a47b0e | ||
![]() |
75a2d20b0b | ||
![]() |
8c15d708e6 | ||
![]() |
539b7ad87b | ||
![]() |
fc7c2c9f5a | ||
![]() |
3cd760f654 | ||
![]() |
2ec0a5d003 | ||
![]() |
1a605e814b | ||
![]() |
fe0053a15d | ||
![]() |
014f3e5aff | ||
![]() |
4a25e3b644 | ||
![]() |
e7f59bc436 | ||
![]() |
488d1ccd5a | ||
![]() |
409b36c254 | ||
![]() |
b0e567dbfa | ||
![]() |
c6086ba281 | ||
![]() |
aede925351 | ||
![]() |
9d38c66510 | ||
![]() |
0c516189c3 | ||
![]() |
c2936ea289 | ||
![]() |
dc33460a34 | ||
![]() |
ffacc93b55 | ||
![]() |
74f0ee2718 | ||
![]() |
7189791d9f | ||
![]() |
dc18d53c8f | ||
![]() |
f71ef8e130 | ||
![]() |
6eec9d8993 | ||
![]() |
9560f98359 | ||
![]() |
217433bf69 | ||
![]() |
ff5db1b939 | ||
![]() |
455a46d3ad | ||
![]() |
039a879104 | ||
![]() |
dfba9ea53b | ||
![]() |
177cce5e8f | ||
![]() |
309d36260e | ||
![]() |
bdc73eb755 | ||
![]() |
0786750eb9 | ||
![]() |
9f5d921275 | ||
![]() |
8991b2d8e3 | ||
![]() |
79dffce59b | ||
![]() |
e0301a621b | ||
![]() |
1e4361abdc | ||
![]() |
80c26fd278 | ||
![]() |
3339531086 | ||
![]() |
07ad9fbb11 | ||
![]() |
6ddc581d76 | ||
![]() |
dfe94172aa | ||
![]() |
edb632f9c7 | ||
![]() |
7f73612b9f | ||
![]() |
7f130f18b6 | ||
![]() |
d05a4c53aa | ||
![]() |
d99a9b90a0 | ||
![]() |
f311225312 | ||
![]() |
dee17fc313 | ||
![]() |
0662538d60 | ||
![]() |
5b52ad91ac | ||
![]() |
8552c41bff | ||
![]() |
7ecd298285 | ||
![]() |
2546f515d1 | ||
![]() |
8804924d27 | ||
![]() |
e410cae141 | ||
![]() |
140cfaec90 | ||
![]() |
a0f20aac23 | ||
![]() |
93fafb362f | ||
![]() |
9152df5512 | ||
![]() |
415bcd9f7e | ||
![]() |
2224033a85 | ||
![]() |
ade0498684 | ||
![]() |
211b00fff4 | ||
![]() |
77c72d03a8 | ||
![]() |
f429d93351 | ||
![]() |
e428f8b116 | ||
![]() |
9482b9d638 | ||
![]() |
e019c9f720 | ||
![]() |
e2778366e9 | ||
![]() |
36d2f8339c | ||
![]() |
a4a0c3b9fd | ||
![]() |
1b1b6c8af8 | ||
![]() |
064fd2bc68 | ||
![]() |
13152ab6ea | ||
![]() |
1743d821eb | ||
![]() |
ff16e577ef | ||
![]() |
86062c6e94 | ||
![]() |
8ce2350563 | ||
![]() |
cb8989af7f | ||
![]() |
877fa45eb4 | ||
![]() |
e33942486d | ||
![]() |
9606e080ef | ||
![]() |
d5cd9c55be | ||
![]() |
9c197ced80 | ||
![]() |
f8424599e1 | ||
![]() |
ccee18057a | ||
![]() |
1ad0f342ad | ||
![]() |
7a16ac574b | ||
![]() |
f46738f750 | ||
![]() |
fb8ff3fece | ||
![]() |
7498dd3800 | ||
![]() |
957c31b9c5 | ||
![]() |
b260570e8c | ||
![]() |
46542747b7 | ||
![]() |
d64480fc9b | ||
![]() |
c00e694d40 | ||
![]() |
f0761cc95e | ||
![]() |
068554955c | ||
![]() |
a2f915f556 | ||
![]() |
cffd35c974 | ||
![]() |
2ef2aa98a4 | ||
![]() |
ba98b30aa4 | ||
![]() |
87c0f9c803 | ||
![]() |
ce620b9e28 | ||
![]() |
fcbb5536eb | ||
![]() |
72df6dbd80 | ||
![]() |
3a6b023dbf | ||
![]() |
8b67f1358d | ||
![]() |
2aebb3b8db | ||
![]() |
f2b38be2b0 | ||
![]() |
a43001f30d | ||
![]() |
17abe1ea5c | ||
![]() |
6aaefab618 | ||
![]() |
cd867fb4d1 | ||
![]() |
25988f61a6 | ||
![]() |
be421e580d | ||
![]() |
641ab25470 | ||
![]() |
a508539c2e | ||
![]() |
cac79d9a0d | ||
![]() |
9ede8118da | ||
![]() |
e47761750a | ||
![]() |
2f181ce7c9 | ||
![]() |
3a13d4a1de | ||
![]() |
719de00e0f | ||
![]() |
ddffe99f53 | ||
![]() |
9ade596f06 | ||
![]() |
46f413b7f2 | ||
![]() |
5fe2e7b8ad | ||
![]() |
3008cbb5f4 | ||
![]() |
f7983960e5 | ||
![]() |
bb8007bb7c | ||
![]() |
01751ba97a | ||
![]() |
f41475d11c | ||
![]() |
d8914d9b6d | ||
![]() |
94cc2ad365 | ||
![]() |
e07464b81c | ||
![]() |
9d231b55b5 | ||
![]() |
77d8dac3c1 | ||
![]() |
e160015283 | ||
![]() |
aeb0cac3ee | ||
![]() |
a539f94837 | ||
![]() |
df70751071 | ||
![]() |
e55f1dff78 | ||
![]() |
47646e1c62 | ||
![]() |
80e673f20c | ||
![]() |
9ca8c5480c | ||
![]() |
4d0d3c7ead | ||
![]() |
58137aadc9 | ||
![]() |
82a59ae479 | ||
![]() |
affd23b14e | ||
![]() |
9c7f249756 | ||
![]() |
e2a0502171 | ||
![]() |
3832a4b355 | ||
![]() |
84f059415c | ||
![]() |
bb292e3199 | ||
![]() |
70541bf561 | ||
![]() |
48dd8e88e3 | ||
![]() |
52c2e0899d | ||
![]() |
9955d5b62f | ||
![]() |
8e26247fa1 | ||
![]() |
72924e2692 | ||
![]() |
8c2c8d630f | ||
![]() |
5ccf0baa6b | ||
![]() |
8fff0ccdf2 | ||
![]() |
35264bfb97 | ||
![]() |
4c9b0dc8e9 | ||
![]() |
4deb7caa83 | ||
![]() |
45f1a512b4 | ||
![]() |
ab95fdc087 | ||
![]() |
df608b9ded | ||
![]() |
6a4c81d160 | ||
![]() |
040d658540 | ||
![]() |
cbc9913e9c | ||
![]() |
f007cbd5ee | ||
![]() |
7351591df7 | ||
![]() |
5222d4cbba | ||
![]() |
ef9966815e | ||
![]() |
ad73440ce9 | ||
![]() |
c81cfdc455 | ||
![]() |
134c3804db | ||
![]() |
e355df5eda | ||
![]() |
a68e0a95f4 | ||
![]() |
6fd6facf72 | ||
![]() |
00102d4048 | ||
![]() |
adbeff11d4 | ||
![]() |
a8fe329678 | ||
![]() |
a4b61bf730 | ||
![]() |
ee592def0c | ||
![]() |
c14e117239 | ||
![]() |
b6e94dde0c | ||
![]() |
63a4a44d7e | ||
![]() |
eb8ab5d527 | ||
![]() |
b778d40d65 | ||
![]() |
2f570672dc |
1
.github/CONTRIBUTING.md
vendored
1
.github/CONTRIBUTING.md
vendored
@@ -12,6 +12,7 @@ Do not report crashes in the GitHub issue tracker. NewPipe has an automated cras
|
||||
* Search the [existing issues](https://github.com/theScrabi/NewPipe/issues) first to make sure your issue/feature hasn't been reported/requested before
|
||||
* Check if this issue/feature is already fixed/implemented in the repository
|
||||
* If you are an android/java developer you are always welcome to fix/implement an issue/a feature yourself
|
||||
* Use english
|
||||
|
||||
## Bugfixing
|
||||
* If you want to help NewPipe getting bug free, you can send me a mail to tnp@newpipe.schabi.org to let me know that you intent to help, and than register at our [sentry](https://support.schabi.org) setup.
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,3 +9,4 @@
|
||||
/*.iml
|
||||
gradle.properties
|
||||
*~
|
||||
.weblate
|
||||
|
@@ -5,7 +5,7 @@ android:
|
||||
components:
|
||||
# The BuildTools version used by NewPipe
|
||||
- tools
|
||||
- build-tools-25.0.0
|
||||
- build-tools-25.0.2
|
||||
|
||||
# The SDK version used to compile NewPipe
|
||||
- android-25
|
||||
|
16
README.md
16
README.md
@@ -19,11 +19,16 @@ Project status:
|
||||
|
||||
## Screenshots
|
||||
|
||||
[<img src="screenshots/screenshot_1.png" width=150>](screenshots/screenshot_1.png)
|
||||
[<img src="screenshots/screenshot_2.png" width=150>](screenshots/screenshot_2.png)
|
||||
[<img src="screenshots/screenshot_3.png" width=150>](screenshots/screenshot_3.png)
|
||||
[<img src="screenshots/screenshot_4.png" width=150>](screenshots/screenshot_4.png)
|
||||
[<img src="screenshots/screenshot_5.png" width=150>](screenshots/screenshot_5.png)
|
||||
[<img src="screenshots/screenshot_1.png" width=160>](screenshots/screenshot_1.png)
|
||||
[<img src="screenshots/screenshot_2.png" width=160>](screenshots/screenshot_2.png)
|
||||
[<img src="screenshots/screenshot_3.png" width=160>](screenshots/screenshot_3.png)
|
||||
[<img src="screenshots/screenshot_4.png" width=160>](screenshots/screenshot_4.png)
|
||||
[<img src="screenshots/screenshot_5.png" width=160>](screenshots/screenshot_5.png)
|
||||
[<img src="screenshots/screenshot_6.png" width=160>](screenshots/screenshot_6.png)
|
||||
[<img src="screenshots/screenshot_7.png" width=160>](screenshots/screenshot_7.png)
|
||||
[<img src="screenshots/screenshot_8.png" width=160>](screenshots/screenshot_8.png)
|
||||
[<img src="screenshots/screenshot_9.png" width=160>](screenshots/screenshot_9.png)
|
||||
|
||||
|
||||
## Description
|
||||
|
||||
@@ -35,6 +40,7 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
|
||||
* Display general information about a video
|
||||
* Watch YouTube videos
|
||||
* Listen to YouTube videos (experimental)
|
||||
* Popup mode (floating player)
|
||||
* Select the streaming player to watch the video with
|
||||
* Download videos
|
||||
* Download audio only
|
||||
|
@@ -8,8 +8,8 @@ android {
|
||||
applicationId "org.schabi.newpipe"
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 25
|
||||
versionCode 30
|
||||
versionName "0.9.3"
|
||||
versionCode 35
|
||||
versionName "0.9.8"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
@@ -39,14 +39,16 @@ dependencies {
|
||||
compile 'com.android.support:support-v4:25.3.1'
|
||||
compile 'com.android.support:design:25.3.1'
|
||||
compile 'com.android.support:recyclerview-v7:25.3.1'
|
||||
|
||||
compile 'com.google.code.gson:gson:2.7'
|
||||
compile 'org.jsoup:jsoup:1.8.3'
|
||||
compile 'org.mozilla:rhino:1.7.7'
|
||||
compile 'info.guardianproject.netcipher:netcipher:1.2'
|
||||
compile 'de.hdodenhof:circleimageview:2.0.0'
|
||||
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
|
||||
compile 'com.github.nirhart:parallaxscroll:1.0'
|
||||
compile 'com.google.code.gson:gson:2.7'
|
||||
compile 'com.nononsenseapps:filepicker:3.0.0'
|
||||
compile 'ch.acra:acra:4.9.0'
|
||||
compile 'info.guardianproject.netcipher:netcipher:1.2'
|
||||
|
||||
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
|
||||
compile 'de.hdodenhof:circleimageview:2.0.0'
|
||||
compile 'com.github.nirhart:parallaxscroll:1.0'
|
||||
compile 'com.nononsenseapps:filepicker:3.0.0'
|
||||
compile 'com.google.android.exoplayer:exoplayer:r2.3.1'
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,7 @@ import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.report.AcraReportSenderFactory;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.settings.SettingsActivity;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
|
||||
import info.guardianproject.netcipher.NetCipher;
|
||||
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
||||
@@ -82,6 +83,8 @@ public class App extends Application {
|
||||
// DO NOT REMOVE THIS FUNCTION!!!
|
||||
// Otherwise downloadPathPreference has invalid value.
|
||||
SettingsActivity.initSettings(this);
|
||||
|
||||
ThemeHelper.setTheme(getApplicationContext());
|
||||
}
|
||||
|
||||
/**
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,7 @@ import android.os.Bundle;
|
||||
import android.support.v4.app.NavUtils;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.MenuItem;
|
||||
import android.webkit.CookieManager;
|
||||
import android.webkit.ValueCallback;
|
||||
@@ -48,10 +49,15 @@ public class ReCaptchaActivity extends AppCompatActivity {
|
||||
// Set return to Cancel by default
|
||||
setResult(RESULT_CANCELED);
|
||||
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
actionBar.setTitle(R.string.reCaptcha_title);
|
||||
actionBar.setDisplayShowTitleEnabled(true);
|
||||
if (actionBar != null) {
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
actionBar.setTitle(R.string.reCaptcha_title);
|
||||
actionBar.setDisplayShowTitleEnabled(true);
|
||||
}
|
||||
|
||||
WebView myWebView = (WebView) findViewById(R.id.reCaptchaWebView);
|
||||
|
||||
|
@@ -33,26 +33,39 @@ import java.util.HashSet;
|
||||
* to the part of the service which can handle the url.
|
||||
*/
|
||||
public class RouterActivity extends Activity {
|
||||
//private static final String TAG = "RouterActivity"
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
String videoUrl = getUrl(getIntent());
|
||||
handleUrl(videoUrl);
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
protected void handleUrl(String url) {
|
||||
try {
|
||||
NavigationHelper.openByLink(this, url);
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
/**
|
||||
* Removes invisible separators (\p{Z}) and punctuation characters including
|
||||
* brackets (\p{P}). See http://www.regular-expressions.info/unicode.html for
|
||||
* more details.
|
||||
*/
|
||||
private final static String REGEX_REMOVE_FROM_URL = "[\\p{Z}\\p{P}]";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
handleIntent(getIntent());
|
||||
finish();
|
||||
}
|
||||
|
||||
private void handleIntent(Intent intent) {
|
||||
String videoUrl = "";
|
||||
protected final static String REGEX_REMOVE_FROM_URL = "[\\p{Z}\\p{P}]";
|
||||
|
||||
protected String getUrl(Intent intent) {
|
||||
// first gather data and find service
|
||||
String videoUrl = null;
|
||||
if (intent.getData() != null) {
|
||||
// this means the video was called though another app
|
||||
videoUrl = intent.getData().toString();
|
||||
@@ -62,14 +75,10 @@ public class RouterActivity extends Activity {
|
||||
videoUrl = getUris(extraText)[0];
|
||||
}
|
||||
|
||||
try {
|
||||
NavigationHelper.openByLink(this, videoUrl);
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
return videoUrl;
|
||||
}
|
||||
|
||||
private static String removeHeadingGibberish(final String input) {
|
||||
protected String removeHeadingGibberish(final String input) {
|
||||
int start = 0;
|
||||
for (int i = input.indexOf("://") - 1; i >= 0; i--) {
|
||||
if (!input.substring(i, i + 1).matches("\\p{L}")) {
|
||||
@@ -80,7 +89,7 @@ public class RouterActivity extends Activity {
|
||||
return input.substring(start, input.length());
|
||||
}
|
||||
|
||||
private static String trim(final String input) {
|
||||
protected String trim(final String input) {
|
||||
if (input == null || input.length() < 1) {
|
||||
return input;
|
||||
} else {
|
||||
@@ -103,7 +112,7 @@ public class RouterActivity extends Activity {
|
||||
* @param sharedText text to scan for URLs.
|
||||
* @return potential URLs
|
||||
*/
|
||||
private String[] getUris(final String sharedText) {
|
||||
protected String[] getUris(final String sharedText) {
|
||||
final Collection<String> result = new HashSet<>();
|
||||
if (sharedText != null) {
|
||||
final String[] array = sharedText.split("\\p{Space}");
|
||||
|
@@ -1,9 +1,7 @@
|
||||
package org.schabi.newpipe;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
@@ -12,57 +10,26 @@ import org.schabi.newpipe.player.PopupVideoPlayer;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
import org.schabi.newpipe.util.PermissionHelper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* This activity is thought to open video streams form an external app using the popup player.
|
||||
* Get the url from the intent and open a popup player
|
||||
*/
|
||||
public class RouterPopupActivity extends Activity {
|
||||
//private static final String TAG = "RouterPopupActivity";
|
||||
|
||||
/**
|
||||
* Removes invisible separators (\p{Z}) and punctuation characters including
|
||||
* brackets (\p{P}). See http://www.regular-expressions.info/unicode.html for
|
||||
* more details.
|
||||
*/
|
||||
private final static String REGEX_REMOVE_FROM_URL = "[\\p{Z}\\p{P}]";
|
||||
public class RouterPopupActivity extends RouterActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
handleIntent(getIntent());
|
||||
finish();
|
||||
}
|
||||
|
||||
|
||||
private void handleIntent(Intent intent) {
|
||||
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();
|
||||
return;
|
||||
}
|
||||
String videoUrl = "";
|
||||
StreamingService service;
|
||||
|
||||
// first gather data and find service
|
||||
if (intent.getData() != null) {
|
||||
// this means the video was called though another app
|
||||
videoUrl = intent.getData().toString();
|
||||
} 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];
|
||||
}
|
||||
|
||||
service = NewPipe.getServiceByUrl(videoUrl);
|
||||
StreamingService service = NewPipe.getServiceByUrl(url);
|
||||
if (service == null) {
|
||||
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
Intent callIntent = new Intent(this, PopupVideoPlayer.class);
|
||||
switch (service.getLinkTypeByUrl(videoUrl)) {
|
||||
switch (service.getLinkTypeByUrl(url)) {
|
||||
case STREAM:
|
||||
break;
|
||||
default:
|
||||
@@ -70,61 +37,8 @@ public class RouterPopupActivity extends Activity {
|
||||
return;
|
||||
}
|
||||
|
||||
callIntent.putExtra(Constants.KEY_URL, videoUrl);
|
||||
callIntent.putExtra(Constants.KEY_URL, url);
|
||||
callIntent.putExtra(Constants.KEY_SERVICE_ID, service.getServiceId());
|
||||
startService(callIntent);
|
||||
}
|
||||
|
||||
private static String removeHeadingGibberish(final String input) {
|
||||
int start = 0;
|
||||
for (int i = input.indexOf("://") - 1; i >= 0; i--) {
|
||||
if (!input.substring(i, i + 1).matches("\\p{L}")) {
|
||||
start = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return input.substring(start, input.length());
|
||||
}
|
||||
|
||||
private static String trim(final String input) {
|
||||
if (input == null || input.length() < 1) {
|
||||
return input;
|
||||
} else {
|
||||
String output = input;
|
||||
while (output.length() > 0 && output.substring(0, 1).matches(REGEX_REMOVE_FROM_URL)) {
|
||||
output = output.substring(1);
|
||||
}
|
||||
while (output.length() > 0
|
||||
&& output.substring(output.length() - 1, output.length()).matches(REGEX_REMOVE_FROM_URL)) {
|
||||
output = output.substring(0, output.length() - 1);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all Strings which look remotely like URLs from a text.
|
||||
* Used if NewPipe was called through share menu.
|
||||
*
|
||||
* @param sharedText text to scan for URLs.
|
||||
* @return potential URLs
|
||||
*/
|
||||
private String[] getUris(final String sharedText) {
|
||||
final Collection<String> result = new HashSet<>();
|
||||
if (sharedText != null) {
|
||||
final String[] array = sharedText.split("\\p{Space}");
|
||||
for (String s : array) {
|
||||
s = trim(s);
|
||||
if (s.length() != 0) {
|
||||
if (s.matches(".+://.+")) {
|
||||
result.add(removeHeadingGibberish(s));
|
||||
} else if (s.matches(".+\\..+")) {
|
||||
result.add("http://" + s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toArray(new String[result.size()]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,28 +0,0 @@
|
||||
package org.schabi.newpipe;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
||||
public class ThemableActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.getString("theme", getResources().getString(R.string.light_theme_title)).
|
||||
equals(getResources().getString(R.string.dark_theme_title))) {
|
||||
setTheme(R.style.DarkTheme);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if (PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.getString("theme", getResources().getString(R.string.light_theme_title)).
|
||||
equals(getResources().getString(R.string.dark_theme_title))) {
|
||||
setTheme(R.style.DarkTheme);
|
||||
}
|
||||
}
|
||||
}
|
@@ -8,7 +8,6 @@ import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.NavUtils;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
@@ -26,13 +25,11 @@ import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.settings.NewPipeSettings;
|
||||
import org.schabi.newpipe.settings.SettingsActivity;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Vector;
|
||||
|
||||
import us.shandian.giga.service.DownloadManagerService;
|
||||
import us.shandian.giga.ui.fragment.AllMissionsFragment;
|
||||
@@ -64,17 +61,19 @@ public class DownloadActivity extends AppCompatActivity implements AdapterView.O
|
||||
i.setClass(this, DownloadManagerService.class);
|
||||
startService(i);
|
||||
|
||||
ThemeHelper.setTheme(this);
|
||||
super.onCreate(savedInstanceState);
|
||||
ThemeHelper.setTheme(this, true);
|
||||
setContentView(R.layout.activity_downloader);
|
||||
|
||||
//noinspection ConstantConditions
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
// its ok if this fails, we will catch that error later, and send it as report
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
actionBar.setTitle(R.string.downloads_title);
|
||||
actionBar.setDisplayShowTitleEnabled(true);
|
||||
if (actionBar != null) {
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
actionBar.setTitle(R.string.downloads_title);
|
||||
actionBar.setDisplayShowTitleEnabled(true);
|
||||
}
|
||||
|
||||
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
||||
@@ -159,7 +158,7 @@ public class DownloadActivity extends AppCompatActivity implements AdapterView.O
|
||||
name.setText(getIntent().getStringExtra("fileName"));
|
||||
|
||||
toolbar.setTitle(R.string.add);
|
||||
toolbar.setNavigationIcon(R.drawable.ic_arrow_back_black_24dp);
|
||||
toolbar.setNavigationIcon(ThemeHelper.isLightThemeSelected(this) ? R.drawable.ic_arrow_back_black_24dp : R.drawable.ic_arrow_back_white_24dp);
|
||||
toolbar.inflateMenu(R.menu.dialog_url);
|
||||
|
||||
// Show the dialog
|
||||
@@ -183,7 +182,7 @@ public class DownloadActivity extends AppCompatActivity implements AdapterView.O
|
||||
if (item.getItemId() == R.id.okay) {
|
||||
|
||||
String location;
|
||||
if(audioButton.isChecked()) {
|
||||
if (audioButton.isChecked()) {
|
||||
location = NewPipeSettings.getAudioDownloadPath(DownloadActivity.this);
|
||||
} else {
|
||||
location = NewPipeSettings.getVideoDownloadPath(DownloadActivity.this);
|
||||
@@ -201,7 +200,7 @@ public class DownloadActivity extends AppCompatActivity implements AdapterView.O
|
||||
audioButton.isChecked(), threads.getProgress() + 1);
|
||||
mFragment.notifyChange();
|
||||
|
||||
mPrefs.edit().putInt(THREADS, threads.getProgress() + 1).commit();
|
||||
mPrefs.edit().putInt(THREADS, threads.getProgress() + 1).apply();
|
||||
mPendingUrl = null;
|
||||
dialog.dismiss();
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,165 +0,0 @@
|
||||
package org.schabi.newpipe.download;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
import info.guardianproject.netcipher.NetCipher;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 14.08.15.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
||||
* FileDownloader.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/>.
|
||||
*/
|
||||
|
||||
|
||||
// TODO: FOR HEAVEN SAKE !!! DO NOT SIMPLY USE ASYNCTASK. MAKE THIS A PROPER SERVICE !!!
|
||||
public class FileDownloader extends AsyncTask<Void, Integer, Void> {
|
||||
public static final String TAG = "FileDownloader";
|
||||
|
||||
|
||||
private NotificationManager nm;
|
||||
private NotificationCompat.Builder builder;
|
||||
private int notifyId = 0x1234;
|
||||
private int fileSize = 0xffffffff;
|
||||
|
||||
private final Context context;
|
||||
private final String fileURL;
|
||||
private final File saveFilePath;
|
||||
private final String title;
|
||||
|
||||
private final String debugContext;
|
||||
|
||||
public FileDownloader(Context context, String fileURL, File saveFilePath, String title) {
|
||||
this.context = context;
|
||||
this.fileURL = fileURL;
|
||||
this.saveFilePath = saveFilePath;
|
||||
this.title = title;
|
||||
|
||||
this.debugContext = "'" + fileURL +
|
||||
"' => '" + saveFilePath + "'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a file from a URL in the background using an {@link AsyncTask}.
|
||||
*
|
||||
* @param fileURL HTTP URL of the file to be downloaded
|
||||
* @param saveFilePath path of the directory to save the file
|
||||
* @param title
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void downloadFile(final Context context, final String fileURL, final File saveFilePath, String title) {
|
||||
new FileDownloader(context, fileURL, saveFilePath, title).execute();
|
||||
}
|
||||
|
||||
/** AsyncTask impl: executed in gui thread */
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
Drawable icon = context.getResources().getDrawable(R.mipmap.ic_launcher);
|
||||
builder = new NotificationCompat.Builder(context)
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download)
|
||||
.setLargeIcon(((BitmapDrawable) icon).getBitmap())
|
||||
.setContentTitle(saveFilePath.getName())
|
||||
.setContentText(saveFilePath.getAbsolutePath())
|
||||
.setProgress(fileSize, 0, false);
|
||||
nm.notify(notifyId, builder.build());
|
||||
}
|
||||
|
||||
/** AsyncTask impl: executed in background thread does the download */
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
HttpsURLConnection con = null;
|
||||
InputStream inputStream = null;
|
||||
FileOutputStream outputStream = null;
|
||||
try {
|
||||
con = NetCipher.getHttpsURLConnection(fileURL);
|
||||
int responseCode = con.getResponseCode();
|
||||
|
||||
// always check HTTP response code first
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
fileSize = con.getContentLength();
|
||||
inputStream = new BufferedInputStream(con.getInputStream());
|
||||
outputStream = new FileOutputStream(saveFilePath);
|
||||
|
||||
int bufferSize = 8192;
|
||||
int downloaded = 0;
|
||||
|
||||
int bytesRead = -1;
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
downloaded += bytesRead;
|
||||
if (downloaded % 50000 < bufferSize) {
|
||||
publishProgress(downloaded);
|
||||
}
|
||||
}
|
||||
|
||||
publishProgress(bufferSize);
|
||||
|
||||
} else {
|
||||
Log.i(TAG, "No file to download. Server replied HTTP code: " + responseCode);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "No file to download. Server replied HTTP code: ", e);
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (outputStream != null) {
|
||||
outputStream.close();
|
||||
}
|
||||
if (inputStream != null) {
|
||||
inputStream.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (con != null) {
|
||||
con.disconnect();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(Integer... progress) {
|
||||
builder.setProgress(fileSize, progress[0], false);
|
||||
nm.notify(notifyId, builder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid) {
|
||||
super.onPostExecute(aVoid);
|
||||
nm.cancel(notifyId);
|
||||
}
|
||||
}
|
Submodule app/src/main/java/org/schabi/newpipe/extractor updated: 08457de763...ab530381cf
177
app/src/main/java/org/schabi/newpipe/fragments/BaseFragment.java
Normal file
177
app/src/main/java/org/schabi/newpipe/fragments/BaseFragment.java
Normal file
@@ -0,0 +1,177 @@
|
||||
package org.schabi.newpipe.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.AttrRes;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer;
|
||||
|
||||
import org.schabi.newpipe.MainActivity;
|
||||
import org.schabi.newpipe.R;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
|
||||
public abstract class BaseFragment extends Fragment {
|
||||
protected final String TAG = "BaseFragment@" + Integer.toHexString(hashCode());
|
||||
protected static final boolean DEBUG = MainActivity.DEBUG;
|
||||
|
||||
protected AppCompatActivity activity;
|
||||
|
||||
protected AtomicBoolean isLoading = new AtomicBoolean(false);
|
||||
protected AtomicBoolean wasLoading = new AtomicBoolean(false);
|
||||
|
||||
protected static final ImageLoader imageLoader = ImageLoader.getInstance();
|
||||
protected static final DisplayImageOptions displayImageOptions =
|
||||
new DisplayImageOptions.Builder().displayer(new FadeInBitmapDisplayer(400)).cacheInMemory(false).build();
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Views
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected Toolbar toolbar;
|
||||
|
||||
protected View errorPanel;
|
||||
protected Button errorButtonRetry;
|
||||
protected TextView errorTextView;
|
||||
protected ProgressBar loadingProgressBar;
|
||||
//protected SwipeRefreshLayout swipeRefreshLayout;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Fragment's Lifecycle
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if (DEBUG) Log.d(TAG, "onAttach() called with: context = [" + context + "]");
|
||||
|
||||
activity = (AppCompatActivity) context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
|
||||
|
||||
isLoading.set(false);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View rootView, Bundle savedInstanceState) {
|
||||
if (DEBUG) Log.d(TAG, "onViewCreated() called with: rootView = [" + rootView + "], savedInstanceState = [" + savedInstanceState + "]");
|
||||
initViews(rootView, savedInstanceState);
|
||||
initListeners();
|
||||
wasLoading.set(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
if (DEBUG) Log.d(TAG, "onDestroyView() called");
|
||||
toolbar = null;
|
||||
|
||||
errorPanel = null;
|
||||
errorButtonRetry = null;
|
||||
errorTextView = null;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Init
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected void initViews(View rootView, Bundle savedInstanceState) {
|
||||
toolbar = (Toolbar) activity.findViewById(R.id.toolbar);
|
||||
|
||||
loadingProgressBar = (ProgressBar) rootView.findViewById(R.id.loading_progress_bar);
|
||||
//swipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipe_refresh);
|
||||
|
||||
errorPanel = rootView.findViewById(R.id.error_panel);
|
||||
errorButtonRetry = (Button) rootView.findViewById(R.id.error_button_retry);
|
||||
errorTextView = (TextView) rootView.findViewById(R.id.error_message_view);
|
||||
}
|
||||
|
||||
protected void initListeners() {
|
||||
errorButtonRetry.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onRetryButtonClicked();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected abstract void reloadContent();
|
||||
|
||||
protected void onRetryButtonClicked() {
|
||||
if (DEBUG) Log.d(TAG, "onRetryButtonClicked() called");
|
||||
reloadContent();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected void setErrorMessage(String message, boolean showRetryButton) {
|
||||
if (errorTextView == null || activity == null) return;
|
||||
|
||||
errorTextView.setText(message);
|
||||
if (showRetryButton) animateView(errorButtonRetry, true, 300);
|
||||
else animateView(errorButtonRetry, false, 0);
|
||||
|
||||
animateView(errorPanel, true, 300);
|
||||
isLoading.set(false);
|
||||
|
||||
animateView(loadingProgressBar, false, 200);
|
||||
}
|
||||
|
||||
protected int getResourceIdFromAttr(@AttrRes int attr) {
|
||||
TypedArray a = activity.getTheme().obtainStyledAttributes(new int[]{attr});
|
||||
int attributeResourceId = a.getResourceId(0, 0);
|
||||
a.recycle();
|
||||
return attributeResourceId;
|
||||
}
|
||||
|
||||
public static void showMenuTooltip(View v, String message) {
|
||||
final int[] screenPos = new int[2];
|
||||
final Rect displayFrame = new Rect();
|
||||
v.getLocationOnScreen(screenPos);
|
||||
v.getWindowVisibleDisplayFrame(displayFrame);
|
||||
|
||||
final Context context = v.getContext();
|
||||
final int width = v.getWidth();
|
||||
final int height = v.getHeight();
|
||||
final int midy = screenPos[1] + height / 2;
|
||||
int referenceX = screenPos[0] + width / 2;
|
||||
if (ViewCompat.getLayoutDirection(v) == View.LAYOUT_DIRECTION_LTR) {
|
||||
final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
|
||||
referenceX = screenWidth - referenceX; // mirror
|
||||
}
|
||||
Toast cheatSheet = Toast.makeText(context, message, Toast.LENGTH_SHORT);
|
||||
if (midy < displayFrame.height()) {
|
||||
// Show along the top; follow action buttons
|
||||
cheatSheet.setGravity(Gravity.TOP | Gravity.END, referenceX,
|
||||
screenPos[1] + height - displayFrame.top);
|
||||
} else {
|
||||
// Show along the bottom center
|
||||
cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);
|
||||
}
|
||||
cheatSheet.show();
|
||||
}
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
package org.schabi.newpipe.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
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 org.schabi.newpipe.MainActivity;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
|
||||
public class MainFragment extends Fragment {
|
||||
private final String TAG = "MainFragment@" + Integer.toHexString(hashCode());
|
||||
private static final boolean DEBUG = MainActivity.DEBUG;
|
||||
|
||||
private AppCompatActivity activity;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Fragment's LifeCycle
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if (DEBUG) Log.d(TAG, "onAttach() called with: context = [" + context + "]");
|
||||
activity = ((AppCompatActivity) context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
if (DEBUG) Log.d(TAG, "onCreateView() called with: inflater = [" + inflater + "], container = [" + container + "], savedInstanceState = [" + savedInstanceState + "]");
|
||||
return inflater.inflate(R.layout.fragment_main, container, false);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Menu
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "], inflater = [" + inflater + "]");
|
||||
inflater.inflate(R.menu.main_fragment_menu, menu);
|
||||
|
||||
ActionBar supportActionBar = activity.getSupportActionBar();
|
||||
if (supportActionBar != null) {
|
||||
supportActionBar.setDisplayShowTitleEnabled(false);
|
||||
supportActionBar.setDisplayHomeAsUpEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_search:
|
||||
NavigationHelper.openSearchFragment(getFragmentManager(), 0, "");
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
@@ -1,10 +0,0 @@
|
||||
package org.schabi.newpipe.fragments;
|
||||
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
|
||||
/**
|
||||
* Interface for communication purposes between activity and fragment
|
||||
*/
|
||||
public interface OnItemSelectedListener {
|
||||
void onItemSelected(StreamingService.LinkType linkType, int serviceId, String url, String name);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -2,16 +2,16 @@ package org.schabi.newpipe.fragments.detail;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Spinner;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.MediaFormat;
|
||||
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
||||
import org.schabi.newpipe.util.Utils;
|
||||
|
||||
@@ -19,27 +19,28 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 18.08.15.
|
||||
*
|
||||
* <p>
|
||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
||||
* DetailsMenuHandler.java is part of NewPipe.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
class ActionBarHandler {
|
||||
private static final String TAG = ActionBarHandler.class.toString();
|
||||
private static final String TAG = "ActionBarHandler";
|
||||
|
||||
private AppCompatActivity activity;
|
||||
private int selectedVideoStream = -1;
|
||||
@@ -52,11 +53,8 @@ class ActionBarHandler {
|
||||
// those are edited directly. Typically VideoDetailFragment will implement those callbacks.
|
||||
private OnActionListener onShareListener;
|
||||
private OnActionListener onOpenInBrowserListener;
|
||||
private OnActionListener onOpenInPopupListener;
|
||||
private OnActionListener onDownloadListener;
|
||||
private OnActionListener onPlayWithKodiListener;
|
||||
private OnActionListener onPlayAudioListener;
|
||||
|
||||
|
||||
// Triggered when a stream related action is triggered.
|
||||
public interface OnActionListener {
|
||||
@@ -67,46 +65,25 @@ class ActionBarHandler {
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"deprecation", "ConstantConditions"})
|
||||
public void setupNavMenu(AppCompatActivity activity) {
|
||||
this.activity = activity;
|
||||
try {
|
||||
activity.getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
|
||||
} catch (NullPointerException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
public void setupStreamList(final List<VideoStream> videoStreams, Spinner toolbarSpinner) {
|
||||
if (activity == null) return;
|
||||
selectedVideoStream = 0;
|
||||
|
||||
public void setupStreamList(final List<VideoStream> videoStreams) {
|
||||
if (activity != null) {
|
||||
selectedVideoStream = 0;
|
||||
|
||||
|
||||
// this array will be shown in the dropdown menu for selecting the stream/resolution.
|
||||
String[] itemArray = new String[videoStreams.size()];
|
||||
for (int i = 0; i < videoStreams.size(); i++) {
|
||||
VideoStream item = videoStreams.get(i);
|
||||
itemArray[i] = MediaFormat.getNameById(item.format) + " " + item.resolution;
|
||||
int defaultResolutionIndex = Utils.getDefaultResolution(activity, videoStreams);
|
||||
boolean isExternalPlayerEnabled = PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(activity.getString(R.string.use_external_video_player_key), false);
|
||||
toolbarSpinner.setAdapter(new SpinnerToolbarAdapter(activity, videoStreams, isExternalPlayerEnabled));
|
||||
toolbarSpinner.setSelection(defaultResolutionIndex);
|
||||
toolbarSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
selectedVideoStream = position;
|
||||
}
|
||||
int defaultResolution = Utils.getDefaultResolution(activity, videoStreams);
|
||||
|
||||
ArrayAdapter<String> itemAdapter = new ArrayAdapter<>(activity.getBaseContext(),
|
||||
android.R.layout.simple_spinner_dropdown_item, itemArray);
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
}
|
||||
});
|
||||
|
||||
ActionBar ab = activity.getSupportActionBar();
|
||||
//todo: make this throwsable
|
||||
assert ab != null : "Could not get actionbar";
|
||||
ab.setListNavigationCallbacks(itemAdapter
|
||||
, new ActionBar.OnNavigationListener() {
|
||||
@Override
|
||||
public boolean onNavigationItemSelected(int itemPosition, long itemId) {
|
||||
selectedVideoStream = (int) itemId;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
ab.setSelectedNavigationItem(defaultResolution);
|
||||
}
|
||||
}
|
||||
|
||||
public void setupMenu(Menu menu, MenuInflater inflater) {
|
||||
@@ -116,55 +93,40 @@ class ActionBarHandler {
|
||||
// appcompat itemsinflater.inflate(R.menu.videoitem_detail, menu);
|
||||
|
||||
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||
inflater.inflate(R.menu.videoitem_detail, menu);
|
||||
inflater.inflate(R.menu.video_detail_menu, menu);
|
||||
|
||||
showPlayWithKodiAction(defaultPreferences
|
||||
.getBoolean(activity.getString(R.string.show_play_with_kodi_key), false));
|
||||
updateItemsVisibility();
|
||||
}
|
||||
|
||||
public void updateItemsVisibility(){
|
||||
showPlayWithKodiAction(defaultPreferences.getBoolean(activity.getString(R.string.show_play_with_kodi_key), false));
|
||||
}
|
||||
|
||||
public boolean onItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
switch (id) {
|
||||
case R.id.menu_item_share: {
|
||||
/*
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_SEND);
|
||||
intent.putExtra(Intent.EXTRA_TEXT, websiteUrl);
|
||||
intent.setType("text/plain");
|
||||
activity.startActivity(Intent.createChooser(intent, activity.getString(R.string.share_dialog_title)));
|
||||
*/
|
||||
if(onShareListener != null) {
|
||||
if (onShareListener != null) {
|
||||
onShareListener.onActionSelected(selectedVideoStream);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_item_openInBrowser: {
|
||||
if(onOpenInBrowserListener != null) {
|
||||
if (onOpenInBrowserListener != null) {
|
||||
onOpenInBrowserListener.onActionSelected(selectedVideoStream);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
case R.id.menu_item_download:
|
||||
if(onDownloadListener != null) {
|
||||
if (onDownloadListener != null) {
|
||||
onDownloadListener.onActionSelected(selectedVideoStream);
|
||||
}
|
||||
return true;
|
||||
case R.id.action_play_with_kodi:
|
||||
if(onPlayWithKodiListener != null) {
|
||||
if (onPlayWithKodiListener != null) {
|
||||
onPlayWithKodiListener.onActionSelected(selectedVideoStream);
|
||||
}
|
||||
return true;
|
||||
case R.id.menu_item_play_audio:
|
||||
if(onPlayAudioListener != null) {
|
||||
onPlayAudioListener.onActionSelected(selectedVideoStream);
|
||||
}
|
||||
return true;
|
||||
case R.id.menu_item_popup: {
|
||||
if(onOpenInPopupListener != null) {
|
||||
onOpenInPopupListener.onActionSelected(selectedVideoStream);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
Log.e(TAG, "Menu Item not known");
|
||||
}
|
||||
@@ -183,10 +145,6 @@ class ActionBarHandler {
|
||||
onOpenInBrowserListener = listener;
|
||||
}
|
||||
|
||||
public void setOnOpenInPopupListener(OnActionListener listener) {
|
||||
onOpenInPopupListener = listener;
|
||||
}
|
||||
|
||||
public void setOnDownloadListener(OnActionListener listener) {
|
||||
onDownloadListener = listener;
|
||||
}
|
||||
@@ -195,14 +153,6 @@ class ActionBarHandler {
|
||||
onPlayWithKodiListener = listener;
|
||||
}
|
||||
|
||||
public void setOnPlayAudioListener(OnActionListener listener) {
|
||||
onPlayAudioListener = listener;
|
||||
}
|
||||
|
||||
public void showAudioAction(boolean visible) {
|
||||
menu.findItem(R.id.menu_item_play_audio).setVisible(visible);
|
||||
}
|
||||
|
||||
public void showDownloadAction(boolean visible) {
|
||||
menu.findItem(R.id.menu_item_download).setVisible(visible);
|
||||
}
|
||||
|
@@ -0,0 +1,74 @@
|
||||
package org.schabi.newpipe.fragments.detail;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.MediaFormat;
|
||||
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SpinnerToolbarAdapter extends BaseAdapter {
|
||||
private final List<VideoStream> videoStreams;
|
||||
private final boolean showIconNoAudio;
|
||||
|
||||
private final Context context;
|
||||
|
||||
public SpinnerToolbarAdapter(Context context, List<VideoStream> videoStreams, boolean showIconNoAudio) {
|
||||
this.context = context;
|
||||
this.videoStreams = videoStreams;
|
||||
this.showIconNoAudio = showIconNoAudio;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return videoStreams.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return videoStreams.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getDropDownView(int position, View convertView, ViewGroup parent) {
|
||||
return getCustomView(position, convertView, parent, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
return getCustomView(((Spinner) parent).getSelectedItemPosition(), convertView, parent, false);
|
||||
}
|
||||
|
||||
private View getCustomView(int position, View convertView, ViewGroup parent, boolean isDropdownItem) {
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(context).inflate(R.layout.resolutions_spinner_item, parent, false);
|
||||
}
|
||||
|
||||
ImageView woSoundIcon = (ImageView) convertView.findViewById(R.id.wo_sound_icon);
|
||||
TextView text = (TextView) convertView.findViewById(android.R.id.text1);
|
||||
VideoStream item = (VideoStream) getItem(position);
|
||||
text.setText(MediaFormat.getNameById(item.format) + " " + item.resolution);
|
||||
|
||||
int visibility = !showIconNoAudio ? View.GONE
|
||||
: item.isVideoOnly ? View.VISIBLE
|
||||
: isDropdownItem ? View.INVISIBLE
|
||||
: View.GONE;
|
||||
woSoundIcon.setVisibility(visibility);
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
}
|
@@ -1,11 +1,14 @@
|
||||
package org.schabi.newpipe.fragments.detail;
|
||||
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public class StackItem implements Serializable {
|
||||
private String title, url;
|
||||
private StreamInfo info;
|
||||
|
||||
public StackItem(String url, String title) {
|
||||
this.title = title;
|
||||
@@ -24,6 +27,14 @@ public class StackItem implements Serializable {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setInfo(StreamInfo info) {
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public StreamInfo getInfo() {
|
||||
return info;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getUrl() + " > " + getTitle();
|
||||
|
@@ -0,0 +1,85 @@
|
||||
package org.schabi.newpipe.fragments.detail;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import org.schabi.newpipe.MainActivity;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public class StreamInfoCache {
|
||||
private static String TAG = "StreamInfoCache@";
|
||||
private static final boolean DEBUG = MainActivity.DEBUG;
|
||||
private static final StreamInfoCache instance = new StreamInfoCache();
|
||||
private static final int MAX_ITEMS_ON_CACHE = 20;
|
||||
|
||||
private final LinkedHashMap<String, StreamInfo> myCache = new LinkedHashMap<>();
|
||||
|
||||
private StreamInfoCache() {
|
||||
TAG += "" + Integer.toHexString(hashCode());
|
||||
}
|
||||
|
||||
public static StreamInfoCache getInstance() {
|
||||
if (DEBUG) Log.d(TAG, "getInstance() called");
|
||||
return instance;
|
||||
}
|
||||
|
||||
public boolean hasKey(@NonNull String url) {
|
||||
if (DEBUG) Log.d(TAG, "hasKey() called with: url = [" + url + "]");
|
||||
return !TextUtils.isEmpty(url) && myCache.containsKey(url) && myCache.get(url) != null;
|
||||
}
|
||||
|
||||
public StreamInfo getFromKey(@NonNull String url) {
|
||||
if (DEBUG) Log.d(TAG, "getFromKey() called with: url = [" + url + "]");
|
||||
return myCache.get(url);
|
||||
}
|
||||
|
||||
public void putInfo(@NonNull StreamInfo info) {
|
||||
if (DEBUG) Log.d(TAG, "putInfo() called with: info = [" + info + "]");
|
||||
putInfo(info.webpage_url, info);
|
||||
}
|
||||
|
||||
public void putInfo(@NonNull String url, @NonNull StreamInfo info) {
|
||||
if (DEBUG) Log.d(TAG, "putInfo() called with: url = [" + url + "], info = [" + info + "]");
|
||||
myCache.put(url, info);
|
||||
}
|
||||
|
||||
public void removeInfo(@NonNull StreamInfo info) {
|
||||
if (DEBUG) Log.d(TAG, "removeInfo() called with: info = [" + info + "]");
|
||||
myCache.remove(info.webpage_url);
|
||||
}
|
||||
|
||||
public void removeInfo(@NonNull String url) {
|
||||
if (DEBUG) Log.d(TAG, "removeInfo() called with: url = [" + url + "]");
|
||||
myCache.remove(url);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void clearCache() {
|
||||
if (DEBUG) Log.d(TAG, "clearCache() called");
|
||||
myCache.clear();
|
||||
}
|
||||
|
||||
public void removeOldEntries() {
|
||||
if (DEBUG) Log.d(TAG, "removeOldEntries() called , size = " + getSize());
|
||||
if (getSize() > MAX_ITEMS_ON_CACHE) {
|
||||
Iterator<String> iterator = myCache.keySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
iterator.next();
|
||||
iterator.remove();
|
||||
if (DEBUG) Log.d(TAG, "getSize() = " + getSize());
|
||||
if (getSize() <= MAX_ITEMS_ON_CACHE) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return myCache.size();
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,49 +0,0 @@
|
||||
package org.schabi.newpipe.fragments.search;
|
||||
|
||||
import android.support.v7.widget.SearchView;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 02.08.16.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||
* SearchSuggestionListener.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 SearchSuggestionListener implements SearchView.OnSuggestionListener{
|
||||
|
||||
private final SearchView searchView;
|
||||
private final SuggestionListAdapter adapter;
|
||||
|
||||
public SearchSuggestionListener(SearchView searchView, SuggestionListAdapter adapter) {
|
||||
this.searchView = searchView;
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSuggestionSelect(int position) {
|
||||
String suggestion = adapter.getSuggestion(position);
|
||||
searchView.setQuery(suggestion,true);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSuggestionClick(int position) {
|
||||
String suggestion = adapter.getSuggestion(position);
|
||||
searchView.setQuery(suggestion,true);
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -1,216 +0,0 @@
|
||||
package org.schabi.newpipe.fragments.search;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
||||
import org.schabi.newpipe.extractor.search.SearchEngine;
|
||||
import org.schabi.newpipe.extractor.search.SearchResult;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 02.08.16.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||
* SearchWorker.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 SearchWorker {
|
||||
private static final String TAG = SearchWorker.class.toString();
|
||||
|
||||
public interface SearchWorkerResultListener {
|
||||
void onResult(SearchResult result);
|
||||
void onNothingFound(final int stringResource);
|
||||
void onError(String message);
|
||||
void onReCaptchaChallenge();
|
||||
}
|
||||
|
||||
private class ResultRunnable implements Runnable {
|
||||
private final SearchResult result;
|
||||
private int requestId = 0;
|
||||
public ResultRunnable(SearchResult result, int requestId) {
|
||||
this.result = result;
|
||||
this.requestId = requestId;
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
if(this.requestId == SearchWorker.this.requestId) {
|
||||
searchWorkerResultListener.onResult(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SearchRunnable implements Runnable {
|
||||
public static final String YOUTUBE = "Youtube";
|
||||
private final String query;
|
||||
private final int page;
|
||||
private final EnumSet<SearchEngine.Filter> filter;
|
||||
final Handler h = new Handler();
|
||||
private volatile boolean runs = true;
|
||||
private Activity a = null;
|
||||
private int serviceId = -1;
|
||||
public SearchRunnable(int serviceId,
|
||||
String query,
|
||||
int page,
|
||||
EnumSet<SearchEngine.Filter> filter,
|
||||
Activity activity,
|
||||
int requestId) {
|
||||
this.serviceId = serviceId;
|
||||
this.query = query;
|
||||
this.page = page;
|
||||
this.filter = filter;
|
||||
this.a = activity;
|
||||
}
|
||||
void terminate() {
|
||||
runs = false;
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
final String serviceName = NewPipe.getNameOfService(serviceId);
|
||||
SearchResult result = null;
|
||||
SearchEngine engine = null;
|
||||
|
||||
try {
|
||||
engine = NewPipe.getService(serviceId)
|
||||
.getSearchEngineInstance();
|
||||
} catch(ExtractionException e) {
|
||||
ErrorActivity.reportError(h, a, e, null, null,
|
||||
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
||||
Integer.toString(serviceId), query, R.string.general_error));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(a);
|
||||
String searchLanguageKey = a.getString(R.string.search_language_key);
|
||||
String searchLanguage = sp.getString(searchLanguageKey,
|
||||
a.getString(R.string.default_language_value));
|
||||
result = SearchResult
|
||||
.getSearchResult(engine, query, page, searchLanguage, filter);
|
||||
if(runs) {
|
||||
h.post(new ResultRunnable(result, requestId));
|
||||
}
|
||||
|
||||
// look for errors during extraction
|
||||
// soft errors:
|
||||
View rootView = a.findViewById(android.R.id.content);
|
||||
if(result != null &&
|
||||
!result.errors.isEmpty()) {
|
||||
Log.e(TAG, "OCCURRED ERRORS DURING SEARCH EXTRACTION:");
|
||||
for(Throwable e : result.errors) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "------");
|
||||
}
|
||||
|
||||
if(result.resultList.isEmpty()&& !result.errors.isEmpty()) {
|
||||
// if it compleatly failes dont show snackbar, instead show error directlry
|
||||
ErrorActivity.reportError(h, a, result.errors, null, null,
|
||||
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
||||
serviceName, query, R.string.parsing_error));
|
||||
} else {
|
||||
// if it partly show snackbar
|
||||
ErrorActivity.reportError(h, a, result.errors, null, rootView,
|
||||
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
||||
serviceName, query, R.string.light_parsing_error));
|
||||
}
|
||||
}
|
||||
// hard errors:
|
||||
} catch (ReCaptchaException e) {
|
||||
h.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
searchWorkerResultListener.onReCaptchaChallenge();
|
||||
}
|
||||
});
|
||||
} catch(IOException e) {
|
||||
h.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
searchWorkerResultListener.onNothingFound(R.string.network_error);
|
||||
}
|
||||
});
|
||||
e.printStackTrace();
|
||||
} catch(final SearchEngine.NothingFoundException e) {
|
||||
h.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
searchWorkerResultListener.onError(e.getMessage());
|
||||
}
|
||||
});
|
||||
} catch(ExtractionException e) {
|
||||
ErrorActivity.reportError(h, a, e, null, null,
|
||||
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
||||
serviceName, query, R.string.parsing_error));
|
||||
//postNewErrorToast(h, R.string.parsing_error);
|
||||
e.printStackTrace();
|
||||
|
||||
} catch(Exception e) {
|
||||
ErrorActivity.reportError(h, a, e, null, null,
|
||||
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
||||
/* todo: this shoudl not be assigned static */ YOUTUBE, query, R.string.general_error));
|
||||
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static SearchWorker searchWorker = null;
|
||||
private SearchWorkerResultListener searchWorkerResultListener = null;
|
||||
private SearchRunnable runnable = null;
|
||||
private int requestId = 0; //prevents running requests that have already ben expired
|
||||
|
||||
public static SearchWorker getInstance() {
|
||||
return searchWorker == null ? (searchWorker = new SearchWorker()) : searchWorker;
|
||||
}
|
||||
|
||||
public void setSearchWorkerResultListener(SearchWorkerResultListener listener) {
|
||||
searchWorkerResultListener = listener;
|
||||
}
|
||||
|
||||
private SearchWorker() {
|
||||
|
||||
}
|
||||
|
||||
public void search(int serviceId,
|
||||
String query,
|
||||
int page,
|
||||
Activity a,
|
||||
EnumSet<SearchEngine.Filter> filter) {
|
||||
if(runnable != null) {
|
||||
terminate();
|
||||
}
|
||||
runnable = new SearchRunnable(serviceId, query, page, filter, a, requestId);
|
||||
Thread thread = new Thread(runnable);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
public void terminate() {
|
||||
if (runnable == null) return;
|
||||
requestId++;
|
||||
runnable.terminate();
|
||||
}
|
||||
}
|
@@ -75,6 +75,11 @@ public class SuggestionListAdapter extends ResourceCursorAdapter {
|
||||
return ((Cursor) getItem(position)).getString(INDEX_TITLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence convertToString(Cursor cursor) {
|
||||
return cursor.getString(INDEX_TITLE);
|
||||
}
|
||||
|
||||
private class ViewHolder {
|
||||
private final TextView suggestionTitle;
|
||||
private ViewHolder(View view) {
|
||||
|
@@ -1,105 +0,0 @@
|
||||
package org.schabi.newpipe.fragments.search;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.SuggestionExtractor;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 02.08.16.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||
* SuggestionSearchRunnable.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 SuggestionSearchRunnable implements Runnable{
|
||||
|
||||
/**
|
||||
* Runnable to update a {@link SuggestionListAdapter}
|
||||
*/
|
||||
private class SuggestionResultRunnable implements Runnable{
|
||||
|
||||
private final List<String> suggestions;
|
||||
|
||||
private SuggestionResultRunnable(List<String> suggestions) {
|
||||
this.suggestions = suggestions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
adapter.updateAdapter(suggestions);
|
||||
}
|
||||
}
|
||||
|
||||
private final int serviceId;
|
||||
private final String query;
|
||||
private final Handler h = new Handler();
|
||||
private final Activity a;
|
||||
private final SuggestionListAdapter adapter;
|
||||
public SuggestionSearchRunnable(int serviceId, String query,
|
||||
Activity activity, SuggestionListAdapter adapter) {
|
||||
this.serviceId = serviceId;
|
||||
this.query = query;
|
||||
this.a = activity;
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
SuggestionExtractor se =
|
||||
NewPipe.getService(serviceId).getSuggestionExtractorInstance();
|
||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(a);
|
||||
String searchLanguageKey = a.getString(R.string.search_language_key);
|
||||
String searchLanguage = sp.getString(searchLanguageKey,
|
||||
a.getString(R.string.default_language_value));
|
||||
List<String> suggestions = se.suggestionList(query, searchLanguage);
|
||||
h.post(new SuggestionResultRunnable(suggestions));
|
||||
} catch (ExtractionException e) {
|
||||
ErrorActivity.reportError(h, a, e, null, a.findViewById(android.R.id.content),
|
||||
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
||||
NewPipe.getNameOfService(serviceId), query, R.string.parsing_error));
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
postNewErrorToast(h, R.string.network_error);
|
||||
e.printStackTrace();
|
||||
} catch (Exception e) {
|
||||
ErrorActivity.reportError(h, a, e, null, a.findViewById(android.R.id.content),
|
||||
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
||||
NewPipe.getNameOfService(serviceId), query, R.string.general_error));
|
||||
}
|
||||
}
|
||||
|
||||
private void postNewErrorToast(Handler h, final int stringResource) {
|
||||
h.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(a, a.getString(stringResource),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@@ -1,7 +1,6 @@
|
||||
package org.schabi.newpipe.info_list;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
@@ -35,16 +34,17 @@ public class ChannelInfoItemHolder extends InfoItemHolder {
|
||||
public final TextView itemSubscriberCountView;
|
||||
public final TextView itemVideoCountView;
|
||||
public final TextView itemChannelDescriptionView;
|
||||
public final Button itemButton;
|
||||
|
||||
public final View itemRoot;
|
||||
|
||||
ChannelInfoItemHolder(View v) {
|
||||
super(v);
|
||||
itemRoot = v.findViewById(R.id.itemRoot);
|
||||
itemThumbnailView = (CircleImageView) v.findViewById(R.id.itemThumbnailView);
|
||||
itemChannelTitleView = (TextView) v.findViewById(R.id.itemChannelTitleView);
|
||||
itemSubscriberCountView = (TextView) v.findViewById(R.id.itemSubscriberCountView);
|
||||
itemVideoCountView = (TextView) v.findViewById(R.id.itemVideoCountView);
|
||||
itemChannelDescriptionView = (TextView) v.findViewById(R.id.itemChannelDescriptionView);
|
||||
itemButton = (Button) v.findViewById(R.id.item_button);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package org.schabi.newpipe.info_list;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -18,20 +19,20 @@ import org.schabi.newpipe.extractor.stream_info.StreamInfoItem;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 26.09.16.
|
||||
*
|
||||
* <p>
|
||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||
* InfoItemBuilder.java is part of NewPipe.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@@ -48,11 +49,13 @@ public class InfoItemBuilder {
|
||||
private final String billion;
|
||||
|
||||
private static final String TAG = InfoItemBuilder.class.toString();
|
||||
|
||||
public interface OnInfoItemSelectedListener {
|
||||
void selected(int serviceId, String url, String title);
|
||||
}
|
||||
|
||||
private Context mContext = null;
|
||||
private LayoutInflater inflater;
|
||||
private View rootView = null;
|
||||
private ImageLoader imageLoader = ImageLoader.getInstance();
|
||||
private DisplayImageOptions displayImageOptions =
|
||||
@@ -70,6 +73,7 @@ public class InfoItemBuilder {
|
||||
thousand = context.getString(R.string.short_thousand);
|
||||
million = context.getString(R.string.short_million);
|
||||
billion = context.getString(R.string.short_billion);
|
||||
inflater = LayoutInflater.from(context);
|
||||
}
|
||||
|
||||
public void setOnStreamInfoItemSelectedListener(
|
||||
@@ -83,9 +87,9 @@ public class InfoItemBuilder {
|
||||
}
|
||||
|
||||
public void buildByHolder(InfoItemHolder holder, final InfoItem i) {
|
||||
if(i.infoType() != holder.infoType())
|
||||
if (i.infoType() != holder.infoType())
|
||||
return;
|
||||
switch(i.infoType()) {
|
||||
switch (i.infoType()) {
|
||||
case STREAM:
|
||||
buildStreamInfoItem((StreamInfoItemHolder) holder, (StreamInfoItem) i);
|
||||
break;
|
||||
@@ -103,15 +107,15 @@ public class InfoItemBuilder {
|
||||
public View buildView(ViewGroup parent, final InfoItem info) {
|
||||
View itemView = null;
|
||||
InfoItemHolder holder = null;
|
||||
switch(info.infoType()) {
|
||||
switch (info.infoType()) {
|
||||
case STREAM:
|
||||
itemView = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.stream_item, parent, false);
|
||||
//long start = System.nanoTime();
|
||||
itemView = inflater.inflate(R.layout.stream_item, parent, false);
|
||||
//Log.d(TAG, "time to inflate: " + ((System.nanoTime() - start) / 1000000L) + "ms");
|
||||
holder = new StreamInfoItemHolder(itemView);
|
||||
break;
|
||||
case CHANNEL:
|
||||
itemView = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.channel_item, parent, false);
|
||||
itemView = inflater.inflate(R.layout.channel_item, parent, false);
|
||||
holder = new ChannelInfoItemHolder(itemView);
|
||||
break;
|
||||
case PLAYLIST:
|
||||
@@ -124,43 +128,39 @@ public class InfoItemBuilder {
|
||||
}
|
||||
|
||||
private void buildStreamInfoItem(StreamInfoItemHolder holder, final StreamInfoItem info) {
|
||||
if(info.infoType() != InfoItem.InfoType.STREAM) {
|
||||
if (info.infoType() != InfoItem.InfoType.STREAM) {
|
||||
Log.e("InfoItemBuilder", "Info type not yet supported");
|
||||
}
|
||||
// fill holder with information
|
||||
holder.itemVideoTitleView.setText(info.title);
|
||||
if(info.uploader != null && !info.uploader.isEmpty()) {
|
||||
holder.itemUploaderView.setText(info.uploader);
|
||||
} else {
|
||||
holder.itemUploaderView.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
if(info.duration > 0) {
|
||||
if (!TextUtils.isEmpty(info.title)) holder.itemVideoTitleView.setText(info.title);
|
||||
|
||||
if (!TextUtils.isEmpty(info.uploader)) holder.itemUploaderView.setText(info.uploader);
|
||||
else holder.itemUploaderView.setVisibility(View.INVISIBLE);
|
||||
|
||||
if (info.duration > 0) {
|
||||
holder.itemDurationView.setText(getDurationString(info.duration));
|
||||
} else {
|
||||
if(info.stream_type == AbstractStreamInfo.StreamType.LIVE_STREAM) {
|
||||
if (info.stream_type == AbstractStreamInfo.StreamType.LIVE_STREAM) {
|
||||
holder.itemDurationView.setText(R.string.duration_live);
|
||||
} else {
|
||||
holder.itemDurationView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
if(info.view_count >= 0) {
|
||||
if (info.view_count >= 0) {
|
||||
holder.itemViewCountView.setText(shortViewCount(info.view_count));
|
||||
} else {
|
||||
holder.itemViewCountView.setVisibility(View.GONE);
|
||||
}
|
||||
if(info.upload_date != null && !info.upload_date.isEmpty()) {
|
||||
holder.itemUploadDateView.setText(info.upload_date + " • ");
|
||||
}
|
||||
if (!TextUtils.isEmpty(info.upload_date)) holder.itemUploadDateView.setText(info.upload_date + " • ");
|
||||
|
||||
holder.itemThumbnailView.setImageResource(R.drawable.dummy_thumbnail);
|
||||
if(info.thumbnail_url != null && !info.thumbnail_url.isEmpty()) {
|
||||
if (!TextUtils.isEmpty(info.thumbnail_url)) {
|
||||
imageLoader.displayImage(info.thumbnail_url,
|
||||
holder.itemThumbnailView,
|
||||
displayImageOptions,
|
||||
holder.itemThumbnailView, displayImageOptions,
|
||||
new ImageErrorLoadingListener(mContext, rootView, info.service_id));
|
||||
}
|
||||
|
||||
holder.itemButton.setOnClickListener(new View.OnClickListener() {
|
||||
holder.itemRoot.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
onStreamInfoItemSelectedListener.selected(info.service_id, info.webpage_url, info.getTitle());
|
||||
@@ -169,20 +169,20 @@ public class InfoItemBuilder {
|
||||
}
|
||||
|
||||
private void buildChannelInfoItem(ChannelInfoItemHolder holder, final ChannelInfoItem info) {
|
||||
holder.itemChannelTitleView.setText(info.getTitle());
|
||||
if (!TextUtils.isEmpty(info.getTitle())) holder.itemChannelTitleView.setText(info.getTitle());
|
||||
holder.itemSubscriberCountView.setText(shortSubscriber(info.subscriberCount) + " • ");
|
||||
holder.itemVideoCountView.setText(info.videoAmount + " " + videosS);
|
||||
holder.itemChannelDescriptionView.setText(info.description);
|
||||
if (!TextUtils.isEmpty(info.description)) holder.itemChannelDescriptionView.setText(info.description);
|
||||
|
||||
holder.itemThumbnailView.setImageResource(R.drawable.buddy_channel_item);
|
||||
if(info.thumbnailUrl != null && !info.thumbnailUrl.isEmpty()) {
|
||||
if (!TextUtils.isEmpty(info.thumbnailUrl)) {
|
||||
imageLoader.displayImage(info.thumbnailUrl,
|
||||
holder.itemThumbnailView,
|
||||
displayImageOptions,
|
||||
new ImageErrorLoadingListener(mContext, rootView, info.serviceId));
|
||||
}
|
||||
|
||||
holder.itemButton.setOnClickListener(new View.OnClickListener() {
|
||||
holder.itemRoot.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
onChannelInfoItemSelectedListener.selected(info.serviceId, info.getLink(), info.channelName);
|
||||
@@ -191,15 +191,15 @@ public class InfoItemBuilder {
|
||||
}
|
||||
|
||||
|
||||
public String shortViewCount(Long viewCount){
|
||||
if(viewCount >= 1000000000){
|
||||
return Long.toString(viewCount/1000000000)+ billion + " " + viewsS;
|
||||
}else if(viewCount>=1000000){
|
||||
return Long.toString(viewCount/1000000)+ million + " " + viewsS;
|
||||
}else if(viewCount>=1000){
|
||||
return Long.toString(viewCount/1000)+ thousand + " " + viewsS;
|
||||
}else {
|
||||
return Long.toString(viewCount)+ " " + viewsS;
|
||||
public String shortViewCount(Long viewCount) {
|
||||
if (viewCount >= 1000000000) {
|
||||
return Long.toString(viewCount / 1000000000) + billion + " " + viewsS;
|
||||
} else if (viewCount >= 1000000) {
|
||||
return Long.toString(viewCount / 1000000) + million + " " + viewsS;
|
||||
} else if (viewCount >= 1000) {
|
||||
return Long.toString(viewCount / 1000) + thousand + " " + viewsS;
|
||||
} else {
|
||||
return Long.toString(viewCount) + " " + viewsS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,13 +227,13 @@ public class InfoItemBuilder {
|
||||
int seconds = duration % 60;
|
||||
|
||||
//handle days
|
||||
if(days > 0) {
|
||||
if (days > 0) {
|
||||
output = Integer.toString(days) + ":";
|
||||
}
|
||||
// handle hours
|
||||
if(hours > 0 || !output.isEmpty()) {
|
||||
if(hours > 0) {
|
||||
if(hours >= 10 || output.isEmpty()) {
|
||||
if (hours > 0 || !output.isEmpty()) {
|
||||
if (hours > 0) {
|
||||
if (hours >= 10 || output.isEmpty()) {
|
||||
output += Integer.toString(hours);
|
||||
} else {
|
||||
output += "0" + Integer.toString(hours);
|
||||
@@ -244,9 +244,9 @@ public class InfoItemBuilder {
|
||||
output += ":";
|
||||
}
|
||||
//handle minutes
|
||||
if(minutes > 0 || !output.isEmpty()) {
|
||||
if(minutes > 0) {
|
||||
if(minutes >= 10 || output.isEmpty()) {
|
||||
if (minutes > 0 || !output.isEmpty()) {
|
||||
if (minutes > 0) {
|
||||
if (minutes >= 10 || output.isEmpty()) {
|
||||
output += Integer.toString(minutes);
|
||||
} else {
|
||||
output += "0" + Integer.toString(minutes);
|
||||
@@ -258,11 +258,11 @@ public class InfoItemBuilder {
|
||||
}
|
||||
|
||||
//handle seconds
|
||||
if(output.isEmpty()) {
|
||||
if (output.isEmpty()) {
|
||||
output += "0:";
|
||||
}
|
||||
|
||||
if(seconds >= 10) {
|
||||
if (seconds >= 10) {
|
||||
output += Integer.toString(seconds);
|
||||
} else {
|
||||
output += "0" + Integer.toString(seconds);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user