[Android] [PATCH] Extended browsing for videos WIP

Geoffrey Métais geoffrey.metais at gmail.com
Wed Oct 1 19:22:52 CEST 2014


Only videos for now, will be adapted to other media soon
Need to find a batter 'grid launcher' too.
---
 vlc-android/AndroidManifest.xml                    |   4 +
 vlc-android/res/layout/tv_vertical_grid            |   6 +
 .../src/org/videolan/vlc/gui/tv/GridFragment.java  | 189 +++++++++++++++++++++
 .../org/videolan/vlc/gui/tv/MainTvActivity.java    |  37 ++--
 .../vlc/gui/tv/MediaItemDetailsFragment.java       |   4 +-
 .../videolan/vlc/gui/tv/VerticalGridActivity.java  |  17 ++
 6 files changed, 242 insertions(+), 15 deletions(-)
 create mode 100644 vlc-android/res/layout/tv_vertical_grid
 create mode 100644 vlc-android/src/org/videolan/vlc/gui/tv/GridFragment.java
 create mode 100644 vlc-android/src/org/videolan/vlc/gui/tv/VerticalGridActivity.java

diff --git a/vlc-android/AndroidManifest.xml b/vlc-android/AndroidManifest.xml
index cd95905..bb73720 100644
--- a/vlc-android/AndroidManifest.xml
+++ b/vlc-android/AndroidManifest.xml
@@ -54,6 +54,10 @@
         android:exported="true"
         android:theme="@style/Theme.Leanback"/>
 
+      <activity android:name="org.videolan.vlc.gui.tv.VerticalGridActivity"
+        android:exported="true"
+        android:theme="@style/Theme.Leanback"/>
+
         <activity android:name=".gui.CompatErrorActivity" />
         <activity android:name=".gui.PreferencesActivity" />
         <activity
