Merge branch 'master' of github.com:theScrabi/NewPipe
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -7,3 +7,4 @@ | ||||
| /app/app.iml | ||||
| /.idea | ||||
| /*.iml | ||||
| gradle.properties | ||||
|   | ||||
| @@ -8,8 +8,8 @@ android { | ||||
|         applicationId "org.schabi.newpipe" | ||||
|         minSdkVersion 15 | ||||
|         targetSdkVersion 23 | ||||
|         versionCode 9 | ||||
|         versionName "0.7.0" | ||||
|         versionCode 10 | ||||
|         versionName "0.7.1" | ||||
|     } | ||||
|     buildTypes { | ||||
|         release { | ||||
| @@ -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' | ||||
| } | ||||
|   | ||||
| @@ -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" | ||||
| @@ -29,43 +30,46 @@ | ||||
|             <meta-data | ||||
|                 android:name="android.support.PARENT_ACTIVITY" | ||||
|                 android:value=".VideoItemListActivity" /> | ||||
|  | ||||
|             <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.BROWSABLE" /> | ||||
|  | ||||
|                 <data | ||||
|                     android:host="youtube.com" | ||||
|                     android:scheme="http" | ||||
|                     android:pathPattern="/?*#*/*watch"/> | ||||
|                 <data | ||||
|                     android:host="youtube.com" | ||||
|                     android:scheme="https" | ||||
|                     android:pathPattern="/?*#*/*watch"/> | ||||
|                 <data | ||||
|                     android:host="www.youtube.com" | ||||
|                     android:scheme="http" | ||||
|                     android:pathPattern="/?*#*/*watch"/> | ||||
|                 <data | ||||
|                     android:host="www.youtube.com" | ||||
|                     android:scheme="https" | ||||
|                     android:pathPattern="/?*#*/*watch"/> | ||||
|                 <data | ||||
|                     android:host="m.youtube.com" | ||||
|                     android:scheme="http" | ||||
|                     android:pathPattern="/?*#*/*watch"/> | ||||
|                 <data | ||||
|                     android:host="m.youtube.com" | ||||
|                     android:scheme="https" | ||||
|                     android:pathPattern="/?*#*/*watch"/> | ||||
|                 <data | ||||
|                     android:host="youtu.be" | ||||
|                     android:scheme="https" | ||||
|                     android:pathPrefix="/"/> | ||||
|                 <data | ||||
|                     android:host="youtu.be" | ||||
|                     android:scheme="http" | ||||
|                     android:pathPrefix="/"/> | ||||
|                 <data android:scheme="http" /> | ||||
|                 <data android:scheme="https" /> | ||||
|                 <data android:host="youtube.com" /> | ||||
|                 <data android:host="m.youtube.com" /> | ||||
|                 <data android:host="www.youtube.com" /> | ||||
|                 <data android:pathPrefix="/v/" /> | ||||
|                 <data android:pathPrefix="/watch" /> | ||||
|             </intent-filter> | ||||
|             <intent-filter> | ||||
|                 <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.BROWSABLE" /> | ||||
|  | ||||
|                 <data android:scheme="http" /> | ||||
|                 <data android:scheme="https" /> | ||||
|                 <data android:host="youtu.be" /> | ||||
|                 <data android:pathPrefix="/" /> | ||||
|             </intent-filter> | ||||
|             <intent-filter> | ||||
|                 <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.BROWSABLE" /> | ||||
|  | ||||
|                 <data android:scheme="vnd.youtube" /> | ||||
|                 <data android:scheme="vnd.youtube.launch" /> | ||||
|             </intent-filter> | ||||
|         </activity> | ||||
|         <activity android:name=".PlayVideoActivity" | ||||
| @@ -74,15 +78,26 @@ | ||||
|             android:parentActivityName=".VideoItemDetailActivity" | ||||
|             tools:ignore="UnusedAttribute"> | ||||
|         </activity> | ||||
|         <!--TODO: make label a translatable string --> | ||||
|         <service | ||||
|             android:name=".BackgroundPlayer" | ||||
|             android:label="NewPipe Background Player" | ||||
|             android:exported="false" > | ||||
|         </service> | ||||
|             android:label="@string/background_player_name" | ||||
|             android:exported="false" /> | ||||
|         <activity | ||||
|             android:name=".SettingsActivity" | ||||
|             android:label="@string/title_activity_settings" > | ||||
|         </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> | ||||
| </manifest> | ||||
|   | ||||
							
								
								
									
										46
									
								
								app/src/main/java/org/schabi/newpipe/App.java
									
									
									
									
									
										Normal 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; | ||||
