Merge branch 'master' of github.com:theScrabi/NewPipe

This commit is contained in:
Adam Howard 2016-01-03 19:44:13 +00:00
commit 95b73f35f7
42 changed files with 916 additions and 340 deletions

1
.gitignore vendored
View File

@ -7,3 +7,4 @@
/app/app.iml /app/app.iml
/.idea /.idea
/*.iml /*.iml
gradle.properties

View File

@ -8,8 +8,8 @@ android {
applicationId "org.schabi.newpipe" applicationId "org.schabi.newpipe"
minSdkVersion 15 minSdkVersion 15
targetSdkVersion 23 targetSdkVersion 23
versionCode 9 versionCode 10
versionName "0.7.0" versionName "0.7.1"
} }
buildTypes { buildTypes {
release { release {
@ -17,7 +17,7 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
} }
lintOptions { lintOptions {
checkReleaseBuilds false checkReleaseBuilds false
// Or, if you prefer, you can continue to check for errors in release builds, // 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 'com.android.support:recyclerview-v7:23.1.1'
compile 'org.jsoup:jsoup:1.8.3' compile 'org.jsoup:jsoup:1.8.3'
compile 'org.mozilla:rhino:1.7.7' 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.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application <application
android:name=".App"
android:allowBackup="true" android:allowBackup="true"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:logo="@mipmap/ic_launcher" android:logo="@mipmap/ic_launcher"
@ -29,43 +30,46 @@
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".VideoItemListActivity" /> android:value=".VideoItemListActivity" />
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW"/> <action android:name="android.intent.action.VIEW" />
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
<data <data android:scheme="http" />
android:host="youtube.com" <data android:scheme="https" />
android:scheme="http" <data android:host="youtube.com" />
android:pathPattern="/?*#*/*watch"/> <data android:host="m.youtube.com" />
<data <data android:host="www.youtube.com" />
android:host="youtube.com" <data android:pathPrefix="/v/" />
android:scheme="https" <data android:pathPrefix="/watch" />
android:pathPattern="/?*#*/*watch"/> </intent-filter>
<data <intent-filter>
android:host="www.youtube.com" <action android:name="android.intent.action.VIEW" />
android:scheme="http" <action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
android:pathPattern="/?*#*/*watch"/> <action android:name="android.nfc.action.NDEF_DISCOVERED" />
<data
android:host="www.youtube.com" <category android:name="android.intent.category.DEFAULT" />
android:scheme="https" <category android:name="android.intent.category.BROWSABLE" />
android:pathPattern="/?*#*/*watch"/>
<data <data android:scheme="http" />
android:host="m.youtube.com" <data android:scheme="https" />
android:scheme="http" <data android:host="youtu.be" />
android:pathPattern="/?*#*/*watch"/> <data android:pathPrefix="/" />
<data </intent-filter>
android:host="m.youtube.com" <intent-filter>
android:scheme="https" <action android:name="android.intent.action.VIEW" />
android:pathPattern="/?*#*/*watch"/> <action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
<data <action android:name="android.nfc.action.NDEF_DISCOVERED" />
android:host="youtu.be"
android:scheme="https" <category android:name="android.intent.category.DEFAULT" />
android:pathPrefix="/"/> <category android:name="android.intent.category.BROWSABLE" />
<data
android:host="youtu.be" <data android:scheme="vnd.youtube" />
android:scheme="http" <data android:scheme="vnd.youtube.launch" />
android:pathPrefix="/"/>
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".PlayVideoActivity" <activity android:name=".PlayVideoActivity"
@ -74,15 +78,26 @@
android:parentActivityName=".VideoItemDetailActivity" android:parentActivityName=".VideoItemDetailActivity"
tools:ignore="UnusedAttribute"> tools:ignore="UnusedAttribute">
</activity> </activity>
<!--TODO: make label a translatable string -->
<service <service
android:name=".BackgroundPlayer" android:name=".BackgroundPlayer"
android:label="NewPipe Background Player" android:label="@string/background_player_name"
android:exported="false" > android:exported="false" />
</service>
<activity <activity
android:name=".SettingsActivity" android:name=".SettingsActivity"
android:label="@string/title_activity_settings" > android:label="@string/title_activity_settings" >
</activity> </activity>
<activity
android:name=".PanicResponderActivity"
android:launchMode="singleInstance"
android:noHistory="true"
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="info.guardianproject.panic.action.TRIGGER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".ExitActivity"
android:theme="@android:style/Theme.NoDisplay" />
</application> </application>
</manifest> </manifest>

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

