Merge "Rework background manager and add TestCases"
diff --git a/api/current.txt b/api/current.txt
index 1ce5aca..1cdcbc0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1227,16 +1227,19 @@
public final class BackgroundManager {
method public void attach(android.view.Window);
method public void attachToView(android.view.View);
+ method public void clearDrawable();
method public final int getColor();
- method public android.graphics.drawable.Drawable getDefaultDimLayer();
- method public android.graphics.drawable.Drawable getDimLayer();
+ method public deprecated android.graphics.drawable.Drawable getDefaultDimLayer();
+ method public deprecated android.graphics.drawable.Drawable getDimLayer();
method public android.graphics.drawable.Drawable getDrawable();
method public static android.support.v17.leanback.app.BackgroundManager getInstance(android.app.Activity);
method public boolean isAttached();
+ method public boolean isAutoReleaseOnStop();
method public void release();
+ method public void setAutoReleaseOnStop(boolean);
method public void setBitmap(android.graphics.Bitmap);
method public void setColor(int);
- method public void setDimLayer(android.graphics.drawable.Drawable);
+ method public deprecated void setDimLayer(android.graphics.drawable.Drawable);
method public void setDrawable(android.graphics.drawable.Drawable);
method public void setThemeDrawableResourceId(int);
}
@@ -1329,6 +1332,7 @@
method public android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener();
method public android.support.v17.leanback.app.RowsFragment getRowsFragment();
method public int getSelectedPosition();
+ method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getSelectedRowViewHolder();
method public final boolean isHeadersTransitionOnBackEnabled();
method public boolean isInHeadersTransition();
method public boolean isShowingHeaders();
@@ -1402,6 +1406,7 @@
public static class BrowseFragment.MainFragmentRowsAdapter<T extends android.app.Fragment> {
ctor public BrowseFragment.MainFragmentRowsAdapter(T);
+ method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
method public final T getFragment();
method public int getSelectedPosition();
method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
@@ -1431,6 +1436,7 @@
method public android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener();
method public android.support.v17.leanback.app.RowsSupportFragment getRowsSupportFragment();
method public int getSelectedPosition();
+ method public android.support.v17.leanback.widget.RowPresenter.ViewHolder getSelectedRowViewHolder();
method public final boolean isHeadersTransitionOnBackEnabled();
method public boolean isInHeadersTransition();
method public boolean isShowingHeaders();
@@ -1504,6 +1510,7 @@
public static class BrowseSupportFragment.MainFragmentRowsAdapter<T extends android.support.v4.app.Fragment> {
ctor public BrowseSupportFragment.MainFragmentRowsAdapter(T);
+ method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
method public final T getFragment();
method public int getSelectedPosition();
method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
@@ -2084,6 +2091,7 @@
ctor public RowsFragment();
method public deprecated void enableRowScaling(boolean);
method protected android.support.v17.leanback.widget.VerticalGridView findGridViewFromRoot(android.view.View);
+ method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
method public android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapter getMainFragmentAdapter();
method public android.support.v17.leanback.app.BrowseFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
@@ -2109,6 +2117,7 @@
ctor public RowsSupportFragment();
method public deprecated void enableRowScaling(boolean);
method protected android.support.v17.leanback.widget.VerticalGridView findGridViewFromRoot(android.view.View);
+ method public android.support.v17.leanback.widget.RowPresenter.ViewHolder findRowViewHolderByPosition(int);
method public android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapter getMainFragmentAdapter();
method public android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
@@ -3656,6 +3665,8 @@
method public final android.support.v17.leanback.widget.Row getRow();
method public final java.lang.Object getRowObject();
method public final float getSelectLevel();
+ method public java.lang.Object getSelectedItem();
+ method public android.support.v17.leanback.widget.Presenter.ViewHolder getSelectedItemViewHolder();
method public final boolean isExpanded();
method public final boolean isSelected();
method public final void setActivated(boolean);
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BackgroundHelper.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BackgroundHelper.java
index 54e851d..43506cd 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BackgroundHelper.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BackgroundHelper.java
@@ -25,7 +25,13 @@
import android.support.v17.leanback.app.BackgroundManager;
import android.support.v4.content.ContextCompat;
import android.util.Log;
+import android.view.View;
+/**
+ * App uses BackgroundHelper for each Activity, it wraps BackgroundManager and provides:
+ * 1. AsyncTask to load bitmap in background thread.
+ * 2. Using a BitmapCache to cache loaded bitmaps.
+ */
public class BackgroundHelper {
private static final String TAG = "BackgroundHelper";
@@ -36,37 +42,73 @@
// in case multiple backgrounds are set in quick succession.
private static final int SET_BACKGROUND_DELAY_MS = 100;
+ /**
+ * An very simple example of BitmapCache.
+ */
+ public static class BitmapCache {
+ Bitmap mLastBitmap;
+ Object mLastToken;
+
+ // Singleton BitmapCache shared by multiple activities/backgroundHelper.
+ static BitmapCache sInstance = new BitmapCache();
+
+ private BitmapCache() {
+ }
+
+ /**
+ * Get cached bitmap by token, returns null if missing cache.
+ */
+ public Bitmap getCache(Object token) {
+ if (token == null ? mLastToken == null : token.equals(mLastToken)) {
+ if (DEBUG) Log.v(TAG, "hitCache token:" + token + " " + mLastBitmap);
+ return mLastBitmap;
+ }
+ return null;
+ }
+
+ /**
+ * Add cached bitmap.
+ */
+ public void putCache(Object token, Bitmap bitmap) {
+ if (DEBUG) Log.v(TAG, "putCache token:" + token + " " + bitmap);
+ mLastToken = token;
+ mLastBitmap = bitmap;
+ }
+
+ /**
+ * Add singleton of BitmapCache shared across activities.
+ */
+ public static BitmapCache getInstance() {
+ return sInstance;
+ }
+ }
+
+
static class Request {
Object mImageToken;
- Activity mActivity;
Bitmap mResult;
- Request(Activity activity, Object imageToken) {
- mActivity = activity;
+ Request(Object imageToken) {
mImageToken = imageToken;
}
}
- public BackgroundHelper() {
+ public BackgroundHelper(Activity activity) {
if (DEBUG && !ENABLED) Log.v(TAG, "BackgroundHelper: disabled");
+ mActivity = activity;
}
class LoadBackgroundRunnable implements Runnable {
Request mRequest;
- LoadBackgroundRunnable(Activity activity, Object imageToken) {
- mRequest = new Request(activity, imageToken);
+ LoadBackgroundRunnable(Object imageToken) {
+ mRequest = new Request(imageToken);
}
@Override
public void run() {
- if (mTask != null) {
- if (DEBUG) Log.v(TAG, "Cancelling task");
- mTask.cancel(true);
- }
if (DEBUG) Log.v(TAG, "Executing task");
- mTask = new LoadBitmapTask();
- mTask.execute(mRequest);
+ new LoadBitmapTask().execute(mRequest);
mRunnable = null;
}
}
@@ -78,7 +120,7 @@
if (DEBUG) Log.v(TAG, "doInBackground cancelled " + cancelled);
Request request = params[0];
if (!cancelled) {
- request.mResult = loadBitmap(request.mActivity, request.mImageToken);
+ request.mResult = loadBitmap(request.mImageToken);
}
return request;
}
@@ -86,10 +128,8 @@
@Override
protected void onPostExecute(Request request) {
if (DEBUG) Log.v(TAG, "onPostExecute");
- applyBackground(request.mActivity, request.mResult);
- if (mTask == this) {
- mTask = null;
- }
+ BitmapCache.getInstance().putCache(request.mImageToken, request.mResult);
+ mBackgroundManager.setBitmap(request.mResult);
}
@Override
@@ -97,11 +137,11 @@
if (DEBUG) Log.v(TAG, "onCancelled");
}
- private Bitmap loadBitmap(Activity activity, Object imageToken) {
+ private Bitmap loadBitmap(Object imageToken) {
if (imageToken instanceof Integer) {
final int resourceId = (Integer) imageToken;
if (DEBUG) Log.v(TAG, "load resourceId " + resourceId);
- Drawable drawable = ContextCompat.getDrawable(activity, resourceId);
+ Drawable drawable = ContextCompat.getDrawable(mActivity, resourceId);
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
}
@@ -109,54 +149,93 @@
return null;
}
- private void applyBackground(Activity activity, Bitmap bitmap) {
- BackgroundManager backgroundManager = BackgroundManager.getInstance(activity);
- if (backgroundManager == null || !backgroundManager.isAttached()) {
- return;
- }
- backgroundManager.setBitmap(bitmap);
- }
}
- private LoadBackgroundRunnable mRunnable;
- private LoadBitmapTask mTask;
+ final Activity mActivity;
+ BackgroundManager mBackgroundManager;
+ LoadBackgroundRunnable mRunnable;
// Allocate a dedicated handler because there may be no view available
// when setBackground is invoked.
- private Handler mHandler = new Handler();
+ static Handler sHandler = new Handler();
- public void setBackground(Activity activity, Object imageToken) {
+ void createBackgroundManagerIfNeeded() {
+ if (mBackgroundManager == null) {
+ mBackgroundManager = BackgroundManager.getInstance(mActivity);
+ }
+ }
+
+ /**
+ * Attach BackgroundManager to activity window.
+ */
+ public void attachToWindow() {
if (!ENABLED) {
return;
}
+ if (DEBUG) Log.v(TAG, "attachToWindow " + mActivity);
+ createBackgroundManagerIfNeeded();
+ mBackgroundManager.attach(mActivity.getWindow());
+ }
+
+ /**
+ * Attach BackgroundManager to a view inside activity.
+ */
+ public void attachToView(View backgroundView) {
+ if (!ENABLED) {
+ return;
+ }
+ if (DEBUG) Log.v(TAG, "attachToView " + mActivity + " " + backgroundView);
+ createBackgroundManagerIfNeeded();
+ mBackgroundManager.attachToView(backgroundView);
+ }
+
+ /**
+ * Sets a background bitmap. It will look up the cache first if missing, an AsyncTask will
+ * will be launched to load the bitmap.
+ */
+ public void setBackground(Object imageToken) {
+ if (!ENABLED) {
+ return;
+ }
+ if (DEBUG) Log.v(TAG, "set imageToken " + imageToken + " to " + mActivity);
+ createBackgroundManagerIfNeeded();
+ if (imageToken == null) {
+ mBackgroundManager.setDrawable(null);
+ return;
+ }
+ Bitmap cachedBitmap = BitmapCache.getInstance().getCache(imageToken);
+ if (cachedBitmap != null) {
+ mBackgroundManager.setBitmap(cachedBitmap);
+ return;
+ }
if (mRunnable != null) {
- mHandler.removeCallbacks(mRunnable);
+ sHandler.removeCallbacks(mRunnable);
}
- mRunnable = new LoadBackgroundRunnable(activity, imageToken);
- mHandler.postDelayed(mRunnable, SET_BACKGROUND_DELAY_MS);
+ mRunnable = new LoadBackgroundRunnable(imageToken);
+ sHandler.postDelayed(mRunnable, SET_BACKGROUND_DELAY_MS);
}
- public void setDrawable(Activity activity, Drawable drawable) {
- BackgroundManager backgroundManager = BackgroundManager.getInstance(activity);
- if (!backgroundManager.isAttached()) {
- backgroundManager.attachToView(activity.findViewById(R.id.details_background_view));
- }
- backgroundManager.setDrawable(drawable);
- }
-
- static public void attach(Activity activity) {
+ /**
+ * Clear Drawable.
+ */
+ public void clearDrawable() {
if (!ENABLED) {
return;
}
- if (DEBUG) Log.v(TAG, "attach to activity " + activity);
- BackgroundManager.getInstance(activity).attach(activity.getWindow());
+ if (DEBUG) Log.v(TAG, "clearDrawable to " + mActivity);
+ createBackgroundManagerIfNeeded();
+ mBackgroundManager.clearDrawable();
}
- static public void release(Activity activity) {
+ /**
+ * Directly sets a Drawable as background.
+ */
+ public void setDrawable(Drawable drawable) {
if (!ENABLED) {
return;
}
- if (DEBUG) Log.v(TAG, "release from activity " + activity);
- BackgroundManager.getInstance(activity).release();
+ if (DEBUG) Log.v(TAG, "setDrawable " + drawable + " to " + mActivity);
+ createBackgroundManagerIfNeeded();
+ mBackgroundManager.setDrawable(drawable);
}
}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseActivity.java
index 38f2fb8..d91aa17 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseActivity.java
@@ -24,15 +24,4 @@
setContentView(R.layout.browse);
}
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- BackgroundHelper.attach(this);
- }
-
- @Override
- public void onStop() {
- BackgroundHelper.release(this);
- super.onStop();
- }
}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseErrorActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseErrorActivity.java
index 9b978f9..12b1177 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseErrorActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseErrorActivity.java
@@ -36,18 +36,6 @@
testError();
}
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- BackgroundHelper.attach(this);
- }
-
- @Override
- public void onStop() {
- BackgroundHelper.release(this);
- super.onStop();
- }
-
private void testError() {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseErrorSupportActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseErrorSupportActivity.java
index 963325e..79ef723 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseErrorSupportActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseErrorSupportActivity.java
@@ -39,18 +39,6 @@
testError();
}
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- BackgroundHelper.attach(this);
- }
-
- @Override
- public void onStop() {
- BackgroundHelper.release(this);
- super.onStop();
- }
-
private void testError() {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseFragment.java
index 378b7b5..6c1ce85 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseFragment.java
@@ -14,6 +14,7 @@
package com.example.android.leanback;
import android.app.Fragment;
+import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
@@ -49,7 +50,7 @@
private static final long HEADER_ID3 = 1003;
private ArrayObjectAdapter mRowsAdapter;
- private BackgroundHelper mBackgroundHelper = new BackgroundHelper();
+ private BackgroundHelper mBackgroundHelper;
// For good performance, it's important to use a single instance of
// a card presenter for all rows using that presenter.
@@ -65,6 +66,9 @@
Log.i(TAG, "onCreate");
super.onCreate(savedInstanceState);
+ mBackgroundHelper = new BackgroundHelper(getActivity());
+ mBackgroundHelper.attachToWindow();
+
setBadgeDrawable(ResourcesCompat.getDrawable(getActivity().getResources(),
R.drawable.ic_title, getActivity().getTheme()));
setTitle("Leanback Sample App");
@@ -84,12 +88,13 @@
RowPresenter.ViewHolder rowViewHolder, Row row) {
Log.i(TAG, "onItemSelected: " + item + " row " + row);
- if (isShowingHeaders()) {
- mBackgroundHelper.setBackground(getActivity(), null);
- } else if (item instanceof PhotoItem) {
- mBackgroundHelper.setBackground(
- getActivity(), ((PhotoItem) item).getImageResourceId());
- }
+ updateBackgroundToSelection();
+ }
+ });
+ setBrowseTransitionListener(new BrowseTransitionListener() {
+ @Override
+ public void onHeadersTransitionStop(boolean withHeaders) {
+ updateBackgroundToSelection();
}
});
if (TEST_ENTRANCE_TRANSITION) {
@@ -110,6 +115,26 @@
}
@Override
+ public void onStart() {
+ super.onStart();
+ updateBackgroundToSelection();
+ }
+
+ void updateBackgroundToSelection() {
+ if (!isShowingHeaders()) {
+ RowPresenter.ViewHolder rowViewHolder = getSelectedRowViewHolder();
+ Object item = rowViewHolder == null ? null : rowViewHolder.getSelectedItem();
+ if (item != null) {
+ mBackgroundHelper.setBackground(((PhotoItem) item).getImageResourceId());
+ } else {
+ mBackgroundHelper.clearDrawable();
+ }
+ } else {
+ mBackgroundHelper.clearDrawable();
+ }
+ }
+
+ @Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportActivity.java
index e96c220..83cb9e2 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportActivity.java
@@ -27,15 +27,4 @@
setContentView(R.layout.browse_support);
}
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- BackgroundHelper.attach(this);
- }
-
- @Override
- public void onStop() {
- BackgroundHelper.release(this);
- super.onStop();
- }
}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportFragment.java
index 0773d47..1a8ab1f 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportFragment.java
@@ -52,7 +52,7 @@
private static final long HEADER_ID3 = 1003;
private ArrayObjectAdapter mRowsAdapter;
- private BackgroundHelper mBackgroundHelper = new BackgroundHelper();
+ private BackgroundHelper mBackgroundHelper;
// For good performance, it's important to use a single instance of
// a card presenter for all rows using that presenter.
@@ -68,6 +68,9 @@
Log.i(TAG, "onCreate");
super.onCreate(savedInstanceState);
+ mBackgroundHelper = new BackgroundHelper(getActivity());
+ mBackgroundHelper.attachToWindow();
+
setBadgeDrawable(ResourcesCompat.getDrawable(getActivity().getResources(),
R.drawable.ic_title, getActivity().getTheme()));
setTitle("Leanback Sample App");
@@ -87,12 +90,13 @@
RowPresenter.ViewHolder rowViewHolder, Row row) {
Log.i(TAG, "onItemSelected: " + item + " row " + row);
- if (isShowingHeaders()) {
- mBackgroundHelper.setBackground(getActivity(), null);
- } else if (item instanceof PhotoItem) {
- mBackgroundHelper.setBackground(
- getActivity(), ((PhotoItem) item).getImageResourceId());
- }
+ updateBackgroundToSelection();
+ }
+ });
+ setBrowseTransitionListener(new BrowseTransitionListener() {
+ @Override
+ public void onHeadersTransitionStop(boolean withHeaders) {
+ updateBackgroundToSelection();
}
});
if (TEST_ENTRANCE_TRANSITION) {
@@ -113,6 +117,26 @@
}
@Override
+ public void onStart() {
+ super.onStart();
+ updateBackgroundToSelection();
+ }
+
+ void updateBackgroundToSelection() {
+ if (!isShowingHeaders()) {
+ RowPresenter.ViewHolder rowViewHolder = getSelectedRowViewHolder();
+ Object item = rowViewHolder == null ? null : rowViewHolder.getSelectedItem();
+ if (item != null) {
+ mBackgroundHelper.setBackground(((PhotoItem) item).getImageResourceId());
+ } else {
+ mBackgroundHelper.clearDrawable();
+ }
+ } else {
+ mBackgroundHelper.clearDrawable();
+ }
+ }
+
+ @Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsFragment.java
index cae115f..09d9526 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsFragment.java
@@ -47,7 +47,7 @@
private ArrayObjectAdapter mRowsAdapter;
private PhotoItem mPhotoItem;
final CardPresenter cardPresenter = new CardPresenter();
- private BackgroundHelper mBackgroundHelper = new BackgroundHelper();
+ private BackgroundHelper mBackgroundHelper;
private static final int ACTION_PLAY = 1;
private static final int ACTION_RENT = 2;
@@ -68,6 +68,9 @@
Log.i(TAG, "onCreate");
super.onCreate(savedInstanceState);
+ mBackgroundHelper = new BackgroundHelper(getActivity());
+ mBackgroundHelper.attachToWindow();
+
Context context = getActivity();
setBadgeDrawable(ResourcesCompat.getDrawable(context.getResources(),
R.drawable.ic_title, context.getTheme()));
@@ -216,8 +219,7 @@
public void onStart() {
super.onStart();
if (mPhotoItem != null) {
- mBackgroundHelper.setBackground(
- getActivity(), mPhotoItem.getImageResourceId());
+ mBackgroundHelper.setBackground(mPhotoItem.getImageResourceId());
}
}
}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsSupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsSupportFragment.java
index eb1e201..28a61d9 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsSupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsSupportFragment.java
@@ -50,7 +50,7 @@
private ArrayObjectAdapter mRowsAdapter;
private PhotoItem mPhotoItem;
final CardPresenter cardPresenter = new CardPresenter();
- private BackgroundHelper mBackgroundHelper = new BackgroundHelper();
+ private BackgroundHelper mBackgroundHelper;
private static final int ACTION_PLAY = 1;
private static final int ACTION_RENT = 2;
@@ -71,6 +71,9 @@
Log.i(TAG, "onCreate");
super.onCreate(savedInstanceState);
+ mBackgroundHelper = new BackgroundHelper(getActivity());
+ mBackgroundHelper.attachToWindow();
+
Context context = getActivity();
setBadgeDrawable(ResourcesCompat.getDrawable(context.getResources(),
R.drawable.ic_title, context.getTheme()));
@@ -219,8 +222,7 @@
public void onStart() {
super.onStart();
if (mPhotoItem != null) {
- mBackgroundHelper.setBackground(
- getActivity(), mPhotoItem.getImageResourceId());
+ mBackgroundHelper.setBackground(mPhotoItem.getImageResourceId());
}
}
}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsFragment.java
index 7a77766..fae9b2b 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsFragment.java
@@ -43,7 +43,9 @@
import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.content.res.ResourcesCompat;
import android.util.Log;
+import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.Toast;
public class NewDetailsFragment extends android.support.v17.leanback.app.DetailsFragment {
@@ -73,7 +75,7 @@
private FullWidthDetailsOverviewSharedElementHelper mHelper;
private DetailsBackgroundParallaxHelper mParallaxHelper;
private DetailsFragmentVideoHelper mVideoHelper;
- private BackgroundHelper mBackgroundHelper = new BackgroundHelper();
+ private BackgroundHelper mBackgroundHelper;
private int mBitmapMinVerticalOffset = -100;
private MediaPlayerGlue mMediaPlayerGlue;
private VideoFragment mVideoFragment;
@@ -92,6 +94,8 @@
super.onCreate(savedInstanceState);
initializeTest();
+ mBackgroundHelper = new BackgroundHelper(getActivity());
+
final Context context = getActivity();
setBadgeDrawable(ResourcesCompat.getDrawable(context.getResources(), R.drawable.ic_title,
context.getTheme()));
@@ -200,6 +204,14 @@
}
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = super.onCreateView(inflater, container, savedInstanceState);
+ mBackgroundHelper.attachToView(view.findViewById(R.id.details_background_view));
+ return view;
+ }
+
public void setItem(PhotoItem photoItem) {
mPhotoItem = photoItem;
@@ -281,7 +293,7 @@
mMediaPlayerGlue.setTitle("Diving with Sharks");
mMediaPlayerGlue.setVideoUrl("http://techslides.com/demos/sample-videos/small.mp4");
- mBackgroundHelper.setDrawable(getActivity(), mParallaxHelper.getDrawable());
+ mBackgroundHelper.setDrawable(mParallaxHelper.getDrawable());
mParallaxHelper.setCoverImageBitmap(bitmap);
}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsSupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsSupportFragment.java
index 79ccb75..d34e17a 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsSupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsSupportFragment.java
@@ -46,7 +46,9 @@
import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.content.res.ResourcesCompat;
import android.util.Log;
+import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.Toast;
public class NewDetailsSupportFragment extends android.support.v17.leanback.app.DetailsSupportFragment {
@@ -76,7 +78,7 @@
private FullWidthDetailsOverviewSharedElementHelper mHelper;
private DetailsBackgroundParallaxHelper mParallaxHelper;
private DetailsFragmentVideoHelper mVideoHelper;
- private BackgroundHelper mBackgroundHelper = new BackgroundHelper();
+ private BackgroundHelper mBackgroundHelper;
private int mBitmapMinVerticalOffset = -100;
private MediaPlayerGlue mMediaPlayerGlue;
private VideoSupportFragment mVideoSupportFragment;
@@ -95,6 +97,8 @@
super.onCreate(savedInstanceState);
initializeTest();
+ mBackgroundHelper = new BackgroundHelper(getActivity());
+
final Context context = getActivity();
setBadgeDrawable(ResourcesCompat.getDrawable(context.getResources(), R.drawable.ic_title,
context.getTheme()));
@@ -203,6 +207,14 @@
}
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = super.onCreateView(inflater, container, savedInstanceState);
+ mBackgroundHelper.attachToView(view.findViewById(R.id.details_background_view));
+ return view;
+ }
+
public void setItem(PhotoItem photoItem) {
mPhotoItem = photoItem;
@@ -284,7 +296,7 @@
mMediaPlayerGlue.setTitle("Diving with Sharks");
mMediaPlayerGlue.setVideoUrl("http://techslides.com/demos/sample-videos/small.mp4");
- mBackgroundHelper.setDrawable(getActivity(), mParallaxHelper.getDrawable());
+ mBackgroundHelper.setDrawable(mParallaxHelper.getDrawable());
mParallaxHelper.setCoverImageBitmap(bitmap);
}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleVideoSupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleVideoSupportFragment.java
index a3af52f..dde7bd2 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleVideoSupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleVideoSupportFragment.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
/* This file is auto-generated from OnboardingDemoFragment.java. DO NOT MODIFY. */
/*
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/VideoSupportActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/VideoSupportActivity.java
index 64759d4..751df64 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/VideoSupportActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/VideoSupportActivity.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
/* This file is auto-generated from OnboardingDemoFragment.java. DO NOT MODIFY. */
/*
diff --git a/v17/leanback/res/drawable/lb_background.xml b/v17/leanback/res/drawable/lb_background.xml
index 4732bc1..305a228 100644
--- a/v17/leanback/res/drawable/lb_background.xml
+++ b/v17/leanback/res/drawable/lb_background.xml
@@ -15,11 +15,8 @@
limitations under the License.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
- <item android:drawable="@color/lb_grey" android:id="@+id/background_theme"/>
- <item android:drawable="@color/lb_grey" android:id="@+id/background_color"/>
<!-- Replaced at runtime with image to fade out -->
<item android:drawable="@color/lb_grey" android:id="@+id/background_imageout"/>
<!-- Replaced at runtime with image to fade in -->
<item android:drawable="@color/lb_grey" android:id="@+id/background_imagein"/>
- <item android:drawable="@color/lb_background_protection" android:id="@+id/background_dim" />
</layer-list>
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BackgroundFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BackgroundFragment.java
index d3547f3..6b442c1 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BackgroundFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BackgroundFragment.java
@@ -13,18 +13,17 @@
*/
package android.support.v17.leanback.app;
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
import android.app.Fragment;
import android.support.annotation.RestrictTo;
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
/**
* Fragment used by the background manager.
* @hide
*/
@RestrictTo(GROUP_ID)
-public final class BackgroundFragment extends Fragment implements
- BackgroundManager.FragmentStateQueriable {
+public final class BackgroundFragment extends Fragment {
private BackgroundManager mBackgroundManager;
void setBackgroundManager(BackgroundManager backgroundManager) {
@@ -58,6 +57,14 @@
}
@Override
+ public void onStop() {
+ if (mBackgroundManager != null) {
+ mBackgroundManager.onStop();
+ }
+ super.onStop();
+ }
+
+ @Override
public void onDestroy() {
super.onDestroy();
// mBackgroundManager might be null:
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java b/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java
index 8809ef4..428d702 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java
@@ -26,16 +26,17 @@
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
+import android.os.Build;
import android.os.Handler;
import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
import android.support.v17.leanback.R;
import android.support.v17.leanback.widget.BackgroundHelper;
import android.support.v4.content.ContextCompat;
+import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v4.view.animation.FastOutLinearInInterpolator;
import android.util.Log;
import android.view.LayoutInflater;
@@ -83,15 +84,10 @@
// support continuity between fragments of different applications if desired.
public final class BackgroundManager {
- interface FragmentStateQueriable {
- boolean isResumed();
- }
-
static final String TAG = "BackgroundManager";
static final boolean DEBUG = false;
static final int FULL_ALPHA = 255;
- private static final int DIM_ALPHA_ON_SOLID = (int) (0.8f * FULL_ALPHA);
private static final int CHANGE_BG_DELAY_MS = 500;
private static final int FADE_DURATION = 500;
@@ -107,35 +103,52 @@
Context mContext;
Handler mHandler;
- private Window mWindow;
private WindowManager mWindowManager;
private View mBgView;
private BackgroundContinuityService mService;
private int mThemeDrawableResourceId;
- private FragmentStateQueriable mFragmentState;
+ private BackgroundFragment mFragmentState;
+ private boolean mAutoReleaseOnStop = true;
private int mHeightPx;
private int mWidthPx;
+ int mBackgroundColor;
Drawable mBackgroundDrawable;
- private int mBackgroundColor;
private boolean mAttached;
private long mLastSetTime;
private final Interpolator mAccelerateInterpolator;
private final Interpolator mDecelerateInterpolator;
- private final ValueAnimator mAnimator;
- private final ValueAnimator mDimAnimator;
+ final ValueAnimator mAnimator;
- private static class BitmapDrawable extends Drawable {
+ static class BitmapDrawable extends Drawable {
- static class ConstantState extends Drawable.ConstantState {
- Bitmap mBitmap;
- Matrix mMatrix;
- Paint mPaint;
+ static final class ConstantState extends Drawable.ConstantState {
+ final Bitmap mBitmap;
+ final Matrix mMatrix;
+ final Paint mPaint = new Paint();
+
+ ConstantState(Bitmap bitmap, Matrix matrix) {
+ mBitmap = bitmap;
+ mMatrix = matrix != null ? matrix : new Matrix();
+ mPaint.setFilterBitmap(true);
+ }
+
+ ConstantState(ConstantState copyFrom) {
+ mBitmap = copyFrom.mBitmap;
+ mMatrix = copyFrom.mMatrix != null ? new Matrix(copyFrom.mMatrix) : new Matrix();
+ if (copyFrom.mPaint.getAlpha() != FULL_ALPHA) {
+ mPaint.setAlpha(copyFrom.mPaint.getAlpha());
+ }
+ if (copyFrom.mPaint.getColorFilter() != null) {
+ mPaint.setColorFilter(copyFrom.mPaint.getColorFilter());
+ }
+ mPaint.setFilterBitmap(true);
+ }
@Override
public Drawable newDrawable() {
- return new BitmapDrawable(null, mBitmap, mMatrix);
+ return new BitmapDrawable(this);
}
@Override
@@ -144,17 +157,19 @@
}
}
- private ConstantState mState = new ConstantState();
+ ConstantState mState;
+ boolean mMutated;
BitmapDrawable(Resources resources, Bitmap bitmap) {
this(resources, bitmap, null);
}
BitmapDrawable(Resources resources, Bitmap bitmap, Matrix matrix) {
- mState.mBitmap = bitmap;
- mState.mMatrix = matrix != null ? matrix : new Matrix();
- mState.mPaint = new Paint();
- mState.mPaint.setFilterBitmap(true);
+ mState = new ConstantState(bitmap, matrix);
+ }
+
+ BitmapDrawable(ConstantState state) {
+ mState = state;
}
Bitmap getBitmap() {
@@ -179,6 +194,7 @@
@Override
public void setAlpha(int alpha) {
+ mutate();
if (mState.mPaint.getAlpha() != alpha) {
mState.mPaint.setAlpha(alpha);
invalidateSelf();
@@ -191,7 +207,9 @@
*/
@Override
public void setColorFilter(ColorFilter cf) {
+ mutate();
mState.mPaint.setColorFilter(cf);
+ invalidateSelf();
}
public ColorFilter getColorFilter() {
@@ -202,59 +220,43 @@
public ConstantState getConstantState() {
return mState;
}
+
+ @NonNull
+ @Override
+ public Drawable mutate() {
+ if (!mMutated) {
+ mMutated = true;
+ mState = new ConstantState(mState);
+ }
+ return this;
+ }
}
- static class DrawableWrapper {
- private int mAlpha = FULL_ALPHA;
- private Drawable mDrawable;
- private ColorFilter mColorFilter;
+ static final class DrawableWrapper {
+ int mAlpha = FULL_ALPHA;
+ final Drawable mDrawable;
public DrawableWrapper(Drawable drawable) {
mDrawable = drawable;
- updateAlpha();
- updateColorFilter();
}
public DrawableWrapper(DrawableWrapper wrapper, Drawable drawable) {
mDrawable = drawable;
- mAlpha = wrapper.getAlpha();
- updateAlpha();
- mColorFilter = wrapper.getColorFilter();
- updateColorFilter();
+ mAlpha = wrapper.mAlpha;
}
public Drawable getDrawable() {
return mDrawable;
}
- public void setAlpha(int alpha) {
- mAlpha = alpha;
- updateAlpha();
- }
- public int getAlpha() {
- return mAlpha;
- }
- private void updateAlpha() {
- mDrawable.setAlpha(mAlpha);
- }
-
- public ColorFilter getColorFilter() {
- return mColorFilter;
- }
- public void setColorFilter(ColorFilter colorFilter) {
- mColorFilter = colorFilter;
- updateColorFilter();
- }
- private void updateColorFilter() {
- mDrawable.setColorFilter(mColorFilter);
- }
public void setColor(int color) {
((ColorDrawable) mDrawable).setColor(color);
}
}
- static class TranslucentLayerDrawable extends LayerDrawable {
- private DrawableWrapper[] mWrapper;
- private Paint mPaint = new Paint();
+ static final class TranslucentLayerDrawable extends LayerDrawable {
+ DrawableWrapper[] mWrapper;
+ int mAlpha = FULL_ALPHA;
+ boolean mSuspendInvalidation;
public TranslucentLayerDrawable(Drawable[] drawables) {
super(drawables);
@@ -267,20 +269,20 @@
@Override
public void setAlpha(int alpha) {
- if (mPaint.getAlpha() != alpha) {
- int previousAlpha = mPaint.getAlpha();
- mPaint.setAlpha(alpha);
+ mAlpha = alpha;
+ invalidateSelf();
+ }
+
+ void setWrapperAlpha(int wrapperIndex, int alpha) {
+ if (mWrapper[wrapperIndex] != null) {
+ mWrapper[wrapperIndex].mAlpha = alpha;
invalidateSelf();
- onAlphaChanged(previousAlpha, alpha);
}
}
// Queried by system transitions
public int getAlpha() {
- return mPaint.getAlpha();
- }
-
- protected void onAlphaChanged(int oldAlpha, int newAlpha) {
+ return mAlpha;
}
@Override
@@ -292,7 +294,6 @@
mWrapper[i] = new DrawableWrapper(mWrapper[i], getDrawable(i));
}
}
- invalidateSelf();
return drawable;
}
@@ -323,115 +324,80 @@
for (int i = 0; i < getNumberOfLayers(); i++) {
if (getId(i) == id) {
mWrapper[i] = null;
- super.setDrawableByLayerId(id, createEmptyDrawable(context));
+ if (!(getDrawable(i) instanceof EmptyDrawable)) {
+ super.setDrawableByLayerId(id, createEmptyDrawable(context));
+ }
break;
}
}
}
- public DrawableWrapper findWrapperById(int id) {
+ public int findWrapperIndexById(int id) {
for (int i = 0; i < getNumberOfLayers(); i++) {
if (getId(i) == id) {
- return mWrapper[i];
+ return i;
}
}
- return null;
- }
-
- @Override
- public void draw(Canvas canvas) {
- if (mPaint.getAlpha() < FULL_ALPHA) {
- canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(),
- mPaint, Canvas.ALL_SAVE_FLAG);
- }
- super.draw(canvas);
- if (mPaint.getAlpha() < FULL_ALPHA) {
- canvas.restore();
- }
- }
- }
-
- /**
- * Optimizes drawing when the dim drawable is an alpha-only color and imagein is opaque.
- * When the layer drawable is translucent (activity transition) then we can avoid the slow
- * saveLayer/restore draw path.
- */
- private class OptimizedTranslucentLayerDrawable extends TranslucentLayerDrawable {
- private PorterDuffColorFilter mColorFilter;
- private boolean mUpdatingColorFilter;
-
- public OptimizedTranslucentLayerDrawable(Drawable[] drawables) {
- super(drawables);
- }
-
- @Override
- protected void onAlphaChanged(int oldAlpha, int newAlpha) {
- if (newAlpha == FULL_ALPHA && oldAlpha < FULL_ALPHA) {
- if (DEBUG) Log.v(TAG, "transition complete");
- postChangeRunnable();
- }
- }
-
- @Override
- public void invalidateSelf() {
- super.invalidateSelf();
- updateColorFilter();
+ return -1;
}
@Override
public void invalidateDrawable(Drawable who) {
- if (!mUpdatingColorFilter) {
- invalidateSelf();
+ // Prevent invalidate when temporarily change child drawable's alpha in draw()
+ if (!mSuspendInvalidation) {
+ super.invalidateDrawable(who);
}
}
- private void updateColorFilter() {
- DrawableWrapper dimWrapper = findWrapperById(R.id.background_dim);
- DrawableWrapper imageInWrapper = findWrapperById(R.id.background_imagein);
- DrawableWrapper imageOutWrapper = findWrapperById(R.id.background_imageout);
-
- mColorFilter = null;
- if (imageInWrapper != null && imageInWrapper.getAlpha() == FULL_ALPHA
- && dimWrapper.getDrawable() instanceof ColorDrawable) {
- int dimColor = ((ColorDrawable) dimWrapper.getDrawable()).getColor();
- if (Color.red(dimColor) == 0
- && Color.green(dimColor) == 0
- && Color.blue(dimColor) == 0) {
- int dimAlpha = 255 - Color.alpha(dimColor);
- int color = Color.argb(getAlpha(), dimAlpha, dimAlpha, dimAlpha);
- mColorFilter = new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY);
- }
- }
- mUpdatingColorFilter = true;
- if (imageInWrapper != null) {
- imageInWrapper.setColorFilter(mColorFilter);
- }
- if (imageOutWrapper != null) {
- imageOutWrapper.setColorFilter(null);
- }
- mUpdatingColorFilter = false;
- }
-
@Override
public void draw(Canvas canvas) {
- DrawableWrapper imageInWrapper = findWrapperById(R.id.background_imagein);
- if (imageInWrapper != null && imageInWrapper.getDrawable() != null
- && imageInWrapper.getColorFilter() != null) {
- imageInWrapper.getDrawable().draw(canvas);
- } else {
- super.draw(canvas);
+ for (int i = 0; i < mWrapper.length; i++) {
+ final Drawable d;
+ // For each child drawable, we multiple Wrapper's alpha and LayerDrawable's alpha
+ // temporarily using mSuspendInvalidation to suppress invalidate event.
+ if (mWrapper[i] != null && (d = mWrapper[i].getDrawable()) != null) {
+ int alpha = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
+ ? DrawableCompat.getAlpha(d) : FULL_ALPHA;
+ final int savedAlpha = alpha;
+ int multiple = 0;
+ if (mAlpha < FULL_ALPHA) {
+ alpha = alpha * mAlpha;
+ multiple++;
+ }
+ if (mWrapper[i].mAlpha < FULL_ALPHA) {
+ alpha = alpha * mWrapper[i].mAlpha;
+ multiple++;
+ }
+ if (multiple == 0) {
+ d.draw(canvas);
+ } else {
+ if (multiple == 1) {
+ alpha = alpha / FULL_ALPHA;
+ } else if (multiple == 2) {
+ alpha = alpha / (FULL_ALPHA * FULL_ALPHA);
+ }
+ try {
+ mSuspendInvalidation = true;
+ d.setAlpha(alpha);
+ d.draw(canvas);
+ d.setAlpha(savedAlpha);
+ } finally {
+ mSuspendInvalidation = false;
+ }
+ }
+ }
}
}
}
- private TranslucentLayerDrawable createOptimizedTranslucentLayerDrawable(
+ TranslucentLayerDrawable createTranslucentLayerDrawable(
LayerDrawable layerDrawable) {
int numChildren = layerDrawable.getNumberOfLayers();
Drawable[] drawables = new Drawable[numChildren];
for (int i = 0; i < numChildren; i++) {
drawables[i] = layerDrawable.getDrawable(i);
}
- TranslucentLayerDrawable result = new OptimizedTranslucentLayerDrawable(drawables);
+ TranslucentLayerDrawable result = new TranslucentLayerDrawable(drawables);
for (int i = 0; i < numChildren; i++) {
result.setId(i, layerDrawable.getId(i));
}
@@ -439,7 +405,8 @@
}
TranslucentLayerDrawable mLayerDrawable;
- private Drawable mDimDrawable;
+ int mImageInWrapperIndex;
+ int mImageOutWrapperIndex;
ChangeBackgroundRunnable mChangeRunnable;
private boolean mChangeRunnablePending;
@@ -474,25 +441,8 @@
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int fadeInAlpha = (Integer) animation.getAnimatedValue();
- DrawableWrapper imageInWrapper = getImageInWrapper();
- if (imageInWrapper != null) {
- imageInWrapper.setAlpha(fadeInAlpha);
- } else {
- DrawableWrapper imageOutWrapper = getImageOutWrapper();
- if (imageOutWrapper != null) {
- imageOutWrapper.setAlpha(255 - fadeInAlpha);
- }
- }
- }
- };
-
- private final ValueAnimator.AnimatorUpdateListener mDimUpdateListener =
- new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- DrawableWrapper dimWrapper = getDimWrapper();
- if (dimWrapper != null) {
- dimWrapper.setAlpha((Integer) animation.getAnimatedValue());
+ if (mImageInWrapperIndex != -1) {
+ mLayerDrawable.setWrapperAlpha(mImageInWrapperIndex, fadeInAlpha);
}
}
};
@@ -501,7 +451,7 @@
* Shared memory continuity service.
*/
private static class BackgroundContinuityService {
- private static final String TAG = "BackgroundContinuityService";
+ private static final String TAG = "BackgroundContinuity";
private static boolean DEBUG = BackgroundManager.DEBUG;
private static BackgroundContinuityService sService = new BackgroundContinuityService();
@@ -544,6 +494,7 @@
}
public void setColor(int color) {
mColor = color;
+ mDrawable = null;
}
public void setDrawable(Drawable drawable) {
mDrawable = drawable;
@@ -569,6 +520,14 @@
}
}
+ Drawable getDefaultDrawable() {
+ if (mBackgroundColor != Color.TRANSPARENT) {
+ return new ColorDrawable(mBackgroundColor);
+ } else {
+ return getThemeDrawable();
+ }
+ }
+
private Drawable getThemeDrawable() {
Drawable drawable = null;
if (mThemeDrawableResourceId != -1) {
@@ -619,9 +578,6 @@
mAnimator.addUpdateListener(mAnimationUpdateListener);
mAnimator.setInterpolator(defaultInterpolator);
- mDimAnimator = new ValueAnimator();
- mDimAnimator.addUpdateListener(mDimUpdateListener);
-
TypedArray ta = activity.getTheme().obtainStyledAttributes(new int[] {
android.R.attr.windowBackground });
mThemeDrawableResourceId = ta.getResourceId(0, -1);
@@ -652,22 +608,12 @@
DrawableWrapper getImageInWrapper() {
return mLayerDrawable == null
- ? null : mLayerDrawable.findWrapperById(R.id.background_imagein);
+ ? null : mLayerDrawable.mWrapper[mImageInWrapperIndex];
}
DrawableWrapper getImageOutWrapper() {
return mLayerDrawable == null
- ? null : mLayerDrawable.findWrapperById(R.id.background_imageout);
- }
-
- DrawableWrapper getDimWrapper() {
- return mLayerDrawable == null
- ? null : mLayerDrawable.findWrapperById(R.id.background_dim);
- }
-
- private DrawableWrapper getColorWrapper() {
- return mLayerDrawable == null
- ? null : mLayerDrawable.findWrapperById(R.id.background_color);
+ ? null : mLayerDrawable.mWrapper[mImageOutWrapperIndex];
}
/**
@@ -675,21 +621,12 @@
* At that point the view becomes visible.
*/
void onActivityStart() {
- if (mService == null) {
- return;
- }
- if (mLayerDrawable == null) {
- if (DEBUG) {
- Log.v(TAG, "onActivityStart " + this + " released state, syncing with service");
- }
- syncWithService();
- } else {
- if (DEBUG) {
- Log.v(TAG, "onActivityStart " + this + " updating service color "
- + mBackgroundColor + " drawable " + mBackgroundDrawable);
- }
- mService.setColor(mBackgroundColor);
- mService.setDrawable(mBackgroundDrawable);
+ updateImmediate();
+ }
+
+ void onStop() {
+ if (isAutoReleaseOnStop()) {
+ release();
}
}
@@ -712,33 +649,6 @@
updateImmediate();
}
- private void lazyInit() {
- if (mLayerDrawable != null) {
- return;
- }
-
- LayerDrawable layerDrawable = (LayerDrawable)
- ContextCompat.getDrawable(mContext, R.drawable.lb_background).mutate();
- mLayerDrawable = createOptimizedTranslucentLayerDrawable(layerDrawable);
- BackgroundHelper.setBackgroundPreservingAlpha(mBgView, mLayerDrawable);
-
- mLayerDrawable.clearDrawable(R.id.background_imageout, mContext);
- mLayerDrawable.updateDrawable(R.id.background_theme, getThemeDrawable());
-
- updateDimWrapper();
- }
-
- private void updateDimWrapper() {
- if (mDimDrawable == null) {
- mDimDrawable = getDefaultDimLayer();
- }
- Drawable dimDrawable = mDimDrawable.getConstantState().newDrawable(
- mContext.getResources()).mutate();
- if (mLayerDrawable != null) {
- mLayerDrawable.updateDrawable(R.id.background_dim, dimDrawable);
- }
- }
-
/**
* Makes the background visible on the given Window. The background manager must be attached
* when the background is set.
@@ -762,7 +672,6 @@
private void attachBehindWindow(Window window) {
if (DEBUG) Log.v(TAG, "attachBehindWindow " + window);
- mWindow = window;
mWindowManager = window.getWindowManager();
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
@@ -786,6 +695,9 @@
* Adds the composite drawable to the given view.
*/
public void attachToView(View sceneRoot) {
+ if (mAttached) {
+ throw new IllegalStateException("Already attached to " + mBgView);
+ }
mBgView = sceneRoot;
mAttached = true;
syncWithService();
@@ -811,7 +723,6 @@
}
mWindowManager = null;
- mWindow = null;
mBgView = null;
mAttached = false;
@@ -822,56 +733,48 @@
}
/**
- * Release references to Drawables. Typically called to reduce memory
- * overhead when not visible.
- * <p>
- * When an Activity is started, if the BackgroundManager has not been
- * released, the continuity service is updated from the BackgroundManager
- * state. If the BackgroundManager was released, the BackgroundManager
- * inherits the current state from the continuity service.
+ * Release references to Drawable/Bitmap. Typically called in Activity onStop() to reduce memory
+ * overhead when not visible. It's app's responsibility to restore the drawable/bitmap in
+ * Activity onStart(). The method is automatically called in onStop() when
+ * {@link #isAutoReleaseOnStop()} is true.
+ * @see #setAutoReleaseOnStop(boolean)
*/
public void release() {
if (DEBUG) Log.v(TAG, "release " + this);
if (mLayerDrawable != null) {
mLayerDrawable.clearDrawable(R.id.background_imagein, mContext);
mLayerDrawable.clearDrawable(R.id.background_imageout, mContext);
- mLayerDrawable.clearDrawable(R.id.background_theme, mContext);
mLayerDrawable = null;
}
if (mChangeRunnable != null) {
mHandler.removeCallbacks(mChangeRunnable);
mChangeRunnable = null;
}
- releaseBackgroundBitmap();
- }
-
- void releaseBackgroundBitmap() {
mBackgroundDrawable = null;
}
- void setBackgroundDrawable(Drawable drawable) {
- mBackgroundDrawable = drawable;
- mService.setDrawable(mBackgroundDrawable);
- }
-
/**
* Sets the drawable used as a dim layer.
+ * @deprecated No longer support dim layer.
*/
+ @Deprecated
public void setDimLayer(Drawable drawable) {
- mDimDrawable = drawable;
- updateDimWrapper();
}
/**
* Returns the drawable used as a dim layer.
+ * @deprecated No longer support dim layer.
*/
+ @Deprecated
public Drawable getDimLayer() {
- return mDimDrawable;
+ return null;
}
/**
* Returns the default drawable used as a dim layer.
+ * @deprecated No longer support dim layer.
*/
+ @Deprecated
public Drawable getDefaultDimLayer() {
return ContextCompat.getDrawable(mContext, R.color.lb_background_protection);
}
@@ -901,29 +804,33 @@
}
}
+ private void lazyInit() {
+ if (mLayerDrawable != null) {
+ return;
+ }
+
+ LayerDrawable layerDrawable = (LayerDrawable)
+ ContextCompat.getDrawable(mContext, R.drawable.lb_background).mutate();
+ mLayerDrawable = createTranslucentLayerDrawable(layerDrawable);
+ mImageInWrapperIndex = mLayerDrawable.findWrapperIndexById(R.id.background_imagein);
+ mImageOutWrapperIndex = mLayerDrawable.findWrapperIndexById(R.id.background_imageout);
+ BackgroundHelper.setBackgroundPreservingAlpha(mBgView, mLayerDrawable);
+ }
+
private void updateImmediate() {
+ if (!mAttached) {
+ return;
+ }
lazyInit();
- DrawableWrapper colorWrapper = getColorWrapper();
- if (colorWrapper != null) {
- colorWrapper.setColor(mBackgroundColor);
- }
- DrawableWrapper dimWrapper = getDimWrapper();
- if (dimWrapper != null) {
- dimWrapper.setAlpha(mBackgroundColor == Color.TRANSPARENT ? 0 : DIM_ALPHA_ON_SOLID);
- }
- showWallpaper(mBackgroundColor == Color.TRANSPARENT);
-
if (mBackgroundDrawable == null) {
- mLayerDrawable.clearDrawable(R.id.background_imagein, mContext);
+ if (DEBUG) Log.v(TAG, "Use defefault background");
+ mLayerDrawable.updateDrawable(R.id.background_imagein, getDefaultDrawable());
} else {
- if (DEBUG) Log.v(TAG, "Background drawable is available");
- mLayerDrawable.updateDrawable(
- R.id.background_imagein, mBackgroundDrawable);
- if (dimWrapper != null) {
- dimWrapper.setAlpha(FULL_ALPHA);
- }
+ if (DEBUG) Log.v(TAG, "Background drawable is available " + mBackgroundDrawable);
+ mLayerDrawable.updateDrawable(R.id.background_imagein, mBackgroundDrawable);
}
+ mLayerDrawable.clearDrawable(R.id.background_imageout, mContext);
}
/**
@@ -933,13 +840,13 @@
public void setColor(@ColorInt int color) {
if (DEBUG) Log.v(TAG, "setColor " + Integer.toHexString(color));
+ mService.setColor(color);
mBackgroundColor = color;
- mService.setColor(mBackgroundColor);
-
- DrawableWrapper colorWrapper = getColorWrapper();
- if (colorWrapper != null) {
- colorWrapper.setColor(mBackgroundColor);
+ mBackgroundDrawable = null;
+ if (mLayerDrawable == null) {
+ return;
}
+ setDrawableInternal(getDefaultDrawable());
}
/**
@@ -950,7 +857,26 @@
*/
public void setDrawable(Drawable drawable) {
if (DEBUG) Log.v(TAG, "setBackgroundDrawable " + drawable);
- setDrawableInternal(drawable);
+
+ mService.setDrawable(drawable);
+ mBackgroundDrawable = drawable;
+ if (mLayerDrawable == null) {
+ return;
+ }
+ if (drawable == null) {
+ setDrawableInternal(getDefaultDrawable());
+ } else {
+ setDrawableInternal(drawable);
+ }
+ }
+
+ /**
+ * Clears the Drawable set by {@link #setDrawable(Drawable)} or {@link #setBitmap(Bitmap)}.
+ * BackgroundManager will show a solid color set by {@link #setColor(int)} or theme drawable
+ * if color is not provided.
+ */
+ public void clearDrawable() {
+ setDrawable(null);
}
private void setDrawableInternal(Drawable drawable) {
@@ -967,17 +893,6 @@
mChangeRunnable = null;
}
- // If layer drawable is null then the activity hasn't started yet.
- // If the layer drawable alpha is zero then the activity transition hasn't started yet.
- // In these cases we can update the background immediately and let activity transition
- // fade it in.
- if (mLayerDrawable == null || mLayerDrawable.getAlpha() == 0) {
- if (DEBUG) Log.v(TAG, "setDrawableInternal null or alpha is zero");
- setBackgroundDrawable(drawable);
- updateImmediate();
- return;
- }
-
mChangeRunnable = new ChangeBackgroundRunnable(drawable);
mChangeRunnablePending = true;
@@ -1000,7 +915,7 @@
}
if (bitmap == null) {
- setDrawableInternal(null);
+ setDrawable(null);
return;
}
@@ -1040,45 +955,22 @@
BitmapDrawable bitmapDrawable = new BitmapDrawable(mContext.getResources(), bitmap, matrix);
- setDrawableInternal(bitmapDrawable);
+ setDrawable(bitmapDrawable);
}
- void applyBackgroundChanges() {
- if (!mAttached) {
- return;
- }
+ /**
+ * Enable or disable call release() in Activity onStop(). Default is true.
+ * @param autoReleaseOnStop True to call release() in Activity onStop(), false otherwise.
+ */
+ public void setAutoReleaseOnStop(boolean autoReleaseOnStop) {
+ mAutoReleaseOnStop = autoReleaseOnStop;
+ }
- if (DEBUG) Log.v(TAG, "applyBackgroundChanges drawable " + mBackgroundDrawable);
-
- int dimAlpha = -1;
-
- if (getImageOutWrapper() != null) {
- dimAlpha = mBackgroundColor == Color.TRANSPARENT ? 0 : DIM_ALPHA_ON_SOLID;
- }
-
- DrawableWrapper imageInWrapper = getImageInWrapper();
- if (imageInWrapper == null && mBackgroundDrawable != null) {
- if (DEBUG) Log.v(TAG, "creating new imagein drawable");
- imageInWrapper = mLayerDrawable.updateDrawable(
- R.id.background_imagein, mBackgroundDrawable);
- if (DEBUG) Log.v(TAG, "imageInWrapper animation starting");
- imageInWrapper.setAlpha(0);
- dimAlpha = FULL_ALPHA;
- }
-
- mAnimator.setDuration(FADE_DURATION);
- mAnimator.start();
-
- DrawableWrapper dimWrapper = getDimWrapper();
- if (dimWrapper != null && dimAlpha >= 0) {
- if (DEBUG) Log.v(TAG, "dimwrapper animation starting to " + dimAlpha);
- mDimAnimator.cancel();
- mDimAnimator.setIntValues(dimWrapper.getAlpha(), dimAlpha);
- mDimAnimator.setDuration(FADE_DURATION);
- mDimAnimator.setInterpolator(
- dimAlpha == FULL_ALPHA ? mDecelerateInterpolator : mAccelerateInterpolator);
- mDimAnimator.start();
- }
+ /**
+ * @return True if release() in Activity.onStop(), false otherwise.
+ */
+ public boolean isAutoReleaseOnStop() {
+ return mAutoReleaseOnStop;
}
/**
@@ -1108,14 +1000,19 @@
return true;
}
}
+ if (first instanceof ColorDrawable && second instanceof ColorDrawable) {
+ if (((ColorDrawable) first).getColor() == ((ColorDrawable) second).getColor()) {
+ return true;
+ }
+ }
return false;
}
/**
* Task which changes the background.
*/
- class ChangeBackgroundRunnable implements Runnable {
- Drawable mDrawable;
+ final class ChangeBackgroundRunnable implements Runnable {
+ final Drawable mDrawable;
ChangeBackgroundRunnable(Drawable drawable) {
mDrawable = drawable;
@@ -1133,15 +1030,13 @@
return;
}
- if (sameDrawable(mDrawable, mBackgroundDrawable)) {
- if (DEBUG) Log.v(TAG, "new drawable same as current");
- return;
- }
-
- releaseBackgroundBitmap();
-
DrawableWrapper imageInWrapper = getImageInWrapper();
if (imageInWrapper != null) {
+ if (sameDrawable(mDrawable, imageInWrapper.getDrawable())) {
+ if (DEBUG) Log.v(TAG, "new drawable same as current");
+ return;
+ }
+
if (DEBUG) Log.v(TAG, "moving image in to image out");
// Order is important! Setting a drawable "removes" the
// previous one from the view
@@ -1150,37 +1045,40 @@
imageInWrapper.getDrawable());
}
- setBackgroundDrawable(mDrawable);
applyBackgroundChanges();
}
+
+ void applyBackgroundChanges() {
+ if (!mAttached) {
+ return;
+ }
+
+ if (DEBUG) Log.v(TAG, "applyBackgroundChanges drawable " + mDrawable);
+
+ DrawableWrapper imageInWrapper = getImageInWrapper();
+ if (imageInWrapper == null && mDrawable != null) {
+ if (DEBUG) Log.v(TAG, "creating new imagein drawable");
+ imageInWrapper = mLayerDrawable.updateDrawable(
+ R.id.background_imagein, mDrawable);
+ if (DEBUG) Log.v(TAG, "imageInWrapper animation starting");
+ mLayerDrawable.setWrapperAlpha(mImageInWrapperIndex, 0);
+ }
+
+ mAnimator.setDuration(FADE_DURATION);
+ mAnimator.start();
+
+ }
+
+ }
+
+ static class EmptyDrawable extends BitmapDrawable {
+ EmptyDrawable(Resources res) {
+ super(res, (Bitmap) null);
+ }
}
static Drawable createEmptyDrawable(Context context) {
- Bitmap bitmap = null;
- return new BitmapDrawable(context.getResources(), bitmap);
+ return new EmptyDrawable(context.getResources());
}
- private void showWallpaper(boolean show) {
- if (DEBUG) Log.v(TAG, "showWallpaper called with " + show);
- if (mWindow == null) {
- return;
- }
-
- WindowManager.LayoutParams layoutParams = mWindow.getAttributes();
- if (show) {
- if ((layoutParams.flags & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
- return;
- }
- if (DEBUG) Log.v(TAG, "showing wallpaper");
- layoutParams.flags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
- } else {
- if ((layoutParams.flags & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) == 0) {
- return;
- }
- if (DEBUG) Log.v(TAG, "hiding wallpaper");
- layoutParams.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
- }
-
- mWindow.setAttributes(layoutParams);
- }
}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
index 4630c79..a94b7f3 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
@@ -481,11 +481,19 @@
}
/**
- * Returns the selected position.
+ * @return The position of selected row.
*/
public int getSelectedPosition() {
return 0;
}
+
+ /**
+ * @param position Position of Row.
+ * @return Row ViewHolder.
+ */
+ public RowPresenter.ViewHolder findRowViewHolderByPosition(int position) {
+ return null;
+ }
}
private boolean createMainFragment(ObjectAdapter adapter, int position) {
@@ -1485,6 +1493,17 @@
}
/**
+ * @return selected row ViewHolder inside fragment created by {@link MainFragmentRowsAdapter}.
+ */
+ public RowPresenter.ViewHolder getSelectedRowViewHolder() {
+ if (mMainFragmentRowsAdapter != null) {
+ int rowPos = mMainFragmentRowsAdapter.getSelectedPosition();
+ return mMainFragmentRowsAdapter.findRowViewHolderByPosition(rowPos);
+ }
+ return null;
+ }
+
+ /**
* Sets the selected row position.
*/
public void setSelectedPosition(int position, boolean smooth) {
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java
index 4979a24..a08ce67 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java
@@ -484,11 +484,19 @@
}
/**
- * Returns the selected position.
+ * @return The position of selected row.
*/
public int getSelectedPosition() {
return 0;
}
+
+ /**
+ * @param position Position of Row.
+ * @return Row ViewHolder.
+ */
+ public RowPresenter.ViewHolder findRowViewHolderByPosition(int position) {
+ return null;
+ }
}
private boolean createMainFragment(ObjectAdapter adapter, int position) {
@@ -1488,6 +1496,17 @@
}
/**
+ * @return selected row ViewHolder inside fragment created by {@link MainFragmentRowsAdapter}.
+ */
+ public RowPresenter.ViewHolder getSelectedRowViewHolder() {
+ if (mMainFragmentRowsAdapter != null) {
+ int rowPos = mMainFragmentRowsAdapter.getSelectedPosition();
+ return mMainFragmentRowsAdapter.findRowViewHolderByPosition(rowPos);
+ }
+ return null;
+ }
+
+ /**
* Sets the selected row position.
*/
public void setSelectedPosition(int position, boolean smooth) {
diff --git a/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java b/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java
index 6e1c008..ca486ed 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java
@@ -556,6 +556,19 @@
}
}
+ /**
+ * Find row ViewHolder by position in adapter.
+ * @param position Position of row.
+ * @return ViewHolder of Row.
+ */
+ public RowPresenter.ViewHolder findRowViewHolderByPosition(int position) {
+ if (mVerticalGridView == null) {
+ return null;
+ }
+ return getRowViewHolder((ItemBridgeAdapter.ViewHolder) mVerticalGridView
+ .findViewHolderForAdapterPosition(position));
+ }
+
public static class MainFragmentAdapter extends BrowseFragment.MainFragmentAdapter<RowsFragment> {
public MainFragmentAdapter(RowsFragment fragment) {
@@ -641,5 +654,10 @@
public int getSelectedPosition() {
return getFragment().getSelectedPosition();
}
+
+ @Override
+ public RowPresenter.ViewHolder findRowViewHolderByPosition(int position) {
+ return getFragment().findRowViewHolderByPosition(position);
+ }
}
}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/RowsSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/RowsSupportFragment.java
index a5acb41..d6c8d3f 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/RowsSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/RowsSupportFragment.java
@@ -559,6 +559,19 @@
}
}
+ /**
+ * Find row ViewHolder by position in adapter.
+ * @param position Position of row.
+ * @return ViewHolder of Row.
+ */
+ public RowPresenter.ViewHolder findRowViewHolderByPosition(int position) {
+ if (mVerticalGridView == null) {
+ return null;
+ }
+ return getRowViewHolder((ItemBridgeAdapter.ViewHolder) mVerticalGridView
+ .findViewHolderForAdapterPosition(position));
+ }
+
public static class MainFragmentAdapter extends BrowseSupportFragment.MainFragmentAdapter<RowsSupportFragment> {
public MainFragmentAdapter(RowsSupportFragment fragment) {
@@ -644,5 +657,10 @@
public int getSelectedPosition() {
return getFragment().getSelectedPosition();
}
+
+ @Override
+ public RowPresenter.ViewHolder findRowViewHolderByPosition(int position) {
+ return getFragment().findRowViewHolderByPosition(position);
+ }
}
}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java
index 790cee3..c4658e7 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java
@@ -122,6 +122,21 @@
}
return ibvh.getViewHolder();
}
+
+ @Override
+ public Presenter.ViewHolder getSelectedItemViewHolder() {
+ return getItemViewHolder(getSelectedPosition());
+ }
+
+ @Override
+ public Object getSelectedItem() {
+ ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder) mGridView
+ .findViewHolderForAdapterPosition(getSelectedPosition());
+ if (ibvh == null) {
+ return null;
+ }
+ return ibvh.getItem();
+ }
}
/**
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java
index 8267145..034ecc3 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java
@@ -290,6 +290,21 @@
public final BaseOnItemViewClickedListener getOnItemViewClickedListener() {
return mOnItemViewClickedListener;
}
+ /**
+ * Return {@link ViewHolder} of currently selected item inside a row ViewHolder.
+ * @return The selected item's ViewHolder.
+ */
+ public Presenter.ViewHolder getSelectedItemViewHolder() {
+ return null;
+ }
+
+ /**
+ * Return currently selected item inside a row ViewHolder.
+ * @return The selected item.
+ */
+ public Object getSelectedItem() {
+ return null;
+ }
}
private RowHeaderPresenter mHeaderPresenter = new RowHeaderPresenter();
diff --git a/v17/leanback/tests/AndroidManifest.xml b/v17/leanback/tests/AndroidManifest.xml
index e8b81b5..16b78cb 100644
--- a/v17/leanback/tests/AndroidManifest.xml
+++ b/v17/leanback/tests/AndroidManifest.xml
@@ -75,6 +75,9 @@
android:theme="@style/Theme.Leanback"
android:exported="true" />
+ <activity android:name="android.support.v17.leanback.app.TestActivity"
+ android:theme="@style/Theme.Leanback"
+ android:exported="true" />
</application>
</manifest>
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/BackgroundManagerTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/BackgroundManagerTest.java
new file mode 100644
index 0000000..7e7873a
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/BackgroundManagerTest.java
@@ -0,0 +1,651 @@
+/*
+ * Copyright (C) 2016 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 android.support.v17.leanback.app;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v17.leanback.testutils.PollingCheck;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+
+/**
+ * @hide from javadoc
+ */
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class BackgroundManagerTest {
+
+ @Rule
+ public TestName mUnitTestName = new TestName();
+
+ @Rule
+ public TestActivity.TestActivityTestRule mRule;
+
+ String generateProviderName(String name) {
+ return mUnitTestName.getMethodName() + "_" + name;
+ }
+
+ void waitForActivityStop(final TestActivity activity) {
+ PollingCheck.waitFor(5000/* timeout */, new PollingCheck.PollingCheckCondition() {
+ @Override
+ public boolean canPreProceed() {
+ return false;
+ }
+
+ @Override
+ public boolean canProceed() {
+ // two step: first ChangeRunnable gets run.
+ // then Animator is finished.
+ return !activity.isStarted();
+ }
+ });
+ }
+
+ void waitForBackgroundAnimationFinish(final BackgroundManager manager) {
+ PollingCheck.waitFor(5000/* timeout */, new PollingCheck.PollingCheckCondition() {
+ @Override
+ public boolean canPreProceed() {
+ return false;
+ }
+
+ @Override
+ public boolean canProceed() {
+ // two step: first ChangeRunnable gets run.
+ // then Animator is finished.
+ return manager.mLayerDrawable != null && manager.mChangeRunnable == null
+ && !manager.mAnimator.isRunning();
+ }
+ });
+ }
+
+ void setBitmapAndVerify(final BackgroundManager manager, final Bitmap bitmap)
+ throws Throwable {
+ mRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ manager.setBitmap(bitmap);
+ }
+ });
+ waitForBackgroundAnimationFinish(manager);
+ assertIsBitmapDrawable(manager, bitmap);
+ }
+
+ void setDrawableAndVerify(final BackgroundManager manager, final Drawable drawable)
+ throws Throwable {
+ mRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ manager.setDrawable(drawable);
+ }
+ });
+ waitForBackgroundAnimationFinish(manager);
+ assertIsDrawable(manager, drawable);
+ }
+
+ void setBitmapNullAndVerifyColor(final BackgroundManager manager, final int color)
+ throws Throwable {
+ mRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ manager.setBitmap(null);
+ }
+ });
+ waitForBackgroundAnimationFinish(manager);
+ assertIsColorDrawable(manager, color);
+ }
+
+ void setDrawableNullAndVerifyColor(final BackgroundManager manager, final int color)
+ throws Throwable {
+ mRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ manager.setDrawable(null);
+ }
+ });
+ waitForBackgroundAnimationFinish(manager);
+ assertIsColorDrawable(manager, color);
+ }
+
+ void setColorAndVerify(final BackgroundManager manager, final int color)
+ throws Throwable {
+ mRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ manager.setColor(color);
+ }
+ });
+ waitForBackgroundAnimationFinish(manager);
+ assertIsColorDrawable(manager, color);
+ }
+
+ /**
+ * Launch TestActivity without using TestActivityRule.
+ */
+ TestActivity launchActivity(String name, final TestActivity.Provider provider2)
+ throws Throwable {
+ final String providerName2 = generateProviderName(name);
+ TestActivity.setProvider(providerName2, provider2);
+ mRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Intent intent = new Intent(mRule.getActivity(), TestActivity.class);
+ intent.putExtra(TestActivity.EXTRA_PROVIDER, providerName2);
+ mRule.getActivity().startActivity(intent);
+ }
+ });
+
+ PollingCheck.waitFor(5000/*timeout*/, new PollingCheck.PollingCheckCondition() {
+ @Override
+ public boolean canPreProceed() {
+ return false;
+ }
+
+ @Override
+ public boolean canProceed() {
+ return provider2.getActivity() != null && provider2.getActivity().isStarted();
+ }
+ });
+ return provider2.getActivity();
+ }
+
+ void assertIsColorDrawable(BackgroundManager manager, int color) {
+ assertNull(manager.mLayerDrawable.mWrapper[0]);
+ assertTrue(manager.mLayerDrawable.getDrawable(0)
+ instanceof BackgroundManager.EmptyDrawable);
+ assertEquals(manager.mLayerDrawable.mWrapper[1].mAlpha, 255);
+ assertEquals(((ColorDrawable) manager.mLayerDrawable.getDrawable(1)).getColor(), color);
+ assertNull(manager.mBackgroundDrawable);
+ }
+
+ void assertIsBitmapDrawable(BackgroundManager manager, Bitmap bitmap) {
+ assertNull(manager.mLayerDrawable.mWrapper[0]);
+ assertTrue(manager.mLayerDrawable.getDrawable(0)
+ instanceof BackgroundManager.EmptyDrawable);
+ assertEquals(manager.mLayerDrawable.mWrapper[1].mAlpha, 255);
+ assertSame(((BackgroundManager.BitmapDrawable) manager.mLayerDrawable.getDrawable(1))
+ .mState.mBitmap, bitmap);
+ assertSame(((BackgroundManager.BitmapDrawable) manager.mBackgroundDrawable)
+ .mState.mBitmap, bitmap);
+ }
+
+ void assertIsDrawable(BackgroundManager manager, Drawable drawable) {
+ assertNull(manager.mLayerDrawable.mWrapper[0]);
+ assertTrue(manager.mLayerDrawable.getDrawable(0)
+ instanceof BackgroundManager.EmptyDrawable);
+ assertEquals(manager.mLayerDrawable.mWrapper[1].mAlpha, 255);
+ assertSame(manager.mLayerDrawable.getDrawable(1), drawable);
+ assertSame(manager.mBackgroundDrawable, drawable);
+ }
+
+ Bitmap createBitmap(int width, int height, int color) {
+ final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ Paint paint = new Paint();
+ paint.setColor(color);
+ canvas.drawRect(0, 0, width, height, paint);
+ return bitmap;
+ }
+
+ Drawable createDrawable(int width, int height, int color) {
+ final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ Paint paint = new Paint();
+ paint.setColor(color);
+ canvas.drawRect(0, 0, width, height, paint);
+ return new BitmapDrawable(mRule.getActivity().getResources(), bitmap);
+ }
+
+ void testSwitchBackgrounds(final BackgroundManager manager) throws Throwable {
+ setBitmapAndVerify(manager, createBitmap(200, 100, Color.GREEN));
+
+ // Drawable -> Drawable
+ setDrawableAndVerify(manager, createDrawable(200, 100, Color.MAGENTA));
+
+ setBitmapAndVerify(manager, createBitmap(200, 100, Color.GRAY));
+
+ // Drawable -> Color
+ setColorAndVerify(manager, Color.RED);
+
+ // Color -> Drawable
+ setBitmapAndVerify(manager, createBitmap(200, 100, Color.BLACK));
+
+ // Set Drawable to null -> show last Color
+ setBitmapNullAndVerifyColor(manager, Color.RED);
+
+ // Color -> Drawable
+ setBitmapAndVerify(manager, createBitmap(200, 100, Color.MAGENTA));
+ }
+
+ @Test
+ public void establishInOnAttachToWindow() throws Throwable {
+ TestActivity.Provider provider1 = new TestActivity.Provider() {
+ @Override
+ public void onAttachedToWindow(TestActivity activity) {
+ BackgroundManager.getInstance(activity).attach(activity.getWindow());
+ }
+
+ @Override
+ public void onStart(TestActivity activity) {
+ BackgroundManager.getInstance(activity).setColor(Color.BLUE);
+ }
+ };
+ mRule = new TestActivity.TestActivityTestRule(provider1, generateProviderName("activity1"));
+ final TestActivity activity1 = mRule.launchActivity();
+
+ BackgroundManager manager = BackgroundManager.getInstance(activity1);
+ waitForBackgroundAnimationFinish(manager);
+ assertIsColorDrawable(manager, Color.BLUE);
+
+ testSwitchBackgrounds(manager);
+ }
+
+ @Test
+ public void multipleSetBitmaps() throws Throwable {
+ TestActivity.Provider provider1 = new TestActivity.Provider() {
+ @Override
+ public void onAttachedToWindow(TestActivity activity) {
+ BackgroundManager.getInstance(activity).attach(activity.getWindow());
+ }
+
+ @Override
+ public void onStart(TestActivity activity) {
+ BackgroundManager.getInstance(activity).setColor(Color.BLUE);
+ }
+ };
+ mRule = new TestActivity.TestActivityTestRule(provider1,
+ generateProviderName("activity1"));
+ final TestActivity activity1 = mRule.launchActivity();
+
+ final BackgroundManager manager = BackgroundManager.getInstance(activity1);
+ waitForBackgroundAnimationFinish(manager);
+ assertIsColorDrawable(manager, Color.BLUE);
+
+ final Bitmap bitmap1 = createBitmap(200, 100, Color.RED);
+ final Bitmap bitmap2 = createBitmap(200, 100, Color.GRAY);
+ final Bitmap bitmap3 = createBitmap(200, 100, Color.GREEN);
+ final Bitmap bitmap4 = createBitmap(200, 100, Color.MAGENTA);
+ mRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ manager.setBitmap(bitmap1);
+ manager.setBitmap(bitmap2);
+ manager.setBitmap(bitmap3);
+ manager.setBitmap(bitmap4);
+ }
+ });
+ waitForBackgroundAnimationFinish(manager);
+ assertIsBitmapDrawable(manager, bitmap4);
+ }
+
+ @Test
+ public void multipleSetBitmapsAndColor() throws Throwable {
+ TestActivity.Provider provider1 = new TestActivity.Provider() {
+ @Override
+ public void onAttachedToWindow(TestActivity activity) {
+ BackgroundManager.getInstance(activity).attach(activity.getWindow());
+ }
+
+ @Override
+ public void onStart(TestActivity activity) {
+ BackgroundManager.getInstance(activity).setColor(Color.BLUE);
+ }
+ };
+ mRule = new TestActivity.TestActivityTestRule(provider1, generateProviderName("activity1"));
+ final TestActivity activity1 = mRule.launchActivity();
+
+ final BackgroundManager manager = BackgroundManager.getInstance(activity1);
+ waitForBackgroundAnimationFinish(manager);
+ assertIsColorDrawable(manager, Color.BLUE);
+
+ final Bitmap bitmap1 = createBitmap(200, 100, Color.RED);
+ final Bitmap bitmap2 = createBitmap(200, 100, Color.GRAY);
+ final Bitmap bitmap3 = createBitmap(200, 100, Color.GREEN);
+ final int color = Color.MAGENTA;
+ mRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ manager.setBitmap(bitmap1);
+ manager.setBitmap(bitmap2);
+ }
+ });
+ mRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ manager.setBitmap(bitmap3);
+ manager.setColor(color);
+ }
+ });
+ waitForBackgroundAnimationFinish(manager);
+ assertIsColorDrawable(manager, color);
+ }
+
+ @Test
+ public void establishInOnCreate() throws Throwable {
+ TestActivity.Provider provider1 = new TestActivity.Provider() {
+ @Override
+ public void onCreate(TestActivity activity, Bundle savedInstanceState) {
+ super.onCreate(activity, savedInstanceState);
+ BackgroundManager.getInstance(activity).attach(activity.getWindow());
+ }
+
+ @Override
+ public void onStart(TestActivity activity) {
+ BackgroundManager.getInstance(activity).setColor(Color.BLUE);
+ }
+ };
+ mRule = new TestActivity.TestActivityTestRule(provider1, generateProviderName("activity1"));
+ final TestActivity activity1 = mRule.launchActivity();
+
+ BackgroundManager manager = BackgroundManager.getInstance(activity1);
+ waitForBackgroundAnimationFinish(manager);
+ assertIsColorDrawable(manager, Color.BLUE);
+
+ testSwitchBackgrounds(manager);
+ }
+
+ @Test
+ public void establishInOnStart() throws Throwable {
+ TestActivity.Provider provider1 = new TestActivity.Provider() {
+ @Override
+ public void onStart(TestActivity activity) {
+ BackgroundManager m = BackgroundManager.getInstance(activity);
+ m.attach(activity.getWindow());
+ m.setColor(Color.BLUE);
+ }
+ };
+ mRule = new TestActivity.TestActivityTestRule(provider1, generateProviderName("activity1"));
+ final TestActivity activity1 = mRule.launchActivity();
+
+ BackgroundManager manager = BackgroundManager.getInstance(activity1);
+ waitForBackgroundAnimationFinish(manager);
+ assertIsColorDrawable(manager, Color.BLUE);
+
+ testSwitchBackgrounds(manager);
+ }
+
+ @Test
+ public void assignColorImmediately() throws Throwable {
+ TestActivity.Provider provider1 = new TestActivity.Provider() {
+ @Override
+ public void onStart(TestActivity activity) {
+ BackgroundManager m = BackgroundManager.getInstance(activity);
+ // if we set color before attach, it will be assigned immediately
+ m.setColor(Color.BLUE);
+ m.attach(activity.getWindow());
+ assertIsColorDrawable(m, Color.BLUE);
+ }
+ };
+ mRule = new TestActivity.TestActivityTestRule(provider1, generateProviderName("activity1"));
+ final TestActivity activity1 = mRule.launchActivity();
+
+ BackgroundManager manager = BackgroundManager.getInstance(activity1);
+
+ testSwitchBackgrounds(manager);
+ }
+
+ @Test
+ public void assignBitmapImmediately() throws Throwable {
+ final Bitmap bitmap = createBitmap(200, 100, Color.BLUE);
+ TestActivity.Provider provider1 = new TestActivity.Provider() {
+ @Override
+ public void onStart(TestActivity activity) {
+ BackgroundManager m = BackgroundManager.getInstance(activity);
+ // if we set bitmap before attach, it will be assigned immediately
+ m.setBitmap(bitmap);
+ m.attach(activity.getWindow());
+ assertIsBitmapDrawable(m, bitmap);
+ }
+ };
+ mRule = new TestActivity.TestActivityTestRule(provider1, generateProviderName("activity1"));
+ final TestActivity activity1 = mRule.launchActivity();
+
+ BackgroundManager manager = BackgroundManager.getInstance(activity1);
+
+ testSwitchBackgrounds(manager);
+ }
+
+
+ @Test
+ public void inheritBitmapByNewActivity() throws Throwable {
+ final Bitmap bitmap = createBitmap(200, 100, Color.BLUE);
+ TestActivity.Provider provider1 = new TestActivity.Provider() {
+ @Override
+ public void onStart(TestActivity activity) {
+ BackgroundManager m = BackgroundManager.getInstance(activity);
+ // if we set bitmap before attach, it will be assigned immediately
+ m.setBitmap(bitmap);
+ m.attach(activity.getWindow());
+ assertIsBitmapDrawable(m, bitmap);
+ }
+ };
+ mRule = new TestActivity.TestActivityTestRule(provider1, generateProviderName("activity1"));
+ final TestActivity activity1 = mRule.launchActivity();
+ activity1.finish();
+
+ TestActivity.Provider provider2 = new TestActivity.Provider() {
+ @Override
+ public void onStart(TestActivity activity) {
+ BackgroundManager m = BackgroundManager.getInstance(activity);
+ m.attach(activity.getWindow());
+ assertIsBitmapDrawable(m, bitmap);
+ }
+ };
+
+ TestActivity activity2 = launchActivity("activity2", provider2);
+ waitForActivityStop(activity1);
+
+ BackgroundManager manager2 = BackgroundManager.getInstance(activity2);
+ assertIsBitmapDrawable(manager2, bitmap);
+ activity2.finish();
+ }
+
+ @Test
+ public void inheritColorByNewActivity() throws Throwable {
+ final int color = Color.BLUE;
+ TestActivity.Provider provider1 = new TestActivity.Provider() {
+ @Override
+ public void onStart(TestActivity activity) {
+ BackgroundManager m = BackgroundManager.getInstance(activity);
+ // if we set color before attach, it will be assigned immediately
+ m.setColor(color);
+ m.attach(activity.getWindow());
+ assertIsColorDrawable(m, color);
+ }
+ };
+ mRule = new TestActivity.TestActivityTestRule(provider1, generateProviderName("activity1"));
+ final TestActivity activity1 = mRule.launchActivity();
+ activity1.finish();
+
+ TestActivity.Provider provider2 = new TestActivity.Provider() {
+ @Override
+ public void onStart(TestActivity activity) {
+ BackgroundManager m = BackgroundManager.getInstance(activity);
+ m.attach(activity.getWindow());
+ assertIsColorDrawable(m, color);
+ }
+ };
+ TestActivity activity2 = launchActivity("activity2", provider2);
+ waitForActivityStop(activity1);
+
+ BackgroundManager manager2 = BackgroundManager.getInstance(activity2);
+ assertIsColorDrawable(manager2, color);
+ activity2.finish();
+ }
+
+ @Test
+ public void returnFromNewActivity() throws Throwable {
+ final int color = Color.RED;
+ final Bitmap bitmap = createBitmap(200, 100, Color.BLUE);
+ TestActivity.Provider provider1 = new TestActivity.Provider() {
+ @Override
+ public void onCreate(TestActivity activity, Bundle savedInstanceState) {
+ super.onCreate(activity, savedInstanceState);
+ BackgroundManager m = BackgroundManager.getInstance(activity);
+ // if we set bitmap before attach, it will be assigned immediately
+ m.setColor(color);
+ m.setBitmap(bitmap);
+ m.attach(activity.getWindow());
+ assertIsBitmapDrawable(m, bitmap);
+ }
+
+ };
+ mRule = new TestActivity.TestActivityTestRule(provider1, generateProviderName("activity1"));
+ final TestActivity activity1 = mRule.launchActivity();
+ final BackgroundManager manager1 = BackgroundManager.getInstance(activity1);
+
+ TestActivity.Provider provider2 = new TestActivity.Provider() {
+ @Override
+ public void onStart(TestActivity activity) {
+ BackgroundManager m = BackgroundManager.getInstance(activity);
+ m.attach(activity.getWindow());
+ assertIsBitmapDrawable(m, bitmap);
+ }
+ };
+ TestActivity activity2 = launchActivity("activity2", provider2);
+ waitForActivityStop(activity1);
+ final BackgroundManager manager2 = BackgroundManager.getInstance(activity2);
+
+ final Bitmap bitmap2 = createBitmap(200, 100, Color.GREEN);
+ setBitmapAndVerify(manager2, bitmap2);
+
+ // after activity2 is launched, activity will lose its bitmap and released LayerDrawable
+ assertNull(manager1.mBackgroundDrawable);
+ assertNull(manager1.mLayerDrawable);
+
+ activity2.finish();
+
+ // when return from the other app, last drawable is cleared.
+ waitForBackgroundAnimationFinish(manager1);
+ assertIsColorDrawable(manager1, color);
+ }
+
+ @Test
+ public void manuallyReleaseInOnStop() throws Throwable {
+ final int color = Color.RED;
+ final Bitmap bitmap = createBitmap(200, 100, Color.BLUE);
+ TestActivity.Provider provider1 = new TestActivity.Provider() {
+ @Override
+ public void onCreate(TestActivity activity, Bundle savedInstanceState) {
+ super.onCreate(activity, savedInstanceState);
+ BackgroundManager m = BackgroundManager.getInstance(activity);
+ m.setAutoReleaseOnStop(false);
+ // if we set bitmap before attach, it will be assigned immediately
+ m.setColor(color);
+ m.setBitmap(bitmap);
+ m.attach(activity.getWindow());
+ assertIsBitmapDrawable(m, bitmap);
+ }
+
+ @Override
+ public void onStop(TestActivity activity) {
+ BackgroundManager.getInstance(activity).release();
+ }
+ };
+ mRule = new TestActivity.TestActivityTestRule(provider1, generateProviderName("activity1"));
+ final TestActivity activity1 = mRule.launchActivity();
+ final BackgroundManager manager1 = BackgroundManager.getInstance(activity1);
+
+ TestActivity.Provider provider2 = new TestActivity.Provider() {
+ @Override
+ public void onStart(TestActivity activity) {
+ BackgroundManager m = BackgroundManager.getInstance(activity);
+ m.attach(activity.getWindow());
+ assertIsBitmapDrawable(m, bitmap);
+ }
+ };
+ TestActivity activity2 = launchActivity("activity2", provider2);
+ waitForActivityStop(activity1);
+ final BackgroundManager manager2 = BackgroundManager.getInstance(activity2);
+
+ final Bitmap bitmap2 = createBitmap(200, 100, Color.GREEN);
+ setBitmapAndVerify(manager2, bitmap2);
+
+ // after activity2 is launched, activity will lose its bitmap and released LayerDrawable
+ assertNull(manager1.mBackgroundDrawable);
+ assertNull(manager1.mLayerDrawable);
+
+ activity2.finish();
+
+ // when return from the other app, last drawable is cleared.
+ waitForBackgroundAnimationFinish(manager1);
+ assertIsColorDrawable(manager1, color);
+ }
+
+ @Test
+ public void disableAutoRelease() throws Throwable {
+ final int color = Color.RED;
+ final Bitmap bitmap = createBitmap(200, 100, Color.BLUE);
+ TestActivity.Provider provider1 = new TestActivity.Provider() {
+ @Override
+ public void onCreate(TestActivity activity, Bundle savedInstanceState) {
+ super.onCreate(activity, savedInstanceState);
+ BackgroundManager m = BackgroundManager.getInstance(activity);
+ m.setAutoReleaseOnStop(false);
+ // if we set bitmap before attach, it will be assigned immediately
+ m.setColor(color);
+ m.setBitmap(bitmap);
+ m.attach(activity.getWindow());
+ assertIsBitmapDrawable(m, bitmap);
+ }
+
+ };
+ mRule = new TestActivity.TestActivityTestRule(provider1, generateProviderName("activity1"));
+ final TestActivity activity1 = mRule.launchActivity();
+ final BackgroundManager manager1 = BackgroundManager.getInstance(activity1);
+
+ TestActivity.Provider provider2 = new TestActivity.Provider() {
+ @Override
+ public void onStart(TestActivity activity) {
+ BackgroundManager m = BackgroundManager.getInstance(activity);
+ m.attach(activity.getWindow());
+ assertIsBitmapDrawable(m, bitmap);
+ }
+ };
+ TestActivity activity2 = launchActivity("activity2", provider2);
+ waitForActivityStop(activity1);
+ final BackgroundManager manager2 = BackgroundManager.getInstance(activity2);
+
+ final Bitmap bitmap2 = createBitmap(200, 100, Color.GREEN);
+ setBitmapAndVerify(manager2, bitmap2);
+
+ // after activity2 is launched, activity will keep its drawable because
+ // setAutoReleaseOnStop(false)
+ assertIsBitmapDrawable(manager1, bitmap);
+
+ activity2.finish();
+
+ // when return from the activity, it's still the same bitmap
+ waitForBackgroundAnimationFinish(manager1);
+ assertIsBitmapDrawable(manager1, bitmap);
+ }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/TestActivity.java b/v17/leanback/tests/java/android/support/v17/leanback/app/TestActivity.java
new file mode 100644
index 0000000..7f9b408
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/TestActivity.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2016 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 android.support.v17.leanback.app;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.CallSuper;
+import android.support.test.rule.ActivityTestRule;
+
+import java.util.HashMap;
+
+/**
+ * A general Activity that allows test set a Provider to custom activity's behavior in life
+ * cycle events.
+ */
+public class TestActivity extends Activity {
+
+ public static class Provider {
+
+ TestActivity mActivity;
+
+ /**
+ * @return Currently attached activity.
+ */
+ public TestActivity getActivity() {
+ return mActivity;
+ }
+
+ @CallSuper
+ public void onCreate(TestActivity activity, Bundle savedInstanceState) {
+ mActivity = activity;
+ }
+
+ public void onAttachedToWindow(TestActivity activity) {
+ }
+
+ public void onStart(TestActivity activity) {
+ }
+
+ public void onStop(TestActivity activity) {
+ }
+
+ public void onPause(TestActivity activity) {
+ }
+
+ public void onResume(TestActivity activity) {
+ }
+
+ public void onDestroy(TestActivity activity) {
+ }
+ }
+
+ public static class TestActivityTestRule extends ActivityTestRule<TestActivity> {
+
+ String mProviderName;
+ public TestActivityTestRule(TestActivity.Provider provider, String providerName) {
+ super(TestActivity.class, false, false);
+ mProviderName = providerName;
+ provider.mActivity = null;
+ TestActivity.setProvider(mProviderName, provider);
+ }
+
+ public TestActivity launchActivity() {
+ Intent intent = new Intent();
+ intent.putExtra(TestActivity.EXTRA_PROVIDER, mProviderName);
+ return launchActivity(intent);
+ }
+ }
+
+ public static final String EXTRA_PROVIDER = "testActivityProvider";
+
+ static HashMap<String, Provider> sProviders = new HashMap();
+
+ String mProviderName;
+ Provider mProvider;
+ boolean mStarted;
+
+ public TestActivity() {
+ }
+
+ public static void setProvider(String name, Provider provider) {
+ sProviders.put(name, provider);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mProviderName = getIntent().getStringExtra(EXTRA_PROVIDER);
+ mProvider = sProviders.get(mProviderName);
+ if (mProvider != null) {
+ mProvider.onCreate(this, savedInstanceState);
+ }
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (mProvider != null) {
+ mProvider.onAttachedToWindow(this);
+ }
+ }
+
+ public boolean isStarted() {
+ return mStarted;
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mStarted = true;
+ if (mProvider != null) {
+ mProvider.onStart(this);
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ if (mProvider != null) {
+ mProvider.onPause(this);
+ }
+ super.onPause();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (mProvider != null) {
+ mProvider.onResume(this);
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ mStarted = false;
+ if (mProvider != null) {
+ mProvider.onStop(this);
+ }
+ super.onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (mProvider != null) {
+ mProvider.onDestroy(this);
+ setProvider(mProviderName, null);
+ }
+ super.onDestroy();
+ }
+}