|     } | ||||
| } | ||||
| @@ -17,6 +17,7 @@ import android.os.IBinder; | ||||
| import android.os.PowerManager; | ||||
| import android.support.v7.app.NotificationCompat; | ||||
| import android.util.Log; | ||||
| import android.widget.RemoteViews; | ||||
| import android.widget.Toast; | ||||
|  | ||||
| import java.io.IOException; | ||||
| @@ -113,9 +114,9 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare | ||||
|         private int noteID = TAG.hashCode(); | ||||
|         private BackgroundPlayer owner; | ||||
|         private NotificationManager noteMgr; | ||||
|         private NotificationCompat.Builder noteBuilder; | ||||
|         private WifiManager.WifiLock wifiLock; | ||||
|         private Bitmap videoThumbnail = null; | ||||
|         private NotificationCompat.Builder noteBuilder; | ||||
|  | ||||
|         public PlayerThread(String src, String title, BackgroundPlayer owner) { | ||||
|             this.source = src; | ||||
| @@ -124,10 +125,9 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare | ||||
|             mediaPlayer = new MediaPlayer(); | ||||
|             mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void run() { | ||||
|             Resources res = getApplicationContext().getResources(); | ||||
|  | ||||
|             mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);//cpu lock | ||||
|             try { | ||||
|                 mediaPlayer.setDataSource(source); | ||||
| @@ -174,54 +174,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare | ||||
|             filter.addAction(ACTION_STOP); | ||||
|             registerReceiver(broadcastReceiver, filter); | ||||
|  | ||||
|             PendingIntent playPI = PendingIntent.getBroadcast(owner, noteID, | ||||
|                     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(); | ||||
|             Notification note = buildNotification(); | ||||
|  | ||||
|             Intent openDetailView = new Intent(getApplicationContext(), | ||||
|                     VideoItemDetailActivity.class); | ||||
| @@ -249,7 +202,6 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare | ||||
|                     Log.d(TAG, "sleep failure"); | ||||
|                 } | ||||
|             }*/ | ||||
|  | ||||
|         } | ||||
|  | ||||
|         private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { | ||||
| @@ -303,5 +255,93 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare | ||||
|                 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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -57,26 +57,29 @@ public class DownloadDialog extends DialogFragment { | ||||
|                     @Override | ||||
|                     public void onClick(DialogInterface dialog, int which) { | ||||
|                         Context context = getActivity(); | ||||
|                         SharedPreferences defaultPreferences = PreferenceManager.getDefaultSharedPreferences(context); | ||||
|                         SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); | ||||
|                         String suffix = ""; | ||||
|                         String title = arguments.getString(TITLE); | ||||
|                         String url = ""; | ||||
|                         String downloadFolder = Environment.DIRECTORY_DOWNLOADS; | ||||
|                         switch(which) { | ||||
|                             case 0:     // Video | ||||
|                                 suffix = arguments.getString(FILE_SUFFIX_VIDEO); | ||||
|                                 url = arguments.getString(VIDEO_URL); | ||||
|                                 downloadFolder = Environment.DIRECTORY_MOVIES; | ||||
|                                 break; | ||||
|                             case 1: | ||||
|                                 suffix = arguments.getString(FILE_SUFFIX_AUDIO); | ||||
|                                 url = arguments.getString(AUDIO_URL); | ||||
|                                 downloadFolder = Environment.DIRECTORY_MUSIC; | ||||
|                                 break; | ||||
|                             default: | ||||
|                                 Log.d(TAG, "lolz"); | ||||
|                         } | ||||
|                         //to avoid hard-coded string like "/storage/emulated/0/NewPipe" | ||||
|                         final File dir = new File(defaultPreferences.getString( | ||||
|                                 "download_path_preference", | ||||
|                                 Environment.getExternalStorageDirectory().getAbsolutePath() + "/NewPipe")); | ||||
|                         //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()) { | ||||
| @@ -84,17 +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( | ||||
|                                 defaultPreferences.getString("download_path_preference", "/storage/emulated/0/NewPipe") | ||||
|                                         + "/" + 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(); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 }); | ||||
|   | ||||
| @@ -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(); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
							
								
								
									
										36
									
								
								app/src/main/java/org/schabi/newpipe/ExitActivity.java
									
									
									
									
									
										Normal 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); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										77
									
								
								app/src/main/java/org/schabi/newpipe/Localization.java
									
									
									
									
									
										Normal 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); | ||||
