[Android] [PATCH 05/11] Basic Audio player for Android TV
Geoffrey Métais
geoffrey.metais at gmail.com
Mon Nov 17 17:53:42 CET 2014
---
vlc-android/AndroidManifest.xml | 9 +-
vlc-android/res/layout/tv_audio_player.xml | 99 +++++++++
.../org/videolan/vlc/gui/tv/MainTvActivity.java | 149 ++++++++-----
.../vlc/gui/tv/MediaItemDetailsFragment.java | 71 +++++-
.../src/org/videolan/vlc/gui/tv/TvUtil.java | 2 +-
.../gui/tv/audioplayer/AudioPlayerActivity.java | 239 +++++++++++++++++++++
.../gui/tv/audioplayer/DividerItemDecoration.java | 105 +++++++++
.../vlc/gui/tv/audioplayer/PlaylistAdapter.java | 72 +++++++
8 files changed, 684 insertions(+), 62 deletions(-)
create mode 100644 vlc-android/res/layout/tv_audio_player.xml
create mode 100644 vlc-android/src/org/videolan/vlc/gui/tv/audioplayer/AudioPlayerActivity.java
create mode 100644 vlc-android/src/org/videolan/vlc/gui/tv/audioplayer/DividerItemDecoration.java
create mode 100644 vlc-android/src/org/videolan/vlc/gui/tv/audioplayer/PlaylistAdapter.java
diff --git a/vlc-android/AndroidManifest.xml b/vlc-android/AndroidManifest.xml
index 3897789..b4fe660 100644
--- a/vlc-android/AndroidManifest.xml
+++ b/vlc-android/AndroidManifest.xml
@@ -31,8 +31,10 @@
android:label="@string/app_name"
android:theme="@style/Theme.Leanback">
- <intent-filter>
+ <intent-filter
+ android:priority="5">
<action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity>
@@ -62,6 +64,11 @@
android:exported="true"
android:theme="@style/Theme.Leanback"/>
+ <activity android:name="org.videolan.vlc.gui.tv.audioplayer.AudioPlayerActivity"
+ android:exported="true"
+ android:launchMode="singleInstance"
+ android:theme="@style/Theme.Leanback"/>
+
<activity android:name=".gui.CompatErrorActivity" />
<activity android:name=".gui.PreferencesActivity" />
<activity
diff --git a/vlc-android/res/layout/tv_audio_player.xml b/vlc-android/res/layout/tv_audio_player.xml
new file mode 100644
index 0000000..06793dc
--- /dev/null
+++ b/vlc-android/res/layout/tv_audio_player.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:weightSum="3.0" >
+
+ <!-- Cover -->
+ <ImageView
+ android:id="@+id/album_cover"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="2"
+ android:src="@drawable/background_cone" >
+ </ImageView>
+
+ <!-- Playlist -->
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/playlist"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:scrollbars="vertical" />
+ </LinearLayout>
+
+ <!-- Media HUD -->
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_alignParentBottom="true"
+ android:paddingRight="30dip"
+ android:paddingLeft="30dip"
+ android:paddingTop="10dip"
+ android:paddingBottom="5dip"
+ android:background="#33000000">
+ <TextView
+ android:id="@+id/media_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_alignParentTop="true"
+ android:textAppearance="@style/TextAppearance.AppCompat.SearchResult.Title"/>
+ <TextView
+ android:id="@+id/media_artist"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_below="@id/media_title"
+ android:textAppearance="@style/TextAppearance.AppCompat.SearchResult.Subtitle"/>
+ <ProgressBar
+ android:id="@+id/media_progress"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_centerHorizontal="true"
+ android:layout_below="@id/media_artist"
+ android:layout_alignStart="@+id/media_controls"
+ android:layout_alignEnd="@+id/media_controls"
+ android:indeterminate="false"/>
+ <!-- Media control buttons -->
+ <LinearLayout
+ android:id="@+id/media_controls"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_centerHorizontal="true"
+ android:layout_below="@id/media_progress">
+ <ImageView
+ android:id="@+id/button_previous"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_previous"
+ android:clickable="true"
+ android:onClick="onClick"/>
+ <ImageView
+ android:id="@+id/button_play"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dp"
+ android:layout_marginRight="10dp"
+ android:src="@drawable/ic_play"
+ android:clickable="true"
+ android:onClick="onClick"/>
+ <ImageView
+ android:id="@+id/button_next"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_next"
+ android:clickable="true"
+ android:onClick="onClick"/>
+ </LinearLayout>
+ </RelativeLayout>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/vlc-android/src/org/videolan/vlc/gui/tv/MainTvActivity.java b/vlc-android/src/org/videolan/vlc/gui/tv/MainTvActivity.java
index 1894879..5f8d137 100644
--- a/vlc-android/src/org/videolan/vlc/gui/tv/MainTvActivity.java
+++ b/vlc-android/src/org/videolan/vlc/gui/tv/MainTvActivity.java
@@ -24,15 +24,16 @@ import java.util.HashMap;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
+import org.videolan.libvlc.LibVLC;
import org.videolan.libvlc.Media;
import org.videolan.vlc.MediaDatabase;
import org.videolan.vlc.MediaLibrary;
import org.videolan.vlc.R;
import org.videolan.vlc.Thumbnailer;
-import org.videolan.vlc.VLCApplication;
+import org.videolan.vlc.gui.audio.AudioUtil;
+import org.videolan.vlc.gui.tv.audioplayer.AudioPlayerActivity;
import org.videolan.vlc.gui.video.VideoBrowserInterface;
import org.videolan.vlc.gui.video.VideoListHandler;
-import org.videolan.vlc.gui.video.VideoPlayerActivity;
import org.videolan.vlc.util.Util;
import android.app.Activity;
@@ -40,9 +41,9 @@ import android.app.FragmentManager;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Parcelable;
import android.support.v17.leanback.app.BackgroundManager;
import android.support.v17.leanback.app.BrowseFragment;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
@@ -57,7 +58,7 @@ import android.view.View.OnClickListener;
public class MainTvActivity extends Activity implements VideoBrowserInterface {
- private static final int NUM_VIDEOS_PREVIEW = 5;
+ private static final int NUM_ITEMS_PREVIEW = 5;
public static final String TAG = "BrowseActivity";
@@ -65,7 +66,7 @@ public class MainTvActivity extends Activity implements VideoBrowserInterface {
protected final CyclicBarrier mBarrier = new CyclicBarrier(2);
private MediaLibrary mMediaLibrary;
private Thumbnailer mThumbnailer;
- protected Media mItemToUpdate;
+ private Media mItemToUpdate;
ArrayObjectAdapter mRowsAdapter;
ArrayObjectAdapter videoAdapter;
ArrayObjectAdapter audioAdapter;
@@ -91,6 +92,16 @@ public class MainTvActivity extends Activity implements VideoBrowserInterface {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ /*
+ * skip browser and show direcly Audio Player if a song is playing
+ */
+ if (LibVLC.getExistingInstance() != null){
+ if (LibVLC.getExistingInstance().isPlaying()){
+ startActivity(new Intent(this, AudioPlayerActivity.class));
+ finish();
+ return;
+ }
+ }
mContext = this;
setContentView(R.layout.tv_main_fragment);
@@ -162,55 +173,7 @@ public class MainTvActivity extends Activity implements VideoBrowserInterface {
}
public void updateList() {
- MediaDatabase mediaDatabase = MediaDatabase.getInstance();
- ArrayList<Media> videoList = mMediaLibrary.getVideoItems();
- ArrayList<Media> audioList = mMediaLibrary.getAudioItems();
- int size;
- Media item;
- Bitmap picture;
- mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
-
- // Update video section
- if (!videoList.isEmpty()) {
- size = videoList.size();
- mVideoIndex = new HashMap<String, Integer>(size);
- videoAdapter = new ArrayObjectAdapter(
- new CardPresenter());
- for (int i = 0 ; i < NUM_VIDEOS_PREVIEW ; ++i) {
- item = videoList.get(i);
- picture = mediaDatabase.getPicture(this, item.getLocation());
-
- videoAdapter.add(item);
- mVideoIndex.put(item.getLocation(), i);
- if (mThumbnailer != null){
- if (picture== null) {
- mThumbnailer.addJob(item);
- } else {
- MediaDatabase.setPicture(item, picture);
- picture = null;
- }
- }
- }
- // Empty item to launch grid activity
- videoAdapter.add(new Media(null, 0, 0, Media.TYPE_GROUP, null, "Browse more", null, null, null, 0, 0, null, 0, 0));
-
- HeaderItem header = new HeaderItem(HEADER_VIDEO, getString(R.string.video), null);
- mRowsAdapter.add(new ListRow(header, videoAdapter));
- }
-
- // update audio section
- if (!audioList.isEmpty()) {
- audioAdapter = new ArrayObjectAdapter(new CardPresenter());
- for (Media music : audioList) {
- audioAdapter.add(music);
- }
- // Empty item to launch grid activity
- audioAdapter.add(new Media(null, 0, 0, Media.TYPE_GROUP, null, "Browse more", null, null, null, 0, 0, null, 0, 0));
-
- HeaderItem header = new HeaderItem(HEADER_MUSIC, getString(R.string.audio), null);
- mRowsAdapter.add(new ListRow(header, audioAdapter));
- }
- mBrowseFragment.setAdapter(mRowsAdapter);
+ new AsyncUpdate().execute();
}
@Override
@@ -228,4 +191,82 @@ public class MainTvActivity extends Activity implements VideoBrowserInterface {
}
private Handler mHandler = new VideoListHandler(this);
+
+ public class AsyncUpdate extends AsyncTask<Void, Void, Void> {
+
+ public AsyncUpdate() { }
+
+ @Override
+ protected void onPreExecute(){
+ mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
+ }
+ @Override
+ protected Void doInBackground(Void... params) {
+ MediaDatabase mediaDatabase = MediaDatabase.getInstance();
+ ArrayList<Media> videoList = mMediaLibrary.getVideoItems();
+ ArrayList<Media> audioList = mMediaLibrary.getAudioItems();
+ int size;
+ Media item;
+ Bitmap picture;
+
+ // Update video section
+ if (!videoList.isEmpty()) {
+ size = videoList.size();
+ mVideoIndex = new HashMap<String, Integer>(size);
+ videoAdapter = new ArrayObjectAdapter(
+ new CardPresenter());
+ if (NUM_ITEMS_PREVIEW < size)
+ size = NUM_ITEMS_PREVIEW;
+ for (int i = 0 ; i < size ; ++i) {
+ item = videoList.get(i);
+ picture = mediaDatabase.getPicture(mContext, item.getLocation());
+
+ videoAdapter.add(item);
+ mVideoIndex.put(item.getLocation(), i);
+ if (mThumbnailer != null){
+ if (picture== null) {
+ mThumbnailer.addJob(item);
+ } else {
+ MediaDatabase.setPicture(item, picture);
+ picture = null;
+ }
+ }
+ }
+ // Empty item to launch grid activity
+ videoAdapter.add(new Media(null, 0, 0, Media.TYPE_GROUP, null, "Browse more", null, null, null, 0, 0, null, 0, 0));
+
+ HeaderItem header = new HeaderItem(HEADER_VIDEO, getString(R.string.video), null);
+ mRowsAdapter.add(new ListRow(header, videoAdapter));
+ }
+
+ // update audio section
+ if (!audioList.isEmpty()) {
+ size = audioList.size();
+ if (NUM_ITEMS_PREVIEW < size)
+ size = NUM_ITEMS_PREVIEW;
+ audioAdapter = new ArrayObjectAdapter(new CardPresenter());
+ for (int i = 0 ; i < size ; ++i) {
+ item = audioList.get(i);
+ picture = AudioUtil.getCover(mContext, item, 320);
+ if (picture != null){
+ MediaDatabase.setPicture(item, picture);
+ picture = null;
+ }
+ audioAdapter.add(item);
+
+ }
+ // Empty item to launch grid activity
+ audioAdapter.add(new Media(null, 0, 0, Media.TYPE_GROUP, null, "Browse more", null, null, null, 0, 0, null, 0, 0));
+
+ HeaderItem header = new HeaderItem(HEADER_MUSIC, getString(R.string.audio), null);
+ mRowsAdapter.add(new ListRow(header, audioAdapter));
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ mBrowseFragment.setAdapter(mRowsAdapter);
+ }
+ }
}
diff --git a/vlc-android/src/org/videolan/vlc/gui/tv/MediaItemDetailsFragment.java b/vlc-android/src/org/videolan/vlc/gui/tv/MediaItemDetailsFragment.java
index 96f6302..5a53b33 100644
--- a/vlc-android/src/org/videolan/vlc/gui/tv/MediaItemDetailsFragment.java
+++ b/vlc-android/src/org/videolan/vlc/gui/tv/MediaItemDetailsFragment.java
@@ -19,9 +19,20 @@
*****************************************************************************/
package org.videolan.vlc.gui.tv;
+import java.util.ArrayList;
+
+import org.videolan.libvlc.LibVLC;
+import org.videolan.libvlc.LibVlcException;
+import org.videolan.vlc.MediaLibrary;
import org.videolan.vlc.R;
+import org.videolan.vlc.audio.AudioServiceController;
+import org.videolan.vlc.gui.audio.AudioPlayer;
+import org.videolan.vlc.gui.audio.AudioUtil;
+import org.videolan.vlc.gui.tv.audioplayer.AudioPlayerActivity;
+import android.content.Intent;
import android.content.res.Resources;
+import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.v17.leanback.app.DetailsFragment;
import android.support.v17.leanback.widget.Action;
@@ -32,38 +43,78 @@ import android.support.v17.leanback.widget.DetailsOverviewRowPresenter;
import android.support.v17.leanback.widget.HeaderItem;
import android.support.v17.leanback.widget.ListRow;
import android.support.v17.leanback.widget.ListRowPresenter;
+import android.support.v17.leanback.widget.OnActionClickedListener;
+import android.util.Log;
-public class MediaItemDetailsFragment extends DetailsFragment {
+public class MediaItemDetailsFragment extends DetailsFragment implements AudioServiceController.AudioServiceConnectionListener {
private static final String TAG = "MediaItemDetailsFragment";
+ private static final int ID_PLAY = 1;
+ private static final int ID_LISTEN = 2;
private ArrayObjectAdapter mRowsAdapter;
+ private AudioServiceController mAudioController;
+ private AudioPlayer mAudioPlayer;
+ private MediaItemDetails mMedia;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mAudioController = AudioServiceController.getInstance();
buildDetails();
}
+ public void onResume(){
+ super.onResume();
+ }
+
+ public void onPause(){
+ super.onPause();
+ if (mAudioController.isPlaying()){
+ mAudioController.stop();
+ mAudioController.unbindAudioService(getActivity());
+ }
+ }
+
private void buildDetails() {
Bundle extras = getActivity().getIntent().getExtras();
- MediaItemDetails media = extras.getParcelable("item");
+ mMedia = extras.getParcelable("item");
ClassPresenterSelector selector = new ClassPresenterSelector();
// Attach your media item details presenter to the row presenter:
DetailsOverviewRowPresenter rowPresenter =
new DetailsOverviewRowPresenter(new DetailsDescriptionPresenter());
+ rowPresenter.setBackgroundColor(getResources().getColor(R.color.darkorange));
+ rowPresenter.setOnActionClickedListener(new OnActionClickedListener() {
+
+ @Override
+ public void onActionClicked(Action action) {
+ if (action.getId() == ID_LISTEN){
+ mAudioController.bindAudioService(getActivity(), MediaItemDetailsFragment.this);
+ } else if (action.getId() == ID_PLAY){
+ ArrayList<String> locations = new ArrayList<String>();
+ locations.add(mMedia.getLocation());
+ Intent intent = new Intent(getActivity(), AudioPlayerActivity.class);
+ intent.putExtra("locations", locations);
+ startActivity(intent);
+ }
+ }
+ });
selector.addClassPresenter(DetailsOverviewRow.class, rowPresenter);
selector.addClassPresenter(ListRow.class,
new ListRowPresenter());
mRowsAdapter = new ArrayObjectAdapter(selector);
Resources res = getActivity().getResources();
- DetailsOverviewRow detailsOverview = new DetailsOverviewRow(media);
+ DetailsOverviewRow detailsOverview = new DetailsOverviewRow(mMedia);
// Add images and action buttons to the details view
- detailsOverview.setImageDrawable(res.getDrawable(R.drawable.cone));
- detailsOverview.addAction(new Action(1, "Play"));
- detailsOverview.addAction(new Action(2, "Delete"));
+ Bitmap cover = AudioUtil.getCover(getActivity(), MediaLibrary.getInstance().getMediaItem(mMedia.getLocation()), 480);
+ if (cover == null)
+ detailsOverview.setImageDrawable(res.getDrawable(R.drawable.cone));
+ else
+ detailsOverview.setImageBitmap(getActivity(), cover);
+ detailsOverview.addAction(new Action(ID_PLAY, "Play"));
+ detailsOverview.addAction(new Action(ID_LISTEN, "Listen"));
mRowsAdapter.add(detailsOverview);
// Add a Related items row
@@ -78,4 +129,12 @@ public class MediaItemDetailsFragment extends DetailsFragment {
setAdapter(mRowsAdapter);
}
+ @Override
+ public void onConnectionSuccess() {
+ mAudioController.load(mMedia.getLocation(), true);
+ }
+
+ @Override
+ public void onConnectionFailed() {}
+
}
diff --git a/vlc-android/src/org/videolan/vlc/gui/tv/TvUtil.java b/vlc-android/src/org/videolan/vlc/gui/tv/TvUtil.java
index b9d6b20..b75268c 100644
--- a/vlc-android/src/org/videolan/vlc/gui/tv/TvUtil.java
+++ b/vlc-android/src/org/videolan/vlc/gui/tv/TvUtil.java
@@ -18,7 +18,7 @@ public class TvUtil {
Intent intent = new Intent(activity,
DetailsActivity.class);
// pass the item information
- intent.putExtra("item", (Parcelable)new MediaItemDetails(media.getTitle(), media.getArtist(), media.getAlbum()+"\n"+media.getLocation(), media.getLocation()));
+ intent.putExtra("item", (Parcelable)new MediaItemDetails(media.getTitle(), media.getArtist(), media.getAlbum(), media.getLocation()));
activity.startActivity(intent);
} else if (media.getType() == Media.TYPE_GROUP){
Intent intent = new Intent(activity, VerticalGridActivity.class);
diff --git a/vlc-android/src/org/videolan/vlc/gui/tv/audioplayer/AudioPlayerActivity.java b/vlc-android/src/org/videolan/vlc/gui/tv/audioplayer/AudioPlayerActivity.java
new file mode 100644
index 0000000..6fde7f8
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/gui/tv/audioplayer/AudioPlayerActivity.java
@@ -0,0 +1,239 @@
+/*****************************************************************************
+ * AudioPlayerActivity.java
+ *****************************************************************************
+ * Copyright © 2012-2014 VLC authors and VideoLAN
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+package org.videolan.vlc.gui.tv.audioplayer;
+
+import java.util.ArrayList;
+
+import org.videolan.libvlc.Media;
+import org.videolan.vlc.MediaLibrary;
+import org.videolan.vlc.R;
+import org.videolan.vlc.audio.AudioServiceController;
+import org.videolan.vlc.gui.audio.AudioUtil;
+import org.videolan.vlc.gui.tv.audioplayer.PlaylistAdapter.ViewHolder;
+import org.videolan.vlc.interfaces.IAudioPlayer;
+import org.videolan.vlc.util.AndroidDevices;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.Adapter;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+public class AudioPlayerActivity extends Activity implements AudioServiceController.AudioServiceConnectionListener, IAudioPlayer{
+ public static final String TAG = "AudioPlayerActivity";
+
+ private Activity mContext;
+ private AudioServiceController mAudioController;
+ private RecyclerView mRecyclerView;
+ private Adapter<ViewHolder> mAdapter;
+ private RecyclerView.LayoutManager mLayoutManager;
+ private ArrayList<String> mLocations;
+
+ //stick event
+ private static final int JOYSTICK_INPUT_DELAY = 300;
+ private long mLastMove;
+
+ private TextView mTitleTv, mArtistTv;
+ private ImageView mPlayPauseButton, mCover;
+ private ProgressBar mProgressBar;
+
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.tv_audio_player);
+
+ mContext = this;
+ mLocations = getIntent().getStringArrayListExtra("locations");
+ mRecyclerView = (RecyclerView) findViewById(R.id.playlist);
+ mLayoutManager = new LinearLayoutManager(this);
+ mRecyclerView.setLayoutManager(mLayoutManager);
+ mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));
+ if (mLocations == null)
+ mLocations = new ArrayList<String>();
+ else {
+ mAdapter = new PlaylistAdapter(mLocations);
+ mRecyclerView.setAdapter(mAdapter);
+ }
+
+ mAudioController = AudioServiceController.getInstance();
+
+ mTitleTv = (TextView)findViewById(R.id.media_title);
+ mArtistTv = (TextView)findViewById(R.id.media_artist);
+ mPlayPauseButton = (ImageView)findViewById(R.id.button_play);
+ mProgressBar = (ProgressBar)findViewById(R.id.media_progress);
+ mCover = (ImageView)findViewById(R.id.album_cover);
+ }
+
+ public void onStart(){
+ super.onStart();
+ mAudioController.bindAudioService(this, this);
+ mAudioController.addAudioPlayer(this);
+ }
+
+ public void onStop(){
+ super.onStop();
+ mAudioController.removeAudioPlayer(this);
+ mAudioController.unbindAudioService(this);
+ mLocations.clear();
+ }
+
+ @Override
+ public void onConnectionSuccess() {
+ ArrayList<String> medialocations = (ArrayList<String>) mAudioController.getMediaLocations();
+ if (!mLocations.isEmpty() && !mLocations.equals(medialocations))
+ mAudioController.load(mLocations, 0, true);
+ else {
+ mLocations = medialocations;
+ update();
+ mAdapter = new PlaylistAdapter(mLocations);
+ mRecyclerView.setAdapter(mAdapter);
+ }
+ }
+
+ @Override
+ public void onConnectionFailed() {}
+
+ @Override
+ public void update() {
+ mPlayPauseButton.setImageResource(mAudioController.isPlaying() ? R.drawable.ic_pause : R.drawable.ic_play);
+ if (mAudioController.hasMedia()) {
+ mTitleTv.setText(mAudioController.getTitle());
+ mArtistTv.setText(mAudioController.getArtist());
+ mProgressBar.setMax(mAudioController.getLength());
+ Media media = MediaLibrary.getInstance().getMediaItem(mAudioController.getCurrentMediaLocation());
+ Bitmap cover = AudioUtil.getCover(this, media, mCover.getWidth());
+ if (cover == null)
+ cover = mAudioController.getCover();
+ if (cover == null)
+ mCover.setImageResource(R.drawable.background_cone);
+ else
+ mCover.setImageBitmap(cover);
+ }
+ }
+
+ @Override
+ public void updateProgress() {
+ mProgressBar.setProgress(mAudioController.getTime());
+ }
+
+ public boolean onKeyDown(int keyCode, KeyEvent event){
+ switch (keyCode){
+ case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+ case KeyEvent.KEYCODE_MEDIA_PLAY:
+ case KeyEvent.KEYCODE_MEDIA_PAUSE:
+ case KeyEvent.KEYCODE_SPACE:
+ case KeyEvent.KEYCODE_BUTTON_A:
+ togglePlayPause();
+ break;
+ case KeyEvent.KEYCODE_F:
+ case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+ case KeyEvent.KEYCODE_BUTTON_R1:
+ goNext();
+ break;
+ case KeyEvent.KEYCODE_R:
+ case KeyEvent.KEYCODE_MEDIA_REWIND:
+ case KeyEvent.KEYCODE_BUTTON_L1:
+ goPrevious();
+ break;
+ default:
+ return super.onKeyDown(keyCode, event);
+ }
+ return true;
+ }
+
+ @TargetApi(12) //only active for Android 3.1+
+ public boolean dispatchGenericMotionEvent(MotionEvent event){
+
+ InputDevice mInputDevice = event.getDevice();
+
+ float x = AndroidDevices.getCenteredAxis(event, mInputDevice,
+ MotionEvent.AXIS_X);
+ float y = AndroidDevices.getCenteredAxis(event, mInputDevice,
+ MotionEvent.AXIS_Y);
+ float z = AndroidDevices.getCenteredAxis(event, mInputDevice,
+ MotionEvent.AXIS_Z);
+ float rz = AndroidDevices.getCenteredAxis(event, mInputDevice,
+ MotionEvent.AXIS_RZ);
+
+ if (System.currentTimeMillis() - mLastMove > JOYSTICK_INPUT_DELAY){
+ if (Math.abs(x) > 0.3){
+ seek(x > 0.0f ? 10000 : -10000);
+ mLastMove = System.currentTimeMillis();
+ }
+ //TODO Will we change volume in app on TV ?
+ /*else if (Math.abs(rz) > 0.3){
+ mVol = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
+ int delta = -(int) ((rz / 7) * mAudioMax);
+ int vol = (int) Math.min(Math.max(mVol + delta, 0), mAudioMax);
+ setAudioVolume(vol);
+ mLastMove = System.currentTimeMillis();
+ }*/
+ }
+ return true;
+ }
+
+ private void seek(int delta) {
+ int time = mAudioController.getTime()+delta;
+ if (time < 0 || time > mAudioController.getLength())
+ return;
+ mAudioController.setTime(time);
+ }
+
+ public void onClick(View v){
+ switch (v.getId()){
+ case R.id.button_play:
+ togglePlayPause();
+ break;
+ case R.id.button_next:
+ goNext();
+ break;
+ case R.id.button_previous:
+ goPrevious();
+ break;
+ }
+ }
+
+ private void goPrevious() {
+ if (mAudioController.hasPrevious()) {
+ mAudioController.previous();
+ }
+ }
+
+ private void goNext() {
+ if (mAudioController.hasNext()){
+ mAudioController.next();
+ }
+ }
+
+ private void togglePlayPause() {
+ if (mAudioController.isPlaying())
+ mAudioController.pause();
+ else if (mAudioController.hasMedia())
+ mAudioController.play();
+ }
+}
diff --git a/vlc-android/src/org/videolan/vlc/gui/tv/audioplayer/DividerItemDecoration.java b/vlc-android/src/org/videolan/vlc/gui/tv/audioplayer/DividerItemDecoration.java
new file mode 100644
index 0000000..a9c9520
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/gui/tv/audioplayer/DividerItemDecoration.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.videolan.vlc.gui.tv.audioplayer;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+public class DividerItemDecoration extends RecyclerView.ItemDecoration {
+
+ private static final int[] ATTRS = new int[]{
+ android.R.attr.listDivider
+ };
+
+ public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
+
+ public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
+
+ private Drawable mDivider;
+
+ private int mOrientation;
+
+ public DividerItemDecoration(Context context, int orientation) {
+ final TypedArray a = context.obtainStyledAttributes(ATTRS);
+ mDivider = a.getDrawable(0);
+ a.recycle();
+ setOrientation(orientation);
+ }
+
+ public void setOrientation(int orientation) {
+ if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
+ throw new IllegalArgumentException("invalid orientation");
+ }
+ mOrientation = orientation;
+ }
+
+ @Override
+ public void onDraw(Canvas c, RecyclerView parent) {
+ if (mOrientation == VERTICAL_LIST) {
+ drawVertical(c, parent);
+ } else {
+ drawHorizontal(c, parent);
+ }
+ }
+
+ public void drawVertical(Canvas c, RecyclerView parent) {
+ final int left = parent.getPaddingLeft();
+ final int right = parent.getWidth() - parent.getPaddingRight();
+
+ final int childCount = parent.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = parent.getChildAt(i);
+ final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
+ .getLayoutParams();
+ final int top = child.getBottom() + params.bottomMargin;
+ final int bottom = top + mDivider.getIntrinsicHeight();
+ mDivider.setBounds(left, top, right, bottom);
+ mDivider.draw(c);
+ }
+ }
+
+ public void drawHorizontal(Canvas c, RecyclerView parent) {
+ final int top = parent.getPaddingTop();
+ final int bottom = parent.getHeight() - parent.getPaddingBottom();
+
+ final int childCount = parent.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = parent.getChildAt(i);
+ final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
+ .getLayoutParams();
+ final int left = child.getRight() + params.rightMargin;
+ final int right = left + mDivider.getIntrinsicHeight();
+ mDivider.setBounds(left, top, right, bottom);
+ mDivider.draw(c);
+ }
+ }
+
+ @Override
+ public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
+ if (mOrientation == VERTICAL_LIST) {
+ outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
+ } else {
+ outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
+ }
+ }
+}
diff --git a/vlc-android/src/org/videolan/vlc/gui/tv/audioplayer/PlaylistAdapter.java b/vlc-android/src/org/videolan/vlc/gui/tv/audioplayer/PlaylistAdapter.java
new file mode 100644
index 0000000..0eca16d
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/gui/tv/audioplayer/PlaylistAdapter.java
@@ -0,0 +1,72 @@
+/*****************************************************************************
+ * SearchFragment.java
+ *****************************************************************************
+ * Copyright © 2012-2014 VLC authors and VideoLAN
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+package org.videolan.vlc.gui.tv.audioplayer;
+
+import java.util.ArrayList;
+
+import org.videolan.libvlc.Media;
+import org.videolan.vlc.MediaLibrary;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+public class PlaylistAdapter extends RecyclerView.Adapter<PlaylistAdapter.ViewHolder> {
+ private ArrayList<String> mDataset;
+ private static MediaLibrary sMediaLibrary = MediaLibrary.getInstance();
+
+ public static class ViewHolder extends RecyclerView.ViewHolder {
+ public TextView mTitleTv;
+ public TextView mArtistTv;
+ public ViewHolder(View v) {
+ super(v);
+ mTitleTv = (TextView) v.findViewById(android.R.id.text1);
+ mArtistTv = (TextView) v.findViewById(android.R.id.text2);
+ }
+ }
+
+ public PlaylistAdapter(ArrayList<String> myDataset) {
+ mDataset = myDataset;
+ }
+
+ @Override
+ public PlaylistAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
+ int viewType) {
+ View v = LayoutInflater.from(parent.getContext())
+ .inflate(android.R.layout.simple_list_item_2, parent, false);
+
+ ViewHolder vh = new ViewHolder(v);
+ return vh;
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int position) {
+ Media media = sMediaLibrary.getMediaItem(mDataset.get(position));
+ holder.mTitleTv.setText(media.getTitle());
+ holder.mArtistTv.setText(media.getArtist());
+ }
+
+ @Override
+ public int getItemCount() {
+ return mDataset.size();
+ }
+}
\ No newline at end of file
--
1.9.1
More information about the Android
mailing list