1
mirror of https://github.com/TeamNewPipe/NewPipe synced 2025-09-23 21:00:51 +02:00

Compare commits

..

76 Commits

Author SHA1 Message Date
Christian Schabesberger
c81cfdc455 moved on to version 0.9.4 2017-04-28 21:26:11 +02:00
Christian Schabesberger
134c3804db Merge branch 'fix-search' of https://github.com/mauriciocolli/NewPipe into fix-search 2017-04-28 20:42:37 +02:00
Christian Schabesberger
e355df5eda update NewPipeExtractor 2017-04-28 20:19:23 +02:00
Mauricio Colli
a68e0a95f4 Fix NullPointerException 2017-04-28 01:07:54 -03:00
Mauricio Colli
6fd6facf72 Improve animations of VideoDetailFragment 2017-04-28 00:58:50 -03:00
Mauricio Colli
00102d4048 Try to fix travis 2017-04-26 17:07:24 -03:00
Mauricio Colli
adbeff11d4 Minor improvements 2017-04-26 16:32:20 -03:00
Mauricio Colli
a8fe329678 Improve fragments
- They save the state now, that means, no more reloading after rotating the screen or switching between apps
2017-04-26 16:32:04 -03:00
Mauricio Colli
a4b61bf730 Add new workers 2017-04-26 16:25:09 -03:00
Mauricio Colli
ee592def0c Add drawables and improve layouts 2017-04-26 16:24:33 -03:00
Allan Nordhøy
c14e117239 Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (164 of 164 strings)
2017-04-25 03:46:10 +02:00
rapi3
b6e94dde0c Translated using Weblate (Romanian)
Currently translated at 100.0% (164 of 164 strings)
2017-04-23 15:46:17 +02:00
monolifed
63a4a44d7e Translated using Weblate (Turkish)
Currently translated at 100.0% (164 of 164 strings)
2017-04-23 12:46:56 +02:00
Allan Nordhøy
eb8ab5d527 Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (164 of 164 strings)
2017-04-23 00:57:22 +02:00
riotism
b778d40d65 Translated using Weblate (Chinese (Hong Kong))
Currently translated at 97.5% (160 of 164 strings)
2017-04-22 18:44:35 +02:00
rapi3
2f570672dc Translated using Weblate (Romanian)
Currently translated at 100.0% (164 of 164 strings)
2017-04-21 14:11:07 +02:00
anonymous
9290ed490f Translated using Weblate (Turkish)
Currently translated at 100.0% (164 of 164 strings)
2017-04-21 11:12:23 +02:00
monolifed
1bda812c75 Translated using Weblate (Turkish)
Currently translated at 100.0% (164 of 164 strings)
2017-04-21 11:11:23 +02:00
Weblate
ab82406c98 Merge remote-tracking branch 'origin/master' 2017-04-19 17:36:17 +02:00
Mladen Pejaković
be763349df Translated using Weblate (Serbian)
Currently translated at 99.3% (163 of 164 strings)
2017-04-19 17:36:16 +02:00
zmni
91764ad601 Translated using Weblate (Indonesian)
Currently translated at 100.0% (164 of 164 strings)
2017-04-19 17:36:14 +02:00
Christian Schabesberger
1c4031aa38 Merge branch 'master' of github.com:theScrabi/NewPipe 2017-04-18 22:39:16 +02:00
Christian Schabesberger
c3bc648dd4 moved on to version 0.9.3 2017-04-18 22:34:32 +02:00
Christian Schabesberger
c97d794272 updated sdk and support lib 2017-04-18 22:34:04 +02:00
Christian Schabesberger
8e7d2e91e9 Merge branch 'feature-backplayer' of https://github.com/mauriciocolli/NewPipe into mark 2017-04-18 22:06:47 +02:00
Laura Arjona Reina
cf0fbbbd3d Translated using Weblate (Spanish)
Currently translated at 100.0% (164 of 164 strings)
2017-04-18 20:19:12 +02:00
Tobias Groza
7d3ede7946 Translated using Weblate (German)
Currently translated at 100.0% (164 of 164 strings)
2017-04-18 12:58:44 +02:00
Eduardo Caron
1aa308eb5d Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (164 of 164 strings)
2017-04-18 00:35:02 +02:00
anonymous
4bfd30e34a Translated using Weblate (Portuguese (Brazil))
Currently translated at 99.3% (163 of 164 strings)
2017-04-18 00:28:43 +02:00
Eduardo Caron
0f46c90688 Translated using Weblate (Portuguese (Brazil))
Currently translated at 99.3% (163 of 164 strings)
2017-04-18 00:27:31 +02:00
Matej U
aa2173fc4a Translated using Weblate (Slovenian)
Currently translated at 100.0% (164 of 164 strings)
2017-04-17 18:09:10 +02:00
Mauricio Colli
932cb1d19c Remove ugly bitmap sharing and improvements
- Now it is just using the ImageLoader to load the image, and better yet, it already have cache implemented in it
- Improve MainActivity intent handler
- Improve utils classes
2017-04-17 01:21:48 -03:00
Mauricio Colli
18b038d8e4 Improve notification layouts 2017-04-17 01:20:14 -03:00
Mauricio Colli
2ac71c75c0 Improve players
- Background player is using ExoPlayer internally now
2017-04-17 01:19:53 -03:00
naofum
4067ba5232 Translated using Weblate (Japanese)
Currently translated at 100.0% (164 of 164 strings)
2017-04-16 16:48:37 +02:00
Marian Hanzel
ab47dd5a5b Translated using Weblate (Slovak)
Currently translated at 100.0% (164 of 164 strings)
2017-04-15 22:32:17 +02:00
Nathan Follens
3130910307 Translated using Weblate (Dutch)
Currently translated at 100.0% (164 of 164 strings)
2017-04-15 17:35:12 +02:00
Sérgio Marques
22113439a4 Translated using Weblate (Portuguese)
Currently translated at 100.0% (164 of 164 strings)
2017-04-15 16:09:58 +02:00
Weblate
2d25a9ad7a Merge remote-tracking branch 'origin/master' 2017-04-15 16:07:22 +02:00
Freddy Morán Jr
8f0a2cc2f0 Translated using Weblate (Spanish)
Currently translated at 100.0% (161 of 161 strings)
2017-04-15 16:07:22 +02:00
Marian Hanzel
4ca39258c9 Translated using Weblate (Slovak)
Currently translated at 100.0% (161 of 161 strings)
2017-04-15 16:07:21 +02:00
Enol P
d0c0238b8b Translated using Weblate (Asturian)
Currently translated at 98.1% (158 of 161 strings)
2017-04-15 16:07:20 +02:00
Sérgio Marques
f9f08e5169 Translated using Weblate (Portuguese)
Currently translated at 100.0% (161 of 161 strings)
2017-04-15 16:07:18 +02:00
Christian Schabesberger
cfc51b2401 Merge branch 'master' of github.com:theScrabi/NewPipe 2017-04-15 14:05:39 +02:00
Christian Schabesberger
54c2704cb5 switch to dark default theme 2017-04-15 14:05:31 +02:00
Christian Schabesberger
9b51c946fe add 1080p to available features 2017-04-15 13:43:21 +02:00
Christian Schabesberger
06f38cbcb4 Merge branch 'feature-4k60f' of git://github.com/mauriciocolli/NewPipe into 4k 2017-04-15 12:52:03 +02:00
Christian Schabesberger
f368d6b257 update extractor 2017-04-15 12:51:35 +02:00
Marian Hanzel
f4301da14d Translated using Weblate (Slovak)
Currently translated at 100.0% (161 of 161 strings)
2017-04-13 21:49:26 +02:00
Weblate
f34c09f165 Merge remote-tracking branch 'origin/master' 2017-04-13 20:51:06 +02:00
Freddy Morán Jr
6d1db56512 Translated using Weblate (Spanish)
Currently translated at 100.0% (161 of 161 strings)
2017-04-13 20:51:03 +02:00
Christian Schabesberger
b70c07d004 update extractor 2017-04-13 19:41:43 +02:00
Christian Schabesberger
f9f48a5eb6 Merge branch 'master' of github.com:theScrabi/NewPipe 2017-04-13 19:41:28 +02:00
Christian Schabesberger
14e4e73444 update gradle 2017-04-13 19:41:15 +02:00
Nathan Follens
f2ce4d2daf Translated using Weblate (Dutch)
Currently translated at 100.0% (161 of 161 strings)
2017-04-13 19:10:56 +02:00
naofum
468ebdda87 Translated using Weblate (Japanese)
Currently translated at 100.0% (161 of 161 strings)
2017-04-13 16:53:26 +02:00
nautilusx
a1f0fb3b14 Translated using Weblate (German)
Currently translated at 100.0% (161 of 161 strings)
2017-04-13 16:08:34 +02:00
zmni
3c9f4de234 Translated using Weblate (Indonesian)
Currently translated at 100.0% (161 of 161 strings)
2017-04-13 15:59:13 +02:00
Tobias Groza
8ae411619f Translated using Weblate (German)
Currently translated at 96.8% (156 of 161 strings)
2017-04-13 15:56:35 +02:00
nautilusx
61e5c9121a Translated using Weblate (German)
Currently translated at 96.2% (155 of 161 strings)
2017-04-13 15:55:28 +02:00
Weblate
8a3cf0d5dc Merge remote-tracking branch 'origin/master' 2017-04-13 15:53:52 +02:00
Freddy Morán Jr
ecb5df65ac Translated using Weblate (Spanish)
Currently translated at 100.0% (160 of 160 strings)
2017-04-13 15:53:52 +02:00
anonymous
5f1e98a0d3 Translated using Weblate (German)
Currently translated at 96.2% (154 of 160 strings)
2017-04-13 15:53:49 +02:00
Mauricio Colli
3b9a477499 Add resolution support up to 4k and 60 fps
- Up to 4k with 60 fps
    - Not every device can play in that resolution and bitrate
    - Add option to hide these high resolution greater than 1080p (2k,4k) for not clutter the menus