@ -17,6 +17,7 @@ import android.os.IBinder;
import android.os.PowerManager; import android.os.PowerManager;
import android.support.v7.app.NotificationCompat; import android.support.v7.app.NotificationCompat;
import android.util.Log; import android.util.Log;
import android.widget.RemoteViews;
import android.widget.Toast; import android.widget.Toast;
import java.io.IOException; import java.io.IOException;
@ -113,9 +114,9 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
private int noteID = TAG.hashCode(); private int noteID = TAG.hashCode();
private BackgroundPlayer owner; private BackgroundPlayer owner;
private NotificationManager noteMgr; private NotificationManager noteMgr;
private NotificationCompat.Builder noteBuilder;
private WifiManager.WifiLock wifiLock; private WifiManager.WifiLock wifiLock;
private Bitmap videoThumbnail = null; private Bitmap videoThumbnail = null;
private NotificationCompat.Builder noteBuilder;
public PlayerThread(String src, String title, BackgroundPlayer owner) { public PlayerThread(String src, String title, BackgroundPlayer owner) {
this.source = src; this.source = src;
@ -124,10 +125,9 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
mediaPlayer = new MediaPlayer(); mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
} }
@Override @Override
public void run() { public void run() {
Resources res = getApplicationContext().getResources();
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);//cpu lock mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);//cpu lock
try { try {
mediaPlayer.setDataSource(source); mediaPlayer.setDataSource(source);
@ -174,54 +174,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
filter.addAction(ACTION_STOP); filter.addAction(ACTION_STOP);
registerReceiver(broadcastReceiver, filter); registerReceiver(broadcastReceiver, filter);
PendingIntent playPI = PendingIntent.getBroadcast(owner, noteID, Notification note = buildNotification();
new Intent(ACTION_PLAYPAUSE), PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Action playButton = new NotificationCompat.Action.Builder
(R.drawable.ic_play_arrow_white_48dp, "Play", playPI).build();
/*
NotificationCompat.Action pauseButton = new NotificationCompat.Action.Builder
(R.drawable.ic_pause_white_24dp, "Pause", playPI).build();
*/
PendingIntent stopPI = PendingIntent.getBroadcast(owner, noteID,
new Intent(ACTION_STOP), PendingIntent.FLAG_UPDATE_CURRENT);
noteBuilder = new NotificationCompat.Builder(owner);
noteBuilder
.setContentTitle(title)
//really? Id like to put something more helpful here.
//.setContentText("NewPipe is playing in the background")
.setContentText(channelName)
//.setAutoCancel(!mediaPlayer.isPlaying())
.setOngoing(true)
.setDeleteIntent(stopPI)
//doesn't fit with Notification.MediaStyle
//.setProgress(vidLength, 0, false)
.setSmallIcon(R.drawable.ic_play_circle_filled_white_24dp)
.setLargeIcon(videoThumbnail)
.setTicker(
String.format(res.getString(
R.string.backgroundPlayerTickerText), title))
.addAction(playButton);
//.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
//.setLargeIcon(cover)
if(android.os.Build.VERSION.SDK_INT >= 16)
noteBuilder.setPriority(Notification.PRIORITY_LOW);
if(android.os.Build.VERSION.SDK_INT >= 21)
noteBuilder.setCategory(Notification.CATEGORY_TRANSPORT);
noteBuilder.setStyle(new NotificationCompat.MediaStyle()
//.setMediaSession(mMediaSession.getSessionToken())
.setShowActionsInCompactView(new int[]{0})
.setShowCancelButton(true)
.setCancelButtonIntent(stopPI));
if(videoThumbnail != null) {
noteBuilder.setLargeIcon(videoThumbnail);
}
Notification note = noteBuilder.build();
Intent openDetailView = new Intent(getApplicationContext(), Intent openDetailView = new Intent(getApplicationContext(),
VideoItemDetailActivity.class); VideoItemDetailActivity.class);
@ -249,7 +202,6 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
Log.d(TAG, "sleep failure"); Log.d(TAG, "sleep failure");
} }
}*/ }*/
} }
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@ -303,5 +255,93 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
afterPlayCleanup(); afterPlayCleanup();
} }
} }
private Notification buildNotification() {
Notification note;
Resources res = getApplicationContext().getResources();
noteBuilder = new NotificationCompat.Builder(owner);
PendingIntent playPI = PendingIntent.getBroadcast(owner, noteID,
new Intent(ACTION_PLAYPAUSE), PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent stopPI = PendingIntent.getBroadcast(owner, noteID,
new Intent(ACTION_STOP), PendingIntent.FLAG_UPDATE_CURRENT);
/*
NotificationCompat.Action pauseButton = new NotificationCompat.Action.Builder
(R.drawable.ic_pause_white_24dp, "Pause", playPI).build();
*/
noteBuilder
.setOngoing(true)
.setDeleteIntent(stopPI)
//doesn't fit with Notification.MediaStyle
//.setProgress(vidLength, 0, false)
.setSmallIcon(R.drawable.ic_play_circle_filled_white_24dp)
.setTicker(
String.format(res.getString(
R.string.backgroundPlayerTickerText), title));
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();
noteBuilder
.setContentTitle(title)
//really? Id like to put something more helpful here.
//.setContentText("NewPipe is playing in the background")
.setContentText(channelName)
//.setAutoCancel(!mediaPlayer.isPlaying())
.setDeleteIntent(stopPI)
//doesn't fit with Notification.MediaStyle
//.setProgress(vidLength, 0, false)
.setLargeIcon(videoThumbnail)
.addAction(playButton);
//.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
//.setLargeIcon(cover)
if (android.os.Build.VERSION.SDK_INT >= 16)
noteBuilder.setPriority(Notification.PRIORITY_LOW);
noteBuilder.setStyle(new NotificationCompat.MediaStyle()
//.setMediaSession(mMediaSession.getSessionToken())
.setShowActionsInCompactView(new int[]{0})
.setShowCancelButton(true)
.setCancelButtonIntent(stopPI));
if (videoThumbnail != null) {
noteBuilder.setLargeIcon(videoThumbnail);
}
note = noteBuilder.build();
} else {
RemoteViews view =
new RemoteViews(BuildConfig.APPLICATION_ID, R.layout.player_notification);
view.setImageViewBitmap(R.id.backgroundCover, videoThumbnail);
view.setTextViewText(R.id.backgroundSongName, title);
view.setTextViewText(R.id.backgroundArtist, channelName);
view.setOnClickPendingIntent(R.id.backgroundStop, stopPI);
view.setOnClickPendingIntent(R.id.backgroundPlayPause, playPI);
RemoteViews expandedView =
new RemoteViews(BuildConfig.APPLICATION_ID, R.layout.player_notification);
expandedView.setImageViewBitmap(R.id.backgroundCover, videoThumbnail);
expandedView.setTextViewText(R.id.backgroundSongName, title);
expandedView.setTextViewText(R.id.backgroundArtist, channelName);
expandedView.setOnClickPendingIntent(R.id.backgroundStop, stopPI);
expandedView.setOnClickPendingIntent(R.id.backgroundPlayPause, playPI);
noteBuilder.setCategory(Notification.CATEGORY_TRANSPORT);
//Make notification appear on lockscreen
noteBuilder.setVisibility(Notification.VISIBILITY_PUBLIC);
note = noteBuilder.build();
note.contentView = view;
//todo: This never shows up. I was not able to figure out why:
note.bigContentView = expandedView;
}
return note;
}
} }
} }

