[Android] [PATCH 01/12] First Android TV UI draft, WIP
Geoffrey Métais
geoffrey.metais at gmail.com
Mon Nov 17 17:06:39 CET 2014
Basic video browsing with only one list for now.
Thumbnailer is plugged.
Audio browsing should work (but not play)
---
vlc-android/AndroidManifest.xml | 23 +-
vlc-android/project.properties | 2 +-
vlc-android/res/layout/tv_details.xml | 6 +
vlc-android/res/layout/tv_main.xml | 7 +
vlc-android/res/layout/tv_main_fragment.xml | 7 +
vlc-android/res/values/dimens.xml | 2 +
vlc-android/src/org/videolan/vlc/Thumbnailer.java | 16 +-
.../src/org/videolan/vlc/gui/tv/CardPresenter.java | 98 +++++++++
.../org/videolan/vlc/gui/tv/DetailsActivity.java | 14 ++
.../vlc/gui/tv/DetailsDescriptionPresenter.java | 24 +++
.../src/org/videolan/vlc/gui/tv/MainFragment.java | 11 +
.../org/videolan/vlc/gui/tv/MainTvActivity.java | 232 +++++++++++++++++++++
.../org/videolan/vlc/gui/tv/MediaItemDetails.java | 24 +++
.../vlc/gui/tv/MediaItemDetailsFragment.java | 63 ++++++
.../org/videolan/vlc/gui/tv/StringPresenter.java | 29 +++
.../src/org/videolan/vlc/gui/tv/TvMedia.java | 136 ++++++++++++
.../vlc/gui/video/VideoBrowserInterface.java | 15 ++
.../videolan/vlc/gui/video/VideoGridFragment.java | 7 +-
18 files changed, 702 insertions(+), 14 deletions(-)
create mode 100644 vlc-android/res/layout/tv_details.xml
create mode 100644 vlc-android/res/layout/tv_main.xml
create mode 100644 vlc-android/res/layout/tv_main_fragment.xml
create mode 100644 vlc-android/src/org/videolan/vlc/gui/tv/CardPresenter.java
create mode 100644 vlc-android/src/org/videolan/vlc/gui/tv/DetailsActivity.java
create mode 100644 vlc-android/src/org/videolan/vlc/gui/tv/DetailsDescriptionPresenter.java
create mode 100644 vlc-android/src/org/videolan/vlc/gui/tv/MainFragment.java
create mode 100644 vlc-android/src/org/videolan/vlc/gui/tv/MainTvActivity.java
create mode 100644 vlc-android/src/org/videolan/vlc/gui/tv/MediaItemDetails.java
create mode 100644 vlc-android/src/org/videolan/vlc/gui/tv/MediaItemDetailsFragment.java
create mode 100644 vlc-android/src/org/videolan/vlc/gui/tv/StringPresenter.java
create mode 100644 vlc-android/src/org/videolan/vlc/gui/tv/TvMedia.java
create mode 100644 vlc-android/src/org/videolan/vlc/gui/video/VideoBrowserInterface.java
diff --git a/vlc-android/AndroidManifest.xml b/vlc-android/AndroidManifest.xml
index cc26409..151a91a 100644
--- a/vlc-android/AndroidManifest.xml
+++ b/vlc-android/AndroidManifest.xml
@@ -6,8 +6,8 @@
android:versionName="1.0.0-git" >
<uses-sdk
- android:minSdkVersion="7"
- android:targetSdkVersion="21" />
+ android:minSdkVersion="L"
+ android:targetSdkVersion="L" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.INTERNET" />
@@ -27,6 +27,16 @@
android:logo="@drawable/ic_logo_w"
android:theme="@style/Theme.VLC.NoTitleBar" >
<activity
+ android:name="org.videolan.vlc.gui.tv.MainTvActivity"
+ android:label="@string/app_name"
+ android:theme="@style/Theme.Leanback">
+
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity
android:name=".gui.MainActivity"
android:configChanges="orientation|screenSize"
android:icon="@drawable/icon"
@@ -39,6 +49,11 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+
+ <activity android:name="org.videolan.vlc.gui.tv.DetailsActivity"
+ android:exported="true"
+ android:theme="@style/Theme.Leanback"/>
+
<activity android:name=".gui.CompatErrorActivity" />
<activity android:name=".gui.PreferencesActivity" />
<activity
@@ -388,12 +403,12 @@
android:name="android.appwidget.provider"
android:resource="@xml/vlcwidget" />
</receiver>
- <receiver android:name=".RemoteControlClientReceiver" >
+ <!--receiver android:name=".RemoteControlClientReceiver" >
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
<action android:name="org.videolan.vlc.remote.PlayPause" />
</intent-filter>
- </receiver>
+ </receiver-->
</application>
</manifest>
diff --git a/vlc-android/project.properties b/vlc-android/project.properties
index 4ac9888..880b0ad 100644
--- a/vlc-android/project.properties
+++ b/vlc-android/project.properties
@@ -10,7 +10,7 @@
#split.density=true
# Project target.
-target=android-21
+target=android-L
android.library.reference.1=../java-libs/appcompat
android.library.reference.2=../java-libs/WheelView
android.library.reference.3=../java-libs/cardview
diff --git a/vlc-android/res/layout/tv_details.xml b/vlc-android/res/layout/tv_details.xml
new file mode 100644
index 0000000..f000fbe
--- /dev/null
+++ b/vlc-android/res/layout/tv_details.xml
@@ -0,0 +1,6 @@
+<fragment xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="org.videolan.vlc.gui.tv.MediaItemDetailsFragment"
+ android:id="@+id/details_fragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+/>
\ No newline at end of file
diff --git a/vlc-android/res/layout/tv_main.xml b/vlc-android/res/layout/tv_main.xml
new file mode 100644
index 0000000..4568719
--- /dev/null
+++ b/vlc-android/res/layout/tv_main.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<fragment xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="org.videolan.vlc.gui.tv.MainFragment"
+ android:id="@+id/main_browse_fragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+/>
diff --git a/vlc-android/res/layout/tv_main_fragment.xml b/vlc-android/res/layout/tv_main_fragment.xml
new file mode 100644
index 0000000..c220b43
--- /dev/null
+++ b/vlc-android/res/layout/tv_main_fragment.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<fragment xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="android.support.v17.leanback.app.BrowseFragment"
+ android:id="@+id/browse_fragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ />
\ No newline at end of file
diff --git a/vlc-android/res/values/dimens.xml b/vlc-android/res/values/dimens.xml
index 1400716..fdbd4a8 100644
--- a/vlc-android/res/values/dimens.xml
+++ b/vlc-android/res/values/dimens.xml
@@ -10,4 +10,6 @@
<dimen name="grid_card_title_text_size">12sp</dimen>
<dimen name="grid_card_subtitle_text_size">10sp</dimen>
<dimen name="grid_card_vertical_spacing">0dp</dimen>
+ <dimen name="tv_card_width">192dp</dimen>
+ <dimen name="tv_card_height">108dp</dimen>
</resources>
\ No newline at end of file
diff --git a/vlc-android/src/org/videolan/vlc/Thumbnailer.java b/vlc-android/src/org/videolan/vlc/Thumbnailer.java
index 90c789d..7e92819 100644
--- a/vlc-android/src/org/videolan/vlc/Thumbnailer.java
+++ b/vlc-android/src/org/videolan/vlc/Thumbnailer.java
@@ -33,7 +33,7 @@ import org.videolan.libvlc.LibVLC;
import org.videolan.libvlc.LibVlcException;
import org.videolan.libvlc.Media;
import org.videolan.vlc.gui.MainActivity;
-import org.videolan.vlc.gui.video.VideoGridFragment;
+import org.videolan.vlc.gui.video.VideoBrowserInterface;
import org.videolan.vlc.util.BitmapUtil;
import org.videolan.vlc.util.VLCInstance;
@@ -47,7 +47,7 @@ import android.view.Display;
public class Thumbnailer implements Runnable {
public final static String TAG = "VLC/Thumbnailer";
- private VideoGridFragment mVideoGridFragment;
+ private VideoBrowserInterface mVideoBrowser;
private final Queue<Media> mItems = new LinkedList<Media>();
@@ -68,7 +68,7 @@ public class Thumbnailer implements Runnable {
mPrefix = context.getResources().getString(R.string.thumbnail);
}
- public void start(VideoGridFragment videoGridFragment) {
+ public void start(VideoBrowserInterface videoBrowser) {
if (mLibVlc == null) {
try {
mLibVlc = VLCInstance.getLibVlcInstance();
@@ -81,7 +81,7 @@ public class Thumbnailer implements Runnable {
isStopping = false;
if (mThread == null || mThread.getState() == State.TERMINATED) {
- mVideoGridFragment = videoGridFragment;
+ mVideoBrowser = videoBrowser;
mThread = new Thread(this);
mThread.start();
}
@@ -129,7 +129,7 @@ public class Thumbnailer implements Runnable {
Log.d(TAG, "Thumbnailer started");
while (!isStopping) {
- mVideoGridFragment.resetBarrier();
+ mVideoBrowser.resetBarrier();
lock.lock();
// Get the id of the file browser item to create its thumbnail.
boolean interrupted = false;
@@ -177,11 +177,11 @@ public class Thumbnailer implements Runnable {
MediaDatabase.setPicture(item, thumbnail);
// Post to the file browser the new item.
- mVideoGridFragment.setItemToUpdate(item);
+ mVideoBrowser.setItemToUpdate(item);
// Wait for the file browser to process the change.
try {
- mVideoGridFragment.await();
+ mVideoBrowser.await();
} catch (InterruptedException e) {
Log.i(TAG, "interruption probably requested by stop()");
break;
@@ -194,7 +194,7 @@ public class Thumbnailer implements Runnable {
/* cleanup */
MainActivity.hideProgressBar();
MainActivity.clearTextInfo();
- mVideoGridFragment = null;
+ mVideoBrowser = null;
Log.d(TAG, "Thumbnailer stopped");
}
}
diff --git a/vlc-android/src/org/videolan/vlc/gui/tv/CardPresenter.java b/vlc-android/src/org/videolan/vlc/gui/tv/CardPresenter.java
new file mode 100644
index 0000000..5da55d5
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/gui/tv/CardPresenter.java
@@ -0,0 +1,98 @@
+package org.videolan.vlc.gui.tv;
+
+import org.videolan.libvlc.Media;
+import org.videolan.vlc.MediaDatabase;
+import org.videolan.vlc.R;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.support.v17.leanback.widget.ImageCardView;
+import android.support.v17.leanback.widget.Presenter;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class CardPresenter extends Presenter {
+
+ private static final String TAG = "CardPresenter";
+
+ private static Context sContext;
+ private static int CARD_WIDTH = 0;
+ private static int CARD_HEIGHT = 0;
+ private static Resources sResources;
+ private static MediaDatabase sMediaDatabase = MediaDatabase.getInstance();
+ private static Drawable sDefaultCardImage;
+
+ static class ViewHolder extends Presenter.ViewHolder {
+ private Media mMedia;
+ private ImageCardView mCardView;
+
+ public ViewHolder(View view) {
+ super(view);
+ mCardView = (ImageCardView) view;
+ }
+
+ public void setMovie(Media m) {
+ mMedia = m;
+ }
+
+ public Media getMovie() {
+ return mMedia;
+ }
+
+ public ImageCardView getCardView() {
+ return mCardView;
+ }
+
+ protected void updateCardViewImage(String mediaLocation) {
+ mCardView.setMainImage(new BitmapDrawable(sResources, sMediaDatabase.getPicture(sContext, mediaLocation)));
+ }
+
+ protected void updateCardViewImage(Drawable image) {
+ mCardView.setMainImage(image);
+ }
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent) {
+ sContext = parent.getContext();
+ sResources = sContext.getResources();
+ sDefaultCardImage = sContext.getResources().getDrawable(R.drawable.cone);
+ if (CARD_WIDTH == 0) {
+ CARD_WIDTH = sResources.getDimensionPixelSize(
+ R.dimen.tv_card_width);
+ CARD_HEIGHT = sResources.getDimensionPixelSize(
+ R.dimen.tv_card_height);
+ }
+
+ ImageCardView cardView = new ImageCardView(sContext);
+ cardView.setFocusable(true);
+ cardView.setFocusableInTouchMode(true);
+ cardView.setBackgroundColor(sContext.getResources().getColor(R.color.lb_details_overview_bg_color));
+ return new ViewHolder(cardView);
+ }
+
+ @Override
+ public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
+ Media media = (Media) item;
+ ((ViewHolder) viewHolder).setMovie(media);
+
+ ((ViewHolder) viewHolder).mCardView.setTitleText(media.getTitle());
+ ((ViewHolder) viewHolder).mCardView.setContentText(media.getDescription());
+ ((ViewHolder) viewHolder).mCardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT);
+ if (media.isPictureParsed())
+ ((ViewHolder) viewHolder).updateCardViewImage(media.getLocation());
+ else
+ ((ViewHolder) viewHolder).updateCardViewImage(sDefaultCardImage);
+ }
+
+ @Override
+ public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
+ }
+
+ @Override
+ public void onViewAttachedToWindow(Presenter.ViewHolder viewHolder) {
+ // TODO?
+ }
+}
diff --git a/vlc-android/src/org/videolan/vlc/gui/tv/DetailsActivity.java b/vlc-android/src/org/videolan/vlc/gui/tv/DetailsActivity.java
new file mode 100644
index 0000000..9b4bc1d
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/gui/tv/DetailsActivity.java
@@ -0,0 +1,14 @@
+package org.videolan.vlc.gui.tv;
+
+import org.videolan.vlc.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class DetailsActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.tv_details);
+ }
+}
diff --git a/vlc-android/src/org/videolan/vlc/gui/tv/DetailsDescriptionPresenter.java b/vlc-android/src/org/videolan/vlc/gui/tv/DetailsDescriptionPresenter.java
new file mode 100644
index 0000000..22f456a
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/gui/tv/DetailsDescriptionPresenter.java
@@ -0,0 +1,24 @@
+package org.videolan.vlc.gui.tv;
+
+import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter;
+import android.util.Log;
+
+public class DetailsDescriptionPresenter extends
+ AbstractDetailsDescriptionPresenter {
+ public static final String TAG ="DetailsDescriptionPresenter";
+
+ protected void onBindDescription(ViewHolder viewHolder, Object itemData) {
+ Log.d(TAG, "itemData "+itemData);
+ MediaItemDetails details = (MediaItemDetails) itemData;
+ // In a production app, the itemData object contains the information
+ // needed to display details for the media item:
+ // viewHolder.getTitle().setText(details.getShortTitle());
+
+ // Here we provide static data for testing purposes:
+ viewHolder.getTitle().setText(details.getTitle());
+ viewHolder.getSubtitle().setText(details.getSubTitle());
+// viewHolder.getBody().setText(details.getBody());
+ }
+
+
+}
diff --git a/vlc-android/src/org/videolan/vlc/gui/tv/MainFragment.java b/vlc-android/src/org/videolan/vlc/gui/tv/MainFragment.java
new file mode 100644
index 0000000..ae5cc55
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/gui/tv/MainFragment.java
@@ -0,0 +1,11 @@
+package org.videolan.vlc.gui.tv;
+
+import android.annotation.TargetApi;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+
+ at TargetApi(20)
+public class MainFragment extends Fragment {
+
+
+}
diff --git a/vlc-android/src/org/videolan/vlc/gui/tv/MainTvActivity.java b/vlc-android/src/org/videolan/vlc/gui/tv/MainTvActivity.java
new file mode 100644
index 0000000..7650cf1
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/gui/tv/MainTvActivity.java
@@ -0,0 +1,232 @@
+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.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.support.v17.leanback.app.BackgroundManager;
+import android.support.v17.leanback.app.BrowseFragment;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+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.OnItemClickedListener;
+import android.support.v17.leanback.widget.Row;
+import android.support.v4.app.FragmentActivity;
+
+public class MainTvActivity extends FragmentActivity implements VideoBrowserInterface {
+
+ public static final String TAG = "BrowseActivity";
+
+ protected BrowseFragment mBrowseFragment;
+ protected final CyclicBarrier mBarrier = new CyclicBarrier(2);
+ private MediaLibrary mMediaLibrary;
+ private Thumbnailer mThumbnailer;
+ protected Media mItemToUpdate;
+ ArrayObjectAdapter mRowsAdapter;
+ ArrayObjectAdapter videoAdapter;
+ ArrayObjectAdapter audioAdapter;
+ HashMap<String, Integer> mVideoIndex;
+ Drawable mDefaultBackground;
+ Activity mContext;
+
+ 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);
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mContext = this;
+ setContentView(R.layout.tv_main_fragment);
+
+ mMediaLibrary = MediaLibrary.getInstance();
+ mDefaultBackground = getResources().getDrawable(R.drawable.background);
+ final FragmentManager fragmentManager = getFragmentManager();
+ mBrowseFragment = (BrowseFragment) fragmentManager.findFragmentById(
+ R.id.browse_fragment);
+
+ // Set display parameters for the BrowseFragment
+ mBrowseFragment.setHeadersState(BrowseFragment.HEADERS_ENABLED);
+ mBrowseFragment.setTitle(getString(R.string.app_name));
+ mBrowseFragment.setBadgeDrawable(getResources().getDrawable(R.drawable.cone));
+
+ // add a listener for selected items
+ mBrowseFragment.setOnItemClickedListener(mItemClickListener);
+ mMediaLibrary.loadMediaItems(this, true);
+ mThumbnailer = new Thumbnailer(this, getWindowManager().getDefaultDisplay());
+ BackgroundManager.getInstance(this).attach(getWindow());
+ }
+
+ public void onResume() {
+ super.onResume();
+ mMediaLibrary.addUpdateHandler(mHandler);
+ 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();
+ }
+
+ protected void updateBackground(Drawable drawable) {
+ BackgroundManager.getInstance(this).setDrawable(drawable);
+ }
+
+ protected void clearBackground() {
+ BackgroundManager.getInstance(this).setDrawable(mDefaultBackground);
+ }
+
+
+ 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);
+ }
+
+ public void await() throws InterruptedException, BrokenBarrierException {
+ mBarrier.await();
+ }
+
+ public void resetBarrier() {
+ mBarrier.reset();
+ }
+
+ private 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 < videoList.size() ; ++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;
+ }
+ }
+ }
+ HeaderItem header = new HeaderItem(0, "Videos", 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);
+ }
+ HeaderItem header = new HeaderItem(1, "Music", null);
+ mRowsAdapter.add(new ListRow(header, audioAdapter));
+ }
+ mBrowseFragment.setAdapter(mRowsAdapter);
+ }
+
+ @Override
+ public void setItemToUpdate(Media item) {
+ mItemToUpdate = item;
+ mHandler.sendEmptyMessage(UPDATE_ITEM);
+ }
+
+ private void updateItem() {
+ videoAdapter.notifyArrayItemRangeChanged(mVideoIndex.get(mItemToUpdate.getLocation()), 1);
+ try {
+ mBarrier.await();
+ } catch (InterruptedException e) {
+ } catch (BrokenBarrierException e) {}
+ }
+
+ private Handler mHandler = new VideoListHandler(this);
+
+ private static class VideoListHandler extends WeakHandler<MainTvActivity> {
+ public VideoListHandler(MainTvActivity owner) {
+ super(owner);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ MainTvActivity owner = getOwner();
+ if(owner == null) return;
+
+ switch (msg.what) {
+ case UPDATE_ITEM:
+ owner.updateItem();
+ break;
+ case MediaLibrary.MEDIA_ITEMS_UPDATED:
+ owner.updateList();
+ }
+ }
+ };
+}
diff --git a/vlc-android/src/org/videolan/vlc/gui/tv/MediaItemDetails.java b/vlc-android/src/org/videolan/vlc/gui/tv/MediaItemDetails.java
new file mode 100644
index 0000000..a3eb779
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/gui/tv/MediaItemDetails.java
@@ -0,0 +1,24 @@
+package org.videolan.vlc.gui.tv;
+
+public class MediaItemDetails {
+
+ private String title, subTitle, body;
+
+ public MediaItemDetails(String title, String subTitle, String body) {
+ this.title = title;
+ this.subTitle = subTitle;
+ this.body = body;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public String getSubTitle() {
+ return subTitle;
+ }
+
+ public String getBody() {
+ return body;
+ }
+}
diff --git a/vlc-android/src/org/videolan/vlc/gui/tv/MediaItemDetailsFragment.java b/vlc-android/src/org/videolan/vlc/gui/tv/MediaItemDetailsFragment.java
new file mode 100644
index 0000000..d02025a
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/gui/tv/MediaItemDetailsFragment.java
@@ -0,0 +1,63 @@
+package org.videolan.vlc.gui.tv;
+
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.support.v17.leanback.app.DetailsFragment;
+import android.support.v17.leanback.widget.Action;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.ClassPresenterSelector;
+import android.support.v17.leanback.widget.DetailsOverviewRow;
+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.util.Log;
+
+public class MediaItemDetailsFragment extends DetailsFragment {
+ private static final String TAG = "MediaItemDetailsFragment";
+ private ArrayObjectAdapter mRowsAdapter;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ Log.i(TAG, "onCreate");
+ super.onCreate(savedInstanceState);
+
+ buildDetails();
+ }
+
+ private void buildDetails() {
+ Bundle extras = getActivity().getIntent().getExtras();
+ Log.d(TAG, "id "+extras.getLong("id"));
+ TvMedia media = extras.getParcelable("item");
+ ClassPresenterSelector selector = new ClassPresenterSelector();
+ // Attach your media item details presenter to the row presenter:
+ DetailsOverviewRowPresenter rowPresenter =
+ new DetailsOverviewRowPresenter(new DetailsDescriptionPresenter());
+
+ selector.addClassPresenter(DetailsOverviewRow.class, rowPresenter);
+ selector.addClassPresenter(ListRow.class,
+ new ListRowPresenter());
+ mRowsAdapter = new ArrayObjectAdapter(selector);
+
+ Resources res = getActivity().getResources();
+ 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.addAction(new Action(1, "Play"));
+ detailsOverview.addAction(new Action(2, "Delete"));
+ mRowsAdapter.add(detailsOverview);
+
+ // Add a Related items row
+ ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(
+ new StringPresenter());
+ listRowAdapter.add("Media Item 1");
+ listRowAdapter.add("Media Item 2");
+ listRowAdapter.add("Media Item 3");
+ HeaderItem header = new HeaderItem(0, "Related Items", null);
+ mRowsAdapter.add(new ListRow(header, listRowAdapter));
+
+ setAdapter(mRowsAdapter);
+ }
+
+}
diff --git a/vlc-android/src/org/videolan/vlc/gui/tv/StringPresenter.java b/vlc-android/src/org/videolan/vlc/gui/tv/StringPresenter.java
new file mode 100644
index 0000000..4eff8bc
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/gui/tv/StringPresenter.java
@@ -0,0 +1,29 @@
+package org.videolan.vlc.gui.tv;
+
+import org.videolan.vlc.R;
+
+import android.support.v17.leanback.widget.Presenter;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+public class StringPresenter extends Presenter {
+ private static final String TAG = "StringPresenter";
+
+ public ViewHolder onCreateViewHolder(ViewGroup parent) {
+ TextView textView = new TextView(parent.getContext());
+ textView.setFocusable(true);
+ textView.setFocusableInTouchMode(true);
+ textView.setBackground(
+ parent.getContext().getResources().getDrawable(R.drawable.background_cone));
+ return new ViewHolder(textView);
+ }
+
+ public void onBindViewHolder(ViewHolder viewHolder, Object item) {
+ ((TextView) viewHolder.view).setText(item.toString());
+ }
+
+ public void onUnbindViewHolder(ViewHolder viewHolder) {
+ // no op
+ }
+
+}
diff --git a/vlc-android/src/org/videolan/vlc/gui/tv/TvMedia.java b/vlc-android/src/org/videolan/vlc/gui/tv/TvMedia.java
new file mode 100644
index 0000000..cc7381b
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/gui/tv/TvMedia.java
@@ -0,0 +1,136 @@
+package org.videolan.vlc.gui.tv;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class TvMedia implements Parcelable {
+ private long id;
+ private String mediaUrl;
+ private String title;
+ private String description;
+ private int bgImageId;
+ private int cardImageId;
+ private String bgImageUrl;
+ private String cardImageUrl;
+
+ public TvMedia(long id, String title, String description, String bgImageUrl, String cardImageUrl, String mediaUrl) {
+ this.id = id;
+ this.title = title;
+ this.description = description;
+ this.bgImageUrl = bgImageUrl;
+ this.cardImageUrl = cardImageUrl;
+ this.mediaUrl = mediaUrl;
+ }
+
+ public TvMedia(long id, String title, String description, int bgImageId, int cardImageId, String mediaUrl) {
+ this.id = id;
+ this.title = title;
+ this.description = description;
+ this.bgImageId = bgImageId;
+ this.cardImageId = cardImageId;
+ this.mediaUrl = mediaUrl;
+ }
+
+ public long getId(){
+ return id;
+ }
+
+ public String getDescription(){
+ return description;
+ }
+
+ public String getBgImageUrl(){
+ return bgImageUrl;
+ }
+
+ public String getCardImageUrl(){
+ return cardImageUrl;
+ }
+
+ public String getVideoUrl(){
+ return mediaUrl;
+ }
+
+ public String getTitle(){
+ return title;
+ }
+
+ public int getBackgroundImageId() {
+ return bgImageId;
+ }
+
+// public URI getBackgroundImageURI() {
+// try {
+// Log.d("BACK MEDIA: ", bgImageUrl);
+// return new URI(getBgImageUrl());
+// } catch (URISyntaxException e) {
+// Log.d("URI exception: ", bgImageUrl);
+// return null;
+// }
+// }
+
+ public int getCardImageId() {
+ return cardImageId;
+ }
+
+// public URI getCardImageURI() {
+// try {
+// return new URI(getCardImageUrl());
+// } catch (URISyntaxException e) {
+// return null;
+// }
+// }
+
+ @Override
+ public String toString() {
+ return "Movie{" +
+ "id=" + id +
+ ", title='" + title + '\'' +
+ ", mediaUrl='" + mediaUrl + '\'' +
+ ", backgroundImageId='" + bgImageId + '\'' +
+// ", backgroundImageURI='" + getBackgroundImageURI().toString() + '\'' +
+ ", cardImageUrl='" + cardImageUrl + '\'' +
+ '}';
+ }
+
+ @Override
+ public int describeContents() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(id);
+ dest.writeString(mediaUrl);
+ dest.writeString(title);
+ dest.writeString(description);
+ dest.writeInt(bgImageId);
+ dest.writeInt(cardImageId);
+ dest.writeString(bgImageUrl);
+ dest.writeString(cardImageUrl);
+ }
+
+ public static final Parcelable.Creator<TvMedia> CREATOR
+ = new Parcelable.Creator<TvMedia>() {
+ public TvMedia createFromParcel(Parcel in) {
+ return new TvMedia(in);
+ }
+
+ public TvMedia[] newArray(int size) {
+ return new TvMedia[size];
+ }
+ };
+
+ private TvMedia(Parcel in) {
+ id = in.readLong();
+ mediaUrl = in.readString();
+ title = in.readString();
+ description = in.readString();
+ bgImageId = in.readInt();
+ cardImageId = in.readInt();
+ bgImageUrl = in.readString();
+ cardImageUrl = in.readString();
+ }
+
+}
diff --git a/vlc-android/src/org/videolan/vlc/gui/video/VideoBrowserInterface.java b/vlc-android/src/org/videolan/vlc/gui/video/VideoBrowserInterface.java
new file mode 100644
index 0000000..12e5f1d
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/gui/video/VideoBrowserInterface.java
@@ -0,0 +1,15 @@
+package org.videolan.vlc.gui.video;
+
+import java.util.concurrent.BrokenBarrierException;
+
+import org.videolan.libvlc.Media;
+
+public interface VideoBrowserInterface {
+ static final String ACTION_SCAN_START = "org.videolan.vlc.gui.ScanStart";
+ static final String ACTION_SCAN_STOP = "org.videolan.vlc.gui.ScanStop";
+ static final int UPDATE_ITEM = 0;
+
+ public void resetBarrier();
+ public void setItemToUpdate(Media item);
+ public void await() throws InterruptedException, BrokenBarrierException;
+}
diff --git a/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.java b/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.java
index 8de4cbe..ffa12d1 100644
--- a/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.java
+++ b/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.java
@@ -74,7 +74,7 @@ import android.widget.PopupMenu;
import android.widget.PopupMenu.OnMenuItemClickListener;
import android.widget.TextView;
-public class VideoGridFragment extends SherlockGridFragment implements ISortable {
+public class VideoGridFragment extends SherlockGridFragment implements ISortable, VideoBrowserInterface {
public final static String TAG = "VLC/VideoListFragment";
@@ -82,6 +82,11 @@ public class VideoGridFragment extends SherlockGridFragment implements ISortable
protected static final String ACTION_SCAN_STOP = "org.videolan.vlc.gui.ScanStop";
protected static final int UPDATE_ITEM = 0;
+ /* Constants used to switch from Grid to List and vice versa */
+ //FIXME If you know a way to do this in pure XML please do it!
+ private static final int GRID_ITEM_WIDTH_DP = 156;
+ private static final int GRID_HORIZONTAL_SPACING_DP = 20;
+ private static final int GRID_VERTICAL_SPACING_DP = 20;
private static final int GRID_STRETCH_MODE = GridView.STRETCH_COLUMN_WIDTH;
private static final int LIST_STRETCH_MODE = GridView.STRETCH_COLUMN_WIDTH;
--
1.9.1
More information about the Android
mailing list