[Android] [PATCH] recommandations on Android TV

Geoffrey Métais geoffrey.metais at gmail.com
Wed Nov 19 13:31:48 CET 2014


---
 .../vlc/gui/video/VideoPlayerActivity.java         |   2 +-
 vlc-android/tv/AndroidManifest.xml                 |  85 ++++++-----
 .../org/videolan/vlc/gui/tv/BootupReceiver.java    |  57 +++++++
 .../vlc/gui/tv/RecommendationsService.java         | 164 +++++++++++++++++++++
 4 files changed, 271 insertions(+), 37 deletions(-)
 create mode 100644 vlc-android/tv/src/org/videolan/vlc/gui/tv/BootupReceiver.java
 create mode 100644 vlc-android/tv/src/org/videolan/vlc/gui/tv/RecommendationsService.java

diff --git a/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.java b/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.java
index e0779cb..06b9f20 100644
--- a/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.java
+++ b/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.java
@@ -124,7 +124,7 @@ public class VideoPlayerActivity extends Activity implements IVideoPlayer {
 
     // Internal intent identifier to distinguish between internal launch and
     // external intent.
-    private final static String PLAY_FROM_VIDEOGRID = "org.videolan.vlc.gui.video.PLAY_FROM_VIDEOGRID";
+    public final static String PLAY_FROM_VIDEOGRID = "org.videolan.vlc.gui.video.PLAY_FROM_VIDEOGRID";
 
     private SurfaceView mSurfaceView;
     private SurfaceView mSubtitlesSurfaceView;
diff --git a/vlc-android/tv/AndroidManifest.xml b/vlc-android/tv/AndroidManifest.xml
index 8360431..426aa26 100644
--- a/vlc-android/tv/AndroidManifest.xml
+++ b/vlc-android/tv/AndroidManifest.xml
@@ -19,6 +19,7 @@
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <!-- Needed for audio search on TV -->
     <uses-permission android:name="android.permission.RECORD_AUDIO" />
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
 
     <uses-feature
         android:name="android.hardware.touchscreen"
@@ -36,49 +37,47 @@
         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">
+            android:name="org.videolan.vlc.gui.tv.MainTvActivity"
+            android:label="@string/app_name"
+            android:theme="@style/Theme.Leanback" >
+            <intent-filter android:priority="5" >
+                <action android:name="android.intent.action.MAIN" />
 
-		    <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>
-
-      <activity android:name="org.videolan.vlc.gui.tv.DetailsActivity"
-        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="org.videolan.vlc.gui.tv.SearchActivity"
-        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" />
+                <category android:name="android.intent.category.LAUNCHER" />
+                <category android:name="android.intent.category.LEANBACK_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.BrowserActivity"
+            android:name="org.videolan.vlc.gui.tv.VerticalGridActivity"
+            android:exported="true"
+            android:theme="@style/Theme.Leanback" />
+        <activity
+            android:name="org.videolan.vlc.gui.tv.SearchActivity"
+            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="org.videolan.vlc.gui.CompatErrorActivity" />
+        <activity android:name="org.videolan.vlc.gui.PreferencesActivity" />
+        <activity
+            android:name="org.videolan.vlc.gui.BrowserActivity"
             android:label="@string/mediafiles"
             android:theme="@style/Theme.VLC.NoTitleBar" />
-        <activity android:name=".gui.DebugLogActivity" />
+        <activity android:name="org.videolan.vlc.gui.DebugLogActivity" />
         <activity
-            android:name=".gui.NativeCrashActivity"
+            android:name="org.videolan.vlc.gui.NativeCrashActivity"
             android:process=":NativeCrashActivity"
             android:stateNotNeeded="true"
             android:theme="@style/Theme.VLC" />
         <activity
-            android:name=".gui.video.VideoPlayerActivity"
+            android:name="org.videolan.vlc.gui.video.VideoPlayerActivity"
             android:configChanges="orientation|screenSize"
             android:theme="@style/Theme.VLC.Player" >
 
@@ -397,13 +396,13 @@
 
         <service android:name="org.videolan.vlc.audio.AudioService" />
 
-        <receiver android:name=".PhoneStateReceiver" >
+        <receiver android:name="org.videolan.vlc.PhoneStateReceiver" >
             <intent-filter>
                 <action android:name="android.intent.action.PHONE_STATE" />
             </intent-filter>
         </receiver>
         <receiver
-            android:name=".widget.VLCAppWidgetProvider"
+            android:name="org.videolan.vlc.widget.VLCAppWidgetProvider"
             android:exported="false"
             android:label="VLC mini player" >
             <intent-filter>
@@ -414,6 +413,20 @@
                 android:name="android.appwidget.provider"
                 android:resource="@xml/vlcwidget" />
         </receiver>
+
+        <service
+            android:name="org.videolan.vlc.gui.tv.RecommendationsService"
+            android:exported="false" >
+        </service>
+
+        <receiver
+            android:name="org.videolan.vlc.gui.tv.BootupReceiver"
+            android:enabled="true"
+            android:exported="true" >
+          <intent-filter>
+            <action android:name="android.intent.action.BOOT_COMPLETED"/>
+          </intent-filter>
+        </receiver>
     </application>
 
 </manifest>
diff --git a/vlc-android/tv/src/org/videolan/vlc/gui/tv/BootupReceiver.java b/vlc-android/tv/src/org/videolan/vlc/gui/tv/BootupReceiver.java
new file mode 100644
index 0000000..689be57
--- /dev/null
+++ b/vlc-android/tv/src/org/videolan/vlc/gui/tv/BootupReceiver.java
@@ -0,0 +1,57 @@
+/*****************************************************************************
+ * BootupReceiver.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;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class BootupReceiver extends BroadcastReceiver {
+    public BootupReceiver() {
+    }
+    private static final String TAG = "VLC/BootupReceiver";
+
+    private static final long INITIAL_DELAY = 5000;
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (intent.getAction().endsWith(Intent.ACTION_BOOT_COMPLETED)) {
+            Log.d(TAG, "ACTION_BOOT_COMPLETED ");
+            scheduleRecommendationUpdate(context);
+        }
+    }
+
+    private void scheduleRecommendationUpdate(Context context) {
+        AlarmManager alarmManager = (AlarmManager)context.getSystemService(
+                Context.ALARM_SERVICE);
+        Intent recommendationIntent = new Intent(context,
+                RecommendationsService.class);
+        PendingIntent alarmIntent = PendingIntent.getService(context, 0,
+                recommendationIntent, 0);
+
+        alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                INITIAL_DELAY,
+                AlarmManager.INTERVAL_HOUR,
+                alarmIntent);
+    }
+}
diff --git a/vlc-android/tv/src/org/videolan/vlc/gui/tv/RecommendationsService.java b/vlc-android/tv/src/org/videolan/vlc/gui/tv/RecommendationsService.java
new file mode 100644
index 0000000..e2df051
--- /dev/null
+++ b/vlc-android/tv/src/org/videolan/vlc/gui/tv/RecommendationsService.java
@@ -0,0 +1,164 @@
+/*****************************************************************************
+ * RecommendationsService.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;
+
+import android.app.IntentService;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Message;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.TaskStackBuilder;
+import android.util.Log;
+
+import org.videolan.libvlc.Media;
+import org.videolan.vlc.MediaDatabase;
+import org.videolan.vlc.MediaLibrary;
+import org.videolan.vlc.R;
+import org.videolan.vlc.gui.video.VideoPlayerActivity;
+import org.videolan.vlc.util.WeakHandler;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+
+
+/**
+ * An {@link IntentService} subclass for handling asynchronous task requests in
+ * a service on a separate handler thread.
+ * <p/>
+ * TODO: Customize class - update intent actions and extra parameters.
+ */
+public class RecommendationsService extends IntentService {
+
+    private static final String TAG = "VLC/RecommendationsService";
+    private static final int NUM_RECOMMANDATIONS = 10;
+
+    private static NotificationManager sNotificationManager;
+    private static MediaDatabase sMediaDatabase = MediaDatabase.getInstance();
+    private static Context sContext;
+
+    public RecommendationsService() {
+        super("RecommendationsService");
+    }
+
+     @Override
+    public void onCreate() {
+         super.onCreate();
+         sContext = this;
+     }
+
+    @Override
+    protected void onHandleIntent(Intent intent) {
+        if (intent != null) {
+            final String action = intent.getAction();
+            MediaLibrary.getInstance().addUpdateHandler(mHandler);
+        }
+    }
+    private static Notification buildRecommendation(Context context, Media movie)
+            throws IOException {
+
+        if (sNotificationManager == null) {
+            sNotificationManager = (NotificationManager)
+                    sContext.getSystemService(Context.NOTIFICATION_SERVICE);
+        }
+
+        Bundle extras = new Bundle();
+//        if (mBackgroundUri != movie.getBackgroundUri()) {
+//            extras.putString(EXTRA_BACKGROUND_IMAGE_URL, movie.getBackgroundUri());
+//        }
+
+        // build the recommendation as a Notification object
+        Notification notification = new NotificationCompat.BigPictureStyle(
+                new NotificationCompat.Builder(sContext)
+                        .setContentTitle(movie.getTitle())
+                        .setContentText(movie.getDescription())
+                        .setContentInfo("VLC")
+//                        .setSortKey("0.8")
+                        .setPriority(7)
+//                        .setColor(Color.BLUE)
+                        .setCategory("recommendation")
+                        .setLargeIcon(sMediaDatabase.getPicture(sContext, movie.getLocation()))
+                        .setSmallIcon(R.drawable.icon)
+                        .setContentIntent(buildPendingIntent(movie))
+//                        .setExtras(extras))
+        ).build();
+
+        // post the recommendation to the NotificationManager
+        sNotificationManager.notify(0, notification);
+        sNotificationManager = null;
+        return notification;
+    }
+
+    private static PendingIntent buildPendingIntent(Media media) {
+
+        Intent intent = new Intent(sContext, VideoPlayerActivity.class);
+        intent.setAction(VideoPlayerActivity.PLAY_FROM_VIDEOGRID);
+        intent.putExtra("itemLocation", media.getLocation());
+        intent.putExtra("itemTitle", media.getTitle());
+        intent.putExtra("dontParse", false);
+        intent.putExtra("fromStart", false);
+        intent.putExtra("itemPosition", -1);
+
+        TaskStackBuilder stackBuilder = TaskStackBuilder.create(sContext);
+        stackBuilder.addParentStack(VideoPlayerActivity.class);
+        stackBuilder.addNextIntent(intent);
+
+        PendingIntent pi = stackBuilder.getPendingIntent(
+                0, PendingIntent.FLAG_UPDATE_CURRENT);
+        return pi;
+    }
+
+    RecommendationsHandler mHandler = new RecommendationsHandler(this);
+
+    private static class RecommendationsHandler extends WeakHandler<RecommendationsService> {
+        public RecommendationsHandler(RecommendationsService owner) {
+            super(owner);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            ArrayList<Media> videoList = MediaLibrary.getInstance().getVideoItems();
+            ArrayList<Media> videos = new ArrayList<Media>(videoList.size());
+            Bitmap pic;
+            for (Media media : videoList){
+                pic = sMediaDatabase.getPicture(sContext, media.getLocation());
+                if (pic != null && pic.getByteCount() > 4 && media.getTime() == 0) {
+                    videos.add(media);
+                }
+            }
+            if (!videos.isEmpty())
+                Collections.shuffle(videos);
+            videoList = null;
+            int size = Math.min(NUM_RECOMMANDATIONS, videos.size());
+            for (int i = 0 ; i < size ; ++i) {
+                try {
+                    buildRecommendation(sContext, videos.get(i));
+                } catch (IOException e) {
+                    Log.e(TAG, "failed notif for "+ videos.get(i).getTitle(), e);
+                }
+            }
+        }
+    }
+}
-- 
1.9.1



More information about the Android mailing list