diff --git a/vlc-android/res/layout/tv_vertical_grid b/vlc-android/res/layout/tv_vertical_grid
new file mode 100644
index 0000000..7d58289
--- /dev/null
+++ b/vlc-android/res/layout/tv_vertical_grid
@@ -0,0 +1,6 @@
+<fragment xmlns:android="http://schemas.android.com/apk/res/android"
+    android:name="org.videolan.vlc.gui.tv.GridFragment"
+    android:id="@+id/vertical_grid_fragment"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+/>
\ No newline at end of file
diff --git a/vlc-android/src/org/videolan/vlc/gui/tv/GridFragment.java b/vlc-android/src/org/videolan/vlc/gui/tv/GridFragment.java
new file mode 100644
index 0000000..e24f89b
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/gui/tv/GridFragment.java
@@ -0,0 +1,189 @@
+package org.videolan.vlc.gui.tv;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
+
+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.video.VideoBrowserInterface;
+import org.videolan.vlc.gui.video.VideoPlayerActivity;
+import org.videolan.vlc.util.WeakHandler;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.support.v17.leanback.app.VerticalGridFragment;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.OnItemClickedListener;
+import android.support.v17.leanback.widget.OnItemSelectedListener;
+import android.support.v17.leanback.widget.Row;
+import android.support.v17.leanback.widget.VerticalGridPresenter;
+import android.util.Log;
+
+public class GridFragment extends VerticalGridFragment implements VideoBrowserInterface {
+	private static final String TAG = "VerticalGridFragment";
+
+	private static final int NUM_COLUMNS = 5;
+
+	protected final CyclicBarrier mBarrier = new CyclicBarrier(2);
+	protected Media mItemToUpdate;
+	private ArrayObjectAdapter mAdapter;
+	private MediaLibrary mMediaLibrary;
+	private Thumbnailer mThumbnailer;
+	HashMap<String, Integer> mVideoIndex;
+	Context mContext;
+
+	@Override
+	public void onCreate(Bundle savedInstanceState) {
+		Log.i(TAG, "onCreate");
+		super.onCreate(savedInstanceState);
+		mContext = getActivity();
+
+		setTitle(getString(R.string.app_name_full));
+
+		mMediaLibrary = MediaLibrary.getInstance();
+		mThumbnailer = new Thumbnailer(mContext, getActivity().getWindowManager().getDefaultDisplay());
+		setupFragment();
+	}
+
+	public void onResume() {
+		super.onResume();
+		if (mMediaLibrary.isWorking()) {
+			actionScanStart();
+		}
+
+		/* Start the thumbnailer */
+		if (mThumbnailer != null)
+			mThumbnailer.start(this);
+	}
+
+	public void onPause() {
+		super.onPause();
+		mMediaLibrary.removeUpdateHandler(mHandler);
+
+		/* Stop the thumbnailer */
+		if (mThumbnailer != null)
+			mThumbnailer.stop();
+	}
+
+	@Override
+	public void onDestroy() {
+		super.onDestroy();
+		if (mThumbnailer != null)
+			mThumbnailer.clearJobs();
+		mBarrier.reset();
+	}
+
+	private void setupFragment() {
+		int size;
+		Media media;
+		Bitmap picture;
+
+		MediaDatabase mediaDatabase = MediaDatabase.getInstance();
+		VerticalGridPresenter gridPresenter = new VerticalGridPresenter();
+		mAdapter = new ArrayObjectAdapter(new CardPresenter());
+
+		gridPresenter.setNumberOfColumns(NUM_COLUMNS);
+		setGridPresenter(gridPresenter);
+
+
+		ArrayList<Media> videoList = mMediaLibrary.getVideoItems();
+		size = videoList.size();
+		mVideoIndex = new HashMap<String, Integer>(size);
+		
+		for (int i = 0 ; i < size ; ++i){
+			media = videoList.get(i);
+			picture = mediaDatabase.getPicture(mContext, media.getLocation());
+			mAdapter.add(media);
+			mVideoIndex.put(media.getLocation(), i);
+			if (mThumbnailer != null){
+				if (picture== null) {
+					mThumbnailer.addJob(media);
+				} else {
+					MediaDatabase.setPicture(media, picture);
+					picture = null;
+				}
+			}
+		}
+
+		setAdapter(mAdapter);
+
+		/*setOnItemSelectedListener(new OnItemSelectedListener() {
+			@Override
+			public void onItemSelected(Object item, Row row) {
+			}
+		});*/
+
+		setOnItemClickedListener(new OnItemClickedListener() {
+			@Override
+			public void onItemClicked(Object item, Row row) {
+				Media media = (Media)item;
+				if (media.getType() == Media.TYPE_VIDEO){
+					VideoPlayerActivity.start(getActivity(), media.getLocation(), false);
+				}
+			}
+		});
+	}
+
+	public void await() throws InterruptedException, BrokenBarrierException {
+		mBarrier.await();
+	}
+
+	public void resetBarrier() {
+		mBarrier.reset();
+	}
+	@Override
+	public void setItemToUpdate(Media item) {
+		mItemToUpdate = item;
+		mHandler.sendEmptyMessage(UPDATE_ITEM);
+	}
+
+	private void updateItem() {
+		mAdapter.notifyArrayItemRangeChanged(mVideoIndex.get(mItemToUpdate.getLocation()), 1);
+		try {
+			mBarrier.await();
+		} catch (InterruptedException e) {
+		} catch (BrokenBarrierException e) {}
+	}
+
+	public static void actionScanStart() {
+		Intent intent = new Intent();
+		intent.setAction(ACTION_SCAN_START);
+		VLCApplication.getAppContext().sendBroadcast(intent);
+	}
+
+	public static void actionScanStop() {
+		Intent intent = new Intent();
+		intent.setAction(ACTION_SCAN_STOP);
+		VLCApplication.getAppContext().sendBroadcast(intent);
+	}
+
+	private Handler mHandler = new VideoListHandler(this);
+
+	private static class VideoListHandler extends WeakHandler<GridFragment> {
+		public VideoListHandler(GridFragment owner) {
+			super(owner);
+		}
+
+		@Override
+		public void handleMessage(Message msg) {
+			GridFragment owner = getOwner();
+			if(owner == null) return;
+
+			switch (msg.what) {
+			case UPDATE_ITEM:
+				owner.updateItem();
+				break;
+			}
+		}
+	};
+}
\ 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 7650cf1..42ff0c4 100644
--- a/vlc-android/src/org/videolan/vlc/gui/tv/MainTvActivity.java
+++ b/vlc-android/src/org/videolan/vlc/gui/tv/MainTvActivity.java
@@ -17,15 +17,13 @@ import org.videolan.vlc.util.WeakHandler;
 
 import android.app.Activity;
 import android.app.FragmentManager;
-import android.content.ContentResolver;
 import android.content.Intent;
-import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
-import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+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;
@@ -34,9 +32,10 @@ import android.support.v17.leanback.widget.ListRow;
 import android.support.v17.leanback.widget.ListRowPresenter;
 import android.support.v17.leanback.widget.OnItemClickedListener;
 import android.support.v17.leanback.widget.Row;
