From e83ca0dfda14bac93504edb6ca3d39a148b856e4 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Fri, 25 Dec 2015 00:09:35 +0100 Subject: [PATCH] some improvements for background player --- .../org/schabi/newpipe/ActionBarHandler.java | 94 +++++++++------ .../schabi/newpipe/ActivityCommunicator.java | 42 +++++++ .../org/schabi/newpipe/BackgroundPlayer.java | 112 +++++++++++++----- .../newpipe/VideoItemDetailFragment.java | 18 ++- .../schabi/newpipe/VideoItemListActivity.java | 4 +- .../res/drawable-hdpi/ic_pause_white_24dp.png | Bin 0 -> 105 bytes .../ic_play_circle_filled_white_24dp.png | Bin 0 -> 358 bytes .../res/drawable-mdpi/ic_pause_white_24dp.png | Bin 0 -> 83 bytes .../ic_play_circle_filled_white_24dp.png | Bin 0 -> 255 bytes .../drawable-xhdpi/ic_pause_white_24dp.png | Bin 0 -> 90 bytes .../ic_play_circle_filled_white_24dp.png | Bin 0 -> 464 bytes .../drawable-xxhdpi/ic_pause_white_24dp.png | Bin 0 -> 92 bytes .../ic_play_circle_filled_white_24dp.png | Bin 0 -> 666 bytes .../drawable-xxxhdpi/ic_pause_white_24dp.png | Bin 0 -> 94 bytes .../ic_play_circle_filled_white_24dp.png | Bin 0 -> 883 bytes app/src/main/res/values/strings.xml | 2 + 16 files changed, 203 insertions(+), 69 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/ActivityCommunicator.java create mode 100644 app/src/main/res/drawable-hdpi/ic_pause_white_24dp.png create mode 100644 app/src/main/res/drawable-hdpi/ic_play_circle_filled_white_24dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_pause_white_24dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_play_circle_filled_white_24dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_pause_white_24dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_play_circle_filled_white_24dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_pause_white_24dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_play_circle_filled_white_24dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_pause_white_24dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_play_circle_filled_white_24dp.png diff --git a/app/src/main/java/org/schabi/newpipe/ActionBarHandler.java b/app/src/main/java/org/schabi/newpipe/ActionBarHandler.java index 76955d824..23ae0724d 100644 --- a/app/src/main/java/org/schabi/newpipe/ActionBarHandler.java +++ b/app/src/main/java/org/schabi/newpipe/ActionBarHandler.java @@ -3,6 +3,7 @@ package org.schabi.newpipe; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; +import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; import android.preference.PreferenceManager; @@ -40,7 +41,10 @@ class ActionBarHandler { private static final String TAG = ActionBarHandler.class.toString(); private static final String KORE_PACKET = "org.xbmc.kore"; + private int serviceId; private String websiteUrl = ""; + private Bitmap videoThumbnail = null; + private String channelName = ""; private AppCompatActivity activity; private VideoInfo.VideoStream[] videoStreams = null; private VideoInfo.AudioStream audioStream = null; @@ -73,6 +77,18 @@ class ActionBarHandler { } } + public void setServiceId(int id) { + serviceId = id; + } + + public void setSetVideoThumbnail(Bitmap bitmap) { + videoThumbnail = bitmap; + } + + public void setChannelName(String name) { + channelName = name; + } + @SuppressWarnings("deprecation") public void setStreams(VideoInfo.VideoStream[] videoStreams, VideoInfo.AudioStream[] audioStreams) { this.videoStreams = videoStreams; @@ -143,37 +159,41 @@ class ActionBarHandler { } public boolean onItemSelected(MenuItem item) { - int id = item.getItemId(); - switch(id) { - case R.id.menu_item_share: - if(!videoTitle.isEmpty()) { + if(!videoTitle.isEmpty()) { + int id = item.getItemId(); + switch (id) { + case R.id.menu_item_share: { Intent intent = new Intent(); intent.setAction(Intent.ACTION_SEND); intent.putExtra(Intent.EXTRA_TEXT, websiteUrl); intent.setType("text/plain"); activity.startActivity(Intent.createChooser(intent, activity.getString(R.string.shareDialogTitle))); + return true; + } + case R.id.menu_item_openInBrowser: { + openInBrowser(); } return true; - case R.id.menu_item_openInBrowser: { - openInBrowser(); + case R.id.menu_item_download: + downloadVideo(); + return true; + case R.id.action_settings: { + Intent intent = new Intent(activity, SettingsActivity.class); + activity.startActivity(intent); + } + break; + case R.id.action_play_with_kodi: + playWithKodi(); + return true; + case R.id.menu_item_play_audio: + playAudio(); + return true; + default: + Log.e(TAG, "Menu Item not known"); } - return true; - case R.id.menu_item_download: - downloadVideo(); - return true; - case R.id.action_settings: { - Intent intent = new Intent(activity, SettingsActivity.class); - activity.startActivity(intent); - } - break; - case R.id.action_play_with_kodi: - playWithKodi(); - return true; - case R.id.menu_item_play_audio: - playAudio(); - return true; - default: - Log.e(TAG, "Menu Item not known"); + } else { + // That line may not be necessary. + return true; } return false; } @@ -302,18 +322,24 @@ class ActionBarHandler { .getBoolean(activity.getString(R.string.useExternalAudioPlayer), false); Intent intent; - if (!externalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 18)//internal music player: explicit intent - { - intent = new Intent(activity, BackgroundPlayer.class); + if (!externalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 18) { + //internal music player: explicit intent + if (!BackgroundPlayer.isRunning && videoThumbnail != null) { + ActivityCommunicator.getCommunicator() + .backgroundPlayerThumbnail = videoThumbnail; + intent = new Intent(activity, BackgroundPlayer.class); - intent.setAction(Intent.ACTION_VIEW); - Log.i(TAG, "audioStream is null:" + (audioStream == null)); - Log.i(TAG, "audioStream.url is null:"+(audioStream.url==null)); - intent.setDataAndType(Uri.parse(audioStream.url), - MediaFormat.getMimeById(audioStream.format)); - intent.putExtra(Intent.EXTRA_TITLE, videoTitle); - intent.putExtra("title", videoTitle); - activity.startService(intent); + intent.setAction(Intent.ACTION_VIEW); + Log.i(TAG, "audioStream is null:" + (audioStream == null)); + Log.i(TAG, "audioStream.url is null:" + (audioStream.url == null)); + intent.setDataAndType(Uri.parse(audioStream.url), + MediaFormat.getMimeById(audioStream.format)); + intent.putExtra(BackgroundPlayer.TITLE, videoTitle); + intent.putExtra(BackgroundPlayer.WEB_URL, websiteUrl); + intent.putExtra(BackgroundPlayer.SERVICE_ID, serviceId); + intent.putExtra(BackgroundPlayer.CHANNEL_NAME, channelName); + activity.startService(intent); + } } else { intent = new Intent(); try { diff --git a/app/src/main/java/org/schabi/newpipe/ActivityCommunicator.java b/app/src/main/java/org/schabi/newpipe/ActivityCommunicator.java new file mode 100644 index 000000000..b09652a6c --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/ActivityCommunicator.java @@ -0,0 +1,42 @@ +package org.schabi.newpipe; + +/** + * Created by Christian Schabesberger on 24.12.15. + * + * Copyright (C) Christian Schabesberger 2015 + * ActivityCommunicator.java is part of NewPipe. + * + * NewPipe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * NewPipe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with NewPipe. If not, see . + */ + +import android.graphics.Bitmap; + +/** + * Singleton: + * Used to send data between certain Activity/Services within the same process. + * This can be considered as hack inside the Android universe. **/ +public class ActivityCommunicator { + + private static ActivityCommunicator activityCommunicator = null; + + public static ActivityCommunicator getCommunicator() { + if(activityCommunicator == null) { + activityCommunicator = new ActivityCommunicator(); + } + return activityCommunicator; + } + + // Thumbnail send from VideoItemDetailFragment to BackgroundPlayer + public volatile Bitmap backgroundPlayerThumbnail; +} diff --git a/app/src/main/java/org/schabi/newpipe/BackgroundPlayer.java b/app/src/main/java/org/schabi/newpipe/BackgroundPlayer.java index dfea0f1c3..5573e2771 100644 --- a/app/src/main/java/org/schabi/newpipe/BackgroundPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/BackgroundPlayer.java @@ -8,6 +8,8 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.Resources; +import android.graphics.Bitmap; import android.media.AudioManager; import android.media.MediaPlayer; import android.net.wifi.WifiManager; @@ -43,8 +45,22 @@ import java.io.IOException; public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPreparedListener*/ { private static final String TAG = BackgroundPlayer.class.toString(); - private static final String ACTION_STOP = TAG+".STOP"; - private static final String ACTION_PLAYPAUSE = TAG+".PLAYPAUSE"; + private static final String ACTION_STOP = TAG + ".STOP"; + private static final String ACTION_PLAYPAUSE = TAG + ".PLAYPAUSE"; + + // Extra intent arguments + public static final String TITLE = "title"; + public static final String WEB_URL = "web_url"; + public static final String SERVICE_ID = "service_id"; + public static final String CHANNEL_NAME="channel_name"; + + private volatile String webUrl = ""; + private volatile int serviceId = -1; + private volatile String channelName = ""; + + // Determines if the service is already running. + // Prevents launching the service twice. + public static volatile boolean isRunning = false; public BackgroundPlayer() { super(); @@ -58,16 +74,22 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare } @Override public int onStartCommand(Intent intent, int flags, int startId) { - Toast.makeText(this, "Playing in background", Toast.LENGTH_SHORT).show();//todo:translation string + Toast.makeText(this, R.string.backgroundPlayerStartPlayingToast, + Toast.LENGTH_SHORT).show(); String source = intent.getDataString(); //Log.i(TAG, "backgroundPLayer source:"+source); - String videoTitle = intent.getStringExtra("title"); + String videoTitle = intent.getStringExtra(TITLE); + webUrl = intent.getStringExtra(WEB_URL); + serviceId = intent.getIntExtra(SERVICE_ID, -1); + channelName = intent.getStringExtra(CHANNEL_NAME); //do nearly everything in a separate thread PlayerThread player = new PlayerThread(source, videoTitle, this); player.start(); + isRunning = true; + // If we get killed after returning here, don't restart return START_NOT_STICKY; } @@ -81,6 +103,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare @Override public void onDestroy() { //Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); + isRunning = false; } private class PlayerThread extends Thread { @@ -92,6 +115,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare private NotificationManager noteMgr; private NotificationCompat.Builder noteBuilder; private WifiManager.WifiLock wifiLock; + private Bitmap videoThumbnail = null; public PlayerThread(String src, String title, BackgroundPlayer owner) { this.source = src; @@ -102,11 +126,14 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare } @Override public void run() { + Resources res = getApplicationContext().getResources(); + mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);//cpu lock try { mediaPlayer.setDataSource(source); - mediaPlayer.prepare(); //We are already in a separate worker thread, + //We are already in a separate worker thread, //so calling the blocking prepare() method should be ok + mediaPlayer.prepare(); //alternatively: //mediaPlayer.setOnPreparedListener(this); @@ -119,10 +146,18 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare return; } + try { + videoThumbnail = ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail; + } catch (Exception e) { + Log.e(TAG, "Could not get video thumbnail from ActivityCommunicator"); + e.printStackTrace(); + } + WifiManager wifiMgr = ((WifiManager)getSystemService(Context.WIFI_SERVICE)); wifiLock = wifiMgr.createWifiLock(WifiManager.WIFI_MODE_FULL, TAG); - mediaPlayer.setOnCompletionListener(new EndListener(wifiLock));//listen for end of video + //listen for end of video + mediaPlayer.setOnCompletionListener(new EndListener(wifiLock)); //get audio focus /* @@ -142,43 +177,65 @@ 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); + 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_play_arrow_white_48dp, "Pause", playPI).build(); + (R.drawable.ic_pause_white_24dp, "Pause", playPI).build(); + */ PendingIntent stopPI = PendingIntent.getBroadcast(owner, noteID, new Intent(ACTION_STOP), PendingIntent.FLAG_UPDATE_CURRENT); - //todo: make it so that tapping the notification brings you back to the Video's DetailActivity - //using setContentIntent noteBuilder = new NotificationCompat.Builder(owner); noteBuilder - .setPriority(Notification.PRIORITY_LOW) - .setCategory(Notification.CATEGORY_TRANSPORT) .setContentTitle(title) - .setContentText("NewPipe is playing in the background")//todo: translation string + //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) - //.setProgress(vidLength, 0, false) //doesn't fit with Notification.MediaStyle - .setSmallIcon(R.mipmap.ic_launcher) - .setTicker(title + " - NewPipe") + //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)*/ + //.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) - ); + noteBuilder.setStyle(new NotificationCompat.MediaStyle() + //.setMediaSession(mMediaSession.getSessionToken()) + .setShowActionsInCompactView(new int[]{0}) + .setShowCancelButton(true) + .setCancelButtonIntent(stopPI)); + if(videoThumbnail != null) { + noteBuilder.setLargeIcon(videoThumbnail); + } - startForeground(noteID, noteBuilder.build()); + Notification note = noteBuilder.build(); + + Intent openDetailView = new Intent(getApplicationContext(), + VideoItemDetailActivity.class); + openDetailView.putExtra(VideoItemDetailFragment.STREAMING_SERVICE, serviceId); + openDetailView.putExtra(VideoItemDetailFragment.VIDEO_URL, webUrl); + openDetailView.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + note.contentIntent = PendingIntent.getActivity(getApplicationContext(), + noteID, openDetailView, + PendingIntent.FLAG_UPDATE_CURRENT); + + startForeground(noteID, note); //currently decommissioned progressbar looping update code - works, but doesn't fit inside //Notification.MediaStyle Notification layout. @@ -202,7 +259,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - Log.i(TAG, "received broadcast action:"+action); + //Log.i(TAG, "received broadcast action:"+action); if(action.equals(ACTION_PLAYPAUSE)) { if(mediaPlayer.isPlaying()) { mediaPlayer.pause(); @@ -214,7 +271,8 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare } } else if(action.equals(ACTION_STOP)) { - mediaPlayer.stop();//this auto-releases CPU lock + //this auto-releases CPU lock + mediaPlayer.stop(); afterPlayCleanup(); } } diff --git a/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java b/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java index 4290e5c9c..d1e3d10be 100644 --- a/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java @@ -73,7 +73,7 @@ public class VideoItemDetailFragment extends Fragment { * The fragment argument representing the item ID that this fragment * represents. */ - public static final String ARG_ITEM_ID = "item_id"; + //public static final String ARG_ITEM_ID = "item_id"; public static final String VIDEO_URL = "video_url"; public static final String STREAMING_SERVICE = "streaming_service"; public static final String AUTO_PLAY = "auto_play"; @@ -81,6 +81,8 @@ public class VideoItemDetailFragment extends Fragment { private AppCompatActivity activity; private ActionBarHandler actionBarHandler; + private int streamingServiceId = -1; + private boolean autoPlayEnabled = false; private VideoInfo currentVideoInfo = null; private boolean showNextVideoItem = false; @@ -105,6 +107,7 @@ public class VideoItemDetailFragment extends Fragment { this.service = service; this.videoUrl = videoUrl; } + @Override public void run() { try { @@ -176,6 +179,7 @@ public class VideoItemDetailFragment extends Fragment { switch (id) { case SetThumbnailRunnable.VIDEO_THUMBNAIL: thumbnailView = (ImageView) a.findViewById(R.id.detailThumbnailView); + actionBarHandler.setSetVideoThumbnail(thumbnail); break; case SetThumbnailRunnable.CHANNEL_THUMBNAIL: thumbnailView = (ImageView) a.findViewById(R.id.detailUploaderThumbnailView); @@ -238,6 +242,7 @@ public class VideoItemDetailFragment extends Fragment { case VideoInfo.VIDEO_AVAILABLE: { videoTitleView.setText(info.title); uploaderView.setText(info.uploader); + actionBarHandler.setChannelName(info.uploader); Locale locale = getPreferredLocale(); NumberFormat nf = NumberFormat.getInstance(locale); @@ -266,6 +271,7 @@ public class VideoItemDetailFragment extends Fragment { descriptionView.setText(Html.fromHtml(info.description)); descriptionView.setMovementMethod(LinkMovementMethod.getInstance()); + actionBarHandler.setServiceId(streamingServiceId); actionBarHandler.setVideoInfo(info.webpage_url, info.title); actionBarHandler.setStartPosition(info.startPosition); @@ -288,12 +294,12 @@ public class VideoItemDetailFragment extends Fragment { public void onClick(View v) { Intent detailIntent = new Intent(getActivity(), VideoItemDetailActivity.class); - detailIntent.putExtra( - VideoItemDetailFragment.ARG_ITEM_ID, currentVideoInfo.nextVideo.id); + /*detailIntent.putExtra( + 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, 0); + detailIntent.putExtra(VideoItemDetailFragment.STREAMING_SERVICE, streamingServiceId); startActivity(detailIntent); } }); @@ -369,8 +375,8 @@ public class VideoItemDetailFragment extends Fragment { // Otherwise the applications would crash. if(playVideoButton != null) { try { - StreamingService streamingService = ServiceList.getService( - getArguments().getInt(STREAMING_SERVICE)); + streamingServiceId = getArguments().getInt(STREAMING_SERVICE); + StreamingService streamingService = ServiceList.getService(streamingServiceId); Thread videoExtractorThread = new Thread(new VideoExtractorRunnable( getArguments().getString(VIDEO_URL), streamingService)); diff --git a/app/src/main/java/org/schabi/newpipe/VideoItemListActivity.java b/app/src/main/java/org/schabi/newpipe/VideoItemListActivity.java index 37bf67333..6aaf10d41 100644 --- a/app/src/main/java/org/schabi/newpipe/VideoItemListActivity.java +++ b/app/src/main/java/org/schabi/newpipe/VideoItemListActivity.java @@ -190,7 +190,7 @@ public class VideoItemListActivity extends AppCompatActivity // adding or replacing the detail fragment using a // fragment transaction. Bundle arguments = new Bundle(); - arguments.putString(VideoItemDetailFragment.ARG_ITEM_ID, id); + //arguments.putString(VideoItemDetailFragment.ARG_ITEM_ID, id); arguments.putString(VideoItemDetailFragment.VIDEO_URL, webpage_url); arguments.putInt(VideoItemDetailFragment.STREAMING_SERVICE, currentStreamingServiceId); videoFragment = new VideoItemDetailFragment(); @@ -209,7 +209,7 @@ public class VideoItemListActivity extends AppCompatActivity // In single-pane mode, simply start the detail activity // for the selected item ID. Intent detailIntent = new Intent(this, VideoItemDetailActivity.class); - detailIntent.putExtra(VideoItemDetailFragment.ARG_ITEM_ID, id); + //detailIntent.putExtra(VideoItemDetailFragment.ARG_ITEM_ID, id); detailIntent.putExtra(VideoItemDetailFragment.VIDEO_URL, webpage_url); detailIntent.putExtra(VideoItemDetailFragment.STREAMING_SERVICE, currentStreamingServiceId); startActivity(detailIntent); diff --git a/app/src/main/res/drawable-hdpi/ic_pause_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_pause_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..4d2ea05c462291e4a4f8bd30856a25ad33fd420f GIT binary patch literal 105 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8K;fv1aOh{y5d1PRu~43dBH9UPL5 z6iT zv1-Cl6oxNG9SRbBjrL6x9UKB~eGn1x2@FPX>{x_8hU5wQ3Xy0PJdxJ~q z_iq_K_!G`KG5-+yOv%WxTyL@DY?;u*$dMlpS#w6BVnysLnq$!@NFIwK_V|zyX`+ZP zd^r#`i}r-DYZA=~C6Po!ETI&{_o6itALuOWqCRIN4DbBVOhwNXO_4DeUiqe(u`9|H zk>N$Dn7X1|6&a=C(G^);q*N?j(Nz~I6)(>hc~^VWt3CN;Z(D!k$HD;~t$S=!+dM^Y4w`a(JmQ1r_Tu?R;EyWb3-v3h_rb-vx5Y^;8Dbt^ zQ}Ho=u+BTJ?2Yr5Bo;JA_qo!LvEf2ZOH0L>buj8*cmczFVu{wv1nvL;002ovPDHLk FV1lb#Y}x<- literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_pause_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_pause_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..f49aed757118a941b567629ec217cde1aaf257e8 GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZA`BpB)|k7xlYrjj7PUzopr0A0WbcEP)8qwL)}Di@L#=vc2@L4ggOYl3qLMETUUz&Y@xg0B7)Z;_5v!psA;D{ljZ5) zc#?BoPeMz<=Us&de(}5?&XfNETsn+db08ohBH+N95p7%?Q<;9yV8)pwPJEzlFQ~F4 z!bFHq#YWKQ2T}rhj|mEV;Zb}%si4RX8@3cBf&x2i+2WZ6pGkvn67)&SJDs4)PttOu zoGe(91&bs>L;I}W^9vavFY$sIIvn2c4H?rofy)^k_ZRXN2}g3Opo1RgUc#tas$eV= z2%{RRg0)N_jB2F{4l;o-s=X=*WCCGSS5*+n1j4AozbLSe=q>Vyvj52c;BPuYixQ2V zfhuTc+A5l=z~z)#v-=*`>>NI#;~tHSN&GyhXEbnrQ%f3|K3OnNp2-yg(sH4sTU&ah zrE6NeeA4jAyxDndv1LPEw*1;DHoTIq?;gHIIQhp8kRCUrT!jv*C{ m$r5W0{Noqkjk>0hJ%NeAb!~d=tj~okAZ?zmelF{r5}E*xz!xz9 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_play_circle_filled_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_play_circle_filled_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..30330cfad6fc31d88d473c30deb4f1b101e70b23 GIT binary patch literal 666 zcmV;L0%iS)P)KoJ$#a2BE&B1G5KtV4`9-@U>Xiu8Z3hhnNLqTEHU_CFi{b5EA+5lUSV03GGP|Y4B7~O#$)UXE$ zMt7_Swe3NI(Y5rTdwY;zbX`5DZx0fTuBQh@b|B&Ch7$_%Ke9#n$R_TWdgA`<8zjnL zR3;rw+N)z<4_f~(FV|Ok(5&eAvFQHzp}0-`fEJEFG47^*gx!uiHcc`SG9GCd&J5$L)iUJ*c^n<+pYN6wSQT$m`Z~Df*BafCZDwc^29w|978G? plNp)=Km51n;8#DVkv_$ki-GCDeZ`wzAGd%sdb;|#taD0e0szsl7@+_F literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_play_circle_filled_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_play_circle_filled_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..9dc082586133b569032724aed8cec9e6386bb2d0 GIT binary patch literal 883 zcmV-(1C0EMP)VtW-#)^unfcGG6`1cl!3TbBCbQR4az#Z&MMXt1K#o;*Iio^@SKfK& zl?D|~*ndW&kOE z<`WV$Xn z5mr^ck_L=(N0{9*CJ7kj4j^R0h!o&UYM7`>p9CPm1*SSD?jP_QCfoK4Sip4iegMNX zG2Js6?|>6*sN@wehb`s308-pwOLda&0Y70=Kez>S^B>;&o)+?bB<&7Rz-x+cC?nsJ z8^9OTd;k9y{0}cfM@>AN^1*G{b0Fa;33rRh|EQkQ)r!=hx z*b)NBPiaFBP!$5mPw8(RzyOE=JFu=%`1 z-tU2@ZA|tH$}b?!Ii~uPPX7Q!A5~0rNsrXA%Moq~yBji+2VciRCj86S(npU+xC(JR znIH~-_j4Z7pKec3$M56p5Of=K273Wwo@avUk$f0eP%@MPdgmDo?-Ed+I+9)UA?#~d zNq*!b5;Z80Fmn+u%@SAmsmdZL(^nC*?DK-#n(Xl{9kyHsO)|{}|DaQab*8jOBM)$R z@ta_UBFB`ur^Op@w793t5t|gq(rf-YuK6bdM1Tko0V06Z^FN`6oF-LpM=}5a002ov JPDHLkV1l`OfX)B_ literal 0 HcmV?d00001 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cf87951b6..fa767bdf4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -50,6 +50,8 @@ VIDEO & AUDIO INFO ETC + %1$s - NewPipe + Playing in background Video preview thumbnail