View File

@ -57,26 +57,29 @@ public class DownloadDialog extends DialogFragment {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
Context context = getActivity(); Context context = getActivity();
SharedPreferences defaultPreferences = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
String suffix = ""; String suffix = "";
String title = arguments.getString(TITLE); String title = arguments.getString(TITLE);
String url = ""; String url = "";
String downloadFolder = Environment.DIRECTORY_DOWNLOADS;
switch(which) { switch(which) {
case 0: // Video case 0: // Video
suffix = arguments.getString(FILE_SUFFIX_VIDEO); suffix = arguments.getString(FILE_SUFFIX_VIDEO);
url = arguments.getString(VIDEO_URL); url = arguments.getString(VIDEO_URL);
downloadFolder = Environment.DIRECTORY_MOVIES;
break; break;
case 1: case 1:
suffix = arguments.getString(FILE_SUFFIX_AUDIO); suffix = arguments.getString(FILE_SUFFIX_AUDIO);
url = arguments.getString(AUDIO_URL); url = arguments.getString(AUDIO_URL);
downloadFolder = Environment.DIRECTORY_MUSIC;
break; break;
default: default:
Log.d(TAG, "lolz"); Log.d(TAG, "lolz");
} }
//to avoid hard-coded string like "/storage/emulated/0/NewPipe" //to avoid hard-coded string like "/storage/emulated/0/Movies"
final File dir = new File(defaultPreferences.getString( String downloadPath = prefs.getString(getString(R.string.downloadPathPreference),
"download_path_preference", Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + downloadFolder);
Environment.getExternalStorageDirectory().getAbsolutePath() + "/NewPipe")); final File dir = new File(downloadPath);
if(!dir.exists()) { if(!dir.exists()) {
boolean mkdir = dir.mkdir(); //attempt to create directory boolean mkdir = dir.mkdir(); //attempt to create directory
if(!mkdir && !dir.isDirectory()) { if(!mkdir && !dir.isDirectory()) {
@ -84,17 +87,21 @@ public class DownloadDialog extends DialogFragment {
//TODO notify user "download directory should be changed" ? //TODO notify user "download directory should be changed" ?
} }
} }
DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); String saveFilePath = dir + "/" + title + suffix;
DownloadManager.Request request = new DownloadManager.Request( if (App.isUsingTor()) {
Uri.parse(url)); // if using Tor, do not use DownloadManager because the proxy cannot be set
request.setDestinationUri(Uri.fromFile(new File( Downloader.downloadFile(getContext(), url, saveFilePath);
defaultPreferences.getString("download_path_preference", "/storage/emulated/0/NewPipe") } else {
+ "/" + title + suffix))); DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); DownloadManager.Request request = new DownloadManager.Request(
try { Uri.parse(url));
dm.enqueue(request); request.setDestinationUri(Uri.fromFile(new File(saveFilePath)));
} catch (Exception e) { request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
e.printStackTrace(); try {
dm.enqueue(request);
} catch (Exception e) {
e.printStackTrace();
}
} }
} }
}); });

View File