- Add a default resolution for the popup, wil be used when opening in popup mode from another app
2017-04-12 03:07:15 -03:00
Christian Schabesberger
e5bf98a741 move on to version 0.9.2 2017-04-11 22:51:04 +02:00
Christian Schabesberger
06fafc247e move to version 0.9.2 of NewPipeExtractor 2017-04-11 22:49:18 +02:00
Christian Schabesberger
e8bb17b631 Merge branch 'fix-next-video' of git://github.com/mauriciocolli/NewPipe into mul 2017-04-11 22:26:41 +02:00
Freddy Morán Jr
363cd07883 Translated using Weblate (Spanish)
Currently translated at 100.0% (160 of 160 strings)
2017-04-11 18:56:03 +02:00
Mladen Pejaković
2b4a9286c4 Translated using Weblate (Serbian)
Currently translated at 99.3% (159 of 160 strings)
2017-04-11 00:46:21 +02:00
Nathan Follens
34c985026f Translated using Weblate (Dutch)
Currently translated at 100.0% (160 of 160 strings)
2017-04-10 17:40:52 +02:00
naofum
4a0aa42914 Translated using Weblate (Japanese)
Currently translated at 100.0% (160 of 160 strings)
2017-04-10 17:21:54 +02:00
Mauricio Colli
746c2a15bf Migrate to fragments and improvements
- Migrate to fragments
- Fix #487
- Don't show "Open in popup mode" to channel links
- New backstack of videos
- Change the subscribers count to format using `NumberFormat`, for example some locales use `.`  others `,`, this handles it automatically (and the old method had a bug for leading zero, e.g. 4.82.125 instead of 4.082.125)
- Add string 'subscribers' for channels with more than 1 subscriber (plural)
- Popup player chooses the default format and resolution based on the new preference (format)
- Fix taskaffinity of the router activities
- Show title before loading, as it is available from the items already loaded
2017-04-09 14:34:00 -03:00
zmni
9318bb5306 Translated using Weblate (Indonesian)
Currently translated at 100.0% (160 of 160 strings)
2017-04-09 07:39:53 +02:00
Matej U
2a10ceb74f Translated using Weblate (Slovenian)
Currently translated at 100.0% (160 of 160 strings)
2017-04-08 22:08:31 +02:00
Weblate
83f4db59e2 Merge remote-tracking branch 'origin/master' 2017-04-08 21:47:50 +02:00
Olexandr Nesterenko
9f66f759ad Translated using Weblate (Ukrainian)
Currently translated at 84.9% (135 of 159 strings)
2017-04-08 21:47:47 +02:00
183 changed files with 8029 additions and 5691 deletions