|     } | ||||
| } | ||||
| @@ -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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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(); | ||||
|   | ||||
| @@ -1,13 +1,14 @@ | ||||
| package org.schabi.newpipe; | ||||
|  | ||||
| import android.app.Activity; | ||||
| import android.content.Context; | ||||
| import android.content.SharedPreferences; | ||||
| import android.content.Intent; | ||||
| import android.content.res.Configuration; | ||||
| import android.os.Bundle; | ||||
| import android.os.Environment; | ||||
| import android.preference.CheckBoxPreference; | ||||
| import android.preference.Preference; | ||||
| 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; | ||||
| @@ -17,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. | ||||
|  * | ||||
| @@ -39,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 | ||||
| @@ -56,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); | ||||
| @@ -148,17 +187,4 @@ public class SettingsActivity extends PreferenceActivity { | ||||
|         } | ||||
|         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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| package org.schabi.newpipe; | ||||
|  | ||||
| import android.content.Context; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| @@ -33,7 +34,7 @@ class VideoInfoItemViewCreator { | ||||
|         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; | ||||
|         if(convertView == null) { | ||||
|             convertView = inflater.inflate(R.layout.video_item, parent, false); | ||||
| @@ -59,8 +60,7 @@ class VideoInfoItemViewCreator { | ||||
|         if(!info.upload_date.isEmpty()) { | ||||
|             holder.itemUploadDateView.setText(info.upload_date); | ||||
|         } else { | ||||
|             //tweak if necessary: This is a hack to prevent having white space in the layout :P | ||||
|             holder.itemUploadDateView.setText(String.format("%d", info.view_count)); | ||||
|             holder.itemUploadDateView.setText(Localization.localizeViewCount(info.view_count, context)); | ||||
|         } | ||||
|  | ||||
|         return convertView; | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -1,9 +1,7 @@ | ||||
| package org.schabi.newpipe; | ||||
|  | ||||
| import android.annotation.SuppressLint; | ||||
| import android.app.Activity; | ||||
| import android.content.Intent; | ||||
| import android.content.SharedPreferences; | ||||
| import android.content.res.Resources; | ||||
| import android.graphics.Bitmap; | ||||
| import android.graphics.BitmapFactory; | ||||
| @@ -35,13 +33,7 @@ import android.view.MenuItem; | ||||
| import android.widget.Toast; | ||||
|  | ||||
| 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.Date; | ||||
| import java.util.Locale; | ||||
| import java.util.Vector; | ||||
|  | ||||
| import org.schabi.newpipe.services.VideoExtractor; | ||||
| @@ -235,7 +227,7 @@ public class VideoItemDetailFragment extends Fragment { | ||||
|             switch (info.errorCode) { | ||||
|                 case VideoInfo.NO_ERROR: { | ||||
|                     View nextVideoView = videoItemViewCreator | ||||
|                             .getViewFromVideoInfoItem(null, nextVideoFrame, info.nextVideo); | ||||
|                             .getViewFromVideoInfoItem(null, nextVideoFrame, info.nextVideo, getContext()); | ||||
|                     nextVideoFrame.addView(nextVideoView); | ||||
|  | ||||
|  | ||||
| @@ -253,31 +245,20 @@ public class VideoItemDetailFragment extends Fragment { | ||||
|                     uploaderView.setText(info.uploader); | ||||
|                     actionBarHandler.setChannelName(info.uploader); | ||||
|  | ||||
|                     Locale locale = getPreferredLocale(); | ||||
|                     NumberFormat nf = NumberFormat.getInstance(locale); | ||||
|                     String localisedViewCount = nf.format(info.view_count); | ||||
|                     viewCountView.setText( | ||||
|                             String.format( | ||||
|                                     res.getString(R.string.viewCountText), localisedViewCount)); | ||||
|                     String localizedViewCount = Localization.localizeViewCount(info.view_count, getContext()); | ||||
|                     viewCountView.setText(localizedViewCount); | ||||
|  | ||||
|                     thumbsUpView.setText(nf.format(info.like_count)); | ||||
|                     thumbsDownView.setText(nf.format(info.dislike_count)); | ||||
|                     String localizedLikeCount = Localization.localizeNumber(info.like_count, getContext()); | ||||
|                     thumbsUpView.setText(localizedLikeCount); | ||||
|  | ||||
|                     @SuppressLint("SimpleDateFormat") | ||||
|                     SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); | ||||
|                     Date datum = null; | ||||
|                     try { | ||||
|                         datum = formatter.parse(info.upload_date); | ||||
|                     } catch (ParseException e) { | ||||
|                         e.printStackTrace(); | ||||
|                     } | ||||
|                     String localizedDislikeCount = Localization.localizeNumber(info.dislike_count, getContext()); | ||||
|                     thumbsDownView.setText(localizedDislikeCount); | ||||
|  | ||||
|                     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.setMovementMethod(LinkMovementMethod.getInstance()); | ||||
|  | ||||
|                     actionBarHandler.setServiceId(streamingServiceId); | ||||
| @@ -306,7 +287,7 @@ public class VideoItemDetailFragment extends Fragment { | ||||
|                                 VideoItemDetailFragment.ARG_ITEM_ID, currentVideoInfo.nextVideo.id); */ | ||||
|                             detailIntent.putExtra( | ||||
|                                     VideoItemDetailFragment.VIDEO_URL, currentVideoInfo.nextVideo.webpage_url); | ||||
|                             //todo: make id dynamic the following line is crap | ||||
|  | ||||
|                             detailIntent.putExtra(VideoItemDetailFragment.STREAMING_SERVICE, streamingServiceId); | ||||
|                             startActivity(detailIntent); | ||||
|                         } | ||||
| @@ -315,7 +296,7 @@ public class VideoItemDetailFragment extends Fragment { | ||||
|                 break; | ||||
|                 case VideoInfo.ERROR_BLOCKED_BY_GEMA: | ||||
|                     thumbnailView.setImageBitmap(BitmapFactory.decodeResource( | ||||
|                             getResources(), R.drawable.gruese_die_gema_unangebracht)); | ||||
|                             getResources(), R.drawable.gruese_die_gema)); | ||||
|                     backgroundButton.setOnClickListener(new View.OnClickListener() { | ||||
|                         @Override | ||||
|                         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() { | ||||
|         DisplayMetrics displayMetrics = new DisplayMetrics(); | ||||
|         getActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); | ||||
|   | ||||
| @@ -3,6 +3,7 @@ package org.schabi.newpipe; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.os.Bundle; | ||||
| import android.preference.PreferenceManager; | ||||
| import android.support.v4.app.NavUtils; | ||||
| import android.support.v7.app.AppCompatActivity; | ||||
| 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); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -96,7 +96,7 @@ class VideoListAdapter extends BaseAdapter { | ||||
|  | ||||
|     @Override | ||||
|     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)) { | ||||
|             convertView.setBackgroundColor(ContextCompat.getColor(context,R.color.primaryColorYoutube)); | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-hdpi/ic_close_white_24dp.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 221 B | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-mdpi/ic_close_white_24dp.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 175 B | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-nodpi/gruese_die_gema.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 89 KiB | 
| Before Width: | Height: | Size: 16 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-xhdpi/ic_close_white_24dp.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 257 B | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-xxhdpi/ic_close_white_24dp.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 347 B | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-xxxhdpi/ic_close_white_24dp.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 436 B | 
| @@ -8,7 +8,7 @@ | ||||
|     <TextView | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:text="Loading" | ||||
|         android:text="@string/loading" | ||||
|         android:textAppearance="?android:attr/textAppearanceMedium" /> | ||||
|  | ||||
|     <ProgressBar | ||||
|   | ||||
							
								
								
									
										66
									
								
								app/src/main/res/layout/player_notification.xml
									
									
									
									
									
										Normal 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> | ||||
							
								
								
									
										80
									
								
								app/src/main/res/layout/player_notification_expanded.xml
									
									
									
									
									
										Normal 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> | ||||
| @@ -49,4 +49,9 @@ | ||||
|     <string name="detailUploaderThumbnailViewDescription">Avatar de l\'utilisateur</string> | ||||
|     <string name="useExternalVideoPlayerTitle">Utiliser un lecteur vidéo 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> | ||||
|   | ||||
							
								
								
									
										10
									
								
								app/src/main/res/values-he/strings.xml
									
									
									
									
									
										Normal 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
									
								
							
							
						
						| @@ -0,0 +1 @@ | ||||
| values-in | ||||
							
								
								
									
										3
									
								
								app/src/main/res/values-in/strings.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,3 @@ | ||||
| <?xml version='1.0' encoding='utf-8'?> | ||||
| <resources> | ||||
| </resources> | ||||
							
								
								
									
										1
									
								
								app/src/main/res/values-iw
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						| @@ -0,0 +1 @@ | ||||
| values-he | ||||
| @@ -50,4 +50,8 @@ | ||||
|     <string name="useExternalAudioPlayerTitle">外部オーディオ プレイヤーを使用する</string> | ||||
|     <string name="backgroundPlayerStartPlayingToast">バックグラウンドで再生しています</string> | ||||
|  | ||||
|     <string name="background_player_name">NewPipe バックグラウンド プレーヤー</string> | ||||
|     <string name="loading">読み込み中</string> | ||||
|     <string name="play">再生</string> | ||||
|  | ||||
|     </resources> | ||||
|   | ||||
| @@ -51,4 +51,7 @@ | ||||
|     <string name="detailUploaderThumbnailViewDescription">Миниатюра аватара пользователся</string> | ||||
|     <string name="detailThumbsDownImgViewDescription">Дислайки</string> | ||||
|     <string name="detailThumbsUpImgViewDescription">Лайки</string> | ||||
| </resources> | ||||
| <string name="useExternalVideoPlayerTitle">Использовать внешний проигрыватель для видео</string> | ||||
|     <string name="useExternalAudioPlayerTitle">Использовать внешний проигрыватель для аудио</string> | ||||
|     <string name="backgroundPlayerStartPlayingToast">Проигрывание в фоновом режиме</string> | ||||
|     </resources> | ||||
|   | ||||
| @@ -47,4 +47,9 @@ | ||||
|  | ||||
|     <string name="detailThumbsUpImgViewDescription">Všeč mi je</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> | ||||
|   | ||||
| @@ -54,4 +54,10 @@ | ||||
|     <string name="useExternalAudioPlayerTitle">Користи спољашњи аудио плејер</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> | ||||
|   | ||||
| @@ -7,4 +7,5 @@ | ||||
|     <color name="durationText">#efff</color> | ||||
|     <color name="dark_overlay">#6000</color> | ||||
|     <color name="background_gray">#EEEEEE</color> | ||||
|     <color name="background_notification_color">#323232</color> | ||||
| </resources> | ||||
| @@ -71,6 +71,7 @@ | ||||
|         <item>sl</item> | ||||
|         <item>fi</item> | ||||
|         <item>sv</item> | ||||
|         <item>bo</item> | ||||
|         <item>vi</item> | ||||
|         <item>tr</item> | ||||
|         <item>bg</item> | ||||
| @@ -149,6 +150,7 @@ | ||||
|         <item>Slovenščina</item> | ||||
|         <item>Suomi</item> | ||||
|         <item>Svenska</item> | ||||
|         <item>Tibetan བོད་སྐད།</item> | ||||
|         <item>Tiếng Việt</item> | ||||
|         <item>Türkçe</item> | ||||
|         <item>Български</item> | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| <?xml version='1.0' encoding='utf-8'?> | ||||
| <resources> | ||||
|     <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="viewCountText">%1$s views</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&fdid=org.videolan.vlc</string> | ||||
|     <string name="open_in_browser">Open in browser</string> | ||||
|     <string name="share">Share</string> | ||||
|     <string name="loading">Loading</string> | ||||
|     <string name="download">Download</string> | ||||
|     <string name="search">Search</string> | ||||
|     <string name="settings">Settings</string> | ||||
| @@ -53,6 +55,7 @@ | ||||
|     <string name="backgroundPlayerTickerText" translatable="false">%1$s - NewPipe</string> | ||||
|     <string name="backgroundPlayerStartPlayingToast">Playing in background</string> | ||||
|     <string name="c3sUrl" translatable="false">https://www.c3s.cc/</string> | ||||
|     <string name="play">Play</string> | ||||
|  | ||||
|     <!-- Content descriptions (for better accessibility) --> | ||||
|     <string name="itemThumbnailViewDescription">Video preview thumbnail</string> | ||||
| @@ -60,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> | ||||
|   | ||||
| @@ -64,8 +64,7 @@ | ||||
|             android:key="@string/downloadPathPreference" | ||||
|             android:title="@string/downloadLocation" | ||||
|             android:summary="@string/downloadLocationSummary" | ||||
|             android:dialogTitle="@string/downloadLocationDialogTitle" | ||||
|             android:defaultValue=""/> | ||||
|             android:dialogTitle="@string/downloadLocationDialogTitle" /> | ||||
|  | ||||
|         <CheckBoxPreference | ||||
|             android:key="@string/autoPlayThroughIntent" | ||||
| @@ -73,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> | ||||
| Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 62 KiB | 
| @@ -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 | ||||
 Adam Howard
					Adam Howard