aboutsummaryrefslogtreecommitdiff
path: root/src/com/android/tv/recommendation
diff options
context:
space:
mode:
authorNick Chalko <nchalko@google.com>2015-09-01 09:05:04 -0700
committerNick Chalko <nchalko@google.com>2015-09-16 06:46:50 -0700
commit07b043dc3db83d6d20f0e8513b946830ab00e37b (patch)
tree705ade719e5c2853c070fe40b8518a56ac37f6d0 /src/com/android/tv/recommendation
parentb5429e4406a580953bbdac5817e421cf0ab7aae3 (diff)
downloadTV-07b043dc3db83d6d20f0e8513b946830ab00e37b.tar.gz
Sync to ub-tv-friends at 1.06.202
git hash 3c1965f5dcc60243f1fe600cb35f19bd5f01fc27 Change-Id: I90b77790f9074677ecef72a23235d2b33eacb76a
Diffstat (limited to 'src/com/android/tv/recommendation')
-rw-r--r--src/com/android/tv/recommendation/NotificationService.java140
-rw-r--r--src/com/android/tv/recommendation/RecommendationDataManager.java129
-rw-r--r--src/com/android/tv/recommendation/RoutineWatchEvaluator.java2
3 files changed, 158 insertions, 113 deletions
diff --git a/src/com/android/tv/recommendation/NotificationService.java b/src/com/android/tv/recommendation/NotificationService.java
index 00cad116..835a3e53 100644
--- a/src/com/android/tv/recommendation/NotificationService.java
+++ b/src/com/android/tv/recommendation/NotificationService.java
@@ -31,13 +31,17 @@ import android.media.tv.TvInputInfo;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Message;
+import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseLongArray;
import android.view.View;
import com.android.tv.R;
+import com.android.tv.TvApplication;
+import com.android.tv.common.WeakHandler;
import com.android.tv.data.Channel;
import com.android.tv.data.Program;
import com.android.tv.util.BitmapUtils;
@@ -59,8 +63,13 @@ public class NotificationService extends Service implements Recommender.Listener
public static final String ACTION_HIDE_RECOMMENDATION =
"com.android.tv.notification.ACTION_HIDE_RECOMMENDATION";
- private static final String TUNE_PARAMS_RECOMMENDATION_TYPE =
+ /**
+ * Recommendation intent has an extra data for the recommendation type. It'll be also
+ * sent to a TV input as a tune parameter.
+ */
+ public static final String TUNE_PARAMS_RECOMMENDATION_TYPE =
"com.android.tv.recommendation_type";
+
private static final String TYPE_RANDOM_RECOMMENDATION = "random";
private static final String TYPE_ROUTINE_WATCH_RECOMMENDATION = "routine_watch";
private static final String TYPE_ROUTINE_WATCH_AND_FAVORITE_CHANNEL_RECOMMENDATION =
@@ -132,66 +141,52 @@ public class NotificationService extends Service implements Recommender.Listener
getResources().getDimensionPixelOffset(R.dimen.notif_ch_logo_padding_bottom);
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- mTvInputManagerHelper = new TvInputManagerHelper(this);
- mTvInputManagerHelper.start();
-
+ mTvInputManagerHelper = ((TvApplication) getApplicationContext()).getTvInputManagerHelper();
mHandlerThread = new HandlerThread("tv notification");
mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper()) {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_INITIALIZE_RECOMMENDER: {
- mRecommender = new Recommender(
- NotificationService.this, NotificationService.this, true);
- if (TYPE_RANDOM_RECOMMENDATION.equals(mRecommendationType)) {
- mRecommender.registerEvaluator(new RandomEvaluator());
- } else if (TYPE_ROUTINE_WATCH_RECOMMENDATION.equals(mRecommendationType)) {
- mRecommender.registerEvaluator(new RoutineWatchEvaluator());
- } else if (TYPE_ROUTINE_WATCH_AND_FAVORITE_CHANNEL_RECOMMENDATION.equals(
- mRecommendationType)) {
- mRecommender.registerEvaluator(
- new FavoriteChannelEvaluator(), 0.5, 0.5);
- mRecommender.registerEvaluator(new RoutineWatchEvaluator(), 1.0, 1.0);
- } else {
- throw new IllegalStateException("Undefined recommendation type: "
- + mRecommendationType);
- }
- }
- case MSG_SHOW_RECOMMENDATION: {
- if (!mRecommender.isReady()) {
- mShowRecommendationAfterRecommenderReady = true;
- } else {
- showRecommendation();
- }
- break;
- }
- case MSG_UPDATE_RECOMMENDATION: {
- int notificationId = msg.arg1;
- Channel channel = ((Channel) msg.obj);
- if (mNotificationChannels[notificationId] == Channel.INVALID_ID
- || !sendNotification(channel.getId(), notificationId)) {
- changeRecommendation(notificationId);
- }
- break;
- }
- case MSG_HIDE_RECOMMENDATION: {
- if (!mRecommender.isReady()) {
- mShowRecommendationAfterRecommenderReady = false;
- } else {
- hideAllRecommendation();
- }
- break;
- }
- default: {
- super.handleMessage(msg);
- }
- }
- }
- };
+ mHandler = new NotificationHandler(mHandlerThread.getLooper(), this);
mHandler.sendEmptyMessage(MSG_INITIALIZE_RECOMMENDER);
}
+ private void handleInitializeRecommender() {
+ mRecommender = new Recommender(NotificationService.this, NotificationService.this, true);
+ if (TYPE_RANDOM_RECOMMENDATION.equals(mRecommendationType)) {
+ mRecommender.registerEvaluator(new RandomEvaluator());
+ } else if (TYPE_ROUTINE_WATCH_RECOMMENDATION.equals(mRecommendationType)) {
+ mRecommender.registerEvaluator(new RoutineWatchEvaluator());
+ } else if (TYPE_ROUTINE_WATCH_AND_FAVORITE_CHANNEL_RECOMMENDATION
+ .equals(mRecommendationType)) {
+ mRecommender.registerEvaluator(new FavoriteChannelEvaluator(), 0.5, 0.5);
+ mRecommender.registerEvaluator(new RoutineWatchEvaluator(), 1.0, 1.0);
+ } else {
+ throw new IllegalStateException(
+ "Undefined recommendation type: " + mRecommendationType);
+ }
+ }
+
+ private void handleShowRecommendation() {
+ if (!mRecommender.isReady()) {
+ mShowRecommendationAfterRecommenderReady = true;
+ } else {
+ showRecommendation();
+ }
+ }
+
+ private void handleUpdateRecommendation(int notificationId, Channel channel) {
+ if (mNotificationChannels[notificationId] == Channel.INVALID_ID || !sendNotification(
+ channel.getId(), notificationId)) {
+ changeRecommendation(notificationId);
+ }
+ }
+
+ private void handleHideRecommendation() {
+ if (!mRecommender.isReady()) {
+ mShowRecommendationAfterRecommenderReady = false;
+ } else {
+ hideAllRecommendation();
+ }
+ }
+
@Override
public void onDestroy() {
mRecommender.release();
@@ -456,4 +451,37 @@ public class NotificationService extends Service implements Recommender.Listener
}
return -1;
}
+
+ private static class NotificationHandler extends WeakHandler<NotificationService> {
+ public NotificationHandler(@NonNull Looper looper, NotificationService ref) {
+ super(looper, ref);
+ }
+
+ @Override
+ public void handleMessage(Message msg, @NonNull NotificationService notificationService) {
+ switch (msg.what) {
+ case MSG_INITIALIZE_RECOMMENDER: {
+ notificationService.handleInitializeRecommender();
+ break;
+ }
+ case MSG_SHOW_RECOMMENDATION: {
+ notificationService.handleShowRecommendation();
+ break;
+ }
+ case MSG_UPDATE_RECOMMENDATION: {
+ int notificationId = msg.arg1;
+ Channel channel = ((Channel) msg.obj);
+ notificationService.handleUpdateRecommendation(notificationId, channel);
+ break;
+ }
+ case MSG_HIDE_RECOMMENDATION: {
+ notificationService.handleHideRecommendation();
+ break;
+ }
+ default: {
+ super.handleMessage(msg);
+ }
+ }
+ }
+ }
}
diff --git a/src/com/android/tv/recommendation/RecommendationDataManager.java b/src/com/android/tv/recommendation/RecommendationDataManager.java
index 2445cce8..0f59e2bd 100644
--- a/src/com/android/tv/recommendation/RecommendationDataManager.java
+++ b/src/com/android/tv/recommendation/RecommendationDataManager.java
@@ -28,9 +28,12 @@ import android.media.tv.TvInputManager.TvInputCallback;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.Looper;
import android.os.Message;
import android.support.annotation.NonNull;
+import android.support.annotation.WorkerThread;
+import com.android.tv.common.WeakHandler;
import com.android.tv.data.Channel;
import com.android.tv.data.Program;
@@ -71,11 +74,12 @@ public class RecommendationDataManager {
private static final int INVALID_INDEX = -1;
private static RecommendationDataManager sManager;
+ private final static Object sListenerLock = new Object();
private final ContentObserver mContentObserver;
private final Map<Long, ChannelRecord> mChannelRecordMap = new ConcurrentHashMap<>();
private final Map<Long, ChannelRecord> mAvailableChannelRecordMap = new ConcurrentHashMap<>();
- private Context mContext;
+ private final Context mContext;
private boolean mStarted;
private boolean mCancelLoadTask;
private boolean mChannelRecordMapLoaded;
@@ -90,7 +94,6 @@ public class RecommendationDataManager {
private final HandlerThread mHandlerThread;
- @SuppressWarnings("unchecked")
private final Handler mHandler;
private final List<ListenerRecord> mListeners = new ArrayList<>();
@@ -117,7 +120,7 @@ public class RecommendationDataManager {
*/
public void release(@NonNull Listener listener) {
removeListener(listener);
- synchronized (mListeners) {
+ synchronized (sListenerLock) {
if (mListeners.size() == 0) {
stop();
}
@@ -183,46 +186,7 @@ public class RecommendationDataManager {
mContext = context.getApplicationContext();
mHandlerThread = new HandlerThread("RecommendationDataManager");
mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper()) {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_START:
- onStart();
- break;
- case MSG_STOP:
- if (mStarted) {
- onStop();
- }
- break;
- case MSG_UPDATE_CHANNEL:
- if (mStarted) {
- onUpdateChannel((Uri) msg.obj);
- }
- break;
- case MSG_UPDATE_CHANNELS:
- if (mStarted) {
- onUpdateChannels((Uri) msg.obj);
- }
- break;
- case MSG_UPDATE_WATCH_HISTORY:
- if (mStarted) {
- onLoadWatchHistory((Uri) msg.obj);
- }
- break;
- case MSG_NOTIFY_CHANNEL_RECORD_MAP_LOADED:
- if (mStarted) {
- onNotifyChannelRecordMapLoaded();
- }
- break;
- case MSG_NOTIFY_CHANNEL_RECORD_MAP_CHANGED:
- if (mStarted) {
- onNotifyChannelRecordMapChanged();
- }
- break;
- }
- }
- };
+ mHandler = new RecommendationHandler(mHandlerThread.getLooper(), this);
mContentObserver = new RecommendationContentObserver(mHandler);
}
@@ -270,7 +234,7 @@ public class RecommendationDataManager {
}
private void addListener(Listener listener) {
- synchronized (mListeners) {
+ synchronized (sListenerLock) {
if (getListenerIndexLocked(listener) == INVALID_INDEX) {
mListeners.add((new ListenerRecord(listener)));
}
@@ -278,10 +242,11 @@ public class RecommendationDataManager {
}
private void removeListener(Listener listener) {
- synchronized (mListeners) {
+ synchronized (sListenerLock) {
int idx = getListenerIndexLocked(listener);
if (idx != INVALID_INDEX) {
- mListeners.remove(idx);
+ ListenerRecord record = mListeners.remove(idx);
+ record.mListener = null;
}
}
}
@@ -319,6 +284,7 @@ public class RecommendationDataManager {
mStarted = false;
}
+ @WorkerThread
private void onUpdateChannel(Uri uri) {
Channel channel = null;
try (Cursor cursor = mContext.getContentResolver().query(uri, Channel.PROJECTION,
@@ -341,6 +307,7 @@ public class RecommendationDataManager {
}
}
+ @WorkerThread
private void onUpdateChannels(Uri uri) {
List<Channel> channels = new ArrayList<>();
try (Cursor cursor = mContext.getContentResolver().query(uri, Channel.PROJECTION,
@@ -378,6 +345,7 @@ public class RecommendationDataManager {
}
}
+ @WorkerThread
private void onLoadWatchHistory(Uri uri) {
List<WatchedProgram> history = new ArrayList<>();
try (Cursor cursor = mContext.getContentResolver().query(uri, null, null, null, null)) {
@@ -394,7 +362,7 @@ public class RecommendationDataManager {
final ChannelRecord channelRecord =
updateChannelRecordFromWatchedProgram(watchedProgram);
if (mChannelRecordMapLoaded && channelRecord != null) {
- synchronized (mListeners) {
+ synchronized (sListenerLock) {
for (ListenerRecord l : mListeners) {
l.postNewWatchLog(channelRecord);
}
@@ -437,7 +405,7 @@ public class RecommendationDataManager {
private void onNotifyChannelRecordMapLoaded() {
mChannelRecordMapLoaded = true;
- synchronized (mListeners) {
+ synchronized (sListenerLock) {
for (ListenerRecord l : mListeners) {
l.postChannelRecordLoaded();
}
@@ -445,7 +413,7 @@ public class RecommendationDataManager {
}
private void onNotifyChannelRecordMapChanged() {
- synchronized (mListeners) {
+ synchronized (sListenerLock) {
for (ListenerRecord l : mListeners) {
l.postChannelRecordChanged();
}
@@ -551,8 +519,10 @@ public class RecommendationDataManager {
mHandler.post(new Runnable() {
@Override
public void run() {
- if (mListener != null) {
- mListener.onChannelRecordLoaded();
+ synchronized (sListenerLock) {
+ if (mListener != null) {
+ mListener.onChannelRecordLoaded();
+ }
}
}
});
@@ -562,8 +532,10 @@ public class RecommendationDataManager {
mHandler.post(new Runnable() {
@Override
public void run() {
- if (mListener != null) {
- mListener.onNewWatchLog(channelRecord);
+ synchronized (sListenerLock) {
+ if (mListener != null) {
+ mListener.onNewWatchLog(channelRecord);
+ }
}
}
});
@@ -573,11 +545,58 @@ public class RecommendationDataManager {
mHandler.post(new Runnable() {
@Override
public void run() {
- if (mListener != null) {
- mListener.onChannelRecordChanged();
+ synchronized (sListenerLock) {
+ if (mListener != null) {
+ mListener.onChannelRecordChanged();
+ }
}
}
});
}
}
+
+ private static class RecommendationHandler extends WeakHandler<RecommendationDataManager> {
+ public RecommendationHandler(@NonNull Looper looper, RecommendationDataManager ref) {
+ super(looper, ref);
+ }
+
+ @Override
+ public void handleMessage(Message msg, @NonNull RecommendationDataManager dataManager) {
+ switch (msg.what) {
+ case MSG_START:
+ dataManager.onStart();
+ break;
+ case MSG_STOP:
+ if (dataManager.mStarted) {
+ dataManager.onStop();
+ }
+ break;
+ case MSG_UPDATE_CHANNEL:
+ if (dataManager.mStarted) {
+ dataManager.onUpdateChannel((Uri) msg.obj);
+ }
+ break;
+ case MSG_UPDATE_CHANNELS:
+ if (dataManager.mStarted) {
+ dataManager.onUpdateChannels((Uri) msg.obj);
+ }
+ break;
+ case MSG_UPDATE_WATCH_HISTORY:
+ if (dataManager.mStarted) {
+ dataManager.onLoadWatchHistory((Uri) msg.obj);
+ }
+ break;
+ case MSG_NOTIFY_CHANNEL_RECORD_MAP_LOADED:
+ if (dataManager.mStarted) {
+ dataManager.onNotifyChannelRecordMapLoaded();
+ }
+ break;
+ case MSG_NOTIFY_CHANNEL_RECORD_MAP_CHANGED:
+ if (dataManager.mStarted) {
+ dataManager.onNotifyChannelRecordMapChanged();
+ }
+ break;
+ }
+ }
+ }
}
diff --git a/src/com/android/tv/recommendation/RoutineWatchEvaluator.java b/src/com/android/tv/recommendation/RoutineWatchEvaluator.java
index 8f6f203d..694da6bf 100644
--- a/src/com/android/tv/recommendation/RoutineWatchEvaluator.java
+++ b/src/com/android/tv/recommendation/RoutineWatchEvaluator.java
@@ -17,7 +17,6 @@
package com.android.tv.recommendation;
import android.support.annotation.VisibleForTesting;
-import android.text.TextUtils;
import com.android.tv.data.Program;
@@ -26,7 +25,6 @@ import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.concurrent.TimeUnit;
-import java.util.regex.Pattern;
public class RoutineWatchEvaluator extends Recommender.Evaluator {
// TODO: test and refine constant values in WatchedProgramRecommender in order to