@ -1,12 +1,30 @@
package org.schabi.newpipe; 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.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.net.UnknownHostException; 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 14.08.15.
* *
@ -28,6 +46,7 @@ import java.net.UnknownHostException;
*/ */
public class Downloader { public class Downloader {
public static final String TAG = "Downloader";
private static final String USER_AGENT = "Mozilla/5.0"; private static final String USER_AGENT = "Mozilla/5.0";
@ -40,7 +59,7 @@ public class Downloader {
String ret = ""; String ret = "";
try { try {
URL url = new URL(siteUrl); URL url = new URL(siteUrl);
HttpURLConnection con = (HttpURLConnection) url.openConnection(); HttpsURLConnection con = NetCipher.getHttpsURLConnection(url);
con.setRequestProperty("Accept-Language", language); con.setRequestProperty("Accept-Language", language);
ret = dl(con); ret = dl(con);
} }
@ -84,7 +103,7 @@ public class Downloader {
try { try {
URL url = new URL(siteUrl); URL url = new URL(siteUrl);
HttpURLConnection con = (HttpURLConnection) url.openConnection(); HttpsURLConnection con = NetCipher.getHttpsURLConnection(url);
ret = dl(con); ret = dl(con);
} }
catch(Exception e) { catch(Exception e) {
@ -93,4 +112,92 @@ public class Downloader {
return ret; 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

@ -0,0 +1,36 @@
package org.schabi.newpipe;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
public class ExitActivity extends Activity {
@SuppressLint("NewApi")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= 21) {
finishAndRemoveTask();
} else {
finish();
}
System.exit(0);
}
public static void exitAndRemoveFromRecentApps(Activity activity) {
Intent intent = new Intent(activity, ExitActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
| Intent.FLAG_ACTIVITY_CLEAR_TASK
| Intent.FLAG_ACTIVITY_NO_ANIMATION);
activity.startActivity(intent);
}
}

View File

@ -0,0 +1,77 @@
package org.schabi.newpipe;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.preference.PreferenceManager;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
* Created by chschtsch on 12/29/15.
*/
public class Localization {
public static Locale getPreferredLocale(Context context) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
String languageCode = sp.getString(String.valueOf(R.string.searchLanguage), "en");
if(languageCode.length() == 2) {
return new Locale(languageCode);
}
else if(languageCode.contains("_")) {
String country = languageCode
.substring(languageCode.indexOf("_"), languageCode.length());
return new Locale(languageCode.substring(0, 2), country);
}
return Locale.getDefault();
}
public static String localizeViewCount(long viewCount, Context context) {
Locale locale = getPreferredLocale(context);
Resources res = context.getResources();
String viewsString = res.getString(R.string.viewCountText);
NumberFormat nf = NumberFormat.getInstance(locale);
String formattedViewCount = nf.format(viewCount);
return String.format(viewsString, formattedViewCount);
}
public static String localizeNumber(long number, Context context) {
Locale locale = getPreferredLocale(context);
NumberFormat nf = NumberFormat.getInstance(locale);
return nf.format(number);
}
private static String formatDate(String date, Context context) {
Locale locale = getPreferredLocale(context);
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
Date datum = null;
try {
datum = formatter.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
return df.format(datum);
}
public static String localizeDate(String date, Context context) {
Resources res = context.getResources();
String dateString = res.getString(R.string.uploadDateText);
String formattedDate = formatDate(date, context);
return String.format(dateString, formattedDate);
}
}

View File

@ -0,0 +1,32 @@
package org.schabi.newpipe;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
public class PanicResponderActivity extends Activity {
public static final String PANIC_TRIGGER_ACTION = "info.guardianproject.panic.action.TRIGGER";
@SuppressLint("NewApi")
@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
// or if the app reloads the current video after being killed, that should be cleared also
ExitActivity.exitAndRemoveFromRecentApps(this);
}
if (Build.VERSION.SDK_INT >= 21) {
finishAndRemoveTask();
} else {
finish();
}
}
}

View File

@ -187,6 +187,18 @@ public class PlayVideoActivity extends AppCompatActivity {
videoView.pause(); videoView.pause();
} }
@Override
public void onResume() {
super.onResume();
App.checkStartTor(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
prefs = getPreferences(Context.MODE_PRIVATE);
}
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId(); int id = item.getItemId();

View File

@ -1,13 +1,14 @@
package org.schabi.newpipe; package org.schabi.newpipe;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment; import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity; import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment; import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.support.annotation.LayoutRes; import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBar;
@ -17,6 +18,8 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import info.guardianproject.netcipher.proxy.OrbotHelper;
/** /**
* Created by Christian Schabesberger on 31.08.15. * Created by Christian Schabesberger on 31.08.15.
* *
@ -39,6 +42,7 @@ import android.view.ViewGroup;
public class SettingsActivity extends PreferenceActivity { public class SettingsActivity extends PreferenceActivity {
private static final int REQUEST_INSTALL_ORBOT = 0x1234;
private AppCompatDelegate mDelegate = null; private AppCompatDelegate mDelegate = null;
@Override @Override
@ -56,13 +60,48 @@ public class SettingsActivity extends PreferenceActivity {
} }
public static class SettingsFragment extends PreferenceFragment { public static class SettingsFragment extends PreferenceFragment {
private CheckBoxPreference useTorCheckBox;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.settings_screen); 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 @Override
protected void onPostCreate(Bundle savedInstanceState) { protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState); super.onPostCreate(savedInstanceState);
@ -148,17 +187,4 @@ public class SettingsActivity extends PreferenceActivity {
} }
return true; return true;
} }
public static void initSettings(Context context) {
PreferenceManager.setDefaultValues(context, R.xml.settings_screen, false);
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
if(sp.getString(context.getString(R.string.downloadPathPreference), "").isEmpty()){
SharedPreferences.Editor spEditor = sp.edit();
String newPipeDownloadStorage =
Environment.getExternalStorageDirectory().getAbsolutePath() + "/NewPipe";
spEditor.putString(context.getString(R.string.downloadPathPreference)
, newPipeDownloadStorage);
spEditor.apply();
}
}
} }

View File

@ -1,5 +1,6 @@
package org.schabi.newpipe; package org.schabi.newpipe;
import android.content.Context;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -33,7 +34,7 @@ class VideoInfoItemViewCreator {
this.inflater = inflater; this.inflater = inflater;
} }
public View getViewFromVideoInfoItem(View convertView, ViewGroup parent, VideoPreviewInfo info) { public View getViewFromVideoInfoItem(View convertView, ViewGroup parent, VideoPreviewInfo info, Context context) {
ViewHolder holder; ViewHolder holder;
if(convertView == null) { if(convertView == null) {
convertView = inflater.inflate(R.layout.video_item, parent, false); convertView = inflater.inflate(R.layout.video_item, parent, false);
@ -59,8 +60,7 @@ class VideoInfoItemViewCreator {
if(!info.upload_date.isEmpty()) { if(!info.upload_date.isEmpty()) {
holder.itemUploadDateView.setText(info.upload_date); holder.itemUploadDateView.setText(info.upload_date);
} else { } else {
//tweak if necessary: This is a hack to prevent having white space in the layout :P holder.itemUploadDateView.setText(Localization.localizeViewCount(info.view_count, context));
holder.itemUploadDateView.setText(String.format("%d", info.view_count));
} }
return convertView; return convertView;

View File

@ -113,6 +113,12 @@ public class VideoItemDetailActivity extends AppCompatActivity {
.commit(); .commit();
} }
@Override
public void onResume() {
super.onResume();
App.checkStartTor(this);
}
@Override @Override
public void onSaveInstanceState(Bundle outState) { public void onSaveInstanceState(Bundle outState) {
outState.putString(VideoItemDetailFragment.VIDEO_URL, videoUrl); outState.putString(VideoItemDetailFragment.VIDEO_URL, videoUrl);

View File

@ -1,9 +1,7 @@
package org.schabi.newpipe; package org.schabi.newpipe;
import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
@ -35,13 +33,7 @@ import android.view.MenuItem;
import android.widget.Toast; import android.widget.Toast;
import java.net.URL; import java.net.URL;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.Locale;
import java.util.Vector; import java.util.Vector;
import org.schabi.newpipe.services.VideoExtractor; import org.schabi.newpipe.services.VideoExtractor;
@ -235,7 +227,7 @@ public class VideoItemDetailFragment extends Fragment {
switch (info.errorCode) { switch (info.errorCode) {
case VideoInfo.NO_ERROR: { case VideoInfo.NO_ERROR: {
View nextVideoView = videoItemViewCreator View nextVideoView = videoItemViewCreator
.getViewFromVideoInfoItem(null, nextVideoFrame, info.nextVideo); .getViewFromVideoInfoItem(null, nextVideoFrame, info.nextVideo, getContext());
nextVideoFrame.addView(nextVideoView); nextVideoFrame.addView(nextVideoView);
@ -253,31 +245,20 @@ public class VideoItemDetailFragment extends Fragment {
uploaderView.setText(info.uploader); uploaderView.setText(info.uploader);
actionBarHandler.setChannelName(info.uploader); actionBarHandler.setChannelName(info.uploader);
Locale locale = getPreferredLocale(); String localizedViewCount = Localization.localizeViewCount(info.view_count, getContext());
NumberFormat nf = NumberFormat.getInstance(locale); viewCountView.setText(localizedViewCount);
String localisedViewCount = nf.format(info.view_count);
viewCountView.setText(
String.format(
res.getString(R.string.viewCountText), localisedViewCount));
thumbsUpView.setText(nf.format(info.like_count)); String localizedLikeCount = Localization.localizeNumber(info.like_count, getContext());
thumbsDownView.setText(nf.format(info.dislike_count)); thumbsUpView.setText(localizedLikeCount);
@SuppressLint("SimpleDateFormat") String localizedDislikeCount = Localization.localizeNumber(info.dislike_count, getContext());
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); thumbsDownView.setText(localizedDislikeCount);
Date datum = null;
try {
datum = formatter.parse(info.upload_date);
} catch (ParseException e) {
e.printStackTrace();
}
DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM, locale); String localizedDate = Localization.localizeDate(info.upload_date, getContext());
uploadDateView.setText(localizedDate);
String localisedDate = df.format(datum);
uploadDateView.setText(
String.format(res.getString(R.string.uploadDateText), localisedDate));
descriptionView.setText(Html.fromHtml(info.description)); descriptionView.setText(Html.fromHtml(info.description));
descriptionView.setMovementMethod(LinkMovementMethod.getInstance()); descriptionView.setMovementMethod(LinkMovementMethod.getInstance());
actionBarHandler.setServiceId(streamingServiceId); actionBarHandler.setServiceId(streamingServiceId);
@ -306,7 +287,7 @@ public class VideoItemDetailFragment extends Fragment {
VideoItemDetailFragment.ARG_ITEM_ID, currentVideoInfo.nextVideo.id); */ VideoItemDetailFragment.ARG_ITEM_ID, currentVideoInfo.nextVideo.id); */
detailIntent.putExtra( detailIntent.putExtra(
VideoItemDetailFragment.VIDEO_URL, currentVideoInfo.nextVideo.webpage_url); VideoItemDetailFragment.VIDEO_URL, currentVideoInfo.nextVideo.webpage_url);
//todo: make id dynamic the following line is crap
detailIntent.putExtra(VideoItemDetailFragment.STREAMING_SERVICE, streamingServiceId); detailIntent.putExtra(VideoItemDetailFragment.STREAMING_SERVICE, streamingServiceId);
startActivity(detailIntent); startActivity(detailIntent);
} }
@ -315,7 +296,7 @@ public class VideoItemDetailFragment extends Fragment {
break; break;
case VideoInfo.ERROR_BLOCKED_BY_GEMA: case VideoInfo.ERROR_BLOCKED_BY_GEMA:
thumbnailView.setImageBitmap(BitmapFactory.decodeResource( thumbnailView.setImageBitmap(BitmapFactory.decodeResource(
getResources(), R.drawable.gruese_die_gema_unangebracht)); getResources(), R.drawable.gruese_die_gema));
backgroundButton.setOnClickListener(new View.OnClickListener() { backgroundButton.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@ -458,30 +439,6 @@ public class VideoItemDetailFragment extends Fragment {
} }
} }
/**Returns the java.util.Locale object which corresponds to the locale set in NewPipe's preferences.
* Currently not affected by the device's locale.*/
private Locale getPreferredLocale() {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
String languageKey = getContext().getString(R.string.searchLanguage);
//i know the following line defaults languageCode to "en", but java is picky about uninitialised values
// Schabi: well lint tels me the value is redundant. I'll suppress it for now.
@SuppressWarnings("UnusedAssignment")
String languageCode = "en";
languageCode = sp.getString(languageKey, "en");
if(languageCode.length() == 2) {
return new Locale(languageCode);
}
else if(languageCode.contains("_")) {
String country = languageCode
.substring(languageCode.indexOf("_"), languageCode.length());
return new Locale(languageCode.substring(0, 2), country);
}
return Locale.getDefault();
}
private boolean checkIfLandscape() { private boolean checkIfLandscape() {
DisplayMetrics displayMetrics = new DisplayMetrics(); DisplayMetrics displayMetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); getActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);