View File

@@ -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

View File

@@ -46,6 +46,7 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
* Search channels
* Watch videos from a channel
* Orbot/Tor support (not yet directly)
* 1080p/2k/4k support
### Coming Features
@@ -56,7 +57,6 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
* Search/Watch Playlists
* Queeing videos
* Subtitles support
* 1080p support
* livestream support
* ... and many more

View File

@@ -2,14 +2,14 @@ apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion '25.0.0'
buildToolsVersion '25.0.2'
defaultConfig {
applicationId "org.schabi.newpipe"
minSdkVersion 15
targetSdkVersion 25
versionCode 28
versionName "0.9.1"
versionCode 31
versionName "0.9.4"
}
buildTypes {
release {
@@ -35,18 +35,20 @@ dependencies {
testCompile 'org.mockito:mockito-core:1.10.19'
testCompile 'org.json:json:20160810'
compile 'com.android.support:appcompat-v7:25.1.0'
compile 'com.android.support:support-v4:25.1.0'
compile 'com.android.support:design:25.1.0'
compile 'com.android.support:recyclerview-v7:25.1.0'
compile 'com.android.support:appcompat-v7:25.3.1'
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.4'
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'
}

View File

@@ -19,22 +19,15 @@
tools:ignore="AllowBackup">
<activity
android:name=".MainActivity"
android:label="@string/app_name">
android:label="@string/app_name"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".detail.VideoItemDetailActivity"
android:label="@string/title_videoitem_detail"
android:launchMode="singleTask"
android:theme="@style/AppTheme">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity" />
</activity>
<activity
android:name=".player.PlayVideoActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
@@ -47,10 +40,10 @@
android:label="@string/background_player_name" />
<activity
android:name=".player.ExoPlayerActivity"
android:name=".player.MainVideoPlayer"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:label="@string/app_name"
android:launchMode="singleInstance"
android:launchMode="singleTask"
android:theme="@style/PlayerTheme"/>
<activity
@@ -87,9 +80,6 @@
android:label="@string/app_name"
android:launchMode="singleTop"
android:theme="@style/FilePickerTheme"/>
<activity
android:name=".ChannelActivity"
android:launchMode="singleTask" />
<activity
android:name=".ReCaptchaActivity"
android:label="@string/reCaptchaActivity" />
@@ -104,7 +94,9 @@
android:resource="@xml/provider_paths" />
</provider>
<activity android:name=".RouterActivity"
<activity
android:name=".RouterActivity"
android:taskAffinity=""
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@@ -161,7 +153,9 @@
</intent-filter>
</activity>
<activity android:name=".PopupActivity"
<activity
android:name=".RouterPopupActivity"
android:taskAffinity=""
android:theme="@android:style/Theme.NoDisplay"
android:label="@string/popup_mode_share_menu_title">
<intent-filter>
@@ -182,9 +176,6 @@
<data android:pathPrefix="/embed/" />
<data android:pathPrefix="/watch" />
<data android:pathPrefix="/attribution_link" />
<!-- channel prefix -->
<data android:pathPrefix="/channel/"/>
<data android:pathPrefix="/user/"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@@ -212,9 +203,7 @@
</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>

View File

@@ -20,10 +20,6 @@ package org.schabi.newpipe;
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
import android.graphics.Bitmap;
import java.util.List;
/**
* Singleton:
* Used to send data between certain Activity/Services within the same process.
@@ -39,8 +35,5 @@ public class ActivityCommunicator {
return activityCommunicator;
}
// Thumbnail send from VideoItemDetailFragment to BackgroundPlayer
public volatile Bitmap backgroundPlayerThumbnail;
public volatile Class returnActivity;
}

View File

@@ -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

View File

@@ -1,14 +1,14 @@
package org.schabi.newpipe;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.view.View;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.report.ErrorActivity;
/**
* Created by Christian Schabesberger on 01.08.16.
@@ -33,11 +33,11 @@ import org.schabi.newpipe.extractor.NewPipe;
public class ImageErrorLoadingListener implements ImageLoadingListener {
private int serviceId = -1;
private Activity activity = null;
private Context context = null;
private View rootView = null;
public ImageErrorLoadingListener(Activity activity, View rootView, int serviceId) {
this.activity = activity;
public ImageErrorLoadingListener(Context context, View rootView, int serviceId) {
this.context = context;
this.serviceId= serviceId;
this.rootView = rootView;
}
@@ -47,7 +47,7 @@ public class ImageErrorLoadingListener implements ImageLoadingListener {
@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
ErrorActivity.reportError(activity,
ErrorActivity.reportError(context,
failReason.getCause(), null, rootView,
ErrorActivity.ErrorInfo.make(ErrorActivity.LOAD_IMAGE,
NewPipe.getNameOfService(serviceId), imageUri,

File diff suppressed because it is too large Load Diff

View File

@@ -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);

View File

@@ -3,19 +3,14 @@ package org.schabi.newpipe;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.Toast;
import org.schabi.newpipe.detail.VideoItemDetailActivity;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.util.NavStack;
import org.schabi.newpipe.util.NavigationHelper;
import java.util.Collection;
import java.util.HashSet;
/**
/*
* Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org>
* RouterActivity .java is part of NewPipe.
*
@@ -38,7 +33,7 @@ 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.class.toString();
//private static final String TAG = "RouterActivity"
/**
* Removes invisible separators (\p{Z}) and punctuation characters including
@@ -54,6 +49,25 @@ public class RouterActivity extends Activity {
finish();
}
private void handleIntent(Intent intent) {
String videoUrl = "";
// 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];
}
try {
NavigationHelper.openByLink(this, videoUrl);
} catch (Exception e) {
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
}
}
private static String removeHeadingGibberish(final String input) {
int start = 0;
@@ -107,50 +121,4 @@ public class RouterActivity extends Activity {
return result.toArray(new String[result.size()]);
}
private void handleIntent(Intent intent) {
String videoUrl = "";
StreamingService service = null;
// 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);
if(service == null) {
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG)
.show();
return;
} else {
Intent callIntent = new Intent();
switch(service.getLinkTypeByUrl(videoUrl)) {
case CHANNEL:
callIntent.setClass(this, ChannelActivity.class);
break;
case STREAM:
callIntent.setClass(this, VideoItemDetailActivity.class);
callIntent.putExtra(VideoItemDetailActivity.AUTO_PLAY,
PreferenceManager.getDefaultSharedPreferences(this)
.getBoolean(
getString(R.string.autoplay_through_intent_key), false));
break;
case PLAYLIST:
Log.e(TAG, "NOT YET DEFINED");
break;
default:
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG)
.show();
return;
}
callIntent.putExtra(NavStack.URL, videoUrl);
callIntent.putExtra(NavStack.SERVICE_ID, service.getServiceId());
startActivity(callIntent);
}
}
}

View File

@@ -4,24 +4,22 @@ import android.app.Activity;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.player.PopupVideoPlayer;
import org.schabi.newpipe.util.NavStack;
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 playser.
* This activity is thought to open video streams form an external app using the popup player.
*/
public class PopupActivity extends Activity {
private static final String TAG = RouterActivity.class.toString();
public class RouterPopupActivity extends Activity {
//private static final String TAG = "RouterPopupActivity";
/**
* Removes invisible separators (\p{Z}) and punctuation characters including
@@ -38,6 +36,45 @@ public class PopupActivity extends Activity {
}
private void handleIntent(Intent intent) {
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);
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)) {
case STREAM:
break;
default:
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
return;
}
callIntent.putExtra(Constants.KEY_URL, videoUrl);
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--) {
@@ -90,47 +127,4 @@ public class PopupActivity extends Activity {
return result.toArray(new String[result.size()]);
}
private void handleIntent(Intent intent) {
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 = null;
// 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);
if (service == null) {
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG)
.show();
return;
} else {
Intent callIntent = new Intent();
switch (service.getLinkTypeByUrl(videoUrl)) {
case STREAM:
callIntent.setClass(this, PopupVideoPlayer.class);
break;
case PLAYLIST:
Log.e(TAG, "NOT YET DEFINED");
break;
default:
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
return;
}
callIntent.putExtra(NavStack.URL, videoUrl);
callIntent.putExtra(NavStack.SERVICE_ID, service.getServiceId());
startService(callIntent);
}
}
}

View File

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

View File

@@ -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();
}

View File

@@ -1,14 +1,11 @@
package org.schabi.newpipe.download;
import android.Manifest;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.DialogFragment;
@@ -26,34 +23,32 @@ import android.widget.TextView;
import org.schabi.newpipe.App;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.settings.NewPipeSettings;
import org.schabi.newpipe.util.ThemeHelper;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import us.shandian.giga.get.DownloadManager;
import us.shandian.giga.get.DownloadMission;
import us.shandian.giga.service.DownloadManagerService;
/**
* Created by Christian Schabesberger on 21.09.15.
*
* <p>
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
* DownloadDialog.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/>.
*/
@@ -71,8 +66,7 @@ public class DownloadDialog extends DialogFragment {
}
public static DownloadDialog newInstance(Bundle args)
{
public static DownloadDialog newInstance(Bundle args) {
DownloadDialog dialog = new DownloadDialog();
dialog.setArguments(args);
dialog.setStyle(DialogFragment.STYLE_NO_TITLE, 0);
@@ -100,7 +94,7 @@ public class DownloadDialog extends DialogFragment {
final SeekBar threads = (SeekBar) view.findViewById(R.id.threads);
toolbar.setTitle(R.string.download_dialog_title);
toolbar.setNavigationIcon(R.drawable.ic_arrow_back_black_24dp);
toolbar.setNavigationIcon(ThemeHelper.isLightThemeSelected(getActivity()) ? R.drawable.ic_arrow_back_black_24dp : R.drawable.ic_arrow_back_white_24dp);
toolbar.inflateMenu(R.menu.dialog_url);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
@@ -151,16 +145,16 @@ public class DownloadDialog extends DialogFragment {
}
protected void checkDownloadOptions(){
protected void checkDownloadOptions() {
View view = getView();
Bundle arguments = getArguments();
RadioButton audioButton = (RadioButton) view.findViewById(R.id.audio_button);
RadioButton videoButton = (RadioButton) view.findViewById(R.id.video_button);
if(arguments.getString(AUDIO_URL) == null) {
if (arguments.getString(AUDIO_URL) == null) {
audioButton.setVisibility(View.GONE);
videoButton.setChecked(true);
} else if(arguments.getString(VIDEO_URL) == null) {
} else if (arguments.getString(VIDEO_URL) == null) {
videoButton.setVisibility(View.GONE);
audioButton.setChecked(true);
}
@@ -169,11 +163,11 @@ public class DownloadDialog extends DialogFragment {
/**
* #143 #44 #42 #22: make shure that the filename does not contain illegal chars.
* This should fix some of the "cannot download" problems.
* */
*/
private String createFileName(String fName) {
// from http://eng-przemelek.blogspot.de/2009/07/how-to-create-valid-file-name.html
List<String> forbiddenCharsPatterns = new ArrayList<> ();
List<String> forbiddenCharsPatterns = new ArrayList<>();
forbiddenCharsPatterns.add("[:]+"); // Mac OS, but it looks that also Windows XP
forbiddenCharsPatterns.add("[\\*\"/\\\\\\[\\]\\:\\;\\|\\=\\,]+"); // Windows
forbiddenCharsPatterns.add("[^\\w\\d\\.]+"); // last chance... only latin letters and digits
@@ -186,8 +180,7 @@ public class DownloadDialog extends DialogFragment {
//download audio, video or both?
private void download()
{
private void download() {
View view = getView();
Bundle arguments = getArguments();
final EditText name = (EditText) view.findViewById(R.id.file_name);
@@ -199,7 +192,7 @@ public class DownloadDialog extends DialogFragment {
boolean isAudio = audioButton.isChecked();
String url, location, filename;
if(isAudio) {
if (isAudio) {
url = arguments.getString(AUDIO_URL);
location = NewPipeSettings.getAudioDownloadPath(getContext());
filename = fName + arguments.getString(FILE_SUFFIX_AUDIO);
@@ -218,11 +211,11 @@ public class DownloadDialog extends DialogFragment {
private void download(String url, String title,
String fileSuffix, File downloadDir, Context context) {
File saveFilePath = new File(downloadDir,createFileName(title) + fileSuffix);
File saveFilePath = new File(downloadDir, createFileName(title) + fileSuffix);
long id = 0;
Log.i(TAG,"Started downloading '" + url +
Log.i(TAG, "Started downloading '" + url +
"' => '" + saveFilePath + "' #" + id);
if (App.isUsingTor()) {

View File

@@ -0,0 +1,243 @@
package org.schabi.newpipe.fragments;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
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;
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 OnItemSelectedListener onItemSelectedListener;
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;
onItemSelectedListener = (OnItemSelectedListener) 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
//////////////////////////////////////////////////////////////////////////*/
public void animateView(final View view, final boolean enterOrExit, long duration) {
animateView(view, enterOrExit, duration, 0, null);
}
public void animateView(final View view, final boolean enterOrExit, long duration, final Runnable execOnEnd) {
animateView(view, enterOrExit, duration, 0, execOnEnd);
}
public void animateView(final View view, final boolean enterOrExit, long duration, long delay) {
animateView(view, enterOrExit, duration, delay, null);
}
/**
* Animate the view
*
* @param view view that will be animated
* @param enterOrExit true to enter, false to exit
* @param duration how long the animation will take, in milliseconds
* @param delay how long the animation will take to start, in milliseconds
* @param execOnEnd runnable that will be executed when the animation ends
*/
public void animateView(final View view, final boolean enterOrExit, long duration, long delay, final Runnable execOnEnd) {
if (DEBUG) Log.d(TAG, "animateView() called with: view = [" + view + "], enterOrExit = [" + enterOrExit + "], duration = [" + duration + "], execOnEnd = [" + execOnEnd + "]");
if (view == null) return;
if (view.getVisibility() == View.VISIBLE && enterOrExit) {
view.animate().setListener(null).cancel();
view.setVisibility(View.VISIBLE);
view.setAlpha(1f);
if (execOnEnd != null) execOnEnd.run();
return;
} else if ((view.getVisibility() == View.GONE || view.getVisibility() == View.INVISIBLE) && !enterOrExit) {
view.animate().setListener(null).cancel();
view.setVisibility(View.GONE);
view.setAlpha(0f);
if (execOnEnd != null) execOnEnd.run();
return;
}
view.animate().setListener(null).cancel();
view.setVisibility(View.VISIBLE);
if (enterOrExit) {
view.animate().alpha(1f).setDuration(duration).setStartDelay(delay)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (execOnEnd != null) execOnEnd.run();
}
}).start();
} else {
view.animate().alpha(0f)
.setDuration(duration).setStartDelay(delay)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
view.setVisibility(View.GONE);
if (execOnEnd != null) execOnEnd.run();
}
})
.start();
}
}
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();
}
}

View File

@@ -0,0 +1,76 @@
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.openSearch(activity, 0, "");
return true;
}
return super.onOptionsItemSelected(item);
}
}

View File

@@ -0,0 +1,10 @@
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

View File

@@ -0,0 +1,167 @@
package org.schabi.newpipe.fragments.detail;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
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;
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/>.
*/
class ActionBarHandler {
private static final String TAG = "ActionBarHandler";
private AppCompatActivity activity;
private int selectedVideoStream = -1;
private SharedPreferences defaultPreferences;
private Menu menu;
// Only callbacks are listed here, there are more actions which don't need a callback.
// those are edited directly. Typically VideoDetailFragment will implement those callbacks.
private OnActionListener onShareListener;
private OnActionListener onOpenInBrowserListener;
private OnActionListener onDownloadListener;
private OnActionListener onPlayWithKodiListener;
// Triggered when a stream related action is triggered.
public interface OnActionListener {
void onActionSelected(int selectedStreamId);
}
public ActionBarHandler(AppCompatActivity activity) {
this.activity = activity;
}
public void setupStreamList(final List<VideoStream> videoStreams, Spinner toolbarSpinner) {
if (activity == null) return;
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);
ArrayAdapter<String> itemAdapter = new ArrayAdapter<>(activity.getBaseContext(), android.R.layout.simple_spinner_dropdown_item, itemArray);
toolbarSpinner.setAdapter(itemAdapter);
toolbarSpinner.setSelection(defaultResolutionIndex);
toolbarSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
selectedVideoStream = position;
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
public void setupMenu(Menu menu, MenuInflater inflater) {
this.menu = menu;
// CAUTION set item properties programmatically otherwise it would not be accepted by
// appcompat itemsinflater.inflate(R.menu.videoitem_detail, menu);
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(activity);
inflater.inflate(R.menu.video_detail_menu, menu);
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: {
if (onShareListener != null) {
onShareListener.onActionSelected(selectedVideoStream);
}
return true;
}
case R.id.menu_item_openInBrowser: {
if (onOpenInBrowserListener != null) {
onOpenInBrowserListener.onActionSelected(selectedVideoStream);
}
return true;
}
case R.id.menu_item_download:
if (onDownloadListener != null) {
onDownloadListener.onActionSelected(selectedVideoStream);
}
return true;
case R.id.action_play_with_kodi:
if (onPlayWithKodiListener != null) {
onPlayWithKodiListener.onActionSelected(selectedVideoStream);
}
return true;
default:
Log.e(TAG, "Menu Item not known");
}
return false;
}
public int getSelectedVideoStream() {
return selectedVideoStream;
}
public void setOnShareListener(OnActionListener listener) {
onShareListener = listener;
}
public void setOnOpenInBrowserListener(OnActionListener listener) {
onOpenInBrowserListener = listener;
}
public void setOnDownloadListener(OnActionListener listener) {
onDownloadListener = listener;
}
public void setOnPlayWithKodiListener(OnActionListener listener) {
onPlayWithKodiListener = listener;
}
public void showDownloadAction(boolean visible) {
menu.findItem(R.id.menu_item_download).setVisible(visible);
}
public void showPlayWithKodiAction(boolean visible) {
menu.findItem(R.id.action_play_with_kodi).setVisible(visible);
}
}

View File

@@ -0,0 +1,42 @@
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;
this.url = url;
}
public void setTitle(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
public String getUrl() {
return url;
}
public void setInfo(StreamInfo info) {
this.info = info;
}
public StreamInfo getInfo() {
return info;
}
@Override
public String toString() {
return getUrl() + " > " + getTitle();
}
}

View File

@@ -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

View File

@@ -1,4 +1,4 @@
package org.schabi.newpipe.search_fragment;
package org.schabi.newpipe.fragments.search;
import android.support.v7.widget.SearchView;

View File

@@ -1,4 +1,4 @@
package org.schabi.newpipe.search_fragment;
package org.schabi.newpipe.fragments.search;
import android.content.Context;
import android.database.Cursor;
@@ -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) {

View File

@@ -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

View File

@@ -10,8 +10,8 @@ import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.InfoItem;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
/**
* Created by Christian Schabesberger on 01.08.16.
@@ -57,7 +57,7 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
public InfoListAdapter(Activity a, View rootView) {
infoItemBuilder = new InfoItemBuilder(a, rootView);
infoItemList = new Vector<>();
infoItemList = new ArrayList<>();
}
public void setOnStreamInfoItemSelectedListener
@@ -77,7 +77,7 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
}
}
public void clearSteamItemList() {
public void clearStreamItemList() {
infoItemList.clear();
notifyDataSetChanged();
}
@@ -92,12 +92,16 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
notifyDataSetChanged();
}
public List<InfoItem> getItemsList() {
return infoItemList;
}
@Override
public int getItemCount() {
int cound = infoItemList.size();
if(header != null) cound++;
if(footer != null && showFooter) cound++;
return cound;
int count = infoItemList.size();
if(header != null) count++;
if(footer != null && showFooter) count++;
return count;
}
// don't ask why we have to do that this way... it's android accept it -.-

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