-import android.support.v4.app.FragmentActivity;
 
-public class MainTvActivity extends FragmentActivity implements VideoBrowserInterface {
+public class MainTvActivity extends Activity implements VideoBrowserInterface {
+
+	private static final int NUM_VIDEOS_PREVIEW = 5;
 
 	public static final String TAG = "BrowseActivity";
 
@@ -55,14 +54,21 @@ public class MainTvActivity extends FragmentActivity implements VideoBrowserInte
 	OnItemClickedListener mItemClickListener = new OnItemClickedListener() {
 		@Override
 		public void onItemClicked(Object item, Row row) {
-			VideoPlayerActivity.start(mContext, ((Media)item).getLocation(), false);
-			//code to launch a DetailActivity
-			//                    Intent intent = new Intent(MainTvActivity.this,
-			//                            DetailsActivity.class);
-			//                    // pass the item information
-			//                    intent.putExtra("id", row.getId());
-			//                    intent.putExtra("item", (Parcelable)item);
-			//                    startActivity(intent);
+			Media media = (Media)item;
+			if (media.getType() == Media.TYPE_VIDEO){
+				VideoPlayerActivity.start(mContext, media.getLocation(), false);
+			} else if (media.getType() == Media.TYPE_AUDIO){
+
+				Intent intent = new Intent(MainTvActivity.this,
+						DetailsActivity.class);
+				// pass the item information
+				intent.putExtra("id", row.getId());
+				intent.putExtra("item", (Parcelable)new TvMedia(0, media.getTitle(), media.getDescription(), media.getArtworkURL(), media.getArtworkURL(), media.getLocation()));
+				startActivity(intent);
+			} else if (media.getType() == Media.TYPE_GROUP){
+				Intent intent = new Intent(mContext, VerticalGridActivity.class);
+				startActivity(intent);
+			}
 		}
 	};
 
@@ -163,7 +169,7 @@ public class MainTvActivity extends FragmentActivity implements VideoBrowserInte
 			mVideoIndex = new HashMap<String, Integer>(size);
 			videoAdapter = new ArrayObjectAdapter(
 					new CardPresenter());
-			for (int i = 0 ; i < videoList.size() ; ++i) {
+			for (int i = 0 ; i < NUM_VIDEOS_PREVIEW ; ++i) {
 				item = videoList.get(i);
 				picture = mediaDatabase.getPicture(this, item.getLocation());
 
@@ -178,6 +184,9 @@ public class MainTvActivity extends FragmentActivity implements VideoBrowserInte
 					}
 				}
 			}
+			// 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(0, "Videos", null);
 			mRowsAdapter.add(new ListRow(header, videoAdapter));
 		}
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 d02025a..7092b4a 100644
--- a/vlc-android/src/org/videolan/vlc/gui/tv/MediaItemDetailsFragment.java
+++ b/vlc-android/src/org/videolan/vlc/gui/tv/MediaItemDetailsFragment.java
@@ -1,5 +1,7 @@
 package org.videolan.vlc.gui.tv;
 
+import org.videolan.vlc.R;
+
 import android.content.res.Resources;
 import android.os.Bundle;
 import android.support.v17.leanback.app.DetailsFragment;
@@ -43,7 +45,7 @@ public class MediaItemDetailsFragment extends DetailsFragment {
 		DetailsOverviewRow detailsOverview = new DetailsOverviewRow(new MediaItemDetails(media.getTitle(), media.getDescription(), "Big body"));
 
 		// Add images and action buttons to the details view
-		detailsOverview.setImageDrawable(res.getDrawable(media.getCardImageId()));
+		detailsOverview.setImageDrawable(res.getDrawable(R.drawable.cone));
 		detailsOverview.addAction(new Action(1, "Play"));
 		detailsOverview.addAction(new Action(2, "Delete"));
 		mRowsAdapter.add(detailsOverview);
diff --git a/vlc-android/src/org/videolan/vlc/gui/tv/VerticalGridActivity.java b/vlc-android/src/org/videolan/vlc/gui/tv/VerticalGridActivity.java
new file mode 100644
index 0000000..64e96f1
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/gui/tv/VerticalGridActivity.java
@@ -0,0 +1,17 @@
+package org.videolan.vlc.gui.tv;
+
+import org.videolan.vlc.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class VerticalGridActivity extends Activity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.tv_vertical_grid);
+        getWindow().setBackgroundDrawableResource(R.drawable.background);
+    }
+}
-- 
1.9.1



More information about the Android mailing list