mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-09-21 22:20:50 +02:00
Compare commits
152 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
72d90374a0 | ||
![]() |
fc8160acda | ||
![]() |
751ffb9de9 | ||
![]() |
60d636940d | ||
![]() |
7a5cca519a | ||
![]() |
1e93d06a25 | ||
![]() |
4341f8aaec | ||
![]() |
46022d60f3 | ||
![]() |
80ddc76926 | ||
![]() |
bd999b4b0a | ||
![]() |
4cdc387338 | ||
![]() |
df258a0003 | ||
![]() |
7cc1c0fbdd | ||
![]() |
42644c956b | ||
![]() |
00eaedcbfa | ||
![]() |
1ff5a97c85 | ||
![]() |
241414f81b | ||
![]() |
1bf046a8ba | ||
![]() |
61471fdd3c | ||
![]() |
a0524fb136 | ||
![]() |
fa39389a64 | ||
![]() |
697a24e699 | ||
![]() |
cf3bb87a54 | ||
![]() |
14fb7d8a7a | ||
![]() |
d097363b24 | ||
![]() |
bad576c23d | ||
![]() |
8d9e4e7442 | ||
![]() |
431d03c63f | ||
![]() |
24321731d9 | ||
![]() |
fddcade1fb | ||
![]() |
a0aa7dcdc1 | ||
![]() |
d3e9f354b3 | ||
![]() |
e958334406 | ||
![]() |
a42029970d | ||
![]() |
2108e09e13 | ||
![]() |
8c010093e8 | ||
![]() |
adc98be441 | ||
![]() |
fb942912db | ||
![]() |
7f12b58722 | ||
![]() |
05d58f8f98 | ||
![]() |
46c2db310a | ||
![]() |
607408b9b9 | ||
![]() |
b81e8cb81a | ||
![]() |
44ef04d90a | ||
![]() |
3e9d84b109 | ||
![]() |
aa2d8d4833 | ||
![]() |
55f63b948b | ||
![]() |
cece543d6b | ||
![]() |
9204a89319 | ||
![]() |
f8ed96bb25 | ||
![]() |
54d318bf04 | ||
![]() |
f152d66cd8 | ||
![]() |
576786c751 | ||
![]() |
a6b7cd3202 | ||
![]() |
ea9caaaaf0 | ||
![]() |
22d8f434be | ||
![]() |
8360c10141 | ||
![]() |
750dc6f2cc | ||
![]() |
47b556544d | ||
![]() |
f6cee739a6 | ||
![]() |
d1afe0028c | ||
![]() |
a4cace1ef7 | ||
![]() |
707ac8be27 | ||
![]() |
2f2254966a | ||
![]() |
eb57675f55 | ||
![]() |
dde93c5ccf | ||
![]() |
886b5959f7 | ||
![]() |
dc44546ac7 | ||
![]() |
42c6698732 | ||
![]() |
bd014a107f | ||
![]() |
3a7298bb99 | ||
![]() |
aa06e3490d | ||
![]() |
ece889b36b | ||
![]() |
9848f19ce6 | ||
![]() |
e59b087057 | ||
![]() |
5b451d1ac7 | ||
![]() |
f050c05b3c | ||
![]() |
06b8edefbf | ||
![]() |
f3a2a28398 | ||
![]() |
9b0a1fc2ec | ||
![]() |
c87ab234eb | ||
![]() |
68468756a8 | ||
![]() |
9016df0195 | ||
![]() |
7acece9705 | ||
![]() |
d1896c23c0 | ||
![]() |
dae19f03e0 | ||
![]() |
9dafccf0f7 | ||
![]() |
5f270c41ae | ||
![]() |
f9f80e9003 | ||
![]() |
c5063d4269 | ||
![]() |
ccbe18ec1c | ||
![]() |
321a8a8b25 | ||
![]() |
77a9560376 | ||
![]() |
058a039a82 | ||
![]() |
7c744703e4 | ||
![]() |
a934cbb085 | ||
![]() |
cf4158c0d0 | ||
![]() |
dc56eab9b6 | ||
![]() |
37d1f59132 | ||
![]() |
ab0ce55411 | ||
![]() |
c1d66596d1 | ||
![]() |
cbfccdf0d3 | ||
![]() |
9362037177 | ||
![]() |
e25c93bae2 | ||
![]() |
367c434010 | ||
![]() |
02d8463e15 | ||
![]() |
9d5a1d5c43 | ||
![]() |
c8d94f541f | ||
![]() |
27d06eaa6b | ||
![]() |
7f32857e00 | ||
![]() |
2f060f0f52 | ||
![]() |
f89d405226 | ||
![]() |
fd4459e570 | ||
![]() |
eb0df2b101 | ||
![]() |
6c178cfb7e | ||
![]() |
8ced68430d | ||
![]() |
0aade598ff | ||
![]() |
95949fd1ab | ||
![]() |
1a56382112 | ||
![]() |
d610e4b19b | ||
![]() |
fc44d9e36e | ||
![]() |
c07686576a | ||
![]() |
c2400aea4d | ||
![]() |
fb4bf0dde4 | ||
![]() |
e4f638d1ce | ||
![]() |
5c492c01a1 | ||
![]() |
f451e11f82 | ||
![]() |
95b73f35f7 | ||
![]() |
58147e9e12 | ||
![]() |
a8830e2ede | ||
![]() |
9804bb95cc | ||
![]() |
0da1aef763 | ||
![]() |
0a334804a3 | ||
![]() |
94d2f03e9b | ||
![]() |
9127f7f0c2 | ||
![]() |
0bb0226bc2 | ||
![]() |
b3a1a5dcc2 | ||
![]() |
984dd1cc25 | ||
![]() |
5663e543a4 | ||
![]() |
d3879a0398 | ||
![]() |
6bd2468d44 | ||
![]() |
e63d43151b | ||
![]() |
0265da4ae6 | ||
![]() |
ef255d12ae | ||
![]() |
eeb612f9a2 | ||
![]() |
dfcb4edb81 | ||
![]() |
d90162d06f | ||
![]() |
97c924341c | ||
![]() |
cd3f405bff | ||
![]() |
7cfdca7a81 | ||
![]() |
216063dba8 | ||
![]() |
b647bacd72 |
@@ -1,11 +1,11 @@
|
||||
#Contributing
|
||||
#Contribution
|
||||
|
||||
This document contains guidelines on making contributions to NewPipe.
|
||||
|
||||
## Programming
|
||||
|
||||
* Follow the [Google Style Guidelines](https://google.github.io/styleguide/javaguide.html)
|
||||
* Make a new feature on a seperate branch, not on the master branch
|
||||
* Make a new feature on a separate branch, not on the master branch
|
||||
* Make a [pull request](https://github.com/theScrabi/NewPipe/pulls) if you're done with your changes
|
||||
* When submitting changes, you agree that your code will be GPLv3 licensed
|
||||
|
||||
@@ -16,7 +16,7 @@ This document contains guidelines on making contributions to NewPipe.
|
||||
compatibility with all git tools
|
||||
* [This guide](http://chris.beams.io/posts/git-commit/) goes more in depth on what makes a good commit message
|
||||
|
||||
## Translating
|
||||
## Translation
|
||||
|
||||
* NewPipe can be translated on [weblate](https://hosted.weblate.org/projects/newpipe/strings/)
|
||||
|
||||
@@ -30,4 +30,4 @@ This document contains guidelines on making contributions to NewPipe.
|
||||
## Communication
|
||||
|
||||
* For the time being, [Slack](http://invite.chschtsch.ml/) is being used for project communication
|
||||
* Feel free to post suggestions, changes ideas etc!
|
||||
* Feel free to post suggestions, changes, ideas etc!
|
||||
|
@@ -18,7 +18,6 @@ Project status:
|
||||
[<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_6.png" width=250>](screenshots/screenshot_6.png)
|
||||
|
||||
## Description
|
||||
|
||||
@@ -36,6 +35,7 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
|
||||
* Open a video in Kodi
|
||||
* Show Next/Related videos
|
||||
* Search YouTube in a specific language
|
||||
* Orbot/Tor support (no streaming yet, experimental)
|
||||
|
||||
### Coming Features
|
||||
|
||||
|
@@ -2,14 +2,14 @@ apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.2"
|
||||
buildToolsVersion '23.0.2'
|
||||
|
||||
defaultConfig {
|
||||
applicationId "org.schabi.newpipe"
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 23
|
||||
versionCode 10
|
||||
versionName "0.7.1"
|
||||
versionCode 13
|
||||
versionName "0.7.4"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
@@ -17,13 +17,17 @@ android {
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lintOptions {
|
||||
checkReleaseBuilds false
|
||||
// Or, if you prefer, you can continue to check for errors in release builds,
|
||||
// but continue the build even when errors are found:
|
||||
abortOnError false
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -31,8 +35,11 @@ dependencies {
|
||||
compile 'com.android.support:appcompat-v7:23.1.1'
|
||||
compile 'com.android.support:support-v4:23.1.1'
|
||||
compile 'com.android.support:design:23.1.1'
|
||||
compile 'com.android.support:cardview-v7:23.1.1'
|
||||
compile 'com.android.support:recyclerview-v7:23.1.1'
|
||||
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'
|
||||
}
|
||||
|
@@ -0,0 +1,93 @@
|
||||
package org.schabi.newpipe.services.youtube;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import org.schabi.newpipe.crawler.VideoPreviewInfo;
|
||||
import org.schabi.newpipe.crawler.SearchEngine;
|
||||
import org.schabi.newpipe.crawler.services.youtube.YoutubeSearchEngine;
|
||||
import org.schabi.newpipe.Downloader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Created by the-scrabi on 29.12.15.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
||||
* YoutubeSearchEngineTest.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 YoutubeSearchEngineTest extends AndroidTestCase {
|
||||
private SearchEngine.Result result;
|
||||
private ArrayList<String> suggestionReply;
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception{
|
||||
super.setUp();
|
||||
SearchEngine engine = new YoutubeSearchEngine();
|
||||
|
||||
result = engine.search("https://www.youtube.com/results?search_query=bla",
|
||||
0, "de", new Downloader());
|
||||
suggestionReply = engine.suggestionList("hello", new Downloader());
|
||||
}
|
||||
|
||||
public void testIfNoErrorOccur() {
|
||||
assertEquals(result.errorMessage, "");
|
||||
}
|
||||
|
||||
public void testIfListIsNotEmpty() {
|
||||
assertEquals(result.resultList.size() > 0, true);
|
||||
}
|
||||
|
||||
public void testItemsHaveTitle() {
|
||||
for(VideoPreviewInfo i : result.resultList) {
|
||||
assertEquals(i.title.isEmpty(), false);
|
||||
}
|
||||
}
|
||||
|
||||
public void testItemsHaveUploader() {
|
||||
for(VideoPreviewInfo i : result.resultList) {
|
||||
assertEquals(i.uploader.isEmpty(), false);
|
||||
}
|
||||
}
|
||||
|
||||
public void testItemsHaveRightDuration() {
|
||||
for(VideoPreviewInfo i : result.resultList) {
|
||||
assertTrue(i.duration, i.duration.contains(":"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testItemsHaveRightThumbnail() {
|
||||
for (VideoPreviewInfo i : result.resultList) {
|
||||
assertTrue(i.thumbnail_url, i.thumbnail_url.contains("https://"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testItemsHaveRightVideoUrl() {
|
||||
for (VideoPreviewInfo i : result.resultList) {
|
||||
assertTrue(i.webpage_url, i.webpage_url.contains("https://"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testIfSuggestionsAreReplied() {
|
||||
assertEquals(suggestionReply.size() > 0, true);
|
||||
}
|
||||
|
||||
public void testIfSuggestionsAreValid() {
|
||||
for(String s : suggestionReply) {
|
||||
assertTrue(s, !s.isEmpty());
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,109 @@
|
||||
package org.schabi.newpipe.services.youtube;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import org.schabi.newpipe.Downloader;
|
||||
import org.schabi.newpipe.crawler.CrawlingException;
|
||||
import org.schabi.newpipe.crawler.ParsingException;
|
||||
import org.schabi.newpipe.crawler.services.youtube.YoutubeStreamExtractor;
|
||||
import org.schabi.newpipe.crawler.VideoInfo;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by the-scrabi on 30.12.15.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
||||
* YoutubeVideoExtractorDefault.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 YoutubeStreamExtractorDefaultTest extends AndroidTestCase {
|
||||
private YoutubeStreamExtractor extractor;
|
||||
|
||||
public void setUp() throws IOException, CrawlingException {
|
||||
/* some anonymus video test
|
||||
extractor = new YoutubeStreamExtractor("https://www.youtube.com/watch?v=FmG385_uUys",
|
||||
new Downloader()); */
|
||||
/* some vevo video (suggested to test against) */
|
||||
extractor = new YoutubeStreamExtractor("https://www.youtube.com/watch?v=YQHsXMglC9A",
|
||||
new Downloader());
|
||||
}
|
||||
|
||||
public void testGetInvalidTimeStamp() throws ParsingException {
|
||||
assertTrue(Integer.toString(extractor.getTimeStamp()),
|
||||
extractor.getTimeStamp() <= 0);
|
||||
}
|
||||
|
||||
public void testGetValidTimeStamp() throws CrawlingException, IOException {
|
||||
YoutubeStreamExtractor extractor =
|
||||
new YoutubeStreamExtractor("https://youtu.be/FmG385_uUys?t=174", new Downloader());
|
||||
assertTrue(Integer.toString(extractor.getTimeStamp()),
|
||||
extractor.getTimeStamp() == 174);
|
||||
}
|
||||
|
||||
public void testGetTitle() throws ParsingException {
|
||||
assertTrue(!extractor.getTitle().isEmpty());
|
||||
}
|
||||
|
||||
public void testGetDescription() throws ParsingException {
|
||||
assertTrue(extractor.getDescription() != null);
|
||||
}
|
||||
|
||||
public void testGetUploader() throws ParsingException {
|
||||
assertTrue(!extractor.getUploader().isEmpty());
|
||||
}
|
||||
|
||||
public void testGetLength() throws ParsingException {
|
||||
assertTrue(extractor.getLength() > 0);
|
||||
}
|
||||
|
||||
public void testGetViews() throws ParsingException {
|
||||
assertTrue(extractor.getLength() > 0);
|
||||
}
|
||||
|
||||
public void testGetUploadDate() throws ParsingException {
|
||||
assertTrue(extractor.getUploadDate().length() > 0);
|
||||
}
|
||||
|
||||
public void testGetThumbnailUrl() throws ParsingException {
|
||||
assertTrue(extractor.getThumbnailUrl(),
|
||||
extractor.getThumbnailUrl().contains("https://"));
|
||||
}
|
||||
|
||||
public void testGetUploaderThumbnailUrl() throws ParsingException {
|
||||
assertTrue(extractor.getUploaderThumbnailUrl(),
|
||||
extractor.getUploaderThumbnailUrl().contains("https://"));
|
||||
}
|
||||
|
||||
public void testGetAudioStreams() throws ParsingException {
|
||||
assertTrue(!extractor.getAudioStreams().isEmpty());
|
||||
}
|
||||
|
||||
public void testGetVideoStreams() throws ParsingException {
|
||||
for(VideoInfo.VideoStream s : extractor.getVideoStreams()) {
|
||||
assertTrue(s.url,
|
||||
s.url.contains("https://"));
|
||||
assertTrue(s.resolution.length() > 0);
|
||||
assertTrue(Integer.toString(s.format),
|
||||
0 <= s.format && s.format <= 4);
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetDashMpd() throws ParsingException {
|
||||
assertTrue(extractor.getDashMpdUrl(),
|
||||
!extractor.getDashMpdUrl().isEmpty());
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
package org.schabi.newpipe.services.youtube;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import org.schabi.newpipe.Downloader;
|
||||
import org.schabi.newpipe.crawler.CrawlingException;
|
||||
import org.schabi.newpipe.crawler.services.youtube.YoutubeStreamExtractor;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by the-scrabi on 30.12.15.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
||||
* YoutubeVideoExtractorGema.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/>.
|
||||
*/
|
||||
|
||||
|
||||
// This class only works in Germany.
|
||||
public class YoutubeStreamExtractorGemaTest extends AndroidTestCase {
|
||||
|
||||
// Deaktivate this Test Case bevore uploading it githup, otherwise CI will fail.
|
||||
private static final boolean testActive = false;
|
||||
|
||||
public void testGemaError() throws IOException, CrawlingException {
|
||||
if(testActive) {
|
||||
try {
|
||||
new YoutubeStreamExtractor("https://www.youtube.com/watch?v=3O1_3zBUKM8",
|
||||
new Downloader());
|
||||
assertTrue("Gema exception not thrown", false);
|
||||
} catch(YoutubeStreamExtractor.GemaException ge) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -7,6 +7,7 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<application
|
||||
android:name=".App"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:logo="@mipmap/ic_launcher"
|
||||
@@ -15,7 +16,8 @@
|
||||
tools:ignore="AllowBackup">
|
||||
<activity
|
||||
android:name=".VideoItemListActivity"
|
||||
android:label="@string/app_name">
|
||||
android:label="@string/app_name"
|
||||
android:configChanges="orientation|screenSize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
@@ -25,7 +27,9 @@
|
||||
<activity
|
||||
android:name=".VideoItemDetailActivity"
|
||||
android:label="@string/title_videoitem_detail"
|
||||
android:theme="@style/AppTheme">
|
||||
android:theme="@style/AppTheme"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:screenOrientation="portrait">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".VideoItemListActivity" />
|
||||
@@ -83,7 +87,7 @@
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".SettingsActivity"
|
||||
android:label="@string/title_activity_settings" >
|
||||
android:label="@string/settings_activity_title" >
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".PanicResponderActivity"
|
||||
|
File diff suppressed because it is too large
Load Diff
78
app/src/main/java/org/schabi/newpipe/App.java
Normal file
78
app/src/main/java/org/schabi/newpipe/App.java
Normal file
@@ -0,0 +1,78 @@
|
||||
package org.schabi.newpipe;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
|
||||
|
||||
import info.guardianproject.netcipher.NetCipher;
|
||||
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
||||
|
||||
/**
|
||||
* Copyright (C) Hans-Christoph Steiner 2016 <hans@eds.org>
|
||||
* App.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 App extends Application {
|
||||
|
||||
private static boolean useTor;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
// Initialize image loader
|
||||
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(this).build();
|
||||
ImageLoader.getInstance().init(config);
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
if(prefs.getBoolean(getString(R.string.use_tor_key), false)) {
|
||||
OrbotHelper.requestStartTor(this);
|
||||
configureTor(true);
|
||||
} else {
|
||||
configureTor(false);
|
||||
}
|
||||
|
||||
// DO NOT REMOVE THIS FUNCTION!!!
|
||||
// Otherwise downloadPathPreference has invalid value.
|
||||
SettingsActivity.initSettings(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the proxy settings based on whether Tor should be enabled or not.
|
||||
*/
|
||||
static void configureTor(boolean enabled) {
|
||||
useTor = enabled;
|
||||
if (useTor) {
|
||||
NetCipher.useTor();
|
||||
} else {
|
||||
NetCipher.setProxy(null);
|
||||
}
|
||||
}
|
||||
|
||||
static void checkStartTor(Context context) {
|
||||
if (useTor) {
|
||||
OrbotHelper.requestStartTor(context);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isUsingTor() {
|
||||
return useTor;
|
||||
}
|
||||
}
|
@@ -53,7 +53,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
|
||||
public static final String TITLE = "title";
|
||||
public static final String WEB_URL = "web_url";
|
||||
public static final String SERVICE_ID = "service_id";
|
||||
public static final String CHANNEL_NAME="channel_name";
|
||||
public static final String CHANNEL_NAME = "channel_name";
|
||||
|
||||
private volatile String webUrl = "";
|
||||
private volatile int serviceId = -1;
|
||||
@@ -75,7 +75,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
|
||||
}
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
Toast.makeText(this, R.string.backgroundPlayerStartPlayingToast,
|
||||
Toast.makeText(this, R.string.background_player_playing_toast,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
|
||||
String source = intent.getDataString();
|
||||
@@ -117,6 +117,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
|
||||
private WifiManager.WifiLock wifiLock;
|
||||
private Bitmap videoThumbnail = null;
|
||||
private NotificationCompat.Builder noteBuilder;
|
||||
private Notification note;
|
||||
|
||||
public PlayerThread(String src, String title, BackgroundPlayer owner) {
|
||||
this.source = src;
|
||||
@@ -135,9 +136,6 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
|
||||
//so calling the blocking prepare() method should be ok
|
||||
mediaPlayer.prepare();
|
||||
|
||||
//alternatively:
|
||||
//mediaPlayer.setOnPreparedListener(this);
|
||||
//mediaPlayer.prepareAsync(); //prepare async to not block main thread
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
Log.e(TAG, "video source:" + source);
|
||||
@@ -177,16 +175,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
|
||||
filter.addAction(ACTION_STOP);
|
||||
registerReceiver(broadcastReceiver, filter);
|
||||
|
||||
Notification note = buildNotification();
|
||||
|
||||
Intent openDetailView = new Intent(getApplicationContext(),
|
||||
VideoItemDetailActivity.class);
|
||||
openDetailView.putExtra(VideoItemDetailFragment.STREAMING_SERVICE, serviceId);
|
||||
openDetailView.putExtra(VideoItemDetailFragment.VIDEO_URL, webUrl);
|
||||
openDetailView.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
note.contentIntent = PendingIntent.getActivity(getApplicationContext(),
|
||||
noteID, openDetailView,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
note = buildNotification();
|
||||
|
||||
startForeground(noteID, note);
|
||||
|
||||
@@ -207,6 +196,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
|
||||
}*/
|
||||
}
|
||||
|
||||
/**Handles button presses from the notification. */
|
||||
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
@@ -215,11 +205,21 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
|
||||
if(action.equals(ACTION_PLAYPAUSE)) {
|
||||
if(mediaPlayer.isPlaying()) {
|
||||
mediaPlayer.pause();
|
||||
note.contentView.setImageViewResource(R.id.backgroundPlayPause, R.drawable.ic_play_circle_filled_white_24dp);
|
||||
if(android.os.Build.VERSION.SDK_INT >=16){
|
||||
note.bigContentView.setImageViewResource(R.id.backgroundPlayPause, R.drawable.ic_play_circle_filled_white_24dp);
|
||||
}
|
||||
noteMgr.notify(noteID, note);
|
||||
}
|
||||
else {
|
||||
//reacquire CPU lock after releasing it on pause
|
||||
//reacquire CPU lock after auto-releasing it on pause
|
||||
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
|
||||
mediaPlayer.start();
|
||||
note.contentView.setImageViewResource(R.id.backgroundPlayPause, R.drawable.ic_pause_white_24dp);
|
||||
if(android.os.Build.VERSION.SDK_INT >=16){
|
||||
note.bigContentView.setImageViewResource(R.id.backgroundPlayPause, R.drawable.ic_pause_white_24dp);
|
||||
}
|
||||
noteMgr.notify(noteID, note);
|
||||
}
|
||||
}
|
||||
else if(action.equals(ACTION_STOP)) {
|
||||
@@ -231,8 +231,9 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
|
||||
};
|
||||
|
||||
private void afterPlayCleanup() {
|
||||
//noteBuilder.setProgress(0, 0, false);
|
||||
//remove progress bar
|
||||
//noteBuilder.setProgress(0, 0, false);
|
||||
|
||||
//remove notification
|
||||
noteMgr.cancel(noteID);
|
||||
unregisterReceiver(broadcastReceiver);
|
||||
@@ -241,7 +242,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
|
||||
|
||||
//release wifilock
|
||||
wifiLock.release();
|
||||
//remove foreground status of service; make us killable
|
||||
//remove foreground status of service; make BackgroundPlayer killable
|
||||
stopForeground(true);
|
||||
|
||||
stopSelf();
|
||||
@@ -273,6 +274,13 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
|
||||
(R.drawable.ic_pause_white_24dp, "Pause", playPI).build();
|
||||
*/
|
||||
|
||||
//build intent to return to video, on tapping notification
|
||||
Intent openDetailView = new Intent(getApplicationContext(),
|
||||
VideoItemDetailActivity.class);
|
||||
openDetailView.putExtra(VideoItemDetailFragment.STREAMING_SERVICE, serviceId);
|
||||
openDetailView.putExtra(VideoItemDetailFragment.VIDEO_URL, webUrl);
|
||||
openDetailView.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
noteBuilder
|
||||
.setOngoing(true)
|
||||
.setDeleteIntent(stopPI)
|
||||
@@ -281,17 +289,22 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
|
||||
.setSmallIcon(R.drawable.ic_play_circle_filled_white_24dp)
|
||||
.setTicker(
|
||||
String.format(res.getString(
|
||||
R.string.backgroundPlayerTickerText), title));
|
||||
R.string.background_player_time_text), title))
|
||||
.setContentIntent(PendingIntent.getActivity(getApplicationContext(),
|
||||
noteID, openDetailView,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT));
|
||||
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT < 21) {
|
||||
|
||||
NotificationCompat.Action playButton = new NotificationCompat.Action.Builder
|
||||
(R.drawable.ic_play_arrow_white_48dp,
|
||||
res.getString(R.string.play), playPI).build();
|
||||
res.getString(R.string.play_btn_text), playPI).build();
|
||||
|
||||
noteBuilder
|
||||
.setContentTitle(title)
|
||||
//really? Id like to put something more helpful here.
|
||||
//was more of a placeholder than anything else. -medavox
|
||||
//.setContentText("NewPipe is playing in the background")
|
||||
.setContentText(channelName)
|
||||
//.setAutoCancel(!mediaPlayer.isPlaying())
|
||||
@@ -303,6 +316,9 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
|
||||
//.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
//.setLargeIcon(cover)
|
||||
|
||||
//is wrapping this in an SDK version check really necessary,
|
||||
// if we're using NotificationCompat?
|
||||
// the compat libraries should handle this, right? -medavox
|
||||
if (android.os.Build.VERSION.SDK_INT >= 16)
|
||||
noteBuilder.setPriority(Notification.PRIORITY_LOW);
|
||||
|
||||
@@ -324,8 +340,10 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
|
||||
view.setOnClickPendingIntent(R.id.backgroundStop, stopPI);
|
||||
view.setOnClickPendingIntent(R.id.backgroundPlayPause, playPI);
|
||||
|
||||
//possibly found the expandedView problem,
|
||||
//but can't test it as I don't have a 5.0 device. -medavox
|
||||
RemoteViews expandedView =
|
||||
new RemoteViews(BuildConfig.APPLICATION_ID, R.layout.player_notification);
|
||||
new RemoteViews(BuildConfig.APPLICATION_ID, R.layout.player_notification_expanded);
|
||||
expandedView.setImageViewBitmap(R.id.backgroundCover, videoThumbnail);
|
||||
expandedView.setTextViewText(R.id.backgroundSongName, title);
|
||||
expandedView.setTextViewText(R.id.backgroundArtist, channelName);
|
||||
|
@@ -1,20 +1,29 @@
|
||||
package org.schabi.newpipe;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.app.DownloadManager;
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 21.09.15.
|
||||
@@ -51,9 +60,11 @@ public class DownloadDialog extends DialogFragment {
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
arguments = getArguments();
|
||||
super.onCreateDialog(savedInstanceState);
|
||||
if(ContextCompat.checkSelfPermission(this.getContext(),Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED)
|
||||
ActivityCompat.requestPermissions(getActivity(),new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},0);
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setTitle(R.string.downloadDialogTitle)
|
||||
.setItems(R.array.downloadOptions, new DialogInterface.OnClickListener() {
|
||||
builder.setTitle(R.string.download_dialog_title)
|
||||
.setItems(R.array.download_options, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Context context = getActivity();
|
||||
@@ -61,45 +72,83 @@ public class DownloadDialog extends DialogFragment {
|
||||
String suffix = "";
|
||||
String title = arguments.getString(TITLE);
|
||||
String url = "";
|
||||
String downloadFolder = "Download";
|
||||
File downloadDir = NewPipeSettings.getDownloadFolder();
|
||||
switch(which) {
|
||||
case 0: // Video
|
||||
suffix = arguments.getString(FILE_SUFFIX_VIDEO);
|
||||
url = arguments.getString(VIDEO_URL);
|
||||
downloadFolder = "Movies";
|
||||
downloadDir = NewPipeSettings.getVideoDownloadFolder(context);
|
||||
break;
|
||||
case 1:
|
||||
suffix = arguments.getString(FILE_SUFFIX_AUDIO);
|
||||
url = arguments.getString(AUDIO_URL);
|
||||
downloadFolder = "Music";
|
||||
downloadDir = NewPipeSettings.getAudioDownloadFolder(context);
|
||||
break;
|
||||
default:
|
||||
Log.d(TAG, "lolz");
|
||||
}
|
||||
//to avoid hard-coded string like "/storage/emulated/0/Movies"
|
||||
String downloadPath = prefs.getString(getString(R.string.downloadPathPreference),
|
||||
Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + downloadFolder);
|
||||
final File dir = new File(downloadPath);
|
||||
if(!dir.exists()) {
|
||||
boolean mkdir = dir.mkdir(); //attempt to create directory
|
||||
if(!mkdir && !dir.isDirectory()) {
|
||||
Log.e(TAG, "Cant' create directory named " + dir.toString());
|
||||
//TODO notify user "download directory should be changed" ?
|
||||
if(!downloadDir.exists()) {
|
||||
//attempt to create directory
|
||||
boolean mkdir = downloadDir.mkdirs();
|
||||
if(!mkdir && !downloadDir.isDirectory()) {
|
||||
String message = context.getString(R.string.err_dir_create,downloadDir.toString());
|
||||
Log.e(TAG, message);
|
||||
Toast.makeText(context,message , Toast.LENGTH_LONG).show();
|
||||
|
||||
return;
|
||||
}
|
||||
String message = context.getString(R.string.info_dir_created,downloadDir.toString());
|
||||
Log.e(TAG, message);
|
||||
Toast.makeText(context,message , Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
File saveFilePath = new File(downloadDir,createFileName(title) + suffix);
|
||||
|
||||
long id = 0;
|
||||
if (App.isUsingTor()) {
|
||||
// if using Tor, do not use DownloadManager because the proxy cannot be set
|
||||
FileDownloader.downloadFile(getContext(), url, saveFilePath, title);
|
||||
} else {
|
||||
DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
|
||||
DownloadManager.Request request = new DownloadManager.Request(
|
||||
Uri.parse(url));
|
||||
request.setDestinationUri(Uri.fromFile(saveFilePath));
|
||||
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
||||
|
||||
request.setTitle(title);
|
||||
request.setDescription("'" + url +
|
||||
"' => '" + saveFilePath + "'");
|
||||
request.allowScanningByMediaScanner();
|
||||
|
||||
try {
|
||||
id = dm.enqueue(request);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
|
||||
DownloadManager.Request request = new DownloadManager.Request(
|
||||
Uri.parse(url));
|
||||
request.setDestinationUri(Uri.fromFile(new File(dir + "/" + title + suffix)));
|
||||
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
||||
try {
|
||||
dm.enqueue(request);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Log.i(TAG,"Started downloading '" + url +
|
||||
"' => '" + saveFilePath + "' #" + id);
|
||||
}
|
||||
});
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* #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<String> ();
|
||||
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
|
||||
String nameToTest = fName;
|
||||
for (String pattern : forbiddenCharsPatterns) {
|
||||
nameToTest = nameToTest.replaceAll(pattern, "_");
|
||||
}
|
||||
return nameToTest;
|
||||
}
|
||||
}
|
||||
|
@@ -3,14 +3,17 @@ package org.schabi.newpipe;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
import info.guardianproject.netcipher.NetCipher;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 14.08.15.
|
||||
* Created by Christian Schabesberger on 28.01.16.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||
* Downloader.java is part of NewPipe.
|
||||
*
|
||||
* NewPipe is free software: you can redistribute it and/or modify
|
||||
@@ -27,70 +30,61 @@ import java.net.UnknownHostException;
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public class Downloader {
|
||||
|
||||
private static final String USER_AGENT = "Mozilla/5.0";
|
||||
public class Downloader implements org.schabi.newpipe.crawler.Downloader {
|
||||
|
||||
private static final String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0";
|
||||
|
||||
/**Download the text file at the supplied URL as in download(String),
|
||||
* but set the HTTP header field "Accept-Language" to the supplied string.
|
||||
* @param siteUrl the URL of the text file to return the contents of
|
||||
* @param language the language (usually a 2-character code) to set as the preferred language
|
||||
* @return the contents of the specified text file*/
|
||||
public static String download(String siteUrl, String language) {
|
||||
String ret = "";
|
||||
try {
|
||||
URL url = new URL(siteUrl);
|
||||
HttpURLConnection con = (HttpURLConnection) url.openConnection();
|
||||
con.setRequestProperty("Accept-Language", language);
|
||||
ret = dl(con);
|
||||
}
|
||||
catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return ret;
|
||||
public String download(String siteUrl, String language) throws IOException {
|
||||
URL url = new URL(siteUrl);
|
||||
//HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
|
||||
HttpsURLConnection con = NetCipher.getHttpsURLConnection(url);
|
||||
con.setRequestProperty("Accept-Language", language);
|
||||
return dl(con);
|
||||
}
|
||||
|
||||
/**Common functionality between download(String url) and download(String url, String language)*/
|
||||
private static String dl(HttpURLConnection con) throws IOException {
|
||||
private static String dl(HttpsURLConnection con) throws IOException {
|
||||
StringBuilder response = new StringBuilder();
|
||||
BufferedReader in = null;
|
||||
|
||||
try {
|
||||
con.setRequestMethod("GET");
|
||||
con.setRequestProperty("User-Agent", USER_AGENT);
|
||||
|
||||
BufferedReader in = new BufferedReader(
|
||||
in = new BufferedReader(
|
||||
new InputStreamReader(con.getInputStream()));
|
||||
String inputLine;
|
||||
|
||||
while((inputLine = in.readLine()) != null) {
|
||||
response.append(inputLine);
|
||||
}
|
||||
in.close();
|
||||
|
||||
}
|
||||
catch(UnknownHostException uhe) {//thrown when there's no internet connection
|
||||
uhe.printStackTrace();
|
||||
} catch(UnknownHostException uhe) {//thrown when there's no internet connection
|
||||
throw new IOException("unknown host or no network", uhe);
|
||||
//Toast.makeText(getActivity(), uhe.getMessage(), Toast.LENGTH_LONG).show();
|
||||
} catch(Exception e) {
|
||||
throw new IOException(e);
|
||||
} finally {
|
||||
if(in != null) {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
return response.toString();
|
||||
}
|
||||
|
||||
/**Download (via HTTP) the text file located at the supplied URL, and return its contents.
|
||||
* Primarily intended for downloading web pages.
|
||||
* @param siteUrl the URL of the text file to download
|
||||
* @return the contents of the specified text file*/
|
||||
public static String download(String siteUrl) {
|
||||
String ret = "";
|
||||
|
||||
try {
|
||||
URL url = new URL(siteUrl);
|
||||
HttpURLConnection con = (HttpURLConnection) url.openConnection();
|
||||
ret = dl(con);
|
||||
}
|
||||
catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return ret;
|
||||
/**Download (via HTTP) the text file located at the supplied URL, and return its contents.
|
||||
* Primarily intended for downloading web pages.
|
||||
* @param siteUrl the URL of the text file to download
|
||||
* @return the contents of the specified text file*/
|
||||
public String download(String siteUrl) throws IOException {
|
||||
URL url = new URL(siteUrl);
|
||||
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
|
||||
//HttpsURLConnection con = NetCipher.getHttpsURLConnection(url);
|
||||
return dl(con);
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,24 @@ import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* Copyright (C) Hans-Christoph Steiner 2016 <hans@eds.org>
|
||||
* ExitActivity.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 ExitActivity extends Activity {
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
|
169
app/src/main/java/org/schabi/newpipe/FileDownloader.java
Normal file
169
app/src/main/java/org/schabi/newpipe/FileDownloader.java
Normal file
@@ -0,0 +1,169 @@
|
||||
package org.schabi.newpipe;
|
||||
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
@@ -1,6 +1,5 @@
|
||||
package org.schabi.newpipe;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
@@ -15,6 +14,22 @@ import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Created by chschtsch on 12/29/15.
|
||||
*
|
||||
* Copyright (C) Gregory Arkhipov 2015
|
||||
* Localization.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 Localization {
|
||||
@@ -22,7 +37,8 @@ public class Localization {
|
||||
public static Locale getPreferredLocale(Context context) {
|
||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
String languageCode = sp.getString(String.valueOf(R.string.searchLanguage), "en");
|
||||
String languageCode = sp.getString(String.valueOf(R.string.search_language_key),
|
||||
context.getString(R.string.default_language_value));
|
||||
|
||||
if(languageCode.length() == 2) {
|
||||
return new Locale(languageCode);
|
||||
@@ -39,7 +55,7 @@ public class Localization {
|
||||
Locale locale = getPreferredLocale(context);
|
||||
|
||||
Resources res = context.getResources();
|
||||
String viewsString = res.getString(R.string.viewCountText);
|
||||
String viewsString = res.getString(R.string.view_count_text);
|
||||
|
||||
NumberFormat nf = NumberFormat.getInstance(locale);
|
||||
String formattedViewCount = nf.format(viewCount);
|
||||
@@ -69,7 +85,7 @@ public class Localization {
|
||||
|
||||
public static String localizeDate(String date, Context context) {
|
||||
Resources res = context.getResources();
|
||||
String dateString = res.getString(R.string.uploadDateText);
|
||||
String dateString = res.getString(R.string.upload_date_text);
|
||||
|
||||
String formattedDate = formatDate(date, context);
|
||||
return String.format(dateString, formattedDate);
|
||||
|
72
app/src/main/java/org/schabi/newpipe/NewPipeSettings.java
Normal file
72
app/src/main/java/org/schabi/newpipe/NewPipeSettings.java
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Created by k3b on 07.01.2016.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
||||
* NewPipeSettings.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/>.
|
||||
*/
|
||||
|
||||
package org.schabi.newpipe;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Environment;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Helper for global settings
|
||||
*/
|
||||
public class NewPipeSettings {
|
||||
public static void initSettings(Context context) {
|
||||
PreferenceManager.setDefaultValues(context, R.xml.settings, false);
|
||||
getVideoDownloadFolder(context);
|
||||
getAudioDownloadFolder(context);
|
||||
}
|
||||
|
||||
public static File getDownloadFolder() {
|
||||
return getFolder(Environment.DIRECTORY_DOWNLOADS);
|
||||
}
|
||||
|
||||
public static File getVideoDownloadFolder(Context context) {
|
||||
return getFolder(context, R.string.download_path_key, Environment.DIRECTORY_MOVIES);
|
||||
}
|
||||
|
||||
public static File getAudioDownloadFolder(Context context) {
|
||||
return getFolder(context, R.string.download_path_audio_key, Environment.DIRECTORY_MUSIC);
|
||||
}
|
||||
|
||||
private static File getFolder(Context context, int keyID, String defaultDirectoryName) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
final String key = context.getString(keyID);
|
||||
String downloadPath = prefs.getString(key, null);
|
||||
if ((downloadPath != null) && (!downloadPath.isEmpty())) return new File(downloadPath.trim());
|
||||
|
||||
final File folder = getFolder(defaultDirectoryName);
|
||||
SharedPreferences.Editor spEditor = prefs.edit();
|
||||
spEditor.putString(key
|
||||
, new File(folder,"NewPipe").getAbsolutePath());
|
||||
spEditor.apply();
|
||||
return folder;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static File getFolder(String defaultDirectoryName) {
|
||||
return new File(Environment.getExternalStorageDirectory(),defaultDirectoryName);
|
||||
}
|
||||
}
|
@@ -6,6 +6,25 @@ import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.media.AudioManager;
|
||||
|
||||
/**
|
||||
* Copyright (C) Hans-Christoph Steiner 2016 <hans@eds.org>
|
||||
* PanicResponderActivity.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 PanicResponderActivity extends Activity {
|
||||
|
||||
@@ -15,7 +34,6 @@ public class PanicResponderActivity extends Activity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Intent intent = getIntent();
|
||||
if (intent != null && PANIC_TRIGGER_ACTION.equals(intent.getAction())) {
|
||||
// TODO explicitly clear the search results once they are restored when the app restarts
|
||||
|
@@ -6,6 +6,7 @@ import android.content.SharedPreferences;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.media.MediaPlayer;
|
||||
import android.media.AudioManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@@ -78,7 +79,7 @@ public class PlayVideoActivity extends AppCompatActivity {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_play_video);
|
||||
|
||||
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
||||
isLandscape = checkIfLandscape();
|
||||
hasSoftKeys = checkIfHasSoftKeys();
|
||||
|
||||
@@ -129,7 +130,7 @@ public class PlayVideoActivity extends AppCompatActivity {
|
||||
public void onPrepared(MediaPlayer mp) {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
videoView.seekTo(position);
|
||||
if (position == 0) {
|
||||
if (position <= 0) {
|
||||
videoView.start();
|
||||
showUi();
|
||||
} else {
|
||||
@@ -187,6 +188,18 @@ public class PlayVideoActivity extends AppCompatActivity {
|
||||
videoView.pause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
App.checkStartTor(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
prefs = getPreferences(Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
@@ -199,7 +212,7 @@ public class PlayVideoActivity extends AppCompatActivity {
|
||||
intent.setAction(Intent.ACTION_SEND);
|
||||
intent.putExtra(Intent.EXTRA_TEXT, videoUrl);
|
||||
intent.setType("text/plain");
|
||||
startActivity(Intent.createChooser(intent, getString(R.string.shareDialogTitle)));
|
||||
startActivity(Intent.createChooser(intent, getString(R.string.share_dialog_title)));
|
||||
break;
|
||||
case R.id.menu_item_screen_rotation:
|
||||
toggleOrientation();
|
||||
|
@@ -1,9 +1,19 @@
|
||||
package org.schabi.newpipe;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.preference.CheckBoxPreference;
|
||||
import android.preference.EditTextPreference;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.preference.PreferenceFragment;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.LayoutRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.app.ActionBar;
|
||||
@@ -13,6 +23,8 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 31.08.15.
|
||||
*
|
||||
@@ -33,8 +45,9 @@ import android.view.ViewGroup;
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public class SettingsActivity extends PreferenceActivity {
|
||||
public class SettingsActivity extends PreferenceActivity {
|
||||
|
||||
private static final int REQUEST_INSTALL_ORBOT = 0x1234;
|
||||
private AppCompatDelegate mDelegate = null;
|
||||
|
||||
@Override
|
||||
@@ -51,12 +64,108 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
|
||||
}
|
||||
|
||||
public static class SettingsFragment extends PreferenceFragment {
|
||||
public static class SettingsFragment extends PreferenceFragment{
|
||||
SharedPreferences.OnSharedPreferenceChangeListener prefListener;
|
||||
|
||||
// get keys
|
||||
String DEFAULT_RESOLUTION_PREFERENCE;
|
||||
String DEFAULT_AUDIO_FORMAT_PREFERENCE;
|
||||
String SEARCH_LANGUAGE_PREFERENCE;
|
||||
String DOWNLOAD_PATH_PREFERENCE;
|
||||
String DOWNLOAD_PATH_AUDIO_PREFERENCE;
|
||||
String USE_TOR_KEY;
|
||||
|
||||
private ListPreference defaultResolutionPreference;
|
||||
private ListPreference defaultAudioFormatPreference;
|
||||
private ListPreference searchLanguagePreference;
|
||||
private EditTextPreference downloadPathPreference;
|
||||
private EditTextPreference downloadPathAudioPreference;
|
||||
private CheckBoxPreference useTorCheckBox;
|
||||
private SharedPreferences defaultPreferences;
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
addPreferencesFromResource(R.xml.settings_screen);
|
||||
addPreferencesFromResource(R.xml.settings);
|
||||
|
||||
final Activity activity = getActivity();
|
||||
|
||||
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||
|
||||
// get keys
|
||||
DEFAULT_RESOLUTION_PREFERENCE =getString(R.string.default_resolution_key);
|
||||
DEFAULT_AUDIO_FORMAT_PREFERENCE =getString(R.string.default_audio_format_key);
|
||||
SEARCH_LANGUAGE_PREFERENCE =getString(R.string.search_language_key);
|
||||
DOWNLOAD_PATH_PREFERENCE = getString(R.string.download_path_key);
|
||||
DOWNLOAD_PATH_AUDIO_PREFERENCE = getString(R.string.download_path_audio_key);
|
||||
USE_TOR_KEY = getString(R.string.use_tor_key);
|
||||
|
||||
// get pref objects
|
||||
defaultResolutionPreference =
|
||||
(ListPreference) findPreference(DEFAULT_RESOLUTION_PREFERENCE);
|
||||
defaultAudioFormatPreference =
|
||||
(ListPreference) findPreference(DEFAULT_AUDIO_FORMAT_PREFERENCE);
|
||||
searchLanguagePreference =
|
||||
(ListPreference) findPreference(SEARCH_LANGUAGE_PREFERENCE);
|
||||
downloadPathPreference =
|
||||
(EditTextPreference) findPreference(DOWNLOAD_PATH_PREFERENCE);
|
||||
downloadPathAudioPreference =
|
||||
(EditTextPreference) findPreference(DOWNLOAD_PATH_AUDIO_PREFERENCE);
|
||||
useTorCheckBox = (CheckBoxPreference) findPreference(USE_TOR_KEY);
|
||||
|
||||
prefListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
|
||||
String key) {
|
||||
Activity a = getActivity();
|
||||
updateSummary();
|
||||
|
||||
if (defaultPreferences.getBoolean(USE_TOR_KEY, false)) {
|
||||
if (OrbotHelper.isOrbotInstalled(a)) {
|
||||
App.configureTor(true);
|
||||
OrbotHelper.requestStartTor(a);
|
||||
} else {
|
||||
Intent intent = OrbotHelper.getOrbotInstallIntent(a);
|
||||
a.startActivityForResult(intent, REQUEST_INSTALL_ORBOT);
|
||||
}
|
||||
} else {
|
||||
App.configureTor(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
defaultPreferences.registerOnSharedPreferenceChangeListener(prefListener);
|
||||
|
||||
updateSummary();
|
||||
}
|
||||
|
||||
// This is used to show the status of some preference in the description
|
||||
private void updateSummary() {
|
||||
defaultResolutionPreference.setSummary(
|
||||
defaultPreferences.getString(DEFAULT_RESOLUTION_PREFERENCE,
|
||||
getString(R.string.default_resolution_value)));
|
||||
defaultAudioFormatPreference.setSummary(
|
||||
defaultPreferences.getString(DEFAULT_AUDIO_FORMAT_PREFERENCE,
|
||||
getString(R.string.default_audio_format_value)));
|
||||
searchLanguagePreference.setSummary(
|
||||
defaultPreferences.getString(SEARCH_LANGUAGE_PREFERENCE,
|
||||
getString(R.string.default_language_value)));
|
||||
downloadPathPreference.setSummary(
|
||||
defaultPreferences.getString(DOWNLOAD_PATH_PREFERENCE,
|
||||
getString(R.string.download_path_summary)));
|
||||
downloadPathAudioPreference.setSummary(
|
||||
defaultPreferences.getString(DOWNLOAD_PATH_AUDIO_PREFERENCE,
|
||||
getString(R.string.download_path_audio_summary)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
// try to start tor regardless of resultCode since clicking back after
|
||||
// installing the app does not necessarily return RESULT_OK
|
||||
App.configureTor(requestCode == REQUEST_INSTALL_ORBOT
|
||||
&& OrbotHelper.requestStartTor(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -144,4 +253,8 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void initSettings(Context context) {
|
||||
NewPipeSettings.initSettings(context);
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,10 @@ import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.schabi.newpipe.crawler.VideoPreviewInfo;
|
||||
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 24.10.15.
|
||||
*
|
||||
@@ -29,6 +33,8 @@ import android.widget.TextView;
|
||||
|
||||
class VideoInfoItemViewCreator {
|
||||
private final LayoutInflater inflater;
|
||||
private ImageLoader imageLoader = ImageLoader.getInstance();
|
||||
private DisplayImageOptions displayImageOptions = new DisplayImageOptions.Builder().cacheInMemory(true).build();
|
||||
|
||||
public VideoInfoItemViewCreator(LayoutInflater inflater) {
|
||||
this.inflater = inflater;
|
||||
@@ -44,6 +50,7 @@ class VideoInfoItemViewCreator {
|
||||
holder.itemUploaderView = (TextView) convertView.findViewById(R.id.itemUploaderView);
|
||||
holder.itemDurationView = (TextView) convertView.findViewById(R.id.itemDurationView);
|
||||
holder.itemUploadDateView = (TextView) convertView.findViewById(R.id.itemUploadDateView);
|
||||
holder.itemViewCountView = (TextView) convertView.findViewById(R.id.itemViewCountView);
|
||||
convertView.setTag(holder);
|
||||
} else {
|
||||
holder = (ViewHolder) convertView.getTag();
|
||||
@@ -57,18 +64,31 @@ class VideoInfoItemViewCreator {
|
||||
holder.itemVideoTitleView.setText(info.title);
|
||||
holder.itemUploaderView.setText(info.uploader);
|
||||
holder.itemDurationView.setText(info.duration);
|
||||
holder.itemViewCountView.setText(shortViewCount(info.view_count));
|
||||
if(!info.upload_date.isEmpty()) {
|
||||
holder.itemUploadDateView.setText(info.upload_date);
|
||||
} else {
|
||||
holder.itemUploadDateView.setText(Localization.localizeViewCount(info.view_count, context));
|
||||
holder.itemUploadDateView.setText(info.upload_date+" • ");
|
||||
}
|
||||
|
||||
imageLoader.displayImage(info.thumbnail_url, holder.itemThumbnailView, displayImageOptions);
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
private class ViewHolder {
|
||||
public ImageView itemThumbnailView;
|
||||
public TextView itemVideoTitleView, itemUploaderView, itemDurationView, itemUploadDateView;
|
||||
public TextView itemVideoTitleView, itemUploaderView, itemDurationView, itemUploadDateView, itemViewCountView;
|
||||
}
|
||||
|
||||
private String shortViewCount(Long view_count){
|
||||
if(view_count >= 1000000000){
|
||||
return Long.toString(view_count/1000000000)+"B views";
|
||||
}else if(view_count>=1000000){
|
||||
return Long.toString(view_count/1000000)+"M views";
|
||||
}else if(view_count>=1000){
|
||||
return Long.toString(view_count/1000)+"K views";
|
||||
}else {
|
||||
return Long.toString(view_count)+" views";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package org.schabi.newpipe;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.NavUtils;
|
||||
@@ -10,8 +11,8 @@ import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.schabi.newpipe.services.ServiceList;
|
||||
import org.schabi.newpipe.services.StreamingService;
|
||||
import org.schabi.newpipe.crawler.ServiceList;
|
||||
import org.schabi.newpipe.crawler.StreamingService;
|
||||
|
||||
|
||||
/**
|
||||
@@ -44,7 +45,7 @@ public class VideoItemDetailActivity extends AppCompatActivity {
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_videoitem_detail);
|
||||
|
||||
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
||||
// Show the Up button in the action bar.
|
||||
try {
|
||||
//noinspection ConstantConditions
|
||||
@@ -70,9 +71,9 @@ public class VideoItemDetailActivity extends AppCompatActivity {
|
||||
if (getIntent().getData() != null) {
|
||||
videoUrl = getIntent().getData().toString();
|
||||
StreamingService[] serviceList = ServiceList.getServices();
|
||||
//VideoExtractor videoExtractor = null;
|
||||
//StreamExtractor videoExtractor = null;
|
||||
for (int i = 0; i < serviceList.length; i++) {
|
||||
if (serviceList[i].acceptUrl(videoUrl)) {
|
||||
if (serviceList[i].getUrlIdHandler().acceptUrl(videoUrl)) {
|
||||
arguments.putInt(VideoItemDetailFragment.STREAMING_SERVICE, i);
|
||||
currentStreamingService = i;
|
||||
//videoExtractor = ServiceList.getService(i).getExtractorInstance();
|
||||
@@ -80,7 +81,7 @@ public class VideoItemDetailActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
if(currentStreamingService == -1) {
|
||||
Toast.makeText(this, R.string.urlNotSupportedText, Toast.LENGTH_LONG)
|
||||
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
//arguments.putString(VideoItemDetailFragment.VIDEO_URL,
|
||||
@@ -89,7 +90,7 @@ public class VideoItemDetailActivity extends AppCompatActivity {
|
||||
|
||||
arguments.putBoolean(VideoItemDetailFragment.AUTO_PLAY,
|
||||
PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.getBoolean(getString(R.string.autoPlayThroughIntent), false));
|
||||
.getBoolean(getString(R.string.autoplay_through_intent_key), false));
|
||||
} else {
|
||||
videoUrl = getIntent().getStringExtra(VideoItemDetailFragment.VIDEO_URL);
|
||||
currentStreamingService = getIntent().getIntExtra(VideoItemDetailFragment.STREAMING_SERVICE, -1);
|
||||
@@ -113,6 +114,12 @@ public class VideoItemDetailActivity extends AppCompatActivity {
|
||||
.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
App.checkStartTor(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
outState.putString(VideoItemDetailFragment.VIDEO_URL, videoUrl);
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@ package org.schabi.newpipe;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.NavUtils;
|
||||
@@ -16,7 +17,8 @@ import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.schabi.newpipe.services.ServiceList;
|
||||
import org.schabi.newpipe.crawler.VideoPreviewInfo;
|
||||
import org.schabi.newpipe.crawler.ServiceList;
|
||||
|
||||
/**
|
||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
||||
@@ -112,7 +114,7 @@ public class VideoItemListActivity extends AppCompatActivity
|
||||
//------ todo: remove this line when multiservice support is implemented ------
|
||||
currentStreamingServiceId = ServiceList.getIdOfService("Youtube");
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//to solve issue 38
|
||||
listFragment = (VideoItemListFragment) getSupportFragmentManager()
|
||||
.findFragmentById(R.id.videoitem_list);
|
||||
listFragment.setStreamingService(ServiceList.getService(currentStreamingServiceId));
|
||||
@@ -172,7 +174,13 @@ public class VideoItemListActivity extends AppCompatActivity
|
||||
}
|
||||
}
|
||||
|
||||
PreferenceManager.setDefaultValues(this, R.xml.settings_screen, false);
|
||||
PreferenceManager.setDefaultValues(this, R.xml.settings, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
App.checkStartTor(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -15,12 +15,15 @@ import android.widget.AbsListView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
import org.schabi.newpipe.services.SearchEngine;
|
||||
import org.schabi.newpipe.services.StreamingService;
|
||||
import org.schabi.newpipe.crawler.CrawlingException;
|
||||
import org.schabi.newpipe.crawler.VideoPreviewInfo;
|
||||
import org.schabi.newpipe.crawler.SearchEngine;
|
||||
import org.schabi.newpipe.crawler.StreamingService;
|
||||
|
||||
|
||||
/**
|
||||
@@ -58,14 +61,15 @@ public class VideoItemListFragment extends ListFragment {
|
||||
|
||||
private Thread searchThread = null;
|
||||
private SearchRunnable searchRunnable = null;
|
||||
private Thread loadThumbsThread = null;
|
||||
private LoadThumbsRunnable loadThumbsRunnable = null;
|
||||
// used to track down if results posted by threads ar still valid
|
||||
private int currentRequestId = -1;
|
||||
private ListView list;
|
||||
|
||||
private View footer;
|
||||
|
||||
// used to suppress request for loading a new page while another page is already loading.
|
||||
private boolean loadingNextPage = true;
|
||||
|
||||
private class ResultRunnable implements Runnable {
|
||||
private final SearchEngine.Result result;
|
||||
private final int requestId;
|
||||
@@ -76,6 +80,9 @@ public class VideoItemListFragment extends ListFragment {
|
||||
@Override
|
||||
public void run() {
|
||||
updateListOnResult(result, requestId);
|
||||
if (android.os.Build.VERSION.SDK_INT >= 19) {
|
||||
getListView().removeFooterView(footer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +91,7 @@ public class VideoItemListFragment extends ListFragment {
|
||||
private final String query;
|
||||
private final int page;
|
||||
final Handler h = new Handler();
|
||||
private volatile boolean run = true;
|
||||
private volatile boolean runs = true;
|
||||
private final int requestId;
|
||||
public SearchRunnable(SearchEngine engine, String query, int page, int requestId) {
|
||||
this.engine = engine;
|
||||
@@ -93,40 +100,36 @@ public class VideoItemListFragment extends ListFragment {
|
||||
this.requestId = requestId;
|
||||
}
|
||||
void terminate() {
|
||||
run = false;
|
||||
runs = false;
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
String searchLanguageKey = getContext().getString(R.string.searchLanguage);
|
||||
String searchLanguage = sp.getString(searchLanguageKey, "en");
|
||||
SearchEngine.Result result = engine.search(query, page, searchLanguage);
|
||||
String searchLanguageKey = getContext().getString(R.string.search_language_key);
|
||||
String searchLanguage = sp.getString(searchLanguageKey,
|
||||
getString(R.string.default_language_value));
|
||||
SearchEngine.Result result = engine.search(query, page, searchLanguage,
|
||||
new Downloader());
|
||||
|
||||
Log.i(TAG, "language code passed:\""+searchLanguage+"\"");
|
||||
if(run) {
|
||||
if(runs) {
|
||||
h.post(new ResultRunnable(result, requestId));
|
||||
}
|
||||
} catch(Exception e) {
|
||||
} catch(IOException e) {
|
||||
postNewErrorToast(h, R.string.network_error);
|
||||
e.printStackTrace();
|
||||
} catch(CrawlingException ce) {
|
||||
postNewErrorToast(h, R.string.parsing_error);
|
||||
ce.printStackTrace();
|
||||
} catch(Exception e) {
|
||||
postNewErrorToast(h, R.string.general_error);
|
||||
e.printStackTrace();
|
||||
h.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(getActivity(), "Network Error", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (android.os.Build.VERSION.SDK_INT >= 19) {
|
||||
getListView().removeFooterView(footer);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
<<<
|
||||
private class LoadThumbsRunnable implements Runnable {
|
||||
private final Vector<String> thumbnailUrlList = new Vector<>();
|
||||
private final Vector<Boolean> downloadedList;
|
||||
@@ -153,6 +156,7 @@ public class VideoItemListFragment extends ListFragment {
|
||||
if(!downloadedList.get(i)) {
|
||||
Bitmap thumbnail;
|
||||
try {
|
||||
//todo: make bitmaps not bypass tor
|
||||
thumbnail = BitmapFactory.decodeStream(
|
||||
new URL(thumbnailUrlList.get(i)).openConnection().getInputStream());
|
||||
h.post(new SetThumbnailRunnable(i, thumbnail, requestId));
|
||||
@@ -182,6 +186,9 @@ public class VideoItemListFragment extends ListFragment {
|
||||
}
|
||||
}
|
||||
|
||||
=======
|
||||
>>>>>>> 6d1b4652fc98e5c2d5e19b0f98ba38a731137a70
|
||||
*/
|
||||
public void present(List<VideoPreviewInfo> videoList) {
|
||||
mode = PRESENT_VIDEOS_MODE;
|
||||
setListShown(true);
|
||||
@@ -203,8 +210,9 @@ public class VideoItemListFragment extends ListFragment {
|
||||
}
|
||||
|
||||
private void nextPage() {
|
||||
loadingNextPage = true;
|
||||
lastPage++;
|
||||
Log.d(TAG, getString(R.string.searchPage) + Integer.toString(lastPage));
|
||||
Log.d(TAG, getString(R.string.search_page) + Integer.toString(lastPage));
|
||||
startSearch(query, lastPage);
|
||||
}
|
||||
|
||||
@@ -228,7 +236,7 @@ public class VideoItemListFragment extends ListFragment {
|
||||
Toast.makeText(getActivity(), result.errorMessage, Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
if (!result.suggestion.isEmpty()) {
|
||||
Toast.makeText(getActivity(), getString(R.string.didYouMean) + result.suggestion + " ?",
|
||||
Toast.makeText(getActivity(), getString(R.string.did_you_mean) + result.suggestion + " ?",
|
||||
Toast.LENGTH_LONG).show();
|
||||
}
|
||||
updateList(result.resultList);
|
||||
@@ -240,26 +248,16 @@ public class VideoItemListFragment extends ListFragment {
|
||||
try {
|
||||
videoListAdapter.addVideoList(list);
|
||||
terminateThreads();
|
||||
loadThumbsRunnable = new LoadThumbsRunnable(videoListAdapter.getVideoList(),
|
||||
videoListAdapter.getDownloadedThumbnailList(), currentRequestId);
|
||||
loadThumbsThread = new Thread(loadThumbsRunnable);
|
||||
loadThumbsThread.start();
|
||||
} catch(java.lang.IllegalStateException e) {
|
||||
Log.w(TAG, "Trying to set value while activity doesn't exist anymore.");
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
loadingNextPage = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void terminateThreads() {
|
||||
if(loadThumbsRunnable != null && loadThumbsRunnable.isRunning()) {
|
||||
loadThumbsRunnable.terminate();
|
||||
try {
|
||||
loadThumbsThread.join();
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if(searchThread != null) {
|
||||
searchRunnable.terminate();
|
||||
// No need to join, since we don't really terminate the thread. We just demand
|
||||
@@ -297,7 +295,8 @@ public class VideoItemListFragment extends ListFragment {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
list = getListView();
|
||||
videoListAdapter = new VideoListAdapter(getActivity(), this);
|
||||
footer = ((LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.paginate_footer, null, false);
|
||||
footer = ((LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE))
|
||||
.inflate(R.layout.paginate_footer, null, false);
|
||||
|
||||
|
||||
setListAdapter(videoListAdapter);
|
||||
@@ -318,13 +317,15 @@ public class VideoItemListFragment extends ListFragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
|
||||
public void onScroll(AbsListView view, int firstVisibleItem,
|
||||
int visibleItemCount, int totalItemCount) {
|
||||
if (mode != PRESENT_VIDEOS_MODE
|
||||
&& list.getChildAt(0) != null
|
||||
&& list.getLastVisiblePosition() == list.getAdapter().getCount() - 1
|
||||
&& list.getChildAt(list.getChildCount() - 1).getBottom() <= list.getHeight()) {
|
||||
long time = System.currentTimeMillis();
|
||||
if ((time - lastScrollDate) > 200) {
|
||||
if ((time - lastScrollDate) > 200
|
||||
&& !loadingNextPage) {
|
||||
lastScrollDate = time;
|
||||
getListView().addFooterView(footer);
|
||||
nextPage();
|
||||
@@ -376,4 +377,14 @@ public class VideoItemListFragment extends ListFragment {
|
||||
mActivatedPosition = position;
|
||||
}
|
||||
|
||||
private void postNewErrorToast(Handler h, final int stringResource) {
|
||||
h.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setListShown(true);
|
||||
Toast.makeText(getActivity(), getString(R.string.network_error),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package org.schabi.newpipe;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -9,6 +8,8 @@ import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ListView;
|
||||
|
||||
import org.schabi.newpipe.crawler.VideoPreviewInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
@@ -36,7 +37,6 @@ class VideoListAdapter extends BaseAdapter {
|
||||
private final Context context;
|
||||
private final VideoInfoItemViewCreator viewCreator;
|
||||
private Vector<VideoPreviewInfo> videoList = new Vector<>();
|
||||
private Vector<Boolean> downloadedThumbnailList = new Vector<>();
|
||||
private final ListView listView;
|
||||
|
||||
public VideoListAdapter(Context context, VideoItemListFragment videoListFragment) {
|
||||
@@ -49,15 +49,11 @@ class VideoListAdapter extends BaseAdapter {
|
||||
|
||||
public void addVideoList(List<VideoPreviewInfo> videos) {
|
||||
videoList.addAll(videos);
|
||||
for(int i = 0; i < videos.size(); i++) {
|
||||
downloadedThumbnailList.add(false);
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void clearVideoList() {
|
||||
videoList = new Vector<>();
|
||||
downloadedThumbnailList = new Vector<>();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@@ -65,20 +61,6 @@ class VideoListAdapter extends BaseAdapter {
|
||||
return videoList;
|
||||
}
|
||||
|
||||
public void updateDownloadedThumbnailList(int index) {
|
||||
downloadedThumbnailList.set(index, true);
|
||||
}
|
||||
|
||||
public Vector<Boolean> getDownloadedThumbnailList() {
|
||||
return downloadedThumbnailList;
|
||||
}
|
||||
|
||||
public void setThumbnail(int index, Bitmap thumbnail) {
|
||||
videoList.get(index).thumbnail = thumbnail;
|
||||
downloadedThumbnailList.set(index, true);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return videoList.size();
|
||||
@@ -99,7 +81,7 @@ class VideoListAdapter extends BaseAdapter {
|
||||
convertView = viewCreator.getViewFromVideoInfoItem(convertView, parent, videoList.get(position), context);
|
||||
|
||||
if(listView.isItemChecked(position)) {
|
||||
convertView.setBackgroundColor(ContextCompat.getColor(context,R.color.primaryColorYoutube));
|
||||
convertView.setBackgroundColor(ContextCompat.getColor(context,R.color.light_youtube_primary_color));
|
||||
} else {
|
||||
convertView.setBackgroundColor(0);
|
||||
}
|
||||
|
@@ -0,0 +1,33 @@
|
||||
package org.schabi.newpipe.crawler;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
/**
|
||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
||||
* AbstractVideoInfo.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/>.
|
||||
*/
|
||||
|
||||
/**Common properties between VideoInfo and VideoPreviewInfo.*/
|
||||
public abstract class AbstractVideoInfo {
|
||||
public String id = "";
|
||||
public String title = "";
|
||||
public String uploader = "";
|
||||
public String thumbnail_url = "";
|
||||
public Bitmap thumbnail = null;
|
||||
public String webpage_url = "";
|
||||
public String upload_date = "";
|
||||
public long view_count = -1;
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
package org.schabi.newpipe.crawler;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 30.01.16.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||
* CrawlingException.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 CrawlingException extends Exception {
|
||||
public CrawlingException() {}
|
||||
|
||||
public CrawlingException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CrawlingException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public CrawlingException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
105
app/src/main/java/org/schabi/newpipe/crawler/DashMpdParser.java
Normal file
105
app/src/main/java/org/schabi/newpipe/crawler/DashMpdParser.java
Normal file
@@ -0,0 +1,105 @@
|
||||
package org.schabi.newpipe.crawler;
|
||||
|
||||
import android.util.Xml;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 02.02.16.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||
* DashMpdParser.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 DashMpdParser {
|
||||
|
||||
static class DashMpdParsingException extends ParsingException {
|
||||
DashMpdParsingException(String message, Exception e) {
|
||||
super(message, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<VideoInfo.AudioStream> getAudioStreams(String dashManifestUrl,
|
||||
Downloader downloader)
|
||||
throws DashMpdParsingException {
|
||||
String dashDoc;
|
||||
try {
|
||||
dashDoc = downloader.download(dashManifestUrl);
|
||||
} catch(IOException ioe) {
|
||||
throw new DashMpdParsingException("Could not get dash mpd: " + dashManifestUrl, ioe);
|
||||
}
|
||||
Vector<VideoInfo.AudioStream> audioStreams = new Vector<>();
|
||||
try {
|
||||
XmlPullParser parser = Xml.newPullParser();
|
||||
parser.setInput(new StringReader(dashDoc));
|
||||
String tagName = "";
|
||||
String currentMimeType = "";
|
||||
int currentBandwidth = -1;
|
||||
int currentSamplingRate = -1;
|
||||
boolean currentTagIsBaseUrl = false;
|
||||
for(int eventType = parser.getEventType();
|
||||
eventType != XmlPullParser.END_DOCUMENT;
|
||||
eventType = parser.next() ) {
|
||||
switch(eventType) {
|
||||
case XmlPullParser.START_TAG:
|
||||
tagName = parser.getName();
|
||||
if(tagName.equals("AdaptationSet")) {
|
||||
currentMimeType = parser.getAttributeValue(XmlPullParser.NO_NAMESPACE, "mimeType");
|
||||
} else if(tagName.equals("Representation") && currentMimeType.contains("audio")) {
|
||||
currentBandwidth = Integer.parseInt(
|
||||
parser.getAttributeValue(XmlPullParser.NO_NAMESPACE, "bandwidth"));
|
||||
currentSamplingRate = Integer.parseInt(
|
||||
parser.getAttributeValue(XmlPullParser.NO_NAMESPACE, "audioSamplingRate"));
|
||||
} else if(tagName.equals("BaseURL")) {
|
||||
currentTagIsBaseUrl = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case XmlPullParser.TEXT:
|
||||
// actual stream tag
|
||||
if(currentTagIsBaseUrl &&
|
||||
(currentMimeType.contains("audio"))) {
|
||||
int format = -1;
|
||||
if(currentMimeType.equals(MediaFormat.WEBMA.mimeType)) {
|
||||
format = MediaFormat.WEBMA.id;
|
||||
} else if(currentMimeType.equals(MediaFormat.M4A.mimeType)) {
|
||||
format = MediaFormat.M4A.id;
|
||||
}
|
||||
audioStreams.add(new VideoInfo.AudioStream(parser.getText(),
|
||||
format, currentBandwidth, currentSamplingRate));
|
||||
}
|
||||
break;
|
||||
|
||||
case XmlPullParser.END_TAG:
|
||||
if(tagName.equals("AdaptationSet")) {
|
||||
currentMimeType = "";
|
||||
} else if(tagName.equals("BaseURL")) {
|
||||
currentTagIsBaseUrl = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch(Exception e) {
|
||||
throw new DashMpdParsingException("Could not parse Dash mpd", e);
|
||||
}
|
||||
return audioStreams;
|
||||
}
|
||||
}
|
41
app/src/main/java/org/schabi/newpipe/crawler/Downloader.java
Normal file
41
app/src/main/java/org/schabi/newpipe/crawler/Downloader.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package org.schabi.newpipe.crawler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 28.01.16.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||
* Downloader.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 interface Downloader {
|
||||
|
||||
/**Download the text file at the supplied URL as in download(String),
|
||||
* but set the HTTP header field "Accept-Language" to the supplied string.
|
||||
* @param siteUrl the URL of the text file to return the contents of
|
||||
* @param language the language (usually a 2-character code) to set as the preferred language
|
||||
* @return the contents of the specified text file
|
||||
* @throws IOException*/
|
||||
String download(String siteUrl, String language) throws IOException;
|
||||
|
||||
/**Download (via HTTP) the text file located at the supplied URL, and return its contents.
|
||||
* Primarily intended for downloading web pages.
|
||||
* @param siteUrl the URL of the text file to download
|
||||
* @return the contents of the specified text file
|
||||
* @throws IOException*/
|
||||
String download(String siteUrl) throws IOException;
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe;
|
||||
package org.schabi.newpipe.crawler;
|
||||
|
||||
/**
|
||||
* Created by Adam Howard on 08/11/15.
|
||||
@@ -6,7 +6,7 @@ package org.schabi.newpipe;
|
||||
* Copyright (c) Christian Schabesberger <chris.schabesberger@mailbox.org>
|
||||
* and Adam Howard <achdisposable1@gmail.com> 2015
|
||||
*
|
||||
* VideoListAdapter.java is part of NewPipe.
|
||||
* MediaFormat.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
|
||||
@@ -25,10 +25,12 @@ package org.schabi.newpipe;
|
||||
/**Static data about various media formats support by Newpipe, eg mime type, extension*/
|
||||
|
||||
public enum MediaFormat {
|
||||
//video and audio combined formats
|
||||
// id name suffix mime type
|
||||
MPEG_4 (0x0, "MPEG-4", "mp4", "video/mp4"),
|
||||
v3GPP (0x1, "3GPP", "3gp", "video/3gpp"),
|
||||
WEBM (0x2, "WebM", "webm", "video/webm"),
|
||||
// audio formats
|
||||
M4A (0x3, "m4a", "m4a", "audio/mp4"),
|
||||
WEBMA (0x4, "WebM", "webm", "audio/webm");
|
||||
|
60
app/src/main/java/org/schabi/newpipe/crawler/Parser.java
Normal file
60
app/src/main/java/org/schabi/newpipe/crawler/Parser.java
Normal file
@@ -0,0 +1,60 @@
|
||||
package org.schabi.newpipe.crawler;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 02.02.16.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||
* Parser.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/>.
|
||||
*/
|
||||
|
||||
/** avoid using regex !!! */
|
||||
public class Parser {
|
||||
|
||||
public static class RegexException extends ParsingException {
|
||||
public RegexException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static String matchGroup1(String pattern, String input) throws RegexException {
|
||||
Pattern pat = Pattern.compile(pattern);
|
||||
Matcher mat = pat.matcher(input);
|
||||
boolean foundMatch = mat.find();
|
||||
if (foundMatch) {
|
||||
return mat.group(1);
|
||||
}
|
||||
else {
|
||||
//Log.e(TAG, "failed to find pattern \""+pattern+"\" inside of \""+input+"\"");
|
||||
throw new RegexException("failed to find pattern \""+pattern+" inside of "+input+"\"");
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, String> compatParseMap(final String input) throws UnsupportedEncodingException {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
for(String arg : input.split("&")) {
|
||||
String[] split_arg = arg.split("=");
|
||||
map.put(split_arg[0], URLDecoder.decode(split_arg[1], "UTF-8"));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
package org.schabi.newpipe.crawler;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 31.01.16.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||
* ParsingException.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 ParsingException extends CrawlingException {
|
||||
public ParsingException() {}
|
||||
public ParsingException(String message) {
|
||||
super(message);
|
||||
}
|
||||
public ParsingException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
public ParsingException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user