View File

@ -3,6 +3,7 @@ package org.schabi.newpipe;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.NavUtils; import android.support.v4.app.NavUtils;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.SearchView; import android.support.v7.widget.SearchView;
@ -171,7 +172,13 @@ public class VideoItemListActivity extends AppCompatActivity
} }
} }
SettingsActivity.initSettings(this); PreferenceManager.setDefaultValues(this, R.xml.settings_screen, false);
}
@Override
public void onResume() {
super.onResume();
App.checkStartTor(this);
} }
/** /**

View File

@ -96,7 +96,7 @@ class VideoListAdapter extends BaseAdapter {
@Override @Override
public View getView(int position, View convertView, ViewGroup parent) { public View getView(int position, View convertView, ViewGroup parent) {
convertView = viewCreator.getViewFromVideoInfoItem(convertView, parent, videoList.get(position)); convertView = viewCreator.getViewFromVideoInfoItem(convertView, parent, videoList.get(position), context);
if(listView.isItemChecked(position)) { if(listView.isItemChecked(position)) {
convertView.setBackgroundColor(ContextCompat.getColor(context,R.color.primaryColorYoutube)); convertView.setBackgroundColor(ContextCompat.getColor(context,R.color.primaryColorYoutube));

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

View File

@ -8,7 +8,7 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Loading" android:text="@string/loading"
android:textAppearance="?android:attr/textAppearanceMedium" /> android:textAppearance="?android:attr/textAppearanceMedium" />
<ProgressBar <ProgressBar

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:gravity="center_vertical"
android:orientation="horizontal"
android:background="@color/background_notification_color"
tools:targetApi="jelly_bean">
<ImageView
android:id="@+id/backgroundCover"
android:layout_width="64dp"
android:layout_height="64dp"
android:src="@drawable/dummy_thumbnail"
android:scaleType="centerCrop"/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_weight="1"
android:orientation="vertical" >
<TextView
android:id="@+id/backgroundSongName"
style="@android:style/TextAppearance.StatusBar.EventContent.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:singleLine="true"
android:text="title" />
<TextView
android:id="@+id/backgroundArtist"
style="@android:style/TextAppearance.StatusBar.EventContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:singleLine="true"
android:text="artist" />
</LinearLayout>
<ImageButton
android:id="@+id/backgroundPlayPause"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_margin="5dp"
android:background="#00ffffff"
android:clickable="true"
android:scaleType="fitXY"
android:src="@drawable/ic_play_arrow_white_48dp" />
<ImageButton
android:id="@+id/backgroundStop"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_margin="5dp"
android:background="#00ffffff"
android:clickable="true"
android:scaleType="fitXY"
android:src="@drawable/ic_close_white_24dp" />
</LinearLayout>

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:background="@color/background_notification_color"
tools:targetApi="jelly_bean" >
<ImageView
android:id="@+id/backgroundCover"
android:layout_width="128dp"
android:layout_height="128dp"
android:layout_marginRight="8dp"
android:src="@drawable/dummy_thumbnail"
android:scaleType="centerCrop"/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="@+id/backgroundButtons"
android:layout_toRightOf="@+id/backgroundCover"
android:gravity="center_vertical"
android:orientation="vertical" >
<TextView
android:id="@+id/backgroundSongName"
style="@android:style/TextAppearance.StatusBar.EventContent.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="40dp"
android:ellipsize="marquee"
android:singleLine="true"
android:text="title" />
<TextView
android:id="@+id/backgroundArtist"
style="@android:style/TextAppearance.StatusBar.EventContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:singleLine="true"
android:text="artist" />
</LinearLayout>
<ImageButton
android:id="@+id/backgroundStop"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_alignParentRight="true"
android:layout_margin="5dp"
android:background="#00ffffff"
android:clickable="true"
android:scaleType="fitXY"
android:src="@drawable/ic_close_white_24dp" />
<LinearLayout
android:id="@+id/backgroundButtons"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_alignBottom="@id/backgroundCover"
android:layout_alignParentRight="true"
android:layout_toRightOf="@+id/backgroundCover"
android:orientation="horizontal" >
<ImageButton
android:id="@+id/backgroundPlayPause"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_marginLeft="30dp"
android:layout_marginRight="30dp"
android:layout_weight="1"
android:background="#00ffffff"
android:clickable="true"
android:scaleType="centerInside"
android:src="@drawable/ic_play_arrow_white_48dp" />
</LinearLayout>
</RelativeLayout>

View File

@ -49,4 +49,9 @@
<string name="detailUploaderThumbnailViewDescription">Avatar de l\'utilisateur</string> <string name="detailUploaderThumbnailViewDescription">Avatar de l\'utilisateur</string>
<string name="useExternalVideoPlayerTitle">Utiliser un lecteur vidéo externe</string> <string name="useExternalVideoPlayerTitle">Utiliser un lecteur vidéo externe</string>
<string name="useExternalAudioPlayerTitle">Utiliser un lecteur audio externe</string> <string name="useExternalAudioPlayerTitle">Utiliser un lecteur audio externe</string>
<string name="backgroundPlayerStartPlayingToast">Lecture en arrière-plan</string>
<string name="background_player_name">Lecteur en arrière-plan NewPipe</string>
<string name="loading">Chargement</string>
<string name="play">Lecture</string>
</resources> </resources>

View File

@ -0,0 +1,10 @@
<?xml version='1.0' encoding='utf-8'?>
<resources><string name="viewCountText">%1$s צפיות</string>
<string name="uploadDateText">הועלה בתאריך %1$s</string>
<string name="share">שתף</string>
<string name="search">חפש</string>
<string name="nextVideoTitle">הבא</string>
<string name="download">הורדה</string>
<string name="settings">הגדרות</string>
<string name="title_activity_settings">הגדרות</string>
</resources>

1
app/src/main/res/values-id Symbolic link
View File

@ -0,0 +1 @@
values-in

View File

@ -0,0 +1,3 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
</resources>

1
app/src/main/res/values-iw Symbolic link
View File

@ -0,0 +1 @@
values-he

View File

@ -50,4 +50,8 @@
<string name="useExternalAudioPlayerTitle">外部オーディオ プレイヤーを使用する</string> <string name="useExternalAudioPlayerTitle">外部オーディオ プレイヤーを使用する</string>
<string name="backgroundPlayerStartPlayingToast">バックグラウンドで再生しています</string> <string name="backgroundPlayerStartPlayingToast">バックグラウンドで再生しています</string>
<string name="background_player_name">NewPipe バックグラウンド プレーヤー</string>
<string name="loading">読み込み中</string>
<string name="play">再生</string>
</resources> </resources>

View File

@ -51,4 +51,7 @@
<string name="detailUploaderThumbnailViewDescription">Миниатюра аватара пользователся</string> <string name="detailUploaderThumbnailViewDescription">Миниатюра аватара пользователся</string>
<string name="detailThumbsDownImgViewDescription">Дислайки</string> <string name="detailThumbsDownImgViewDescription">Дислайки</string>
<string name="detailThumbsUpImgViewDescription">Лайки</string> <string name="detailThumbsUpImgViewDescription">Лайки</string>
</resources> <string name="useExternalVideoPlayerTitle">Использовать внешний проигрыватель для видео</string>
<string name="useExternalAudioPlayerTitle">Использовать внешний проигрыватель для аудио</string>
<string name="backgroundPlayerStartPlayingToast">Проигрывание в фоновом режиме</string>
</resources>

View File

@ -47,4 +47,9 @@
<string name="detailThumbsUpImgViewDescription">Všeč mi je</string> <string name="detailThumbsUpImgViewDescription">Všeč mi je</string>
<string name="detailThumbsDownImgViewDescription">Ni mi všeč</string> <string name="detailThumbsDownImgViewDescription">Ni mi všeč</string>
<string name="background_player_name">Ozadnji predvajalnik NewPipe</string>
<string name="loading">Nalaganje ...</string>
<string name="backgroundPlayerStartPlayingToast">Predvajanje v ozadju</string>
<string name="play">Predvajaj</string>
</resources> </resources>

View File

@ -54,4 +54,10 @@
<string name="useExternalAudioPlayerTitle">Користи спољашњи аудио плејер</string> <string name="useExternalAudioPlayerTitle">Користи спољашњи аудио плејер</string>
<string name="backgroundPlayerStartPlayingToast">Пуштам у позадини</string> <string name="backgroundPlayerStartPlayingToast">Пуштам у позадини</string>
</resources> <string name="background_player_name">Позадински плејер за Јутјуб цев</string>
<string name="loading">Учитавам</string>
<string name="play">Пусти</string>
<string name="useTor">Користи Тор</string>
<string name="useTorSummary">Принудно преусмерење саобраћаја кроз Тор за доданту приватност (токови још нису подржани)</string>
</resources>

View File

@ -7,4 +7,5 @@
<color name="durationText">#efff</color> <color name="durationText">#efff</color>
<color name="dark_overlay">#6000</color> <color name="dark_overlay">#6000</color>
<color name="background_gray">#EEEEEE</color> <color name="background_gray">#EEEEEE</color>
<color name="background_notification_color">#323232</color>
</resources> </resources>

View File

@ -71,6 +71,7 @@
<item>sl</item> <item>sl</item>
<item>fi</item> <item>fi</item>
<item>sv</item> <item>sv</item>
<item>bo</item>
<item>vi</item> <item>vi</item>
<item>tr</item> <item>tr</item>
<item>bg</item> <item>bg</item>
@ -149,6 +150,7 @@
<item>Slovenščina</item> <item>Slovenščina</item>
<item>Suomi</item> <item>Suomi</item>
<item>Svenska</item> <item>Svenska</item>
<item>Tibetan བོད་སྐད།</item>
<item>Tiếng Việt</item> <item>Tiếng Việt</item>
<item>Türkçe</item> <item>Türkçe</item>
<item>Български</item> <item>Български</item>

View File

@ -1,6 +1,7 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<resources> <resources>
<string name="app_name" translatable="false">NewPipe</string> <string name="app_name" translatable="false">NewPipe</string>
<string name="background_player_name">NewPipe Background Player</string>
<string name="title_videoitem_detail" translatable="false">NewPipe</string> <string name="title_videoitem_detail" translatable="false">NewPipe</string>
<string name="viewCountText">%1$s views</string> <string name="viewCountText">%1$s views</string>
<string name="uploadDateText">Uploaded on %1$s</string> <string name="uploadDateText">Uploaded on %1$s</string>
@ -10,6 +11,7 @@
<string name="fdroidVLCurl" translatable="false">https://f-droid.org/repository/browse/?fdfilter=vlc&amp;fdid=org.videolan.vlc</string> <string name="fdroidVLCurl" translatable="false">https://f-droid.org/repository/browse/?fdfilter=vlc&amp;fdid=org.videolan.vlc</string>
<string name="open_in_browser">Open in browser</string> <string name="open_in_browser">Open in browser</string>
<string name="share">Share</string> <string name="share">Share</string>
<string name="loading">Loading</string>
<string name="download">Download</string> <string name="download">Download</string>
<string name="search">Search</string> <string name="search">Search</string>
<string name="settings">Settings</string> <string name="settings">Settings</string>
@ -53,6 +55,7 @@
<string name="backgroundPlayerTickerText" translatable="false">%1$s - NewPipe</string> <string name="backgroundPlayerTickerText" translatable="false">%1$s - NewPipe</string>
<string name="backgroundPlayerStartPlayingToast">Playing in background</string> <string name="backgroundPlayerStartPlayingToast">Playing in background</string>
<string name="c3sUrl" translatable="false">https://www.c3s.cc/</string> <string name="c3sUrl" translatable="false">https://www.c3s.cc/</string>
<string name="play">Play</string>
<!-- Content descriptions (for better accessibility) --> <!-- Content descriptions (for better accessibility) -->
<string name="itemThumbnailViewDescription">Video preview thumbnail</string> <string name="itemThumbnailViewDescription">Video preview thumbnail</string>
@ -60,4 +63,6 @@
<string name="detailUploaderThumbnailViewDescription">Uploader thumbnail</string> <string name="detailUploaderThumbnailViewDescription">Uploader thumbnail</string>
<string name="detailThumbsDownImgViewDescription">Dislikes</string> <string name="detailThumbsDownImgViewDescription">Dislikes</string>
<string name="detailThumbsUpImgViewDescription">Likes</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> </resources>

View File

@ -64,8 +64,7 @@
android:key="@string/downloadPathPreference" android:key="@string/downloadPathPreference"
android:title="@string/downloadLocation" android:title="@string/downloadLocation"
android:summary="@string/downloadLocationSummary" android:summary="@string/downloadLocationSummary"
android:dialogTitle="@string/downloadLocationDialogTitle" android:dialogTitle="@string/downloadLocationDialogTitle" />
android:defaultValue=""/>
<CheckBoxPreference <CheckBoxPreference
android:key="@string/autoPlayThroughIntent" android:key="@string/autoPlayThroughIntent"
@ -73,5 +72,10 @@
android:summary="@string/autoPlayThroughIntentSummary" android:summary="@string/autoPlayThroughIntentSummary"
android:defaultValue="false" /> android:defaultValue="false" />
<CheckBoxPreference
android:key="@string/useTor"
android:title="@string/useTor"
android:summary="@string/useTorSummary" />
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View File

@ -1,18 +0,0 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true