From 63bff073b9ccf971d27f5ea22a20f212e092ad98 Mon Sep 17 00:00:00 2001 From: Stevan Medic Date: Fri, 7 Apr 2017 14:59:20 +0200 Subject: [PATCH 1/3] Changed logic for playing next/previous songs --- app/build.gradle | 4 +- .../smedic/tubtub/BackgroundAudioService.java | 44 +++++++------------ .../tubtub/fragments/SearchFragment.java | 3 +- .../tubtub/youtube/YouTubeVideosLoader.java | 18 +++++--- app/src/main/res/layout/playlist_item.xml | 1 + 5 files changed, 32 insertions(+), 38 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 9eabffc..9c16a96 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "com.smedic.tubtub" minSdkVersion 16 targetSdkVersion 25 - versionCode 9 - versionName "1.8" + versionCode 10 + versionName "1.9" } buildTypes { diff --git a/app/src/main/java/com/smedic/tubtub/BackgroundAudioService.java b/app/src/main/java/com/smedic/tubtub/BackgroundAudioService.java index 1748b83..569ac4d 100644 --- a/app/src/main/java/com/smedic/tubtub/BackgroundAudioService.java +++ b/app/src/main/java/com/smedic/tubtub/BackgroundAudioService.java @@ -45,7 +45,6 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.ListIterator; import at.huber.youtubeExtractor.VideoMeta; import at.huber.youtubeExtractor.YouTubeExtractor; @@ -75,15 +74,12 @@ public class BackgroundAudioService extends Service implements MediaPlayer.OnCom private YouTubeVideo videoItem; private boolean isStarting = false; + private int currentSongIndex = 0; private ArrayList youTubeVideos; - private ListIterator iterator; private NotificationCompat.Builder builder = null; - private boolean nextWasCalled = false; - private boolean previousWasCalled = false; - @Override public IBinder onBind(Intent intent) { return null; @@ -177,9 +173,9 @@ private void handleMedia(Intent intent) { mediaType = ItemType.YOUTUBE_MEDIA_TYPE_PLAYLIST; youTubeVideos = (ArrayList) intent.getSerializableExtra(Config.YOUTUBE_TYPE_PLAYLIST); int startPosition = intent.getIntExtra(Config.YOUTUBE_TYPE_PLAYLIST_VIDEO_POS, 0); - - iterator = youTubeVideos.listIterator(startPosition); - playNext(); + videoItem = youTubeVideos.get(startPosition); + currentSongIndex = startPosition; + playVideo(); break; default: Log.d(TAG, "Unknown command"); @@ -371,17 +367,13 @@ private void playNext() { return; } - if (previousWasCalled) { - previousWasCalled = false; - iterator.next(); - } - - if (!iterator.hasNext()) { - iterator = youTubeVideos.listIterator(); + if (youTubeVideos.size() > currentSongIndex + 1) { + currentSongIndex++; + } else { //play 1st song + currentSongIndex = 0; } - videoItem = iterator.next(); - nextWasCalled = true; + videoItem = youTubeVideos.get(currentSongIndex); playVideo(); } @@ -395,17 +387,12 @@ private void playPrevious() { return; } - if (nextWasCalled) { - iterator.previous(); - nextWasCalled = false; + if (currentSongIndex - 1 >= 0) { + currentSongIndex--; + } else { //play last song + currentSongIndex = youTubeVideos.size() - 1; } - - if (!iterator.hasPrevious()) { - iterator = youTubeVideos.listIterator(youTubeVideos.size()); - } - - videoItem = iterator.previous(); - previousWasCalled = true; + videoItem = youTubeVideos.get(youTubeVideos.size() - 1); playVideo(); } @@ -482,6 +469,7 @@ private YtFile getBestStream(SparseArray ytFiles) { */ private void extractUrlAndPlay() { String youtubeLink = Config.YOUTUBE_BASE_URL + videoItem.getId(); + Log.d(TAG, "extractUrlAndPlay: " + videoItem.getId()); new YouTubeExtractor(this) { @Override protected void onExtractionComplete(SparseArray ytFiles, VideoMeta videoMeta) { @@ -500,7 +488,7 @@ protected void onExtractionComplete(SparseArray ytFiles, VideoMeta video mMediaPlayer.prepare(); mMediaPlayer.start(); - Toast.makeText(YTApplication.getAppContext(), videoMeta.getTitle(), + Toast.makeText(YTApplication.getAppContext(), videoItem.getTitle(), Toast.LENGTH_SHORT).show(); } } catch (IOException io) { diff --git a/app/src/main/java/com/smedic/tubtub/fragments/SearchFragment.java b/app/src/main/java/com/smedic/tubtub/fragments/SearchFragment.java index 1acd325..be7701b 100644 --- a/app/src/main/java/com/smedic/tubtub/fragments/SearchFragment.java +++ b/app/src/main/java/com/smedic/tubtub/fragments/SearchFragment.java @@ -163,6 +163,7 @@ public void onFavoriteClicked(YouTubeVideo video, boolean isChecked) { @Override public void onItemClick(YouTubeVideo video) { YouTubeSqlDb.getInstance().videos(YouTubeSqlDb.VIDEOS_TYPE.RECENTLY_WATCHED).create(video); - itemSelected.onVideoSelected(video); + //itemSelected.onVideoSelected(video); + itemSelected.onPlaylistSelected(searchResultsList, searchResultsList.indexOf(video)); } } diff --git a/app/src/main/java/com/smedic/tubtub/youtube/YouTubeVideosLoader.java b/app/src/main/java/com/smedic/tubtub/youtube/YouTubeVideosLoader.java index b0b905f..9ae4a15 100644 --- a/app/src/main/java/com/smedic/tubtub/youtube/YouTubeVideosLoader.java +++ b/app/src/main/java/com/smedic/tubtub/youtube/YouTubeVideosLoader.java @@ -69,14 +69,18 @@ public List loadInBackground() { item.setId(searchResults.get(i).getId().getVideoId()); //video info if (videoResults.get(i) != null) { - BigInteger viewsNumber = videoResults.get(i).getStatistics().getViewCount(); - String viewsFormatted = NumberFormat.getIntegerInstance().format(viewsNumber) + " views"; - item.setViewCount(viewsFormatted); - String isoTime = videoResults.get(i).getContentDetails().getDuration(); - String time = Utils.convertISO8601DurationToNormalTime(isoTime); - item.setDuration(time); + if (videoResults.get(i).getStatistics() != null) { + BigInteger viewsNumber = videoResults.get(i).getStatistics().getViewCount(); + String viewsFormatted = NumberFormat.getIntegerInstance().format(viewsNumber) + " views"; + item.setViewCount(viewsFormatted); + } + if (videoResults.get(i).getContentDetails() != null) { + String isoTime = videoResults.get(i).getContentDetails().getDuration(); + String time = Utils.convertISO8601DurationToNormalTime(isoTime); + item.setDuration(time); + } } else { - item.setDuration("NA"); + item.setDuration(""); } //add to the list diff --git a/app/src/main/res/layout/playlist_item.xml b/app/src/main/res/layout/playlist_item.xml index 6604f63..08aa20c 100644 --- a/app/src/main/res/layout/playlist_item.xml +++ b/app/src/main/res/layout/playlist_item.xml @@ -17,6 +17,7 @@ android:descendantFocusability="blocksDescendants" android:orientation="horizontal" android:paddingBottom="4dp" + android:weightSum="100" android:paddingTop="4dp"> Date: Fri, 7 Apr 2017 16:28:16 +0200 Subject: [PATCH 2/3] Added adaptive stream quality choosing (according to the internet speed) --- app/build.gradle | 10 +-- app/libs/jsr305-1.3.9.jar | Bin 33015 -> 0 bytes .../smedic/tubtub/BackgroundAudioService.java | 61 ++++++++++++++---- 3 files changed, 55 insertions(+), 16 deletions(-) delete mode 100644 app/libs/jsr305-1.3.9.jar diff --git a/app/build.gradle b/app/build.gradle index 9c16a96..49b4718 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -45,9 +45,9 @@ dependencies { testCompile 'junit:junit:4.12' compile(name: 'color_picker_library-release', ext: 'aar') compile 'com.android.support:appcompat-v7' - compile 'com.android.support:design:25.1.1' - compile 'com.android.support:support-v4:25.1.1' - compile 'com.android.support:recyclerview-v7:25.1.1' + compile 'com.android.support:design:25.2.0' + compile 'com.android.support:support-v4:25.2.0' + compile 'com.android.support:recyclerview-v7:25.2.0' compile 'com.google.android.gms:play-services-auth:10.2.0' compile files('libs/google-api-client-android-1.21.0.jar') compile files('libs/google-http-client-android-1.21.0.jar') @@ -56,10 +56,10 @@ dependencies { compile files('libs/google-http-client-jackson2-1.21.0.jar') compile files('libs/google-oauth-client-1.21.0.jar') compile files('libs/jackson-core-2.1.3.jar') - compile files('libs/jsr305-1.3.9.jar') compile files('libs/google-api-services-youtube-v3-rev160-1.21.0.jar') compile files('libs/picasso-2.5.2.jar') compile 'com.github.HaarigerHarald:android-youtubeExtractor:master-SNAPSHOT' compile 'pub.devrel:easypermissions:0.3.0' - compile 'com.android.support:cardview-v7:25.1.1' + compile 'com.facebook.network.connectionclass:connectionclass:1.0.1' //this lib include jsr501 + compile 'com.android.support:cardview-v7:25.2.0' } diff --git a/app/libs/jsr305-1.3.9.jar b/app/libs/jsr305-1.3.9.jar deleted file mode 100644 index a9afc6619b4b9369f0501d06ceb1cf82a1e47cdd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33015 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%0Dh_LWTVixa57{`H<&*p`8t2_1L+v7 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 diff --git a/app/src/main/java/com/smedic/tubtub/BackgroundAudioService.java b/app/src/main/java/com/smedic/tubtub/BackgroundAudioService.java index 569ac4d..124414b 100644 --- a/app/src/main/java/com/smedic/tubtub/BackgroundAudioService.java +++ b/app/src/main/java/com/smedic/tubtub/BackgroundAudioService.java @@ -37,6 +37,9 @@ import android.util.SparseArray; import android.widget.Toast; +import com.facebook.network.connectionclass.ConnectionClassManager; +import com.facebook.network.connectionclass.ConnectionQuality; +import com.facebook.network.connectionclass.DeviceBandwidthSampler; import com.smedic.tubtub.model.ItemType; import com.smedic.tubtub.model.YouTubeVideo; import com.smedic.tubtub.utils.Config; @@ -80,6 +83,9 @@ public class BackgroundAudioService extends Service implements MediaPlayer.OnCom private NotificationCompat.Builder builder = null; + private DeviceBandwidthSampler deviceBandwidthSampler; + private ConnectionQuality connectionQuality = ConnectionQuality.MODERATE; + @Override public IBinder onBind(Intent intent) { return null; @@ -94,6 +100,7 @@ public void onCreate() { mMediaPlayer.setOnPreparedListener(this); initMediaSessions(); initPhoneCallListener(); + deviceBandwidthSampler = DeviceBandwidthSampler.getInstance(); } @Override @@ -111,6 +118,7 @@ public void onCallStateChanged(int state, String incomingNumber) { pauseVideo(); } else if (state == TelephonyManager.CALL_STATE_IDLE) { //Not in call: Play music + Log.d(TAG, "onCallStateChanged: "); resumeVideo(); } else if (state == TelephonyManager.CALL_STATE_OFFHOOK) { //A call is dialing, active or on hold @@ -125,6 +133,11 @@ public void onCallStateChanged(int state, String incomingNumber) { } } + @Override + public void onDestroy() { + super.onDestroy(); + } + /** * Handles intent (player options play/pause/stop...) * @@ -417,7 +430,7 @@ private void pauseVideo() { * Resumes video */ private void resumeVideo() { - if (mMediaPlayer != null) { + if (mMediaPlayer != null && mMediaPlayer.isPlaying()) { mMediaPlayer.start(); } } @@ -449,19 +462,44 @@ private void stopPlayer() { /** * Get the best available audio stream + *

+ * Itags: + * 141 - mp4a - stereo, 44.1 KHz 256 Kbps + * 251 - webm - stereo, 48 KHz 160 Kbps + * 140 - mp4a - stereo, 44.1 KHz 128 Kbps + * 17 - mp4 - stereo, 44.1 KHz 96-100 Kbps * * @param ytFiles Array of available streams * @return Audio stream with highest bitrate */ private YtFile getBestStream(SparseArray ytFiles) { - if (ytFiles.get(141) != null) { - return ytFiles.get(141); //mp4a - stereo, 44.1 KHz 256 Kbps - } else if (ytFiles.get(251) != null) { - return ytFiles.get(251); //webm - stereo, 48 KHz 160 Kbps - } else if (ytFiles.get(140) != null) { - return ytFiles.get(140); //mp4a - stereo, 44.1 KHz 128 Kbps + + connectionQuality = ConnectionClassManager.getInstance().getCurrentBandwidthQuality(); + int[] itags = new int[]{251, 141, 140, 17}; + + if (connectionQuality != null && connectionQuality != ConnectionQuality.UNKNOWN) { + switch (connectionQuality) { + case POOR: + itags = new int[]{17, 140, 251, 141}; + break; + case MODERATE: + itags = new int[]{251, 141, 140, 17}; + break; + case GOOD: + case EXCELLENT: + itags = new int[]{141, 251, 140, 17}; + break; + } + } + + if (ytFiles.get(itags[0]) != null) { + return ytFiles.get(itags[0]); + } else if (ytFiles.get(itags[1]) != null) { + return ytFiles.get(itags[1]); + } else if (ytFiles.get(itags[2]) != null) { + return ytFiles.get(itags[2]); } - return ytFiles.get(17); //mp4 - stereo, 44.1 KHz 96-100 Kbps + return ytFiles.get(itags[3]); } /** @@ -469,7 +507,8 @@ private YtFile getBestStream(SparseArray ytFiles) { */ private void extractUrlAndPlay() { String youtubeLink = Config.YOUTUBE_BASE_URL + videoItem.getId(); - Log.d(TAG, "extractUrlAndPlay: " + videoItem.getId()); + deviceBandwidthSampler.startSampling(); + new YouTubeExtractor(this) { @Override protected void onExtractionComplete(SparseArray ytFiles, VideoMeta videoMeta) { @@ -479,6 +518,7 @@ protected void onExtractionComplete(SparseArray ytFiles, VideoMeta video Toast.LENGTH_SHORT).show(); return; } + deviceBandwidthSampler.stopSampling(); YtFile ytFile = getBestStream(ytFiles); try { if (mMediaPlayer != null) { @@ -488,8 +528,7 @@ protected void onExtractionComplete(SparseArray ytFiles, VideoMeta video mMediaPlayer.prepare(); mMediaPlayer.start(); - Toast.makeText(YTApplication.getAppContext(), videoItem.getTitle(), - Toast.LENGTH_SHORT).show(); + Toast.makeText(YTApplication.getAppContext(), videoItem.getTitle(), Toast.LENGTH_SHORT).show(); } } catch (IOException io) { io.printStackTrace(); From bfc13710474068a801c97498e5c3a5ae7b387737 Mon Sep 17 00:00:00 2001 From: Stevan Medic Date: Wed, 19 Apr 2017 20:13:08 +0200 Subject: [PATCH 3/3] Fixed video item layout --- app/src/main/res/layout/video_item.xml | 143 +++++++++++++------------ 1 file changed, 74 insertions(+), 69 deletions(-) diff --git a/app/src/main/res/layout/video_item.xml b/app/src/main/res/layout/video_item.xml index 995b4bb..7f83d41 100644 --- a/app/src/main/res/layout/video_item.xml +++ b/app/src/main/res/layout/video_item.xml @@ -9,83 +9,88 @@ cardview:cardElevation="2dp" cardview:cardUseCompatPadding="true"> - + android:layout_height="wrap_content"> - + - + - - + - + + - + - + - + - + - - + + + + + \ No newline at end of file