Merge pull request #136 from eighthave/tor-support-for-all-except-streaming

Tor support for all except streaming
This commit is contained in:
Christian Schabesberger 2016-01-03 17:40:02 +01:00
commit 0da1aef763
11 changed files with 250 additions and 15 deletions

View file

@ -17,7 +17,7 @@ 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,
@ -35,4 +35,5 @@ dependencies {
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'
}

View file

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

View file

@ -0,0 +1,46 @@
package org.schabi.newpipe;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import info.guardianproject.netcipher.NetCipher;
import info.guardianproject.netcipher.proxy.OrbotHelper;
public class App extends Application {
private static boolean useTor;
@Override
public void onCreate() {
super.onCreate();
// if Orbot is installed, then default to using Tor, the user can still override
if (OrbotHelper.requestStartTor(this)) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
configureTor(prefs.getBoolean(getString(R.string.useTor), true));
}
}
/**
* 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;
}
}

View file

@ -61,17 +61,17 @@ public class DownloadDialog extends DialogFragment {
String suffix = "";
String title = arguments.getString(TITLE);
String url = "";
String downloadFolder = "Download";
String downloadFolder = Environment.DIRECTORY_DOWNLOADS;
switch(which) {
case 0: // Video
suffix = arguments.getString(FILE_SUFFIX_VIDEO);
url = arguments.getString(VIDEO_URL);
downloadFolder = "Movies";
downloadFolder = Environment.DIRECTORY_MOVIES;
break;
case 1:
suffix = arguments.getString(FILE_SUFFIX_AUDIO);
url = arguments.getString(AUDIO_URL);
downloadFolder = "Music";
downloadFolder = Environment.DIRECTORY_MUSIC;
break;
default:
Log.d(TAG, "lolz");
@ -87,15 +87,21 @@ public class DownloadDialog extends DialogFragment {
//TODO notify user "download directory should be changed" ?
}
}
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();
String saveFilePath = dir + "/" + title + suffix;
if (App.isUsingTor()) {
// if using Tor, do not use DownloadManager because the proxy cannot be set
Downloader.downloadFile(getContext(), url, saveFilePath);
} else {
DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Request request = new DownloadManager.Request(
Uri.parse(url));
request.setDestinationUri(Uri.fromFile(new File(saveFilePath)));
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
try {
dm.enqueue(request);
} catch (Exception e) {
e.printStackTrace();
}
}
}
});

View file

@ -1,12 +1,30 @@
package org.schabi.newpipe;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import 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.MalformedURLException;
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.
*
@ -28,6 +46,7 @@ import java.net.UnknownHostException;
*/
public class Downloader {
public static final String TAG = "Downloader";
private static final String USER_AGENT = "Mozilla/5.0";
@ -40,7 +59,7 @@ public class Downloader {
String ret = "";
try {
URL url = new URL(siteUrl);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
HttpsURLConnection con = NetCipher.getHttpsURLConnection(url);
con.setRequestProperty("Accept-Language", language);
ret = dl(con);
}
@ -84,7 +103,7 @@ public class Downloader {
try {
URL url = new URL(siteUrl);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
HttpsURLConnection con = NetCipher.getHttpsURLConnection(url);
ret = dl(con);
}
catch(Exception e) {
@ -93,4 +112,92 @@ public class Downloader {
return ret;
}
/**
* 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
* @throws IOException
*/
public static void downloadFile(final Context context, final String fileURL, final String saveFilePath) {
new AsyncTask<Void, Integer, Void>() {
private NotificationManager nm;
private NotificationCompat.Builder builder;
private int notifyId = 0x1234;
private int fileSize = 0xffffffff;
@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.substring(saveFilePath.lastIndexOf('/') + 1))
.setContentText(saveFilePath)
.setProgress(fileSize, 0, false);
nm.notify(notifyId, builder.build());
}
@Override
protected Void doInBackground(Void... voids) {
HttpsURLConnection con = 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 inputStream = new BufferedInputStream(con.getInputStream());
FileOutputStream 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);
}
}
outputStream.close();
inputStream.close();
publishProgress(bufferSize);
} else {
Log.i(TAG, "No file to download. Server replied HTTP code: " + responseCode);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (con != null) {
con.disconnect();
con = null;
}
}
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);
}
}.execute();
}
}

View file

@ -187,6 +187,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();

View file

@ -1,7 +1,12 @@
package org.schabi.newpipe;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;
import android.support.annotation.LayoutRes;
@ -13,6 +18,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.
*
@ -35,6 +42,7 @@ import android.view.ViewGroup;
public class SettingsActivity extends PreferenceActivity {
private static final int REQUEST_INSTALL_ORBOT = 0x1234;
private AppCompatDelegate mDelegate = null;
@Override
@ -52,13 +60,48 @@ public class SettingsActivity extends PreferenceActivity {
}
public static class SettingsFragment extends PreferenceFragment {
private CheckBoxPreference useTorCheckBox;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.settings_screen);
// if Orbot is installed, then default to using Tor, the user can still override
useTorCheckBox = (CheckBoxPreference) findPreference(getString(R.string.useTor));
final Activity activity = getActivity();
final boolean useTor = OrbotHelper.isOrbotInstalled(activity);
useTorCheckBox.setDefaultValue(useTor);
useTorCheckBox.setChecked(useTor);
useTorCheckBox.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object o) {
boolean useTor = (Boolean) o;
if (useTor) {
if (OrbotHelper.isOrbotInstalled(activity)) {
App.configureTor(true);
} else {
Intent intent = OrbotHelper.getOrbotInstallIntent(activity);
activity.startActivityForResult(intent, REQUEST_INSTALL_ORBOT);
}
} else {
App.configureTor(false);
}
return true;
}
});
}
}
@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
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);

View file

@ -113,6 +113,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);

View file

@ -175,6 +175,12 @@ public class VideoItemListActivity extends AppCompatActivity
PreferenceManager.setDefaultValues(this, R.xml.settings_screen, false);
}
@Override
public void onResume() {
super.onResume();
App.checkStartTor(this);
}
/**
* Callback method from {@link VideoItemListFragment.Callbacks}
* indicating that the item with the given ID was selected.

View file

@ -63,4 +63,6 @@
<string name="detailUploaderThumbnailViewDescription">Uploader thumbnail</string>
<string name="detailThumbsDownImgViewDescription">Dislikes</string>
<string name="detailThumbsUpImgViewDescription">Likes</string>
<string name="useTor">Use Tor</string>
<string name="useTorSummary">Force download traffic through Tor for increased privacy (streaming videos not yet supported)</string>
</resources>

View file

@ -72,5 +72,10 @@
android:summary="@string/autoPlayThroughIntentSummary"
android:defaultValue="false" />
<CheckBoxPreference
android:key="@string/useTor"
android:title="@string/useTor"
android:summary="@string/useTorSummary" />
</PreferenceCategory>
</PreferenceScreen>