diff options
Diffstat (limited to 'v17')
219 files changed, 10556 insertions, 2062 deletions
diff --git a/v17/leanback/.classpath b/v17/leanback/.classpath index 7bc01d9a9c..f568681311 100644 --- a/v17/leanback/.classpath +++ b/v17/leanback/.classpath @@ -1,9 +1,14 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> - <classpathentry kind="src" path="src"/> - <classpathentry kind="src" path="gen"/> <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/> <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/> <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/> + <classpathentry kind="src" path="src"/> + <classpathentry kind="src" path="api21"/> + <classpathentry kind="src" path="api23"/> + <classpathentry kind="src" path="jbmr2"/> + <classpathentry kind="src" path="common"/> + <classpathentry kind="src" path="kitkat"/> + <classpathentry kind="src" path="gen"/> <classpathentry kind="output" path="bin/classes"/> </classpath> diff --git a/v17/leanback/Android.mk b/v17/leanback/Android.mk index dd03d6a3f0..2dc78a74c3 100644 --- a/v17/leanback/Android.mk +++ b/v17/leanback/Android.mk @@ -28,6 +28,8 @@ LOCAL_AAPT_FLAGS := \ LOCAL_JAR_EXCLUDE_FILES := none include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files := $(LOCAL_SRC_FILES) + # ----------------------------------------------------------------------- # Base sub-library contains classes both needed by api-level specific libraries @@ -38,6 +40,20 @@ LOCAL_SDK_VERSION := 17 LOCAL_SRC_FILES := $(call all-java-files-under, common) include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) + +# ----------------------------------------------------------------------- + +# A helper sub-library that makes direct use of API 23. +include $(CLEAR_VARS) +LOCAL_MODULE := android-support-v17-leanback-api23 +LOCAL_SDK_VERSION := current +LOCAL_SRC_FILES := $(call all-java-files-under, api23) +LOCAL_JAVA_LIBRARIES := android-support-v17-leanback-res android-support-v17-leanback-common +include $(BUILD_STATIC_JAVA_LIBRARY) + +support_module_src_files += $(LOCAL_SRC_FILES) + # ----------------------------------------------------------------------- # A helper sub-library that makes direct use of API 21. @@ -48,6 +64,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, api21) LOCAL_JAVA_LIBRARIES := android-support-v17-leanback-res android-support-v17-leanback-common include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) + # ----------------------------------------------------------------------- # A helper sub-library that makes direct use of KitKat APIs. @@ -58,6 +76,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, kitkat) LOCAL_JAVA_LIBRARIES := android-support-v17-leanback-res android-support-v17-leanback-common include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) + # ----------------------------------------------------------------------- # A helper sub-library that makes direct use of JBMR2 APIs. @@ -68,6 +88,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, jbmr2) LOCAL_JAVA_LIBRARIES := android-support-v17-leanback-res android-support-v17-leanback-common include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) + # ----------------------------------------------------------------------- # Here is the final static library that apps can link against. @@ -79,6 +101,7 @@ LOCAL_MODULE := android-support-v17-leanback LOCAL_SDK_VERSION := 17 LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_STATIC_JAVA_LIBRARIES := android-support-v17-leanback-kitkat android-support-v17-leanback-jbmr2 \ + android-support-v17-leanback-api23 \ android-support-v17-leanback-api21 android-support-v17-leanback-common LOCAL_JAVA_LIBRARIES := \ android-support-v4 \ @@ -86,6 +109,7 @@ LOCAL_JAVA_LIBRARIES := \ android-support-v17-leanback-res include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) # =========================================================== # Common Droiddoc vars @@ -131,7 +155,6 @@ include $(BUILD_DROIDDOC) # --------------------------------------------- support_module := $(LOCAL_MODULE) support_module_api_dir := $(LOCAL_PATH)/api -support_module_src_files := $(leanback.docs.src_files) support_module_java_libraries := $(leanback.docs.java_libraries) support_module_java_packages := android.support.v17.leanback* include $(SUPPORT_API_CHECK) diff --git a/v17/leanback/api/23.txt b/v17/leanback/api/23.0.0.txt index fb2832d820..fb2832d820 100644 --- a/v17/leanback/api/23.txt +++ b/v17/leanback/api/23.0.0.txt diff --git a/v17/leanback/api/23.1.0.txt b/v17/leanback/api/23.1.0.txt new file mode 100644 index 0000000000..962367d3f2 --- /dev/null +++ b/v17/leanback/api/23.1.0.txt @@ -0,0 +1,1796 @@ +package android.support.v17.leanback.app { + + public final class BackgroundManager { + method public void attach(android.view.Window); + method public final int getColor(); + method public android.graphics.drawable.Drawable getDefaultDimLayer(); + method public 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 void release(); + method public void setBitmap(android.graphics.Bitmap); + method public void setColor(int); + method public void setDimLayer(android.graphics.drawable.Drawable); + method public void setDrawable(android.graphics.drawable.Drawable); + method public void setThemeDrawableResourceId(int); + } + + abstract class BaseRowFragment extends android.app.Fragment { + method public final android.support.v17.leanback.widget.ObjectAdapter getAdapter(); + method public final android.support.v17.leanback.widget.PresenterSelector getPresenterSelector(); + method public final void setAdapter(android.support.v17.leanback.widget.ObjectAdapter); + method public final void setPresenterSelector(android.support.v17.leanback.widget.PresenterSelector); + method public void setSelectedPosition(int); + method public void setSelectedPosition(int, boolean); + } + + abstract class BaseRowSupportFragment extends android.support.v4.app.Fragment { + method public final android.support.v17.leanback.widget.ObjectAdapter getAdapter(); + method public final android.support.v17.leanback.widget.PresenterSelector getPresenterSelector(); + method public final void setAdapter(android.support.v17.leanback.widget.ObjectAdapter); + method public final void setPresenterSelector(android.support.v17.leanback.widget.PresenterSelector); + method public void setSelectedPosition(int); + method public void setSelectedPosition(int, boolean); + } + + public class BrowseFragment extends android.support.v17.leanback.app.BrandedFragment { + ctor public BrowseFragment(); + method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, int); + method protected java.lang.Object createEntranceTransition(); + method public void enableRowScaling(boolean); + method public android.support.v17.leanback.widget.ObjectAdapter getAdapter(); + method public int getBrandColor(); + method public int getHeadersState(); + method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener(); + method public android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener(); + method public final boolean isHeadersTransitionOnBackEnabled(); + method public boolean isInHeadersTransition(); + method public boolean isShowingHeaders(); + method protected void onEntranceTransitionEnd(); + method protected void onEntranceTransitionPrepare(); + method protected void onEntranceTransitionStart(); + method public void onSaveInstanceState(android.os.Bundle); + method public void onStart(); + method protected void runEntranceTransition(java.lang.Object); + method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter); + method public void setBrandColor(int); + method public void setBrowseTransitionListener(android.support.v17.leanback.app.BrowseFragment.BrowseTransitionListener); + method public void setHeaderPresenterSelector(android.support.v17.leanback.widget.PresenterSelector); + method public void setHeadersState(int); + method public final void setHeadersTransitionOnBackEnabled(boolean); + method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener); + method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener); + method public void setSelectedPosition(int); + method public void setSelectedPosition(int, boolean); + method public void startHeadersTransition(boolean); + field public static final int HEADERS_DISABLED = 3; // 0x3 + field public static final int HEADERS_ENABLED = 1; // 0x1 + field public static final int HEADERS_HIDDEN = 2; // 0x2 + } + + public static class BrowseFragment.BrowseTransitionListener { + ctor public BrowseFragment.BrowseTransitionListener(); + method public void onHeadersTransitionStart(boolean); + method public void onHeadersTransitionStop(boolean); + } + + public class BrowseSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment { + ctor public BrowseSupportFragment(); + method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, int); + method protected java.lang.Object createEntranceTransition(); + method public void enableRowScaling(boolean); + method public android.support.v17.leanback.widget.ObjectAdapter getAdapter(); + method public int getBrandColor(); + method public int getHeadersState(); + method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener(); + method public android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener(); + method public final boolean isHeadersTransitionOnBackEnabled(); + method public boolean isInHeadersTransition(); + method public boolean isShowingHeaders(); + method protected void onEntranceTransitionEnd(); + method protected void onEntranceTransitionPrepare(); + method protected void onEntranceTransitionStart(); + method public void onSaveInstanceState(android.os.Bundle); + method public void onStart(); + method protected void runEntranceTransition(java.lang.Object); + method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter); + method public void setBrandColor(int); + method public void setBrowseTransitionListener(android.support.v17.leanback.app.BrowseSupportFragment.BrowseTransitionListener); + method public void setHeaderPresenterSelector(android.support.v17.leanback.widget.PresenterSelector); + method public void setHeadersState(int); + method public final void setHeadersTransitionOnBackEnabled(boolean); + method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener); + method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener); + method public void setSelectedPosition(int); + method public void setSelectedPosition(int, boolean); + method public void startHeadersTransition(boolean); + field public static final int HEADERS_DISABLED = 3; // 0x3 + field public static final int HEADERS_ENABLED = 1; // 0x1 + field public static final int HEADERS_HIDDEN = 2; // 0x2 + } + + public static class BrowseSupportFragment.BrowseTransitionListener { + ctor public BrowseSupportFragment.BrowseTransitionListener(); + method public void onHeadersTransitionStart(boolean); + method public void onHeadersTransitionStop(boolean); + } + + public class DetailsFragment extends android.support.v17.leanback.app.BrandedFragment { + ctor public DetailsFragment(); + method protected java.lang.Object createEntranceTransition(); + method public android.support.v17.leanback.widget.ObjectAdapter getAdapter(); + method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener(); + method protected android.view.View inflateTitle(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle); + method protected void onEntranceTransitionEnd(); + method protected void onEntranceTransitionPrepare(); + method protected void onEntranceTransitionStart(); + method protected void onSetDetailsOverviewRowStatus(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int); + method protected void onSetRowStatus(android.support.v17.leanback.widget.RowPresenter, android.support.v17.leanback.widget.RowPresenter.ViewHolder, int, int, int); + method public void onStart(); + method protected void runEntranceTransition(java.lang.Object); + method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter); + method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener); + method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener); + method public void setSelectedPosition(int); + method public void setSelectedPosition(int, boolean); + method protected void setupDetailsOverviewRowPresenter(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter); + method protected void setupPresenter(android.support.v17.leanback.widget.Presenter); + } + + public class DetailsSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment { + ctor public DetailsSupportFragment(); + method protected java.lang.Object createEntranceTransition(); + method public android.support.v17.leanback.widget.ObjectAdapter getAdapter(); + method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener(); + method protected android.view.View inflateTitle(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle); + method protected void onEntranceTransitionEnd(); + method protected void onEntranceTransitionPrepare(); + method protected void onEntranceTransitionStart(); + method protected void onSetDetailsOverviewRowStatus(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int); + method protected void onSetRowStatus(android.support.v17.leanback.widget.RowPresenter, android.support.v17.leanback.widget.RowPresenter.ViewHolder, int, int, int); + method public void onStart(); + method protected void runEntranceTransition(java.lang.Object); + method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter); + method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener); + method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener); + method public void setSelectedPosition(int); + method public void setSelectedPosition(int, boolean); + method protected void setupDetailsOverviewRowPresenter(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter); + method protected void setupPresenter(android.support.v17.leanback.widget.Presenter); + } + + public class ErrorFragment extends android.app.Fragment { + ctor public ErrorFragment(); + method public android.graphics.drawable.Drawable getBackgroundDrawable(); + method public android.graphics.drawable.Drawable getBadgeDrawable(); + method public android.view.View.OnClickListener getButtonClickListener(); + method public java.lang.String getButtonText(); + method public android.graphics.drawable.Drawable getImageDrawable(); + method public java.lang.CharSequence getMessage(); + method public java.lang.String getTitle(); + method public boolean isBackgroundTranslucent(); + method public void setBackgroundDrawable(android.graphics.drawable.Drawable); + method public void setBadgeDrawable(android.graphics.drawable.Drawable); + method public void setButtonClickListener(android.view.View.OnClickListener); + method public void setButtonText(java.lang.String); + method public void setDefaultBackground(boolean); + method public void setImageDrawable(android.graphics.drawable.Drawable); + method public void setMessage(java.lang.CharSequence); + method public void setTitle(java.lang.String); + } + + public class ErrorSupportFragment extends android.support.v4.app.Fragment { + ctor public ErrorSupportFragment(); + method public android.graphics.drawable.Drawable getBackgroundDrawable(); + method public android.graphics.drawable.Drawable getBadgeDrawable(); + method public android.view.View.OnClickListener getButtonClickListener(); + method public java.lang.String getButtonText(); + method public android.graphics.drawable.Drawable getImageDrawable(); + method public java.lang.CharSequence getMessage(); + method public java.lang.String getTitle(); + method public boolean isBackgroundTranslucent(); + method public void setBackgroundDrawable(android.graphics.drawable.Drawable); + method public void setBadgeDrawable(android.graphics.drawable.Drawable); + method public void setButtonClickListener(android.view.View.OnClickListener); + method public void setButtonText(java.lang.String); + method public void setDefaultBackground(boolean); + method public void setImageDrawable(android.graphics.drawable.Drawable); + method public void setMessage(java.lang.CharSequence); + method public void setTitle(java.lang.String); + } + + public class GuidedStepFragment extends android.app.Fragment { + ctor public GuidedStepFragment(); + method public static int add(android.app.FragmentManager, android.support.v17.leanback.app.GuidedStepFragment); + method public static int add(android.app.FragmentManager, android.support.v17.leanback.app.GuidedStepFragment, int); + method public static int addAsRoot(android.app.Activity, android.support.v17.leanback.app.GuidedStepFragment, int); + method public android.view.View getActionItemView(int); + method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getActions(); + method protected int getContainerIdForBackground(); + method public static android.support.v17.leanback.app.GuidedStepFragment getCurrentGuidedStepFragment(android.app.FragmentManager); + method public android.support.v17.leanback.widget.GuidanceStylist getGuidanceStylist(); + method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedActionsStylist(); + method public int getSelectedActionPosition(); + method public int getUiStyle(); + method public void onCreateActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle); + method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateActionsStylist(); + method public android.support.v17.leanback.widget.GuidanceStylist.Guidance onCreateGuidance(android.os.Bundle); + method public android.support.v17.leanback.widget.GuidanceStylist onCreateGuidanceStylist(); + method public void onGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction); + method public void onGuidedActionEdited(android.support.v17.leanback.widget.GuidedAction); + method public void onGuidedActionFocused(android.support.v17.leanback.widget.GuidedAction); + method protected android.app.Fragment onProvideBackgroundFragment(); + method protected void onProvideFragmentTransitions(); + method public int onProvideTheme(); + method public void setActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>); + method public void setSelectedActionPosition(int); + method public void setUiStyle(int); + field public static final java.lang.String EXTRA_UI_STYLE = "uiStyle"; + field public static final int UI_STYLE_ACTIVITY_ROOT = 2; // 0x2 + field public static final int UI_STYLE_DEFAULT = 0; // 0x0 + field public static final int UI_STYLE_ENTRANCE = 1; // 0x1 + } + + public static class GuidedStepFragment.GuidedStepBackgroundFragment extends android.app.Fragment { + ctor public GuidedStepFragment.GuidedStepBackgroundFragment(); + method protected void onProvideFragmentTransitions(); + } + + public class GuidedStepSupportFragment extends android.support.v4.app.Fragment { + ctor public GuidedStepSupportFragment(); + method public static int add(android.support.v4.app.FragmentManager, android.support.v17.leanback.app.GuidedStepSupportFragment); + method public static int add(android.support.v4.app.FragmentManager, android.support.v17.leanback.app.GuidedStepSupportFragment, int); + method public static int addAsRoot(android.support.v4.app.FragmentActivity, android.support.v17.leanback.app.GuidedStepSupportFragment, int); + method public android.view.View getActionItemView(int); + method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getActions(); + method protected int getContainerIdForBackground(); + method public static android.support.v17.leanback.app.GuidedStepSupportFragment getCurrentGuidedStepSupportFragment(android.support.v4.app.FragmentManager); + method public android.support.v17.leanback.widget.GuidanceStylist getGuidanceStylist(); + method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedActionsStylist(); + method public int getSelectedActionPosition(); + method public int getUiStyle(); + method public void onCreateActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle); + method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateActionsStylist(); + method public android.support.v17.leanback.widget.GuidanceStylist.Guidance onCreateGuidance(android.os.Bundle); + method public android.support.v17.leanback.widget.GuidanceStylist onCreateGuidanceStylist(); + method public void onGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction); + method public void onGuidedActionEdited(android.support.v17.leanback.widget.GuidedAction); + method public void onGuidedActionFocused(android.support.v17.leanback.widget.GuidedAction); + method protected android.support.v4.app.Fragment onProvideBackgroundSupportFragment(); + method protected void onProvideFragmentTransitions(); + method public int onProvideTheme(); + method public void setActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>); + method public void setSelectedActionPosition(int); + method public void setUiStyle(int); + field public static final java.lang.String EXTRA_UI_STYLE = "uiStyle"; + field public static final int UI_STYLE_ACTIVITY_ROOT = 2; // 0x2 + field public static final int UI_STYLE_DEFAULT = 0; // 0x0 + field public static final int UI_STYLE_ENTRANCE = 1; // 0x1 + } + + public static class GuidedStepSupportFragment.GuidedStepBackgroundSupportFragment extends android.support.v4.app.Fragment { + ctor public GuidedStepSupportFragment.GuidedStepBackgroundSupportFragment(); + method protected void onProvideFragmentTransitions(); + } + + public class HeadersFragment extends android.support.v17.leanback.app.BaseRowFragment { + ctor public HeadersFragment(); + method public void setOnHeaderClickedListener(android.support.v17.leanback.app.HeadersFragment.OnHeaderClickedListener); + method public void setOnHeaderViewSelectedListener(android.support.v17.leanback.app.HeadersFragment.OnHeaderViewSelectedListener); + } + + static abstract interface HeadersFragment.OnHeaderClickedListener { + method public abstract void onHeaderClicked(); + } + + static abstract interface HeadersFragment.OnHeaderViewSelectedListener { + method public abstract void onHeaderSelected(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row); + } + + public class HeadersSupportFragment extends android.support.v17.leanback.app.BaseRowSupportFragment { + ctor public HeadersSupportFragment(); + method public void setOnHeaderClickedListener(android.support.v17.leanback.app.HeadersSupportFragment.OnHeaderClickedListener); + method public void setOnHeaderViewSelectedListener(android.support.v17.leanback.app.HeadersSupportFragment.OnHeaderViewSelectedListener); + } + + static abstract interface HeadersSupportFragment.OnHeaderClickedListener { + method public abstract void onHeaderClicked(); + } + + static abstract interface HeadersSupportFragment.OnHeaderViewSelectedListener { + method public abstract void onHeaderSelected(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row); + } + + public abstract class MediaControllerGlue extends android.support.v17.leanback.app.PlaybackControlGlue { + ctor public MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]); + ctor public MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]); + method public void attachToMediaController(android.support.v4.media.session.MediaControllerCompat); + method public void detach(); + method public int getCurrentPosition(); + method public int getCurrentSpeedId(); + method public android.graphics.drawable.Drawable getMediaArt(); + method public final android.support.v4.media.session.MediaControllerCompat getMediaController(); + method public int getMediaDuration(); + method public java.lang.CharSequence getMediaSubtitle(); + method public java.lang.CharSequence getMediaTitle(); + method public long getSupportedActions(); + method public boolean hasValidMedia(); + method public boolean isMediaPlaying(); + method protected void pausePlayback(); + method protected void skipToNext(); + method protected void skipToPrevious(); + method protected void startPlayback(int); + } + + public abstract class PlaybackControlGlue implements android.support.v17.leanback.widget.OnActionClickedListener android.view.View.OnKeyListener { + ctor public PlaybackControlGlue(android.content.Context, int[]); + ctor public PlaybackControlGlue(android.content.Context, int[], int[]); + ctor public PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]); + ctor public PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]); + method public android.support.v17.leanback.widget.PlaybackControlsRowPresenter createControlsRowAndPresenter(); + method protected android.support.v17.leanback.widget.SparseArrayObjectAdapter createPrimaryActionsAdapter(android.support.v17.leanback.widget.PresenterSelector); + method public void enableProgressUpdating(boolean); + method public android.content.Context getContext(); + method public android.support.v17.leanback.widget.PlaybackControlsRow getControlsRow(); + method public abstract int getCurrentPosition(); + method public abstract int getCurrentSpeedId(); + method public int[] getFastForwardSpeeds(); + method public android.support.v17.leanback.app.PlaybackOverlayFragment getFragment(); + method public abstract android.graphics.drawable.Drawable getMediaArt(); + method public abstract int getMediaDuration(); + method public abstract java.lang.CharSequence getMediaSubtitle(); + method public abstract java.lang.CharSequence getMediaTitle(); + method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener(); + method public int[] getRewindSpeeds(); + method public abstract long getSupportedActions(); + method public int getUpdatePeriod(); + method public abstract boolean hasValidMedia(); + method public boolean isFadingEnabled(); + method public abstract boolean isMediaPlaying(); + method public void onActionClicked(android.support.v17.leanback.widget.Action); + method public boolean onKey(android.view.View, int, android.view.KeyEvent); + method protected void onMetadataChanged(); + method protected abstract void onRowChanged(android.support.v17.leanback.widget.PlaybackControlsRow); + method protected void onStateChanged(); + method protected abstract void pausePlayback(); + method public void setControlsRow(android.support.v17.leanback.widget.PlaybackControlsRow); + method public void setFadingEnabled(boolean); + method public deprecated void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener); + method protected abstract void skipToNext(); + method protected abstract void skipToPrevious(); + method protected abstract void startPlayback(int); + method public void updateProgress(); + field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1 + field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000 + field public static final int ACTION_FAST_FORWARD = 128; // 0x80 + field public static final int ACTION_PLAY_PAUSE = 64; // 0x40 + field public static final int ACTION_REWIND = 32; // 0x20 + field public static final int ACTION_SKIP_TO_NEXT = 256; // 0x100 + field public static final int ACTION_SKIP_TO_PREVIOUS = 16; // 0x10 + field public static final int PLAYBACK_SPEED_FAST_L0 = 10; // 0xa + field public static final int PLAYBACK_SPEED_FAST_L1 = 11; // 0xb + field public static final int PLAYBACK_SPEED_FAST_L2 = 12; // 0xc + field public static final int PLAYBACK_SPEED_FAST_L3 = 13; // 0xd + field public static final int PLAYBACK_SPEED_FAST_L4 = 14; // 0xe + field public static final int PLAYBACK_SPEED_INVALID = -1; // 0xffffffff + field public static final int PLAYBACK_SPEED_NORMAL = 1; // 0x1 + field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0 + } + + public abstract class PlaybackControlSupportGlue implements android.support.v17.leanback.widget.OnActionClickedListener android.view.View.OnKeyListener { + ctor public PlaybackControlSupportGlue(android.content.Context, int[]); + ctor public PlaybackControlSupportGlue(android.content.Context, int[], int[]); + ctor public PlaybackControlSupportGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlaySupportFragment, int[]); + ctor public PlaybackControlSupportGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlaySupportFragment, int[], int[]); + method public android.support.v17.leanback.widget.PlaybackControlsRowPresenter createControlsRowAndPresenter(); + method protected android.support.v17.leanback.widget.SparseArrayObjectAdapter createPrimaryActionsAdapter(android.support.v17.leanback.widget.PresenterSelector); + method public void enableProgressUpdating(boolean); + method public android.content.Context getContext(); + method public android.support.v17.leanback.widget.PlaybackControlsRow getControlsRow(); + method public abstract int getCurrentPosition(); + method public abstract int getCurrentSpeedId(); + method public int[] getFastForwardSpeeds(); + method public android.support.v17.leanback.app.PlaybackOverlaySupportFragment getFragment(); + method public abstract android.graphics.drawable.Drawable getMediaArt(); + method public abstract int getMediaDuration(); + method public abstract java.lang.CharSequence getMediaSubtitle(); + method public abstract java.lang.CharSequence getMediaTitle(); + method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener(); + method public int[] getRewindSpeeds(); + method public abstract long getSupportedActions(); + method public int getUpdatePeriod(); + method public abstract boolean hasValidMedia(); + method public boolean isFadingEnabled(); + method public abstract boolean isMediaPlaying(); + method public void onActionClicked(android.support.v17.leanback.widget.Action); + method public boolean onKey(android.view.View, int, android.view.KeyEvent); + method protected void onMetadataChanged(); + method protected abstract void onRowChanged(android.support.v17.leanback.widget.PlaybackControlsRow); + method protected void onStateChanged(); + method protected abstract void pausePlayback(); + method public void setControlsRow(android.support.v17.leanback.widget.PlaybackControlsRow); + method public void setFadingEnabled(boolean); + method public deprecated void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener); + method protected abstract void skipToNext(); + method protected abstract void skipToPrevious(); + method protected abstract void startPlayback(int); + method public void updateProgress(); + field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1 + field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000 + field public static final int ACTION_FAST_FORWARD = 128; // 0x80 + field public static final int ACTION_PLAY_PAUSE = 64; // 0x40 + field public static final int ACTION_REWIND = 32; // 0x20 + field public static final int ACTION_SKIP_TO_NEXT = 256; // 0x100 + field public static final int ACTION_SKIP_TO_PREVIOUS = 16; // 0x10 + field public static final int PLAYBACK_SPEED_FAST_L0 = 10; // 0xa + field public static final int PLAYBACK_SPEED_FAST_L1 = 11; // 0xb + field public static final int PLAYBACK_SPEED_FAST_L2 = 12; // 0xc + field public static final int PLAYBACK_SPEED_FAST_L3 = 13; // 0xd + field public static final int PLAYBACK_SPEED_FAST_L4 = 14; // 0xe + field public static final int PLAYBACK_SPEED_INVALID = -1; // 0xffffffff + field public static final int PLAYBACK_SPEED_NORMAL = 1; // 0x1 + field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0 + } + + public class PlaybackOverlayFragment extends android.support.v17.leanback.app.DetailsFragment { + ctor public PlaybackOverlayFragment(); + method public int getBackgroundType(); + method public android.support.v17.leanback.app.PlaybackOverlayFragment.OnFadeCompleteListener getFadeCompleteListener(); + method public final android.support.v17.leanback.app.PlaybackOverlayFragment.InputEventHandler getInputEventHandler(); + method public boolean isFadingEnabled(); + method public void onDestroyView(); + method public void onResume(); + method public void setBackgroundType(int); + method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackOverlayFragment.OnFadeCompleteListener); + method public void setFadingEnabled(boolean); + method public final void setInputEventHandler(android.support.v17.leanback.app.PlaybackOverlayFragment.InputEventHandler); + method public void tickle(); + field public static final int BG_DARK = 1; // 0x1 + field public static final int BG_LIGHT = 2; // 0x2 + field public static final int BG_NONE = 0; // 0x0 + } + + public static abstract interface PlaybackOverlayFragment.InputEventHandler { + method public abstract boolean handleInputEvent(android.view.InputEvent); + } + + public static class PlaybackOverlayFragment.OnFadeCompleteListener { + ctor public PlaybackOverlayFragment.OnFadeCompleteListener(); + method public void onFadeInComplete(); + method public void onFadeOutComplete(); + } + + public class PlaybackOverlaySupportFragment extends android.support.v17.leanback.app.DetailsSupportFragment { + ctor public PlaybackOverlaySupportFragment(); + method public int getBackgroundType(); + method public android.support.v17.leanback.app.PlaybackOverlaySupportFragment.OnFadeCompleteListener getFadeCompleteListener(); + method public final android.support.v17.leanback.app.PlaybackOverlaySupportFragment.InputEventHandler getInputEventHandler(); + method public boolean isFadingEnabled(); + method public void onDestroyView(); + method public void onResume(); + method public void setBackgroundType(int); + method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackOverlaySupportFragment.OnFadeCompleteListener); + method public void setFadingEnabled(boolean); + method public final void setInputEventHandler(android.support.v17.leanback.app.PlaybackOverlaySupportFragment.InputEventHandler); + method public void tickle(); + field public static final int BG_DARK = 1; // 0x1 + field public static final int BG_LIGHT = 2; // 0x2 + field public static final int BG_NONE = 0; // 0x0 + } + + public static abstract interface PlaybackOverlaySupportFragment.InputEventHandler { + method public abstract boolean handleInputEvent(android.view.InputEvent); + } + + public static class PlaybackOverlaySupportFragment.OnFadeCompleteListener { + ctor public PlaybackOverlaySupportFragment.OnFadeCompleteListener(); + method public void onFadeInComplete(); + method public void onFadeOutComplete(); + } + + public class RowsFragment extends android.support.v17.leanback.app.BaseRowFragment { + ctor public RowsFragment(); + method public void enableRowScaling(boolean); + method protected android.support.v17.leanback.widget.VerticalGridView findGridViewFromRoot(android.view.View); + method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener(); + method public android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener(); + method public void setExpand(boolean); + method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener); + method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener); + } + + public class RowsSupportFragment extends android.support.v17.leanback.app.BaseRowSupportFragment { + ctor public RowsSupportFragment(); + method public void enableRowScaling(boolean); + method protected android.support.v17.leanback.widget.VerticalGridView findGridViewFromRoot(android.view.View); + method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener(); + method public android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener(); + method public void setExpand(boolean); + method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener); + method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener); + } + + public class SearchFragment extends android.app.Fragment { + ctor public SearchFragment(); + method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String); + method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, java.lang.String); + method public void displayCompletions(java.util.List<java.lang.String>); + method public void displayCompletions(android.view.inputmethod.CompletionInfo[]); + method public android.graphics.drawable.Drawable getBadgeDrawable(); + method public android.content.Intent getRecognizerIntent(); + method public java.lang.String getTitle(); + method public static android.support.v17.leanback.app.SearchFragment newInstance(java.lang.String); + method public void setBadgeDrawable(android.graphics.drawable.Drawable); + method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener); + method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener); + method public void setSearchQuery(java.lang.String, boolean); + method public void setSearchQuery(android.content.Intent, boolean); + method public void setSearchResultProvider(android.support.v17.leanback.app.SearchFragment.SearchResultProvider); + method public void setSpeechRecognitionCallback(android.support.v17.leanback.widget.SpeechRecognitionCallback); + method public void setTitle(java.lang.String); + method public void startRecognition(); + } + + public static abstract interface SearchFragment.SearchResultProvider { + method public abstract android.support.v17.leanback.widget.ObjectAdapter getResultsAdapter(); + method public abstract boolean onQueryTextChange(java.lang.String); + method public abstract boolean onQueryTextSubmit(java.lang.String); + } + + public class SearchSupportFragment extends android.support.v4.app.Fragment { + ctor public SearchSupportFragment(); + method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String); + method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, java.lang.String); + method public void displayCompletions(java.util.List<java.lang.String>); + method public void displayCompletions(android.view.inputmethod.CompletionInfo[]); + method public android.graphics.drawable.Drawable getBadgeDrawable(); + method public android.content.Intent getRecognizerIntent(); + method public java.lang.String getTitle(); + method public static android.support.v17.leanback.app.SearchSupportFragment newInstance(java.lang.String); + method public void setBadgeDrawable(android.graphics.drawable.Drawable); + method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener); + method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener); + method public void setSearchQuery(java.lang.String, boolean); + method public void setSearchQuery(android.content.Intent, boolean); + method public void setSearchResultProvider(android.support.v17.leanback.app.SearchSupportFragment.SearchResultProvider); + method public void setSpeechRecognitionCallback(android.support.v17.leanback.widget.SpeechRecognitionCallback); + method public void setTitle(java.lang.String); + method public void startRecognition(); + } + + public static abstract interface SearchSupportFragment.SearchResultProvider { + method public abstract android.support.v17.leanback.widget.ObjectAdapter getResultsAdapter(); + method public abstract boolean onQueryTextChange(java.lang.String); + method public abstract boolean onQueryTextSubmit(java.lang.String); + } + + public class VerticalGridFragment extends android.support.v17.leanback.app.BrandedFragment { + ctor public VerticalGridFragment(); + method protected java.lang.Object createEntranceTransition(); + method public android.support.v17.leanback.widget.ObjectAdapter getAdapter(); + method public android.support.v17.leanback.widget.VerticalGridPresenter getGridPresenter(); + method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener(); + method public void onDestroyView(); + method public void onStart(); + method public void onViewCreated(android.view.View, android.os.Bundle); + method protected void runEntranceTransition(java.lang.Object); + method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter); + method public void setGridPresenter(android.support.v17.leanback.widget.VerticalGridPresenter); + method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener); + method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener); + method public void setSelectedPosition(int); + } + + public class VerticalGridSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment { + ctor public VerticalGridSupportFragment(); + method protected java.lang.Object createEntranceTransition(); + method public android.support.v17.leanback.widget.ObjectAdapter getAdapter(); + method public android.support.v17.leanback.widget.VerticalGridPresenter getGridPresenter(); + method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener(); + method public void onDestroyView(); + method public void onStart(); + method public void onViewCreated(android.view.View, android.os.Bundle); + method protected void runEntranceTransition(java.lang.Object); + method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter); + method public void setGridPresenter(android.support.v17.leanback.widget.VerticalGridPresenter); + method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener); + method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener); + method public void setSelectedPosition(int); + } + +} + +package android.support.v17.leanback.database { + + public abstract class CursorMapper { + ctor public CursorMapper(); + method protected abstract java.lang.Object bind(android.database.Cursor); + method protected abstract void bindColumns(android.database.Cursor); + method public java.lang.Object convert(android.database.Cursor); + } + +} + +package android.support.v17.leanback.graphics { + + public final class ColorFilterCache { + method public static android.support.v17.leanback.graphics.ColorFilterCache getColorFilterCache(int); + method public android.graphics.ColorFilter getFilterForLevel(float); + } + + public final class ColorFilterDimmer { + method public void applyFilterToView(android.view.View); + method public static android.support.v17.leanback.graphics.ColorFilterDimmer create(android.support.v17.leanback.graphics.ColorFilterCache, float, float); + method public static android.support.v17.leanback.graphics.ColorFilterDimmer createDefault(android.content.Context); + method public android.graphics.ColorFilter getColorFilter(); + method public android.graphics.Paint getPaint(); + method public void setActiveLevel(float); + } + + public final class ColorOverlayDimmer { + method public int applyToColor(int); + method public static android.support.v17.leanback.graphics.ColorOverlayDimmer createColorOverlayDimmer(int, float, float); + method public static android.support.v17.leanback.graphics.ColorOverlayDimmer createDefault(android.content.Context); + method public void drawColorOverlay(android.graphics.Canvas, android.view.View, boolean); + method public int getAlpha(); + method public float getAlphaFloat(); + method public android.graphics.Paint getPaint(); + method public boolean needsDraw(); + method public void setActiveLevel(float); + } + +} + +package android.support.v17.leanback.system { + + public class Settings { + method public boolean getBoolean(java.lang.String); + method public static android.support.v17.leanback.system.Settings getInstance(android.content.Context); + method public void setBoolean(java.lang.String, boolean); + field public static final java.lang.String PREFER_STATIC_SHADOWS = "PREFER_STATIC_SHADOWS"; + } + +} + +package android.support.v17.leanback.widget { + + public abstract class AbstractDetailsDescriptionPresenter extends android.support.v17.leanback.widget.Presenter { + ctor public AbstractDetailsDescriptionPresenter(); + method protected abstract void onBindDescription(android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter.ViewHolder, java.lang.Object); + method public final void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object); + method public final android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter.ViewHolder onCreateViewHolder(android.view.ViewGroup); + method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder); + } + + public static class AbstractDetailsDescriptionPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder { + ctor public AbstractDetailsDescriptionPresenter.ViewHolder(android.view.View); + method public android.widget.TextView getBody(); + method public android.widget.TextView getSubtitle(); + method public android.widget.TextView getTitle(); + } + + public class Action { + ctor public Action(long); + ctor public Action(long, java.lang.CharSequence); + ctor public Action(long, java.lang.CharSequence, java.lang.CharSequence); + ctor public Action(long, java.lang.CharSequence, java.lang.CharSequence, android.graphics.drawable.Drawable); + method public final void addKeyCode(int); + method public final android.graphics.drawable.Drawable getIcon(); + method public final long getId(); + method public final java.lang.CharSequence getLabel1(); + method public final java.lang.CharSequence getLabel2(); + method public final void removeKeyCode(int); + method public final boolean respondsToKeyCode(int); + method public final void setIcon(android.graphics.drawable.Drawable); + method public final void setId(long); + method public final void setLabel1(java.lang.CharSequence); + method public final void setLabel2(java.lang.CharSequence); + } + + public class ArrayObjectAdapter extends android.support.v17.leanback.widget.ObjectAdapter { + ctor public ArrayObjectAdapter(android.support.v17.leanback.widget.PresenterSelector); + ctor public ArrayObjectAdapter(android.support.v17.leanback.widget.Presenter); + ctor public ArrayObjectAdapter(); + method public void add(java.lang.Object); + method public void add(int, java.lang.Object); + method public void addAll(int, java.util.Collection); + method public void clear(); + method public java.lang.Object get(int); + method public int indexOf(java.lang.Object); + method public void notifyArrayItemRangeChanged(int, int); + method public boolean remove(java.lang.Object); + method public int removeItems(int, int); + method public void replace(int, java.lang.Object); + method public int size(); + method public java.util.List<E> unmodifiableList(); + } + + public class BaseCardView extends android.widget.FrameLayout { + ctor public BaseCardView(android.content.Context); + ctor public BaseCardView(android.content.Context, android.util.AttributeSet); + ctor public BaseCardView(android.content.Context, android.util.AttributeSet, int); + method public int getCardType(); + method public int getExtraVisibility(); + method public int getInfoVisibility(); + method public boolean isSelectedAnimationDelayed(); + method public void setCardType(int); + method public void setExtraVisibility(int); + method public void setInfoVisibility(int); + method public void setSelectedAnimationDelayed(boolean); + field public static final int CARD_REGION_VISIBLE_ACTIVATED = 1; // 0x1 + field public static final int CARD_REGION_VISIBLE_ALWAYS = 0; // 0x0 + field public static final int CARD_REGION_VISIBLE_SELECTED = 2; // 0x2 + field public static final int CARD_TYPE_INFO_OVER = 1; // 0x1 + field public static final int CARD_TYPE_INFO_UNDER = 2; // 0x2 + field public static final int CARD_TYPE_INFO_UNDER_WITH_EXTRA = 3; // 0x3 + field public static final int CARD_TYPE_MAIN_ONLY = 0; // 0x0 + } + + public static class BaseCardView.LayoutParams extends android.widget.FrameLayout.LayoutParams { + ctor public BaseCardView.LayoutParams(android.content.Context, android.util.AttributeSet); + ctor public BaseCardView.LayoutParams(int, int); + ctor public BaseCardView.LayoutParams(android.view.ViewGroup.LayoutParams); + ctor public BaseCardView.LayoutParams(android.support.v17.leanback.widget.BaseCardView.LayoutParams); + field public static final int VIEW_TYPE_EXTRA = 2; // 0x2 + field public static final int VIEW_TYPE_INFO = 1; // 0x1 + field public static final int VIEW_TYPE_MAIN = 0; // 0x0 + field public int viewType; + } + + public class BrowseFrameLayout extends android.widget.FrameLayout { + ctor public BrowseFrameLayout(android.content.Context); + ctor public BrowseFrameLayout(android.content.Context, android.util.AttributeSet); + ctor public BrowseFrameLayout(android.content.Context, android.util.AttributeSet, int); + method public android.support.v17.leanback.widget.BrowseFrameLayout.OnChildFocusListener getOnChildFocusListener(); + method public android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener getOnFocusSearchListener(); + method public void setOnChildFocusListener(android.support.v17.leanback.widget.BrowseFrameLayout.OnChildFocusListener); + method public void setOnFocusSearchListener(android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener); + } + + public static abstract interface BrowseFrameLayout.OnChildFocusListener { + method public abstract void onRequestChildFocus(android.view.View, android.view.View); + method public abstract boolean onRequestFocusInDescendants(int, android.graphics.Rect); + } + + public static abstract interface BrowseFrameLayout.OnFocusSearchListener { + method public abstract android.view.View onFocusSearch(android.view.View, int); + } + + public final class ClassPresenterSelector extends android.support.v17.leanback.widget.PresenterSelector { + ctor public ClassPresenterSelector(); + method public void addClassPresenter(java.lang.Class<?>, android.support.v17.leanback.widget.Presenter); + method public android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object); + } + + public class ControlButtonPresenterSelector extends android.support.v17.leanback.widget.PresenterSelector { + ctor public ControlButtonPresenterSelector(); + method public android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object); + method public android.support.v17.leanback.widget.Presenter getPrimaryPresenter(); + method public android.support.v17.leanback.widget.Presenter getSecondaryPresenter(); + } + + public class CursorObjectAdapter extends android.support.v17.leanback.widget.ObjectAdapter { + ctor public CursorObjectAdapter(android.support.v17.leanback.widget.PresenterSelector); + ctor public CursorObjectAdapter(android.support.v17.leanback.widget.Presenter); + ctor public CursorObjectAdapter(); + method public void changeCursor(android.database.Cursor); + method public void close(); + method public java.lang.Object get(int); + method public final android.database.Cursor getCursor(); + method public final android.support.v17.leanback.database.CursorMapper getMapper(); + method protected final void invalidateCache(int); + method protected final void invalidateCache(int, int); + method public boolean isClosed(); + method protected void onCursorChanged(); + method protected void onMapperChanged(); + method public final void setMapper(android.support.v17.leanback.database.CursorMapper); + method public int size(); + method public android.database.Cursor swapCursor(android.database.Cursor); + } + + public class DetailsOverviewLogoPresenter extends android.support.v17.leanback.widget.Presenter { + ctor public DetailsOverviewLogoPresenter(); + method public boolean isBoundToImage(android.support.v17.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder, android.support.v17.leanback.widget.DetailsOverviewRow); + method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object); + method public android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup); + method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder); + method public void setContext(android.support.v17.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter); + } + + public static class DetailsOverviewLogoPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder { + ctor public DetailsOverviewLogoPresenter.ViewHolder(android.view.View); + field protected android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter mParentPresenter; + field protected android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder mParentViewHolder; + } + + public class DetailsOverviewRow extends android.support.v17.leanback.widget.Row { + ctor public DetailsOverviewRow(java.lang.Object); + method public final deprecated void addAction(android.support.v17.leanback.widget.Action); + method public final deprecated void addAction(int, android.support.v17.leanback.widget.Action); + method public android.support.v17.leanback.widget.Action getActionForKeyCode(int); + method public final deprecated java.util.List<android.support.v17.leanback.widget.Action> getActions(); + method public final android.support.v17.leanback.widget.ObjectAdapter getActionsAdapter(); + method public final android.graphics.drawable.Drawable getImageDrawable(); + method public final java.lang.Object getItem(); + method public boolean isImageScaleUpAllowed(); + method public final deprecated boolean removeAction(android.support.v17.leanback.widget.Action); + method public final void setActionsAdapter(android.support.v17.leanback.widget.ObjectAdapter); + method public final void setImageBitmap(android.content.Context, android.graphics.Bitmap); + method public final void setImageDrawable(android.graphics.drawable.Drawable); + method public void setImageScaleUpAllowed(boolean); + method public final void setItem(java.lang.Object); + } + + public static class DetailsOverviewRow.Listener { + ctor public DetailsOverviewRow.Listener(); + method public void onActionsAdapterChanged(android.support.v17.leanback.widget.DetailsOverviewRow); + method public void onImageDrawableChanged(android.support.v17.leanback.widget.DetailsOverviewRow); + method public void onItemChanged(android.support.v17.leanback.widget.DetailsOverviewRow); + } + + public deprecated class DetailsOverviewRowPresenter extends android.support.v17.leanback.widget.RowPresenter { + ctor public DetailsOverviewRowPresenter(android.support.v17.leanback.widget.Presenter); + method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup); + method public int getBackgroundColor(); + method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener(); + method public boolean isStyleLarge(); + method public final boolean isUsingDefaultSelectEffect(); + method public void setBackgroundColor(int); + method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener); + method public final void setSharedElementEnterTransition(android.app.Activity, java.lang.String, long); + method public final void setSharedElementEnterTransition(android.app.Activity, java.lang.String); + method public void setStyleLarge(boolean); + } + + public final class DetailsOverviewRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder { + ctor public DetailsOverviewRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.Presenter); + field public final android.support.v17.leanback.widget.Presenter.ViewHolder mDetailsDescriptionViewHolder; + } + + public abstract interface FacetProvider { + method public abstract java.lang.Object getFacet(java.lang.Class<?>); + } + + public abstract interface FacetProviderAdapter { + method public abstract android.support.v17.leanback.widget.FacetProvider getFacetProvider(int); + } + + public abstract interface FocusHighlight { + field public static final int ZOOM_FACTOR_LARGE = 3; // 0x3 + field public static final int ZOOM_FACTOR_MEDIUM = 2; // 0x2 + field public static final int ZOOM_FACTOR_NONE = 0; // 0x0 + field public static final int ZOOM_FACTOR_SMALL = 1; // 0x1 + field public static final int ZOOM_FACTOR_XSMALL = 4; // 0x4 + } + + public class FocusHighlightHelper { + ctor public FocusHighlightHelper(); + method public static void setupBrowseItemFocusHighlight(android.support.v17.leanback.widget.ItemBridgeAdapter, int, boolean); + method public static void setupHeaderItemFocusHighlight(android.support.v17.leanback.widget.VerticalGridView); + } + + public abstract interface FragmentAnimationProvider { + method public abstract void onImeAppearing(java.util.List<android.animation.Animator>); + method public abstract void onImeDisappearing(java.util.List<android.animation.Animator>); + } + + public class FullWidthDetailsOverviewRowPresenter extends android.support.v17.leanback.widget.RowPresenter { + ctor public FullWidthDetailsOverviewRowPresenter(android.support.v17.leanback.widget.Presenter); + ctor public FullWidthDetailsOverviewRowPresenter(android.support.v17.leanback.widget.Presenter, android.support.v17.leanback.widget.DetailsOverviewLogoPresenter); + method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup); + method public final int getActionsBackgroundColor(); + method public final int getAlignmentMode(); + method public final int getBackgroundColor(); + method public final int getInitialState(); + method protected int getLayoutResourceId(); + method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener(); + method public final boolean isParticipatingEntranceTransition(); + method public final boolean isUsingDefaultSelectEffect(); + method public final void notifyOnBindLogo(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder); + method protected void onLayoutLogo(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, boolean); + method protected void onLayoutOverviewFrame(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, boolean); + method protected void onStateChanged(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int); + method public final void setActionsBackgroundColor(int); + method public final void setAlignmentMode(int); + method public final void setBackgroundColor(int); + method public final void setInitialState(int); + method public final void setListener(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.Listener); + method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener); + method public final void setParticipatingEntranceTransition(boolean); + method public final void setState(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int); + field public static final int ALIGN_MODE_MIDDLE = 1; // 0x1 + field public static final int ALIGN_MODE_START = 0; // 0x0 + field public static final int STATE_FULL = 1; // 0x1 + field public static final int STATE_HALF = 0; // 0x0 + field public static final int STATE_SMALL = 2; // 0x2 + field protected int mInitialState; + } + + public static abstract class FullWidthDetailsOverviewRowPresenter.Listener { + ctor public FullWidthDetailsOverviewRowPresenter.Listener(); + method public void onBindLogo(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder); + } + + public class FullWidthDetailsOverviewRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder { + ctor public FullWidthDetailsOverviewRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.Presenter, android.support.v17.leanback.widget.DetailsOverviewLogoPresenter); + method protected android.support.v17.leanback.widget.DetailsOverviewRow.Listener createRowListener(); + method public final android.view.ViewGroup getActionsRow(); + method public final android.view.ViewGroup getDetailsDescriptionFrame(); + method public final android.support.v17.leanback.widget.Presenter.ViewHolder getDetailsDescriptionViewHolder(); + method public final android.support.v17.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder getLogoViewHolder(); + method public final android.view.ViewGroup getOverviewView(); + method public final int getState(); + field protected final android.support.v17.leanback.widget.DetailsOverviewRow.Listener mRowListener; + } + + public class FullWidthDetailsOverviewRowPresenter.ViewHolder.DetailsOverviewRowListener extends android.support.v17.leanback.widget.DetailsOverviewRow.Listener { + ctor public FullWidthDetailsOverviewRowPresenter.ViewHolder.DetailsOverviewRowListener(); + } + + public class FullWidthDetailsOverviewSharedElementHelper extends android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.Listener { + ctor public FullWidthDetailsOverviewSharedElementHelper(); + method public boolean getAutoStartSharedElementTransition(); + method public void setAutoStartSharedElementTransition(boolean); + method public void setSharedElementEnterTransition(android.app.Activity, java.lang.String); + method public void setSharedElementEnterTransition(android.app.Activity, java.lang.String, long); + method public void startPostponedEnterTransition(); + } + + public class GuidanceStylist implements android.support.v17.leanback.widget.FragmentAnimationProvider { + ctor public GuidanceStylist(); + method public android.widget.TextView getBreadcrumbView(); + method public android.widget.TextView getDescriptionView(); + method public android.widget.ImageView getIconView(); + method public android.widget.TextView getTitleView(); + method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.support.v17.leanback.widget.GuidanceStylist.Guidance); + method public void onImeAppearing(java.util.List<android.animation.Animator>); + method public void onImeDisappearing(java.util.List<android.animation.Animator>); + method public int onProvideLayoutId(); + } + + public static class GuidanceStylist.Guidance { + ctor public GuidanceStylist.Guidance(java.lang.String, java.lang.String, java.lang.String, android.graphics.drawable.Drawable); + method public java.lang.String getBreadcrumb(); + method public java.lang.String getDescription(); + method public android.graphics.drawable.Drawable getIconDrawable(); + method public java.lang.String getTitle(); + } + + public class GuidedAction extends android.support.v17.leanback.widget.Action { + method public int getCheckSetId(); + method public java.lang.CharSequence getDescription(); + method public java.lang.CharSequence getEditTitle(); + method public android.content.Intent getIntent(); + method public java.lang.CharSequence getTitle(); + method public boolean hasMultilineDescription(); + method public boolean hasNext(); + method public boolean infoOnly(); + method public boolean isChecked(); + method public boolean isEditTitleUsed(); + method public boolean isEditable(); + method public boolean isEnabled(); + method public void setChecked(boolean); + method public void setDescription(java.lang.CharSequence); + method public void setEditTitle(java.lang.CharSequence); + method public void setEnabled(boolean); + method public void setTitle(java.lang.CharSequence); + field public static final int DEFAULT_CHECK_SET_ID = 1; // 0x1 + field public static final int NO_CHECK_SET = 0; // 0x0 + field public static final int NO_DRAWABLE = 0; // 0x0 + } + + public static class GuidedAction.Builder { + ctor public GuidedAction.Builder(); + method public android.support.v17.leanback.widget.GuidedAction build(); + method public android.support.v17.leanback.widget.GuidedAction.Builder checkSetId(int); + method public android.support.v17.leanback.widget.GuidedAction.Builder checked(boolean); + method public android.support.v17.leanback.widget.GuidedAction.Builder description(java.lang.String); + method public android.support.v17.leanback.widget.GuidedAction.Builder editTitle(java.lang.String); + method public android.support.v17.leanback.widget.GuidedAction.Builder editable(boolean); + method public android.support.v17.leanback.widget.GuidedAction.Builder enabled(boolean); + method public android.support.v17.leanback.widget.GuidedAction.Builder hasNext(boolean); + method public android.support.v17.leanback.widget.GuidedAction.Builder icon(android.graphics.drawable.Drawable); + method public android.support.v17.leanback.widget.GuidedAction.Builder iconResourceId(int, android.content.Context); + method public android.support.v17.leanback.widget.GuidedAction.Builder id(long); + method public android.support.v17.leanback.widget.GuidedAction.Builder infoOnly(boolean); + method public android.support.v17.leanback.widget.GuidedAction.Builder intent(android.content.Intent); + method public android.support.v17.leanback.widget.GuidedAction.Builder multilineDescription(boolean); + method public android.support.v17.leanback.widget.GuidedAction.Builder title(java.lang.String); + } + + public class GuidedActionEditText extends android.widget.EditText implements android.support.v17.leanback.widget.ImeKeyMonitor { + ctor public GuidedActionEditText(android.content.Context); + ctor public GuidedActionEditText(android.content.Context, android.util.AttributeSet); + ctor public GuidedActionEditText(android.content.Context, android.util.AttributeSet, int); + method public void setImeKeyListener(android.support.v17.leanback.widget.ImeKeyMonitor.ImeKeyListener); + } + + public class GuidedActionsStylist implements android.support.v17.leanback.widget.FragmentAnimationProvider { + ctor public GuidedActionsStylist(); + method public android.support.v17.leanback.widget.VerticalGridView getActionsGridView(); + method public void onAnimateItemChecked(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean); + method public void onAnimateItemFocused(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean); + method public void onAnimateItemPressed(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean); + method public void onBindViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction); + method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup); + method public android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder onCreateViewHolder(android.view.ViewGroup); + method public void onImeAppearing(java.util.List<android.animation.Animator>); + method public void onImeDisappearing(java.util.List<android.animation.Animator>); + method public int onProvideItemLayoutId(); + method public int onProvideLayoutId(); + field protected android.support.v17.leanback.widget.VerticalGridView mActionsGridView; + field protected android.view.View mMainView; + field protected android.view.View mSelectorView; + } + + public static class GuidedActionsStylist.ViewHolder { + ctor public GuidedActionsStylist.ViewHolder(android.view.View); + method public android.widget.ImageView getCheckmarkView(); + method public android.widget.ImageView getChevronView(); + method public android.view.View getContentView(); + method public android.widget.TextView getDescriptionView(); + method public android.widget.EditText getEditableTitleView(); + method public android.widget.ImageView getIconView(); + method public android.widget.TextView getTitleView(); + field public final android.view.View view; + } + + public class HeaderItem { + ctor public HeaderItem(long, java.lang.String); + ctor public HeaderItem(java.lang.String); + method public final long getId(); + method public final java.lang.String getName(); + } + + public class HorizontalGridView extends android.support.v7.widget.RecyclerView { + ctor public HorizontalGridView(android.content.Context); + ctor public HorizontalGridView(android.content.Context, android.util.AttributeSet); + ctor public HorizontalGridView(android.content.Context, android.util.AttributeSet, int); + method public final boolean getFadingLeftEdge(); + method public final int getFadingLeftEdgeLength(); + method public final int getFadingLeftEdgeOffset(); + method public final boolean getFadingRightEdge(); + method public final int getFadingRightEdgeLength(); + method public final int getFadingRightEdgeOffset(); + method protected void initAttributes(android.content.Context, android.util.AttributeSet); + method public final void setFadingLeftEdge(boolean); + method public final void setFadingLeftEdgeLength(int); + method public final void setFadingLeftEdgeOffset(int); + method public final void setFadingRightEdge(boolean); + method public final void setFadingRightEdgeLength(int); + method public final void setFadingRightEdgeOffset(int); + method public void setNumRows(int); + method public void setRowHeight(int); + } + + public final class HorizontalHoverCardSwitcher extends android.support.v17.leanback.widget.PresenterSwitcher { + ctor public HorizontalHoverCardSwitcher(); + method protected void insertView(android.view.View); + method public void select(android.support.v17.leanback.widget.HorizontalGridView, android.view.View, java.lang.Object); + } + + public class ImageCardView extends android.support.v17.leanback.widget.BaseCardView { + ctor public ImageCardView(android.content.Context, int); + ctor public ImageCardView(android.content.Context, android.util.AttributeSet, int); + ctor public ImageCardView(android.content.Context); + ctor public ImageCardView(android.content.Context, android.util.AttributeSet); + method public android.graphics.drawable.Drawable getBadgeImage(); + method public java.lang.CharSequence getContentText(); + method public android.graphics.drawable.Drawable getInfoAreaBackground(); + method public android.graphics.drawable.Drawable getMainImage(); + method public final android.widget.ImageView getMainImageView(); + method public java.lang.CharSequence getTitleText(); + method public void setBadgeImage(android.graphics.drawable.Drawable); + method public void setContentText(java.lang.CharSequence); + method public void setInfoAreaBackground(android.graphics.drawable.Drawable); + method public void setInfoAreaBackgroundColor(int); + method public void setMainImage(android.graphics.drawable.Drawable); + method public void setMainImage(android.graphics.drawable.Drawable, boolean); + method public void setMainImageAdjustViewBounds(boolean); + method public void setMainImageDimensions(int, int); + method public void setMainImageScaleType(android.widget.ImageView.ScaleType); + method public void setTitleText(java.lang.CharSequence); + field public static final int CARD_TYPE_FLAG_CONTENT = 2; // 0x2 + field public static final int CARD_TYPE_FLAG_ICON_LEFT = 8; // 0x8 + field public static final int CARD_TYPE_FLAG_ICON_RIGHT = 4; // 0x4 + field public static final int CARD_TYPE_FLAG_IMAGE_ONLY = 0; // 0x0 + field public static final int CARD_TYPE_FLAG_TITLE = 1; // 0x1 + } + + public abstract interface ImeKeyMonitor { + method public abstract void setImeKeyListener(android.support.v17.leanback.widget.ImeKeyMonitor.ImeKeyListener); + } + + public static abstract interface ImeKeyMonitor.ImeKeyListener { + method public abstract boolean onKeyPreIme(android.widget.EditText, int, android.view.KeyEvent); + } + + public final class ItemAlignmentFacet { + ctor public ItemAlignmentFacet(); + method public android.support.v17.leanback.widget.ItemAlignmentFacet.ItemAlignmentDef[] getAlignmentDefs(); + method public boolean isMultiAlignment(); + method public void setAlignmentDefs(android.support.v17.leanback.widget.ItemAlignmentFacet.ItemAlignmentDef[]); + field public static final float ITEM_ALIGN_OFFSET_PERCENT_DISABLED = -1.0f; + } + + public static class ItemAlignmentFacet.ItemAlignmentDef { + ctor public ItemAlignmentFacet.ItemAlignmentDef(); + method public final int getItemAlignmentFocusViewId(); + method public final int getItemAlignmentOffset(); + method public final float getItemAlignmentOffsetPercent(); + method public final int getItemAlignmentViewId(); + method public final boolean isItemAlignmentOffsetWithPadding(); + method public final void setItemAlignmentFocusViewId(int); + method public final void setItemAlignmentOffset(int); + method public final void setItemAlignmentOffsetPercent(float); + method public final void setItemAlignmentOffsetWithPadding(boolean); + method public final void setItemAlignmentViewId(int); + } + + public class ItemBridgeAdapter extends android.support.v7.widget.RecyclerView.Adapter implements android.support.v17.leanback.widget.FacetProviderAdapter { + ctor public ItemBridgeAdapter(android.support.v17.leanback.widget.ObjectAdapter, android.support.v17.leanback.widget.PresenterSelector); + ctor public ItemBridgeAdapter(android.support.v17.leanback.widget.ObjectAdapter); + ctor public ItemBridgeAdapter(); + method public void clear(); + method public android.support.v17.leanback.widget.FacetProvider getFacetProvider(int); + method public int getItemCount(); + method public java.util.ArrayList<android.support.v17.leanback.widget.Presenter> getPresenterMapper(); + method public android.support.v17.leanback.widget.ItemBridgeAdapter.Wrapper getWrapper(); + method protected void onAddPresenter(android.support.v17.leanback.widget.Presenter, int); + method protected void onAttachedToWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder); + method protected void onBind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder); + method public final void onBindViewHolder(android.support.v7.widget.RecyclerView.ViewHolder, int); + method protected void onCreate(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder); + method public final android.support.v7.widget.RecyclerView.ViewHolder onCreateViewHolder(android.view.ViewGroup, int); + method protected void onDetachedFromWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder); + method protected void onUnbind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder); + method public final void onViewAttachedToWindow(android.support.v7.widget.RecyclerView.ViewHolder); + method public final void onViewDetachedFromWindow(android.support.v7.widget.RecyclerView.ViewHolder); + method public final void onViewRecycled(android.support.v7.widget.RecyclerView.ViewHolder); + method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter); + method public void setAdapterListener(android.support.v17.leanback.widget.ItemBridgeAdapter.AdapterListener); + method public void setPresenterMapper(java.util.ArrayList<android.support.v17.leanback.widget.Presenter>); + method public void setWrapper(android.support.v17.leanback.widget.ItemBridgeAdapter.Wrapper); + } + + public static class ItemBridgeAdapter.AdapterListener { + ctor public ItemBridgeAdapter.AdapterListener(); + method public void onAddPresenter(android.support.v17.leanback.widget.Presenter, int); + method public void onAttachedToWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder); + method public void onBind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder); + method public void onCreate(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder); + method public void onDetachedFromWindow(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder); + method public void onUnbind(android.support.v17.leanback.widget.ItemBridgeAdapter.ViewHolder); + } + + public class ItemBridgeAdapter.ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder implements android.support.v17.leanback.widget.FacetProvider { + method public final java.lang.Object getExtraObject(); + method public java.lang.Object getFacet(java.lang.Class<?>); + method public final java.lang.Object getItem(); + method public final android.support.v17.leanback.widget.Presenter getPresenter(); + method public final android.support.v17.leanback.widget.Presenter.ViewHolder getViewHolder(); + method public void setExtraObject(java.lang.Object); + } + + public static abstract class ItemBridgeAdapter.Wrapper { + ctor public ItemBridgeAdapter.Wrapper(); + method public abstract android.view.View createWrapper(android.view.View); + method public abstract void wrap(android.view.View, android.view.View); + } + + public class ItemBridgeAdapterShadowOverlayWrapper extends android.support.v17.leanback.widget.ItemBridgeAdapter.Wrapper { + ctor public ItemBridgeAdapterShadowOverlayWrapper(android.support.v17.leanback.widget.ShadowOverlayHelper); + method public android.view.View createWrapper(android.view.View); + method public void wrap(android.view.View, android.view.View); + } + + public class ListRow extends android.support.v17.leanback.widget.Row { + ctor public ListRow(android.support.v17.leanback.widget.HeaderItem, android.support.v17.leanback.widget.ObjectAdapter); + ctor public ListRow(long, android.support.v17.leanback.widget.HeaderItem, android.support.v17.leanback.widget.ObjectAdapter); + ctor public ListRow(android.support.v17.leanback.widget.ObjectAdapter); + method public final android.support.v17.leanback.widget.ObjectAdapter getAdapter(); + } + + public final class ListRowHoverCardView extends android.widget.LinearLayout { + ctor public ListRowHoverCardView(android.content.Context); + ctor public ListRowHoverCardView(android.content.Context, android.util.AttributeSet); + ctor public ListRowHoverCardView(android.content.Context, android.util.AttributeSet, int); + method public final java.lang.CharSequence getDescription(); + method public final java.lang.CharSequence getTitle(); + method public final void setDescription(java.lang.CharSequence); + method public final void setTitle(java.lang.CharSequence); + } + + public class ListRowPresenter extends android.support.v17.leanback.widget.RowPresenter { + ctor public ListRowPresenter(); + ctor public ListRowPresenter(int); + ctor public ListRowPresenter(int, boolean); + method public final boolean areChildRoundedCornersEnabled(); + method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup); + method protected android.support.v17.leanback.widget.ShadowOverlayHelper.Options createShadowOverlayOptions(); + method public final void enableChildRoundedCorners(boolean); + method public int getExpandedRowHeight(); + method public final int getFocusZoomFactor(); + method public final android.support.v17.leanback.widget.PresenterSelector getHoverCardPresenterSelector(); + method public int getRecycledPoolSize(android.support.v17.leanback.widget.Presenter); + method public int getRowHeight(); + method public final boolean getShadowEnabled(); + method public final deprecated int getZoomFactor(); + method public final boolean isFocusDimmerUsed(); + method public final boolean isKeepChildForeground(); + method public boolean isUsingDefaultListSelectEffect(); + method public final boolean isUsingDefaultSelectEffect(); + method public boolean isUsingDefaultShadow(); + method public boolean isUsingZOrder(android.content.Context); + method public void setExpandedRowHeight(int); + method public final void setHoverCardPresenterSelector(android.support.v17.leanback.widget.PresenterSelector); + method public final void setKeepChildForeground(boolean); + method public void setRecycledPoolSize(android.support.v17.leanback.widget.Presenter, int); + method public void setRowHeight(int); + method public final void setShadowEnabled(boolean); + } + + public static class ListRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder { + ctor public ListRowPresenter.ViewHolder(android.view.View, android.support.v17.leanback.widget.HorizontalGridView, android.support.v17.leanback.widget.ListRowPresenter); + method public final android.support.v17.leanback.widget.ItemBridgeAdapter getBridgeAdapter(); + method public final android.support.v17.leanback.widget.HorizontalGridView getGridView(); + method public final android.support.v17.leanback.widget.ListRowPresenter getListRowPresenter(); + } + + public final class ListRowView extends android.widget.LinearLayout { + ctor public ListRowView(android.content.Context); + ctor public ListRowView(android.content.Context, android.util.AttributeSet); + ctor public ListRowView(android.content.Context, android.util.AttributeSet, int); + method public android.support.v17.leanback.widget.HorizontalGridView getGridView(); + } + + public abstract class ObjectAdapter { + ctor public ObjectAdapter(android.support.v17.leanback.widget.PresenterSelector); + ctor public ObjectAdapter(android.support.v17.leanback.widget.Presenter); + ctor public ObjectAdapter(); + method public abstract java.lang.Object get(int); + method public long getId(int); + method public final android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object); + method public final android.support.v17.leanback.widget.PresenterSelector getPresenterSelector(); + method public final boolean hasStableIds(); + method protected final void notifyChanged(); + method protected final void notifyItemRangeChanged(int, int); + method protected final void notifyItemRangeInserted(int, int); + method protected final void notifyItemRangeRemoved(int, int); + method protected void onHasStableIdsChanged(); + method protected void onPresenterSelectorChanged(); + method public final void registerObserver(android.support.v17.leanback.widget.ObjectAdapter.DataObserver); + method public final void setHasStableIds(boolean); + method public final void setPresenterSelector(android.support.v17.leanback.widget.PresenterSelector); + method public abstract int size(); + method public final void unregisterAllObservers(); + method public final void unregisterObserver(android.support.v17.leanback.widget.ObjectAdapter.DataObserver); + field public static final int NO_ID = -1; // 0xffffffff + } + + public static abstract class ObjectAdapter.DataObserver { + ctor public ObjectAdapter.DataObserver(); + method public void onChanged(); + method public void onItemRangeChanged(int, int); + method public void onItemRangeInserted(int, int); + method public void onItemRangeRemoved(int, int); + } + + public abstract interface OnActionClickedListener { + method public abstract void onActionClicked(android.support.v17.leanback.widget.Action); + } + + public abstract interface OnChildLaidOutListener { + method public abstract void onChildLaidOut(android.view.ViewGroup, android.view.View, int, long); + } + + public abstract deprecated interface OnChildSelectedListener { + method public abstract void onChildSelected(android.view.ViewGroup, android.view.View, int, long); + } + + public abstract class OnChildViewHolderSelectedListener { + ctor public OnChildViewHolderSelectedListener(); + method public void onChildViewHolderSelected(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, int, int); + } + + public abstract interface OnItemViewClickedListener { + method public abstract void onItemClicked(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object, android.support.v17.leanback.widget.RowPresenter.ViewHolder, android.support.v17.leanback.widget.Row); + } + + public abstract interface OnItemViewSelectedListener { + method public abstract void onItemSelected(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object, android.support.v17.leanback.widget.RowPresenter.ViewHolder, android.support.v17.leanback.widget.Row); + } + + public class PlaybackControlsRow extends android.support.v17.leanback.widget.Row { + ctor public PlaybackControlsRow(java.lang.Object); + ctor public PlaybackControlsRow(); + method public android.support.v17.leanback.widget.Action getActionForKeyCode(int); + method public android.support.v17.leanback.widget.Action getActionForKeyCode(android.support.v17.leanback.widget.ObjectAdapter, int); + method public int getBufferedProgress(); + method public int getCurrentTime(); + method public final android.graphics.drawable.Drawable getImageDrawable(); + method public final java.lang.Object getItem(); + method public final android.support.v17.leanback.widget.ObjectAdapter getPrimaryActionsAdapter(); + method public final android.support.v17.leanback.widget.ObjectAdapter getSecondaryActionsAdapter(); + method public int getTotalTime(); + method public void setBufferedProgress(int); + method public void setCurrentTime(int); + method public final void setImageBitmap(android.content.Context, android.graphics.Bitmap); + method public final void setImageDrawable(android.graphics.drawable.Drawable); + method public final void setPrimaryActionsAdapter(android.support.v17.leanback.widget.ObjectAdapter); + method public final void setSecondaryActionsAdapter(android.support.v17.leanback.widget.ObjectAdapter); + method public void setTotalTime(int); + } + + public static class PlaybackControlsRow.ClosedCaptioningAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction { + ctor public PlaybackControlsRow.ClosedCaptioningAction(android.content.Context); + ctor public PlaybackControlsRow.ClosedCaptioningAction(android.content.Context, int); + field public static int OFF; + field public static int ON; + } + + public static class PlaybackControlsRow.FastForwardAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction { + ctor public PlaybackControlsRow.FastForwardAction(android.content.Context); + ctor public PlaybackControlsRow.FastForwardAction(android.content.Context, int); + } + + public static class PlaybackControlsRow.HighQualityAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction { + ctor public PlaybackControlsRow.HighQualityAction(android.content.Context); + ctor public PlaybackControlsRow.HighQualityAction(android.content.Context, int); + field public static int OFF; + field public static int ON; + } + + public static class PlaybackControlsRow.MoreActions extends android.support.v17.leanback.widget.Action { + ctor public PlaybackControlsRow.MoreActions(android.content.Context); + } + + public static abstract class PlaybackControlsRow.MultiAction extends android.support.v17.leanback.widget.Action { + ctor public PlaybackControlsRow.MultiAction(int); + method public int getActionCount(); + method public android.graphics.drawable.Drawable getDrawable(int); + method public int getIndex(); + method public java.lang.String getLabel(int); + method public java.lang.String getSecondaryLabel(int); + method public void nextIndex(); + method public void setDrawables(android.graphics.drawable.Drawable[]); + method public void setIndex(int); + method public void setLabels(java.lang.String[]); + method public void setSecondaryLabels(java.lang.String[]); + } + + public static class PlaybackControlsRow.PlayPauseAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction { + ctor public PlaybackControlsRow.PlayPauseAction(android.content.Context); + field public static int PAUSE; + field public static int PLAY; + } + + public static class PlaybackControlsRow.RepeatAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction { + ctor public PlaybackControlsRow.RepeatAction(android.content.Context); + ctor public PlaybackControlsRow.RepeatAction(android.content.Context, int); + ctor public PlaybackControlsRow.RepeatAction(android.content.Context, int, int); + field public static int ALL; + field public static int NONE; + field public static int ONE; + } + + public static class PlaybackControlsRow.RewindAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction { + ctor public PlaybackControlsRow.RewindAction(android.content.Context); + ctor public PlaybackControlsRow.RewindAction(android.content.Context, int); + } + + public static class PlaybackControlsRow.ShuffleAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction { + ctor public PlaybackControlsRow.ShuffleAction(android.content.Context); + ctor public PlaybackControlsRow.ShuffleAction(android.content.Context, int); + field public static int OFF; + field public static int ON; + } + + public static class PlaybackControlsRow.SkipNextAction extends android.support.v17.leanback.widget.Action { + ctor public PlaybackControlsRow.SkipNextAction(android.content.Context); + } + + public static class PlaybackControlsRow.SkipPreviousAction extends android.support.v17.leanback.widget.Action { + ctor public PlaybackControlsRow.SkipPreviousAction(android.content.Context); + } + + public static abstract class PlaybackControlsRow.ThumbsAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction { + ctor public PlaybackControlsRow.ThumbsAction(int, android.content.Context, int, int); + field public static int OUTLINE; + field public static int SOLID; + } + + public static class PlaybackControlsRow.ThumbsDownAction extends android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsAction { + ctor public PlaybackControlsRow.ThumbsDownAction(android.content.Context); + } + + public static class PlaybackControlsRow.ThumbsUpAction extends android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsAction { + ctor public PlaybackControlsRow.ThumbsUpAction(android.content.Context); + } + + public class PlaybackControlsRowPresenter extends android.support.v17.leanback.widget.RowPresenter { + ctor public PlaybackControlsRowPresenter(android.support.v17.leanback.widget.Presenter); + ctor public PlaybackControlsRowPresenter(); + method public boolean areSecondaryActionsHidden(); + method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup); + method public int getBackgroundColor(); + method public android.support.v17.leanback.widget.OnActionClickedListener getOnActionClickedListener(); + method public int getProgressColor(); + method public void setBackgroundColor(int); + method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener); + method public void setProgressColor(int); + method public void setSecondaryActionsHidden(boolean); + method public void showBottomSpace(android.support.v17.leanback.widget.PlaybackControlsRowPresenter.ViewHolder, boolean); + method public void showPrimaryActions(android.support.v17.leanback.widget.PlaybackControlsRowPresenter.ViewHolder); + } + + public class PlaybackControlsRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder { + field public final android.support.v17.leanback.widget.Presenter.ViewHolder mDescriptionViewHolder; + } + + public abstract class Presenter implements android.support.v17.leanback.widget.FacetProvider { + ctor public Presenter(); + method protected static void cancelAnimationsRecursive(android.view.View); + method public final java.lang.Object getFacet(java.lang.Class<?>); + method public abstract void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object); + method public abstract android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup); + method public abstract void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder); + method public void onViewAttachedToWindow(android.support.v17.leanback.widget.Presenter.ViewHolder); + method public void onViewDetachedFromWindow(android.support.v17.leanback.widget.Presenter.ViewHolder); + method public final void setFacet(java.lang.Class<?>, java.lang.Object); + method public void setOnClickListener(android.support.v17.leanback.widget.Presenter.ViewHolder, android.view.View.OnClickListener); + } + + public static class Presenter.ViewHolder implements android.support.v17.leanback.widget.FacetProvider { + ctor public Presenter.ViewHolder(android.view.View); + method public final java.lang.Object getFacet(java.lang.Class<?>); + method public final void setFacet(java.lang.Class<?>, java.lang.Object); + field public final android.view.View view; + } + + public abstract class PresenterSelector { + ctor public PresenterSelector(); + method public abstract android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object); + method public android.support.v17.leanback.widget.Presenter[] getPresenters(); + } + + public abstract class PresenterSwitcher { + ctor public PresenterSwitcher(); + method public void clear(); + method public final android.view.ViewGroup getParentViewGroup(); + method public void init(android.view.ViewGroup, android.support.v17.leanback.widget.PresenterSelector); + method protected abstract void insertView(android.view.View); + method protected void onViewSelected(android.view.View); + method public void select(java.lang.Object); + method protected void showView(android.view.View, boolean); + method public void unselect(); + } + + public class Row { + ctor public Row(long, android.support.v17.leanback.widget.HeaderItem); + ctor public Row(android.support.v17.leanback.widget.HeaderItem); + ctor public Row(); + method public final android.support.v17.leanback.widget.HeaderItem getHeaderItem(); + method public final long getId(); + method public final void setHeaderItem(android.support.v17.leanback.widget.HeaderItem); + method public final void setId(long); + } + + public class RowHeaderPresenter extends android.support.v17.leanback.widget.Presenter { + ctor public RowHeaderPresenter(); + method protected static float getFontDescent(android.widget.TextView, android.graphics.Paint); + method public int getSpaceUnderBaseline(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder); + method public boolean isNullItemVisibilityGone(); + method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object); + method public android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup); + method protected void onSelectLevelChanged(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder); + method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder); + method public void setNullItemVisibilityGone(boolean); + method public final void setSelectLevel(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, float); + } + + public static class RowHeaderPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder { + ctor public RowHeaderPresenter.ViewHolder(android.view.View); + method public final float getSelectLevel(); + } + + public final class RowHeaderView extends android.widget.TextView { + ctor public RowHeaderView(android.content.Context); + ctor public RowHeaderView(android.content.Context, android.util.AttributeSet); + ctor public RowHeaderView(android.content.Context, android.util.AttributeSet, int); + } + + public abstract class RowPresenter extends android.support.v17.leanback.widget.Presenter { + ctor public RowPresenter(); + method protected abstract android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup); + method protected void dispatchItemSelectedListener(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean); + method public void freeze(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean); + method public final android.support.v17.leanback.widget.RowHeaderPresenter getHeaderPresenter(); + method public final android.support.v17.leanback.widget.RowPresenter.ViewHolder getRowViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder); + method public final boolean getSelectEffectEnabled(); + method public final float getSelectLevel(android.support.v17.leanback.widget.Presenter.ViewHolder); + method public final int getSyncActivatePolicy(); + method protected void initializeRowViewHolder(android.support.v17.leanback.widget.RowPresenter.ViewHolder); + method protected boolean isClippingChildren(); + method public boolean isUsingDefaultSelectEffect(); + method protected void onBindRowViewHolder(android.support.v17.leanback.widget.RowPresenter.ViewHolder, java.lang.Object); + method public final void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object); + method public final android.support.v17.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup); + method protected void onRowViewAttachedToWindow(android.support.v17.leanback.widget.RowPresenter.ViewHolder); + method protected void onRowViewDetachedFromWindow(android.support.v17.leanback.widget.RowPresenter.ViewHolder); + method protected void onRowViewExpanded(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean); + method protected void onRowViewSelected(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean); + method protected void onSelectLevelChanged(android.support.v17.leanback.widget.RowPresenter.ViewHolder); + method protected void onUnbindRowViewHolder(android.support.v17.leanback.widget.RowPresenter.ViewHolder); + method public final void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder); + method public final void onViewAttachedToWindow(android.support.v17.leanback.widget.Presenter.ViewHolder); + method public final void onViewDetachedFromWindow(android.support.v17.leanback.widget.Presenter.ViewHolder); + method public void setEntranceTransitionState(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean); + method public final void setHeaderPresenter(android.support.v17.leanback.widget.RowHeaderPresenter); + method public final void setRowViewExpanded(android.support.v17.leanback.widget.Presenter.ViewHolder, boolean); + method public final void setRowViewSelected(android.support.v17.leanback.widget.Presenter.ViewHolder, boolean); + method public final void setSelectEffectEnabled(boolean); + method public final void setSelectLevel(android.support.v17.leanback.widget.Presenter.ViewHolder, float); + method public final void setSyncActivatePolicy(int); + field public static final int SYNC_ACTIVATED_CUSTOM = 0; // 0x0 + field public static final int SYNC_ACTIVATED_TO_EXPANDED = 1; // 0x1 + field public static final int SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED = 3; // 0x3 + field public static final int SYNC_ACTIVATED_TO_SELECTED = 2; // 0x2 + } + + public static class RowPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder { + ctor public RowPresenter.ViewHolder(android.view.View); + method public final android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder getHeaderViewHolder(); + method public final android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener(); + method public final android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener(); + method public android.view.View.OnKeyListener getOnKeyListener(); + method public final android.support.v17.leanback.widget.Row getRow(); + method public final float getSelectLevel(); + method public final boolean isExpanded(); + method public final boolean isSelected(); + method public final void setActivated(boolean); + method public final void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener); + method public final void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener); + method public void setOnKeyListener(android.view.View.OnKeyListener); + method public final void syncActivatedStatus(android.view.View); + field protected final android.support.v17.leanback.graphics.ColorOverlayDimmer mColorDimmer; + } + + public class SearchBar extends android.widget.RelativeLayout { + ctor public SearchBar(android.content.Context); + ctor public SearchBar(android.content.Context, android.util.AttributeSet); + ctor public SearchBar(android.content.Context, android.util.AttributeSet, int); + method public void displayCompletions(java.util.List<java.lang.String>); + method public void displayCompletions(android.view.inputmethod.CompletionInfo[]); + method public android.graphics.drawable.Drawable getBadgeDrawable(); + method public java.lang.CharSequence getHint(); + method public java.lang.String getTitle(); + method public void setBadgeDrawable(android.graphics.drawable.Drawable); + method public void setSearchBarListener(android.support.v17.leanback.widget.SearchBar.SearchBarListener); + method public void setSearchQuery(java.lang.String); + method public void setSpeechRecognitionCallback(android.support.v17.leanback.widget.SpeechRecognitionCallback); + method public void setSpeechRecognizer(android.speech.SpeechRecognizer); + method public void setTitle(java.lang.String); + method public void startRecognition(); + method public void stopRecognition(); + } + + public static abstract interface SearchBar.SearchBarListener { + method public abstract void onKeyboardDismiss(java.lang.String); + method public abstract void onSearchQueryChange(java.lang.String); + method public abstract void onSearchQuerySubmit(java.lang.String); + } + + public class SearchEditText extends android.support.v17.leanback.widget.StreamingTextView { + ctor public SearchEditText(android.content.Context); + ctor public SearchEditText(android.content.Context, android.util.AttributeSet); + ctor public SearchEditText(android.content.Context, android.util.AttributeSet, int); + method public void setOnKeyboardDismissListener(android.support.v17.leanback.widget.SearchEditText.OnKeyboardDismissListener); + } + + public static abstract interface SearchEditText.OnKeyboardDismissListener { + method public abstract void onKeyboardDismiss(); + } + + public class SearchOrbView extends android.widget.FrameLayout implements android.view.View.OnClickListener { + ctor public SearchOrbView(android.content.Context); + ctor public SearchOrbView(android.content.Context, android.util.AttributeSet); + ctor public SearchOrbView(android.content.Context, android.util.AttributeSet, int); + method public void enableOrbColorAnimation(boolean); + method public int getOrbColor(); + method public android.support.v17.leanback.widget.SearchOrbView.Colors getOrbColors(); + method public android.graphics.drawable.Drawable getOrbIcon(); + method public void onClick(android.view.View); + method public void setOnOrbClickedListener(android.view.View.OnClickListener); + method public void setOrbColor(int); + method public deprecated void setOrbColor(int, int); + method public void setOrbColors(android.support.v17.leanback.widget.SearchOrbView.Colors); + method public void setOrbIcon(android.graphics.drawable.Drawable); + } + + public static class SearchOrbView.Colors { + ctor public SearchOrbView.Colors(int); + ctor public SearchOrbView.Colors(int, int); + ctor public SearchOrbView.Colors(int, int, int); + method public static int getBrightColor(int); + field public int brightColor; + field public int color; + field public int iconColor; + } + + public class ShadowOverlayContainer extends android.widget.FrameLayout { + ctor public ShadowOverlayContainer(android.content.Context); + ctor public ShadowOverlayContainer(android.content.Context, android.util.AttributeSet); + ctor public ShadowOverlayContainer(android.content.Context, android.util.AttributeSet, int); + method public int getShadowType(); + method public android.view.View getWrappedView(); + method public deprecated void initialize(boolean, boolean); + method public deprecated void initialize(boolean, boolean, boolean); + method public static void prepareParentForShadow(android.view.ViewGroup); + method public void setOverlayColor(int); + method public void setShadowFocusLevel(float); + method public static boolean supportsDynamicShadow(); + method public static boolean supportsShadow(); + method public void useDynamicShadow(); + method public void useDynamicShadow(float, float); + method public void useStaticShadow(); + method public void wrap(android.view.View); + field public static final int SHADOW_DYNAMIC = 3; // 0x3 + field public static final int SHADOW_NONE = 1; // 0x1 + field public static final int SHADOW_STATIC = 2; // 0x2 + } + + public final class ShadowOverlayHelper { + method public android.support.v17.leanback.widget.ShadowOverlayContainer createShadowOverlayContainer(android.content.Context); + method public int getShadowType(); + method public boolean needsOverlay(); + method public boolean needsRoundedCorner(); + method public boolean needsWrapper(); + method public void onViewCreated(android.view.View); + method public void prepareParentForShadow(android.view.ViewGroup); + method public static void setNoneWrapperOverlayColor(android.view.View, int); + method public static void setNoneWrapperShadowFocusLevel(android.view.View, float); + method public void setOverlayColor(android.view.View, int); + method public void setShadowFocusLevel(android.view.View, float); + method public static boolean supportsDynamicShadow(); + method public static boolean supportsForeground(); + method public static boolean supportsRoundedCorner(); + method public static boolean supportsShadow(); + field public static final int SHADOW_DYNAMIC = 3; // 0x3 + field public static final int SHADOW_NONE = 1; // 0x1 + field public static final int SHADOW_STATIC = 2; // 0x2 + } + + public static final class ShadowOverlayHelper.Builder { + ctor public ShadowOverlayHelper.Builder(); + method public android.support.v17.leanback.widget.ShadowOverlayHelper build(android.content.Context); + method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder keepForegroundDrawable(boolean); + method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsOverlay(boolean); + method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsRoundedCorner(boolean); + method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsShadow(boolean); + method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder options(android.support.v17.leanback.widget.ShadowOverlayHelper.Options); + method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder preferZOrder(boolean); + } + + public static final class ShadowOverlayHelper.Options { + ctor public ShadowOverlayHelper.Options(); + method public android.support.v17.leanback.widget.ShadowOverlayHelper.Options dynamicShadowZ(float, float); + method public final float getDynamicShadowFocusedZ(); + method public final float getDynamicShadowUnfocusedZ(); + method public final int getRoundedCornerRadius(); + method public android.support.v17.leanback.widget.ShadowOverlayHelper.Options roundedCornerRadius(int); + field public static final android.support.v17.leanback.widget.ShadowOverlayHelper.Options DEFAULT; + } + + public final class SinglePresenterSelector extends android.support.v17.leanback.widget.PresenterSelector { + ctor public SinglePresenterSelector(android.support.v17.leanback.widget.Presenter); + method public android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object); + } + + public class SparseArrayObjectAdapter extends android.support.v17.leanback.widget.ObjectAdapter { + ctor public SparseArrayObjectAdapter(android.support.v17.leanback.widget.PresenterSelector); + ctor public SparseArrayObjectAdapter(android.support.v17.leanback.widget.Presenter); + ctor public SparseArrayObjectAdapter(); + method public void clear(int); + method public void clear(); + method public java.lang.Object get(int); + method public int indexOf(java.lang.Object); + method public int indexOf(int); + method public java.lang.Object lookup(int); + method public void notifyArrayItemRangeChanged(int, int); + method public void set(int, java.lang.Object); + method public int size(); + } + + public class SpeechOrbView extends android.support.v17.leanback.widget.SearchOrbView { + ctor public SpeechOrbView(android.content.Context); + ctor public SpeechOrbView(android.content.Context, android.util.AttributeSet); + ctor public SpeechOrbView(android.content.Context, android.util.AttributeSet, int); + method public void setSoundLevel(int); + method public void showListening(); + method public void showNotListening(); + } + + public abstract interface SpeechRecognitionCallback { + method public abstract void recognizeSpeech(); + } + + class StreamingTextView extends android.widget.EditText { + ctor public StreamingTextView(android.content.Context, android.util.AttributeSet); + ctor public StreamingTextView(android.content.Context, android.util.AttributeSet, int); + method public static boolean isLayoutRtl(android.view.View); + method public void reset(); + method public void setFinalRecognizedText(java.lang.CharSequence); + method public void updateRecognizedText(java.lang.String, java.lang.String); + method public void updateRecognizedText(java.lang.String, java.util.List<java.lang.Float>); + } + + public class TitleHelper { + ctor public TitleHelper(android.view.ViewGroup, android.support.v17.leanback.widget.TitleView); + method public android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener getOnFocusSearchListener(); + method public android.view.ViewGroup getSceneRoot(); + method public android.support.v17.leanback.widget.TitleView getTitleView(); + method public void showTitle(boolean); + } + + public class TitleView extends android.widget.FrameLayout { + ctor public TitleView(android.content.Context); + ctor public TitleView(android.content.Context, android.util.AttributeSet); + ctor public TitleView(android.content.Context, android.util.AttributeSet, int); + method public void enableAnimation(boolean); + method public android.graphics.drawable.Drawable getBadgeDrawable(); + method public android.support.v17.leanback.widget.SearchOrbView.Colors getSearchAffordanceColors(); + method public android.view.View getSearchAffordanceView(); + method public java.lang.CharSequence getTitle(); + method public void setBadgeDrawable(android.graphics.drawable.Drawable); + method public void setOnSearchClickedListener(android.view.View.OnClickListener); + method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors); + method public void setTitle(java.lang.String); + } + + public class VerticalGridPresenter extends android.support.v17.leanback.widget.Presenter { + ctor public VerticalGridPresenter(); + ctor public VerticalGridPresenter(int); + ctor public VerticalGridPresenter(int, boolean); + method public final boolean areChildRoundedCornersEnabled(); + method protected android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder createGridViewHolder(android.view.ViewGroup); + method protected android.support.v17.leanback.widget.ShadowOverlayHelper.Options createShadowOverlayOptions(); + method public final void enableChildRoundedCorners(boolean); + method public final int getFocusZoomFactor(); + method public final boolean getKeepChildForeground(); + method public int getNumberOfColumns(); + method public final android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener(); + method public final android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener(); + method public final boolean getShadowEnabled(); + method protected void initializeGridViewHolder(android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder); + method public final boolean isFocusDimmerUsed(); + method public boolean isUsingDefaultShadow(); + method public boolean isUsingZOrder(android.content.Context); + method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object); + method public final android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder onCreateViewHolder(android.view.ViewGroup); + method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder); + method public void setEntranceTransitionState(android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder, boolean); + method public final void setKeepChildForeground(boolean); + method public void setNumberOfColumns(int); + method public final void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener); + method public final void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener); + method public final void setShadowEnabled(boolean); + } + + public static class VerticalGridPresenter.ViewHolder extends android.support.v17.leanback.widget.Presenter.ViewHolder { + ctor public VerticalGridPresenter.ViewHolder(android.support.v17.leanback.widget.VerticalGridView); + method public android.support.v17.leanback.widget.VerticalGridView getGridView(); + } + + public class VerticalGridView extends android.support.v7.widget.RecyclerView { + ctor public VerticalGridView(android.content.Context); + ctor public VerticalGridView(android.content.Context, android.util.AttributeSet); + ctor public VerticalGridView(android.content.Context, android.util.AttributeSet, int); + method protected void initAttributes(android.content.Context, android.util.AttributeSet); + method public void setColumnWidth(int); + method public void setNumColumns(int); + } + +} + diff --git a/v17/leanback/api/current.txt b/v17/leanback/api/current.txt index fb2832d820..a4decd8b8f 100644 --- a/v17/leanback/api/current.txt +++ b/v17/leanback/api/current.txt @@ -37,6 +37,7 @@ package android.support.v17.leanback.app { public class BrowseFragment extends android.support.v17.leanback.app.BrandedFragment { ctor public BrowseFragment(); method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, int); + method protected java.lang.Object createEntranceTransition(); method public void enableRowScaling(boolean); method public android.support.v17.leanback.widget.ObjectAdapter getAdapter(); method public int getBrandColor(); @@ -46,6 +47,12 @@ package android.support.v17.leanback.app { method public final boolean isHeadersTransitionOnBackEnabled(); method public boolean isInHeadersTransition(); method public boolean isShowingHeaders(); + method protected void onEntranceTransitionEnd(); + method protected void onEntranceTransitionPrepare(); + method protected void onEntranceTransitionStart(); + method public void onSaveInstanceState(android.os.Bundle); + method public void onStart(); + method protected void runEntranceTransition(java.lang.Object); method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter); method public void setBrandColor(int); method public void setBrowseTransitionListener(android.support.v17.leanback.app.BrowseFragment.BrowseTransitionListener); @@ -71,6 +78,7 @@ package android.support.v17.leanback.app { public class BrowseSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment { ctor public BrowseSupportFragment(); method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, int); + method protected java.lang.Object createEntranceTransition(); method public void enableRowScaling(boolean); method public android.support.v17.leanback.widget.ObjectAdapter getAdapter(); method public int getBrandColor(); @@ -80,6 +88,12 @@ package android.support.v17.leanback.app { method public final boolean isHeadersTransitionOnBackEnabled(); method public boolean isInHeadersTransition(); method public boolean isShowingHeaders(); + method protected void onEntranceTransitionEnd(); + method protected void onEntranceTransitionPrepare(); + method protected void onEntranceTransitionStart(); + method public void onSaveInstanceState(android.os.Bundle); + method public void onStart(); + method protected void runEntranceTransition(java.lang.Object); method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter); method public void setBrandColor(int); method public void setBrowseTransitionListener(android.support.v17.leanback.app.BrowseSupportFragment.BrowseTransitionListener); @@ -104,11 +118,17 @@ package android.support.v17.leanback.app { public class DetailsFragment extends android.support.v17.leanback.app.BrandedFragment { ctor public DetailsFragment(); + method protected java.lang.Object createEntranceTransition(); method public android.support.v17.leanback.widget.ObjectAdapter getAdapter(); method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener(); method protected android.view.View inflateTitle(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle); + method protected void onEntranceTransitionEnd(); + method protected void onEntranceTransitionPrepare(); + method protected void onEntranceTransitionStart(); method protected void onSetDetailsOverviewRowStatus(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int); method protected void onSetRowStatus(android.support.v17.leanback.widget.RowPresenter, android.support.v17.leanback.widget.RowPresenter.ViewHolder, int, int, int); + method public void onStart(); + method protected void runEntranceTransition(java.lang.Object); method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter); method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener); method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener); @@ -120,11 +140,17 @@ package android.support.v17.leanback.app { public class DetailsSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment { ctor public DetailsSupportFragment(); + method protected java.lang.Object createEntranceTransition(); method public android.support.v17.leanback.widget.ObjectAdapter getAdapter(); method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener(); method protected android.view.View inflateTitle(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle); + method protected void onEntranceTransitionEnd(); + method protected void onEntranceTransitionPrepare(); + method protected void onEntranceTransitionStart(); method protected void onSetDetailsOverviewRowStatus(android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter, android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int); method protected void onSetRowStatus(android.support.v17.leanback.widget.RowPresenter, android.support.v17.leanback.widget.RowPresenter.ViewHolder, int, int, int); + method public void onStart(); + method protected void runEntranceTransition(java.lang.Object); method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter); method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener); method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener); @@ -177,23 +203,109 @@ package android.support.v17.leanback.app { public class GuidedStepFragment extends android.app.Fragment { ctor public GuidedStepFragment(); method public static int add(android.app.FragmentManager, android.support.v17.leanback.app.GuidedStepFragment); + method public static int add(android.app.FragmentManager, android.support.v17.leanback.app.GuidedStepFragment, int); + method public static int addAsRoot(android.app.Activity, android.support.v17.leanback.app.GuidedStepFragment, int); + method public android.support.v17.leanback.widget.GuidedAction findActionById(long); + method public int findActionPositionById(long); + method public android.support.v17.leanback.widget.GuidedAction findButtonActionById(long); + method public int findButtonActionPositionById(long); + method public void finishGuidedStepFragments(); + method public java.lang.String generateStackEntryName(); + method public static java.lang.String generateStackEntryName(int, java.lang.Class); method public android.view.View getActionItemView(int); method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getActions(); + method public android.view.View getButtonActionItemView(int); + method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getButtonActions(); method public static android.support.v17.leanback.app.GuidedStepFragment getCurrentGuidedStepFragment(android.app.FragmentManager); method public android.support.v17.leanback.widget.GuidanceStylist getGuidanceStylist(); method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedActionsStylist(); + method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedButtonActionsStylist(); + method public static java.lang.String getGuidedStepFragmentClassName(java.lang.String); method public int getSelectedActionPosition(); - method protected boolean isEntryTransitionEnabled(); + method public int getSelectedButtonActionPosition(); + method public int getUiStyle(); + method public static boolean isUiStyleDefault(java.lang.String); + method public static boolean isUiStyleEntrance(java.lang.String); + method public void notifyActionChanged(int); + method public void notifyButtonActionChanged(int); + method protected void onAddSharedElementTransition(android.app.FragmentTransaction, android.support.v17.leanback.app.GuidedStepFragment); method public void onCreateActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle); method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateActionsStylist(); + method public android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle); + method public void onCreateButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle); + method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateButtonActionsStylist(); method public android.support.v17.leanback.widget.GuidanceStylist.Guidance onCreateGuidance(android.os.Bundle); method public android.support.v17.leanback.widget.GuidanceStylist onCreateGuidanceStylist(); method public void onGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction); + method public void onGuidedActionEdited(android.support.v17.leanback.widget.GuidedAction); + method public long onGuidedActionEditedAndProceed(android.support.v17.leanback.widget.GuidedAction); method public void onGuidedActionFocused(android.support.v17.leanback.widget.GuidedAction); + method protected void onProvideFragmentTransitions(); method public int onProvideTheme(); + method public void popBackStackToGuidedStepFragment(java.lang.Class, int); method public void setActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>); - method protected void setEntryTransitionEnabled(boolean); + method public void setButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>); method public void setSelectedActionPosition(int); + method public void setSelectedButtonActionPosition(int); + method public void setUiStyle(int); + field public static final java.lang.String EXTRA_UI_STYLE = "uiStyle"; + field public static final int UI_STYLE_ACTIVITY_ROOT = 2; // 0x2 + field public static final int UI_STYLE_ENTRANCE = 1; // 0x1 + field public static final int UI_STYLE_REPLACE = 0; // 0x0 + } + + public class GuidedStepSupportFragment extends android.support.v4.app.Fragment { + ctor public GuidedStepSupportFragment(); + method public static int add(android.support.v4.app.FragmentManager, android.support.v17.leanback.app.GuidedStepSupportFragment); + method public static int add(android.support.v4.app.FragmentManager, android.support.v17.leanback.app.GuidedStepSupportFragment, int); + method public static int addAsRoot(android.support.v4.app.FragmentActivity, android.support.v17.leanback.app.GuidedStepSupportFragment, int); + method public android.support.v17.leanback.widget.GuidedAction findActionById(long); + method public int findActionPositionById(long); + method public android.support.v17.leanback.widget.GuidedAction findButtonActionById(long); + method public int findButtonActionPositionById(long); + method public void finishGuidedStepSupportFragments(); + method public java.lang.String generateStackEntryName(); + method public static java.lang.String generateStackEntryName(int, java.lang.Class); + method public android.view.View getActionItemView(int); + method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getActions(); + method public android.view.View getButtonActionItemView(int); + method public java.util.List<android.support.v17.leanback.widget.GuidedAction> getButtonActions(); + method public static android.support.v17.leanback.app.GuidedStepSupportFragment getCurrentGuidedStepSupportFragment(android.support.v4.app.FragmentManager); + method public android.support.v17.leanback.widget.GuidanceStylist getGuidanceStylist(); + method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedActionsStylist(); + method public android.support.v17.leanback.widget.GuidedActionsStylist getGuidedButtonActionsStylist(); + method public static java.lang.String getGuidedStepSupportFragmentClassName(java.lang.String); + method public int getSelectedActionPosition(); + method public int getSelectedButtonActionPosition(); + method public int getUiStyle(); + method public static boolean isUiStyleDefault(java.lang.String); + method public static boolean isUiStyleEntrance(java.lang.String); + method public void notifyActionChanged(int); + method public void notifyButtonActionChanged(int); + method protected void onAddSharedElementTransition(android.support.v4.app.FragmentTransaction, android.support.v17.leanback.app.GuidedStepSupportFragment); + method public void onCreateActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle); + method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateActionsStylist(); + method public android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle); + method public void onCreateButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>, android.os.Bundle); + method public android.support.v17.leanback.widget.GuidedActionsStylist onCreateButtonActionsStylist(); + method public android.support.v17.leanback.widget.GuidanceStylist.Guidance onCreateGuidance(android.os.Bundle); + method public android.support.v17.leanback.widget.GuidanceStylist onCreateGuidanceStylist(); + method public void onGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction); + method public void onGuidedActionEdited(android.support.v17.leanback.widget.GuidedAction); + method public long onGuidedActionEditedAndProceed(android.support.v17.leanback.widget.GuidedAction); + method public void onGuidedActionFocused(android.support.v17.leanback.widget.GuidedAction); + method protected void onProvideFragmentTransitions(); + method public int onProvideTheme(); + method public void popBackStackToGuidedStepSupportFragment(java.lang.Class, int); + method public void setActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>); + method public void setButtonActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>); + method public void setSelectedActionPosition(int); + method public void setSelectedButtonActionPosition(int); + method public void setUiStyle(int); + field public static final java.lang.String EXTRA_UI_STYLE = "uiStyle"; + field public static final int UI_STYLE_ACTIVITY_ROOT = 2; // 0x2 + field public static final int UI_STYLE_ENTRANCE = 1; // 0x1 + field public static final int UI_STYLE_REPLACE = 0; // 0x0 } public class HeadersFragment extends android.support.v17.leanback.app.BaseRowFragment { @@ -300,12 +412,69 @@ package android.support.v17.leanback.app { field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0 } + public abstract class PlaybackControlSupportGlue implements android.support.v17.leanback.widget.OnActionClickedListener android.view.View.OnKeyListener { + ctor public PlaybackControlSupportGlue(android.content.Context, int[]); + ctor public PlaybackControlSupportGlue(android.content.Context, int[], int[]); + ctor public PlaybackControlSupportGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlaySupportFragment, int[]); + ctor public PlaybackControlSupportGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlaySupportFragment, int[], int[]); + method public android.support.v17.leanback.widget.PlaybackControlsRowPresenter createControlsRowAndPresenter(); + method protected android.support.v17.leanback.widget.SparseArrayObjectAdapter createPrimaryActionsAdapter(android.support.v17.leanback.widget.PresenterSelector); + method public void enableProgressUpdating(boolean); + method public android.content.Context getContext(); + method public android.support.v17.leanback.widget.PlaybackControlsRow getControlsRow(); + method public abstract int getCurrentPosition(); + method public abstract int getCurrentSpeedId(); + method public int[] getFastForwardSpeeds(); + method public android.support.v17.leanback.app.PlaybackOverlaySupportFragment getFragment(); + method public abstract android.graphics.drawable.Drawable getMediaArt(); + method public abstract int getMediaDuration(); + method public abstract java.lang.CharSequence getMediaSubtitle(); + method public abstract java.lang.CharSequence getMediaTitle(); + method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener(); + method public int[] getRewindSpeeds(); + method public abstract long getSupportedActions(); + method public int getUpdatePeriod(); + method public abstract boolean hasValidMedia(); + method public boolean isFadingEnabled(); + method public abstract boolean isMediaPlaying(); + method public void onActionClicked(android.support.v17.leanback.widget.Action); + method public boolean onKey(android.view.View, int, android.view.KeyEvent); + method protected void onMetadataChanged(); + method protected abstract void onRowChanged(android.support.v17.leanback.widget.PlaybackControlsRow); + method protected void onStateChanged(); + method protected abstract void pausePlayback(); + method public void setControlsRow(android.support.v17.leanback.widget.PlaybackControlsRow); + method public void setFadingEnabled(boolean); + method public deprecated void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener); + method protected abstract void skipToNext(); + method protected abstract void skipToPrevious(); + method protected abstract void startPlayback(int); + method public void updateProgress(); + field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1 + field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000 + field public static final int ACTION_FAST_FORWARD = 128; // 0x80 + field public static final int ACTION_PLAY_PAUSE = 64; // 0x40 + field public static final int ACTION_REWIND = 32; // 0x20 + field public static final int ACTION_SKIP_TO_NEXT = 256; // 0x100 + field public static final int ACTION_SKIP_TO_PREVIOUS = 16; // 0x10 + field public static final int PLAYBACK_SPEED_FAST_L0 = 10; // 0xa + field public static final int PLAYBACK_SPEED_FAST_L1 = 11; // 0xb + field public static final int PLAYBACK_SPEED_FAST_L2 = 12; // 0xc + field public static final int PLAYBACK_SPEED_FAST_L3 = 13; // 0xd + field public static final int PLAYBACK_SPEED_FAST_L4 = 14; // 0xe + field public static final int PLAYBACK_SPEED_INVALID = -1; // 0xffffffff + field public static final int PLAYBACK_SPEED_NORMAL = 1; // 0x1 + field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0 + } + public class PlaybackOverlayFragment extends android.support.v17.leanback.app.DetailsFragment { ctor public PlaybackOverlayFragment(); method public int getBackgroundType(); method public android.support.v17.leanback.app.PlaybackOverlayFragment.OnFadeCompleteListener getFadeCompleteListener(); method public final android.support.v17.leanback.app.PlaybackOverlayFragment.InputEventHandler getInputEventHandler(); method public boolean isFadingEnabled(); + method public void onDestroyView(); + method public void onResume(); method public void setBackgroundType(int); method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackOverlayFragment.OnFadeCompleteListener); method public void setFadingEnabled(boolean); @@ -332,6 +501,8 @@ package android.support.v17.leanback.app { method public android.support.v17.leanback.app.PlaybackOverlaySupportFragment.OnFadeCompleteListener getFadeCompleteListener(); method public final android.support.v17.leanback.app.PlaybackOverlaySupportFragment.InputEventHandler getInputEventHandler(); method public boolean isFadingEnabled(); + method public void onDestroyView(); + method public void onResume(); method public void setBackgroundType(int); method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackOverlaySupportFragment.OnFadeCompleteListener); method public void setFadingEnabled(boolean); @@ -379,6 +550,7 @@ package android.support.v17.leanback.app { method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String); method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, java.lang.String); method public void displayCompletions(java.util.List<java.lang.String>); + method public void displayCompletions(android.view.inputmethod.CompletionInfo[]); method public android.graphics.drawable.Drawable getBadgeDrawable(); method public android.content.Intent getRecognizerIntent(); method public java.lang.String getTitle(); @@ -405,6 +577,7 @@ package android.support.v17.leanback.app { method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String); method public static android.os.Bundle createArgs(android.os.Bundle, java.lang.String, java.lang.String); method public void displayCompletions(java.util.List<java.lang.String>); + method public void displayCompletions(android.view.inputmethod.CompletionInfo[]); method public android.graphics.drawable.Drawable getBadgeDrawable(); method public android.content.Intent getRecognizerIntent(); method public java.lang.String getTitle(); @@ -426,11 +599,16 @@ package android.support.v17.leanback.app { method public abstract boolean onQueryTextSubmit(java.lang.String); } - public class VerticalGridFragment extends android.app.Fragment { + public class VerticalGridFragment extends android.support.v17.leanback.app.BrandedFragment { ctor public VerticalGridFragment(); + method protected java.lang.Object createEntranceTransition(); method public android.support.v17.leanback.widget.ObjectAdapter getAdapter(); method public android.support.v17.leanback.widget.VerticalGridPresenter getGridPresenter(); method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener(); + method public void onDestroyView(); + method public void onStart(); + method public void onViewCreated(android.view.View, android.os.Bundle); + method protected void runEntranceTransition(java.lang.Object); method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter); method public void setGridPresenter(android.support.v17.leanback.widget.VerticalGridPresenter); method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener); @@ -438,11 +616,16 @@ package android.support.v17.leanback.app { method public void setSelectedPosition(int); } - public class VerticalGridSupportFragment extends android.support.v4.app.Fragment { + public class VerticalGridSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment { ctor public VerticalGridSupportFragment(); + method protected java.lang.Object createEntranceTransition(); method public android.support.v17.leanback.widget.ObjectAdapter getAdapter(); method public android.support.v17.leanback.widget.VerticalGridPresenter getGridPresenter(); method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener(); + method public void onDestroyView(); + method public void onStart(); + method public void onViewCreated(android.view.View, android.os.Bundle); + method protected void runEntranceTransition(java.lang.Object); method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter); method public void setGridPresenter(android.support.v17.leanback.widget.VerticalGridPresenter); method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener); @@ -722,12 +905,8 @@ package android.support.v17.leanback.widget { } public abstract interface FragmentAnimationProvider { - method public abstract void onActivityEnter(java.util.List<android.animation.Animator>); - method public abstract void onActivityExit(java.util.List<android.animation.Animator>); - method public abstract void onFragmentEnter(java.util.List<android.animation.Animator>); - method public abstract void onFragmentExit(java.util.List<android.animation.Animator>); - method public abstract void onFragmentReenter(java.util.List<android.animation.Animator>); - method public abstract void onFragmentReturn(java.util.List<android.animation.Animator>); + method public abstract void onImeAppearing(java.util.List<android.animation.Animator>); + method public abstract void onImeDisappearing(java.util.List<android.animation.Animator>); } public class FullWidthDetailsOverviewRowPresenter extends android.support.v17.leanback.widget.RowPresenter { @@ -798,13 +977,10 @@ package android.support.v17.leanback.widget { method public android.widget.TextView getDescriptionView(); method public android.widget.ImageView getIconView(); method public android.widget.TextView getTitleView(); - method public void onActivityEnter(java.util.List<android.animation.Animator>); - method public void onActivityExit(java.util.List<android.animation.Animator>); method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.support.v17.leanback.widget.GuidanceStylist.Guidance); - method public void onFragmentEnter(java.util.List<android.animation.Animator>); - method public void onFragmentExit(java.util.List<android.animation.Animator>); - method public void onFragmentReenter(java.util.List<android.animation.Animator>); - method public void onFragmentReturn(java.util.List<android.animation.Animator>); + method public void onDestroyView(); + method public void onImeAppearing(java.util.List<android.animation.Animator>); + method public void onImeDisappearing(java.util.List<android.animation.Animator>); method public int onProvideLayoutId(); } @@ -817,59 +993,112 @@ package android.support.v17.leanback.widget { } public class GuidedAction extends android.support.v17.leanback.widget.Action { + ctor protected GuidedAction(); method public int getCheckSetId(); method public java.lang.CharSequence getDescription(); + method public int getDescriptionEditInputType(); + method public int getDescriptionInputType(); + method public java.lang.CharSequence getEditDescription(); + method public int getEditInputType(); + method public java.lang.CharSequence getEditTitle(); + method public int getInputType(); method public android.content.Intent getIntent(); method public java.lang.CharSequence getTitle(); method public boolean hasMultilineDescription(); method public boolean hasNext(); method public boolean infoOnly(); method public boolean isChecked(); + method public boolean isDescriptionEditable(); + method public boolean isEditTitleUsed(); + method public boolean isEditable(); method public boolean isEnabled(); + method public boolean isFocusable(); method public void setChecked(boolean); + method public void setDescription(java.lang.CharSequence); + method public void setEditDescription(java.lang.CharSequence); + method public void setEditTitle(java.lang.CharSequence); method public void setEnabled(boolean); + method public void setFocusable(boolean); + method public void setTitle(java.lang.CharSequence); + field public static final long ACTION_ID_CANCEL = -5L; // 0xfffffffffffffffbL + field public static final long ACTION_ID_CONTINUE = -7L; // 0xfffffffffffffff9L + field public static final long ACTION_ID_CURRENT = -3L; // 0xfffffffffffffffdL + field public static final long ACTION_ID_FINISH = -6L; // 0xfffffffffffffffaL + field public static final long ACTION_ID_NEXT = -2L; // 0xfffffffffffffffeL + field public static final long ACTION_ID_NO = -9L; // 0xfffffffffffffff7L + field public static final long ACTION_ID_OK = -4L; // 0xfffffffffffffffcL + field public static final long ACTION_ID_YES = -8L; // 0xfffffffffffffff8L + field public static final int CHECKBOX_CHECK_SET_ID = -1; // 0xffffffff field public static final int DEFAULT_CHECK_SET_ID = 1; // 0x1 field public static final int NO_CHECK_SET = 0; // 0x0 - field public static final int NO_DRAWABLE = 0; // 0x0 } public static class GuidedAction.Builder { ctor public GuidedAction.Builder(); - method public android.support.v17.leanback.widget.GuidedAction build(); + method protected final void applyValues(android.support.v17.leanback.widget.GuidedAction); + method public final android.support.v17.leanback.widget.GuidedAction build(); method public android.support.v17.leanback.widget.GuidedAction.Builder checkSetId(int); method public android.support.v17.leanback.widget.GuidedAction.Builder checked(boolean); - method public android.support.v17.leanback.widget.GuidedAction.Builder description(java.lang.String); + method public android.support.v17.leanback.widget.GuidedAction.Builder constructCancel(android.content.Context); + method public android.support.v17.leanback.widget.GuidedAction.Builder constructContinue(android.content.Context); + method public android.support.v17.leanback.widget.GuidedAction.Builder constructFinish(android.content.Context); + method public android.support.v17.leanback.widget.GuidedAction.Builder constructNo(android.content.Context); + method public android.support.v17.leanback.widget.GuidedAction.Builder constructOK(android.content.Context); + method public android.support.v17.leanback.widget.GuidedAction.Builder constructYes(android.content.Context); + method public android.support.v17.leanback.widget.GuidedAction.Builder description(java.lang.CharSequence); + method public android.support.v17.leanback.widget.GuidedAction.Builder descriptionEditInputType(int); + method public android.support.v17.leanback.widget.GuidedAction.Builder descriptionEditable(boolean); + method public android.support.v17.leanback.widget.GuidedAction.Builder descriptionInputType(int); + method public android.support.v17.leanback.widget.GuidedAction.Builder editDescription(java.lang.CharSequence); + method public android.support.v17.leanback.widget.GuidedAction.Builder editInputType(int); + method public android.support.v17.leanback.widget.GuidedAction.Builder editTitle(java.lang.CharSequence); + method public android.support.v17.leanback.widget.GuidedAction.Builder editable(boolean); method public android.support.v17.leanback.widget.GuidedAction.Builder enabled(boolean); + method public android.support.v17.leanback.widget.GuidedAction.Builder focusable(boolean); method public android.support.v17.leanback.widget.GuidedAction.Builder hasNext(boolean); method public android.support.v17.leanback.widget.GuidedAction.Builder icon(android.graphics.drawable.Drawable); method public android.support.v17.leanback.widget.GuidedAction.Builder iconResourceId(int, android.content.Context); method public android.support.v17.leanback.widget.GuidedAction.Builder id(long); method public android.support.v17.leanback.widget.GuidedAction.Builder infoOnly(boolean); + method public android.support.v17.leanback.widget.GuidedAction.Builder inputType(int); method public android.support.v17.leanback.widget.GuidedAction.Builder intent(android.content.Intent); method public android.support.v17.leanback.widget.GuidedAction.Builder multilineDescription(boolean); - method public android.support.v17.leanback.widget.GuidedAction.Builder title(java.lang.String); + method public android.support.v17.leanback.widget.GuidedAction.Builder title(java.lang.CharSequence); + } + + public class GuidedActionEditText extends android.widget.EditText implements android.support.v17.leanback.widget.ImeKeyMonitor { + ctor public GuidedActionEditText(android.content.Context); + ctor public GuidedActionEditText(android.content.Context, android.util.AttributeSet); + ctor public GuidedActionEditText(android.content.Context, android.util.AttributeSet, int); + method public void setImeKeyListener(android.support.v17.leanback.widget.ImeKeyMonitor.ImeKeyListener); } public class GuidedActionsStylist implements android.support.v17.leanback.widget.FragmentAnimationProvider { ctor public GuidedActionsStylist(); method public android.support.v17.leanback.widget.VerticalGridView getActionsGridView(); - method public void onActivityEnter(java.util.List<android.animation.Animator>); - method public void onActivityExit(java.util.List<android.animation.Animator>); + method public int getItemViewType(android.support.v17.leanback.widget.GuidedAction); + method public boolean isButtonActions(); method public void onAnimateItemChecked(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean); method public void onAnimateItemFocused(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean); method public void onAnimateItemPressed(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean); + method public void onAnimateItemPressedCancelled(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder); + method public void onBindCheckMarkView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction); + method public void onBindChevronView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction); method public void onBindViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction); method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup); method public android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder onCreateViewHolder(android.view.ViewGroup); - method public void onFragmentEnter(java.util.List<android.animation.Animator>); - method public void onFragmentExit(java.util.List<android.animation.Animator>); - method public void onFragmentReenter(java.util.List<android.animation.Animator>); - method public void onFragmentReturn(java.util.List<android.animation.Animator>); + method public android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder onCreateViewHolder(android.view.ViewGroup, int); + method public void onDestroyView(); + method protected void onEditingModeChange(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction, boolean); + method public void onImeAppearing(java.util.List<android.animation.Animator>); + method public void onImeDisappearing(java.util.List<android.animation.Animator>); method public int onProvideItemLayoutId(); + method public int onProvideItemLayoutId(int); method public int onProvideLayoutId(); - field protected android.support.v17.leanback.widget.VerticalGridView mActionsGridView; - field protected android.view.View mMainView; - field protected android.view.View mSelectorView; + method public void setAsButtonActions(); + method public void setEditingMode(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction, boolean); + method protected void setupImeOptions(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction); + field public static final int VIEW_TYPE_DEFAULT = 0; // 0x0 } public static class GuidedActionsStylist.ViewHolder { @@ -878,8 +1107,13 @@ package android.support.v17.leanback.widget { method public android.widget.ImageView getChevronView(); method public android.view.View getContentView(); method public android.widget.TextView getDescriptionView(); + method public android.widget.EditText getEditableDescriptionView(); + method public android.widget.EditText getEditableTitleView(); + method public android.view.View getEditingView(); method public android.widget.ImageView getIconView(); method public android.widget.TextView getTitleView(); + method public boolean isInEditing(); + method public boolean isInEditingDescription(); field public final android.view.View view; } @@ -918,9 +1152,10 @@ package android.support.v17.leanback.widget { } public class ImageCardView extends android.support.v17.leanback.widget.BaseCardView { + ctor public ImageCardView(android.content.Context, int); + ctor public ImageCardView(android.content.Context, android.util.AttributeSet, int); ctor public ImageCardView(android.content.Context); ctor public ImageCardView(android.content.Context, android.util.AttributeSet); - ctor public ImageCardView(android.content.Context, android.util.AttributeSet, int); method public android.graphics.drawable.Drawable getBadgeImage(); method public java.lang.CharSequence getContentText(); method public android.graphics.drawable.Drawable getInfoAreaBackground(); @@ -937,6 +1172,19 @@ package android.support.v17.leanback.widget { method public void setMainImageDimensions(int, int); method public void setMainImageScaleType(android.widget.ImageView.ScaleType); method public void setTitleText(java.lang.CharSequence); + field public static final int CARD_TYPE_FLAG_CONTENT = 2; // 0x2 + field public static final int CARD_TYPE_FLAG_ICON_LEFT = 8; // 0x8 + field public static final int CARD_TYPE_FLAG_ICON_RIGHT = 4; // 0x4 + field public static final int CARD_TYPE_FLAG_IMAGE_ONLY = 0; // 0x0 + field public static final int CARD_TYPE_FLAG_TITLE = 1; // 0x1 + } + + public abstract interface ImeKeyMonitor { + method public abstract void setImeKeyListener(android.support.v17.leanback.widget.ImeKeyMonitor.ImeKeyListener); + } + + public static abstract interface ImeKeyMonitor.ImeKeyListener { + method public abstract boolean onKeyPreIme(android.widget.EditText, int, android.view.KeyEvent); } public final class ItemAlignmentFacet { @@ -1012,6 +1260,12 @@ package android.support.v17.leanback.widget { method public abstract void wrap(android.view.View, android.view.View); } + public class ItemBridgeAdapterShadowOverlayWrapper extends android.support.v17.leanback.widget.ItemBridgeAdapter.Wrapper { + ctor public ItemBridgeAdapterShadowOverlayWrapper(android.support.v17.leanback.widget.ShadowOverlayHelper); + method public android.view.View createWrapper(android.view.View); + method public void wrap(android.view.View, android.view.View); + } + public class ListRow extends android.support.v17.leanback.widget.Row { ctor public ListRow(android.support.v17.leanback.widget.HeaderItem, android.support.v17.leanback.widget.ObjectAdapter); ctor public ListRow(long, android.support.v17.leanback.widget.HeaderItem, android.support.v17.leanback.widget.ObjectAdapter); @@ -1035,6 +1289,7 @@ package android.support.v17.leanback.widget { ctor public ListRowPresenter(int, boolean); method public final boolean areChildRoundedCornersEnabled(); method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup); + method protected android.support.v17.leanback.widget.ShadowOverlayHelper.Options createShadowOverlayOptions(); method public final void enableChildRoundedCorners(boolean); method public int getExpandedRowHeight(); method public final int getFocusZoomFactor(); @@ -1044,12 +1299,14 @@ package android.support.v17.leanback.widget { method public final boolean getShadowEnabled(); method public final deprecated int getZoomFactor(); method public final boolean isFocusDimmerUsed(); + method public final boolean isKeepChildForeground(); method public boolean isUsingDefaultListSelectEffect(); method public final boolean isUsingDefaultSelectEffect(); method public boolean isUsingDefaultShadow(); method public boolean isUsingZOrder(android.content.Context); method public void setExpandedRowHeight(int); method public final void setHoverCardPresenterSelector(android.support.v17.leanback.widget.PresenterSelector); + method public final void setKeepChildForeground(boolean); method public void setRecycledPoolSize(android.support.v17.leanback.widget.Presenter, int); method public void setRowHeight(int); method public final void setShadowEnabled(boolean); @@ -1327,7 +1584,6 @@ package android.support.v17.leanback.widget { public abstract class RowPresenter extends android.support.v17.leanback.widget.Presenter { ctor public RowPresenter(); - method public boolean canDrawOutOfBounds(); method protected abstract android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup); method protected void dispatchItemSelectedListener(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean); method public void freeze(android.support.v17.leanback.widget.RowPresenter.ViewHolder, boolean); @@ -1387,6 +1643,7 @@ package android.support.v17.leanback.widget { ctor public SearchBar(android.content.Context, android.util.AttributeSet); ctor public SearchBar(android.content.Context, android.util.AttributeSet, int); method public void displayCompletions(java.util.List<java.lang.String>); + method public void displayCompletions(android.view.inputmethod.CompletionInfo[]); method public android.graphics.drawable.Drawable getBadgeDrawable(); method public java.lang.CharSequence getHint(); method public java.lang.String getTitle(); @@ -1443,15 +1700,14 @@ package android.support.v17.leanback.widget { field public int iconColor; } - public class ShadowOverlayContainer extends android.view.ViewGroup { + public class ShadowOverlayContainer extends android.widget.FrameLayout { ctor public ShadowOverlayContainer(android.content.Context); ctor public ShadowOverlayContainer(android.content.Context, android.util.AttributeSet); ctor public ShadowOverlayContainer(android.content.Context, android.util.AttributeSet, int); method public int getShadowType(); method public android.view.View getWrappedView(); method public deprecated void initialize(boolean, boolean); - method public void initialize(boolean, boolean, boolean); - method protected void onLayout(boolean, int, int, int, int); + method public deprecated void initialize(boolean, boolean, boolean); method public static void prepareParentForShadow(android.view.ViewGroup); method public void setOverlayColor(int); method public void setShadowFocusLevel(float); @@ -1466,6 +1722,48 @@ package android.support.v17.leanback.widget { field public static final int SHADOW_STATIC = 2; // 0x2 } + public final class ShadowOverlayHelper { + method public android.support.v17.leanback.widget.ShadowOverlayContainer createShadowOverlayContainer(android.content.Context); + method public int getShadowType(); + method public boolean needsOverlay(); + method public boolean needsRoundedCorner(); + method public boolean needsWrapper(); + method public void onViewCreated(android.view.View); + method public void prepareParentForShadow(android.view.ViewGroup); + method public static void setNoneWrapperOverlayColor(android.view.View, int); + method public static void setNoneWrapperShadowFocusLevel(android.view.View, float); + method public void setOverlayColor(android.view.View, int); + method public void setShadowFocusLevel(android.view.View, float); + method public static boolean supportsDynamicShadow(); + method public static boolean supportsForeground(); + method public static boolean supportsRoundedCorner(); + method public static boolean supportsShadow(); + field public static final int SHADOW_DYNAMIC = 3; // 0x3 + field public static final int SHADOW_NONE = 1; // 0x1 + field public static final int SHADOW_STATIC = 2; // 0x2 + } + + public static final class ShadowOverlayHelper.Builder { + ctor public ShadowOverlayHelper.Builder(); + method public android.support.v17.leanback.widget.ShadowOverlayHelper build(android.content.Context); + method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder keepForegroundDrawable(boolean); + method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsOverlay(boolean); + method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsRoundedCorner(boolean); + method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder needsShadow(boolean); + method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder options(android.support.v17.leanback.widget.ShadowOverlayHelper.Options); + method public android.support.v17.leanback.widget.ShadowOverlayHelper.Builder preferZOrder(boolean); + } + + public static final class ShadowOverlayHelper.Options { + ctor public ShadowOverlayHelper.Options(); + method public android.support.v17.leanback.widget.ShadowOverlayHelper.Options dynamicShadowZ(float, float); + method public final float getDynamicShadowFocusedZ(); + method public final float getDynamicShadowUnfocusedZ(); + method public final int getRoundedCornerRadius(); + method public android.support.v17.leanback.widget.ShadowOverlayHelper.Options roundedCornerRadius(int); + field public static final android.support.v17.leanback.widget.ShadowOverlayHelper.Options DEFAULT; + } + public final class SinglePresenterSelector extends android.support.v17.leanback.widget.PresenterSelector { ctor public SinglePresenterSelector(android.support.v17.leanback.widget.Presenter); method public android.support.v17.leanback.widget.Presenter getPresenter(java.lang.Object); @@ -1538,8 +1836,10 @@ package android.support.v17.leanback.widget { ctor public VerticalGridPresenter(int, boolean); method public final boolean areChildRoundedCornersEnabled(); method protected android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder createGridViewHolder(android.view.ViewGroup); + method protected android.support.v17.leanback.widget.ShadowOverlayHelper.Options createShadowOverlayOptions(); method public final void enableChildRoundedCorners(boolean); method public final int getFocusZoomFactor(); + method public final boolean getKeepChildForeground(); method public int getNumberOfColumns(); method public final android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener(); method public final android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener(); @@ -1551,6 +1851,8 @@ package android.support.v17.leanback.widget { method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object); method public final android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder onCreateViewHolder(android.view.ViewGroup); method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder); + method public void setEntranceTransitionState(android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder, boolean); + method public final void setKeepChildForeground(boolean); method public void setNumberOfColumns(int); method public final void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener); method public final void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener); diff --git a/v17/leanback/api21/android/support/v17/leanback/transition/FadeAndShortSlide.java b/v17/leanback/api21/android/support/v17/leanback/transition/FadeAndShortSlide.java new file mode 100644 index 0000000000..1762c1522f --- /dev/null +++ b/v17/leanback/api21/android/support/v17/leanback/transition/FadeAndShortSlide.java @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2015 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.transition; + +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.TimeInterpolator; +import android.transition.Fade; +import android.transition.Transition; +import android.transition.TransitionValues; +import android.transition.Visibility; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.DecelerateInterpolator; + +/** + * Execute horizontal slide of 1/4 width and fade (to workaround bug 23718734) + * @hide + */ +public class FadeAndShortSlide extends Visibility { + + private static final TimeInterpolator sDecelerate = new DecelerateInterpolator(); + // private static final TimeInterpolator sAccelerate = new AccelerateInterpolator(); + private static final String PROPNAME_SCREEN_POSITION = + "android:fadeAndShortSlideTransition:screenPosition"; + + private CalculateSlide mSlideCalculator = sCalculateEnd; + private Visibility mFade = new Fade(); + + private interface CalculateSlide { + + /** Returns the translation value for view when it goes out of the scene */ + float getGoneX(ViewGroup sceneRoot, View view, int[] position); + } + + private static final CalculateSlide sCalculateStart = new CalculateSlide() { + @Override + public float getGoneX(ViewGroup sceneRoot, View view, int[] position) { + final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; + final float x; + if (isRtl) { + x = view.getTranslationX() + sceneRoot.getWidth() / 4; + } else { + x = view.getTranslationX() - sceneRoot.getWidth() / 4; + } + return x; + } + }; + + private static final CalculateSlide sCalculateEnd = new CalculateSlide() { + @Override + public float getGoneX(ViewGroup sceneRoot, View view, int[] position) { + final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; + final float x; + if (isRtl) { + x = view.getTranslationX() - sceneRoot.getWidth() / 4; + } else { + x = view.getTranslationX() + sceneRoot.getWidth() / 4; + } + return x; + } + }; + + private static final CalculateSlide sCalculateBoth = new CalculateSlide() { + + @Override + public float getGoneX(ViewGroup sceneRoot, View view, int[] position) { + final int viewCenter = position[0] + view.getWidth() / 2; + sceneRoot.getLocationOnScreen(position); + final int sceneRootCenter = position[0] + sceneRoot.getWidth() / 2; + if (viewCenter < sceneRootCenter) { + return view.getTranslationX() - sceneRoot.getWidth() / 2; + } else { + return view.getTranslationX() + sceneRoot.getWidth() / 2; + } + } + }; + + public FadeAndShortSlide() { + this(Gravity.START); + } + + public FadeAndShortSlide(int slideEdge) { + setSlideEdge(slideEdge); + } + + @Override + public void setEpicenterCallback(EpicenterCallback epicenterCallback) { + super.setEpicenterCallback(epicenterCallback); + mFade.setEpicenterCallback(epicenterCallback); + } + + private void captureValues(TransitionValues transitionValues) { + View view = transitionValues.view; + int[] position = new int[2]; + view.getLocationOnScreen(position); + transitionValues.values.put(PROPNAME_SCREEN_POSITION, position); + } + + @Override + public void captureStartValues(TransitionValues transitionValues) { + super.captureStartValues(transitionValues); + mFade.captureStartValues(transitionValues); + captureValues(transitionValues); + } + + @Override + public void captureEndValues(TransitionValues transitionValues) { + super.captureEndValues(transitionValues); + mFade.captureEndValues(transitionValues); + captureValues(transitionValues); + } + + public void setSlideEdge(int slideEdge) { + switch (slideEdge) { + case Gravity.START: + mSlideCalculator = sCalculateStart; + break; + case Gravity.END: + mSlideCalculator = sCalculateEnd; + break; + case Gravity.START | Gravity.END: + mSlideCalculator = sCalculateBoth; + break; + default: + throw new IllegalArgumentException("Invalid slide direction"); + } + // SidePropagation propagation = new SidePropagation(); + // propagation.setSide(slideEdge); + // setPropagation(propagation); + } + + @Override + public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, + TransitionValues endValues) { + if (endValues == null) { + return null; + } + if (sceneRoot == view) { + // workaround b/25375640, avoid run animation on sceneRoot + return null; + } + int[] position = (int[]) endValues.values.get(PROPNAME_SCREEN_POSITION); + int left = position[0]; + float endX = view.getTranslationX(); + float startX = mSlideCalculator.getGoneX(sceneRoot, view, position); + final Animator slideAnimator = TranslationAnimationCreator.createAnimation(view, endValues, + left, startX, endX, sDecelerate, this); + final AnimatorSet set = new AnimatorSet(); + set.play(slideAnimator).with(mFade.onAppear(sceneRoot, view, startValues, endValues)); + + return set; + } + + @Override + public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, + TransitionValues endValues) { + if (startValues == null) { + return null; + } + if (sceneRoot == view) { + // workaround b/25375640, avoid run animation on sceneRoot + return null; + } + int[] position = (int[]) startValues.values.get(PROPNAME_SCREEN_POSITION); + int left = position[0]; + float startX = view.getTranslationX(); + float endX = mSlideCalculator.getGoneX(sceneRoot, view, position); + final Animator slideAnimator = TranslationAnimationCreator.createAnimation(view, + startValues, left, startX, endX, sDecelerate /* sAccelerate */, this); + final AnimatorSet set = new AnimatorSet(); + set.play(slideAnimator).with(mFade.onDisappear(sceneRoot, view, startValues, endValues)); + + return set; + } + + @Override + public Transition addListener(TransitionListener listener) { + mFade.addListener(listener); + return super.addListener(listener); + } + + @Override + public Transition removeListener(TransitionListener listener) { + mFade.removeListener(listener); + return super.removeListener(listener); + } + + @Override + public Transition clone() { + FadeAndShortSlide clone = null; + clone = (FadeAndShortSlide) super.clone(); + clone.mFade = (Visibility) mFade.clone(); + return clone; + } +} + diff --git a/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java b/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java index 00ebf4c44c..c5a33cb84a 100644 --- a/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java +++ b/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java @@ -14,8 +14,12 @@ package android.support.v17.leanback.transition; import android.R; +import android.app.Fragment; import android.content.Context; import android.transition.ChangeTransform; +import android.transition.Transition; +import android.view.View; +import android.view.ViewGroup; import android.view.Window; import android.view.animation.AnimationUtils; @@ -24,6 +28,24 @@ final class TransitionHelperApi21 { TransitionHelperApi21() { } + public static void setEnterTransition(android.app.Fragment fragment, Object transition) { + fragment.setEnterTransition((Transition)transition); + } + + public static void setExitTransition(android.app.Fragment fragment, Object transition) { + fragment.setExitTransition((Transition)transition); + } + + public static void setSharedElementEnterTransition(android.app.Fragment fragment, + Object transition) { + fragment.setSharedElementEnterTransition((Transition)transition); + } + + public static void addSharedElement(android.app.FragmentTransaction ft, + View view, String transitionName) { + ft.addSharedElement(view, transitionName); + } + public static Object getSharedElementEnterTransition(Window window) { return window.getSharedElementEnterTransition(); } @@ -63,4 +85,12 @@ final class TransitionHelperApi21 { public static Object createDefaultInterpolator(Context context) { return AnimationUtils.loadInterpolator(context, R.interpolator.fast_out_linear_in); } + + public static Object createFadeAndShortSlide(int edge) { + return new FadeAndShortSlide(edge); + } + + public static void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup) { + viewGroup.setTransitionGroup(transitionGroup); + } } diff --git a/v17/leanback/api21/android/support/v17/leanback/transition/TranslationAnimationCreator.java b/v17/leanback/api21/android/support/v17/leanback/transition/TranslationAnimationCreator.java new file mode 100644 index 0000000000..2cc35452cc --- /dev/null +++ b/v17/leanback/api21/android/support/v17/leanback/transition/TranslationAnimationCreator.java @@ -0,0 +1,128 @@ +package android.support.v17.leanback.transition; + +import android.support.v17.leanback.R; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.animation.TimeInterpolator; +import android.graphics.Path; +import android.transition.Transition; +import android.transition.TransitionValues; +import android.view.View; + +/** + * This class is used by Slide and Explode to create an animator that goes from the start position + * to the end position. It takes into account the canceled position so that it will not blink out or + * shift suddenly when the transition is interrupted. + * @hide + */ +class TranslationAnimationCreator { + + /** + * Creates an animator that can be used for x and/or y translations. When interrupted, it sets a + * tag to keep track of the position so that it may be continued from position. + * + * @param view The view being moved. This may be in the overlay for onDisappear. + * @param values The values containing the view in the view hierarchy. + * @param viewPosX The x screen coordinate of view + * @param startX The start translation x of view + * @param endX The end translation x of view + * @param interpolator The interpolator to use with this animator. + * @return An animator that moves from (startX, startY) to (endX, endY) unless there was a + * previous interruption, in which case it moves from the current position to (endX, + * endY). + */ + static Animator createAnimation(View view, TransitionValues values, int viewPosX, float startX, + float endX, TimeInterpolator interpolator, Transition transition) { + float terminalX = view.getTranslationX(); + Integer startPosition = (Integer) values.view.getTag(R.id.transitionPosition); + if (startPosition != null) { + startX = startPosition - viewPosX + terminalX; + } + // Initial position is at translation startX, startY, so position is offset by that + // amount + int startPosX = viewPosX + Math.round(startX - terminalX); + + view.setTranslationX(startX); + if (startX == endX) { + return null; + } + float y = view.getTranslationY(); + Path path = new Path(); + path.moveTo(startX, y); + path.lineTo(endX, y); + ObjectAnimator anim = + ObjectAnimator.ofFloat(view, View.TRANSLATION_X, View.TRANSLATION_Y, path); + + TransitionPositionListener listener = + new TransitionPositionListener(view, values.view, startPosX, terminalX); + transition.addListener(listener); + anim.addListener(listener); + anim.addPauseListener(listener); + anim.setInterpolator(interpolator); + return anim; + } + + private static class TransitionPositionListener extends AnimatorListenerAdapter + implements Transition.TransitionListener { + + private final View mViewInHierarchy; + private final View mMovingView; + private final int mStartX; + private Integer mTransitionPosition; + private float mPausedX; + private final float mTerminalX; + + private TransitionPositionListener(View movingView, View viewInHierarchy, int startX, + float terminalX) { + mMovingView = movingView; + mViewInHierarchy = viewInHierarchy; + mStartX = startX - Math.round(mMovingView.getTranslationX()); + mTerminalX = terminalX; + mTransitionPosition = (Integer) mViewInHierarchy.getTag(R.id.transitionPosition); + if (mTransitionPosition != null) { + mViewInHierarchy.setTag(R.id.transitionPosition, null); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + mTransitionPosition = Math.round(mStartX + mMovingView.getTranslationX()); + mViewInHierarchy.setTag(R.id.transitionPosition, mTransitionPosition); + } + + @Override + public void onAnimationEnd(Animator animator) {} + + @Override + public void onAnimationPause(Animator animator) { + mPausedX = mMovingView.getTranslationX(); + mMovingView.setTranslationX(mTerminalX); + } + + @Override + public void onAnimationResume(Animator animator) { + mMovingView.setTranslationX(mPausedX); + } + + @Override + public void onTransitionStart(Transition transition) {} + + @Override + public void onTransitionEnd(Transition transition) { + mMovingView.setTranslationX(mTerminalX); + } + + @Override + public void onTransitionCancel(Transition transition) {} + + @Override + public void onTransitionPause(Transition transition) {} + + @Override + public void onTransitionResume(Transition transition) {} + } + +} + diff --git a/v17/leanback/api21/android/support/v17/leanback/widget/RoundedRectHelperApi21.java b/v17/leanback/api21/android/support/v17/leanback/widget/RoundedRectHelperApi21.java index c7fa0f36e7..a013ba1989 100644 --- a/v17/leanback/api21/android/support/v17/leanback/widget/RoundedRectHelperApi21.java +++ b/v17/leanback/api21/android/support/v17/leanback/widget/RoundedRectHelperApi21.java @@ -13,31 +13,47 @@ */ package android.support.v17.leanback.widget; -import android.support.v17.leanback.R; -import android.graphics.Color; +import android.util.SparseArray; import android.graphics.Outline; -import android.graphics.drawable.GradientDrawable; import android.view.ViewOutlineProvider; import android.view.View; class RoundedRectHelperApi21 { - private static int sCornerRadius; + private static SparseArray<ViewOutlineProvider> sRoundedRectProvider; + private static final int MAX_CACHED_PROVIDER = 32; + + static final class RoundedRectOutlineProvider extends ViewOutlineProvider { + + private int mRadius; + + RoundedRectOutlineProvider(int radius) { + mRadius = radius; + } - private static final ViewOutlineProvider sOutlineProvider = new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { - if (sCornerRadius == 0) { - sCornerRadius = view.getResources().getDimensionPixelSize( - R.dimen.lb_rounded_rect_corner_radius); - } - outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), sCornerRadius); + outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mRadius); outline.setAlpha(1f); } }; - public static void setClipToRoundedOutline(View view, boolean clip) { - view.setOutlineProvider(clip ? sOutlineProvider : ViewOutlineProvider.BACKGROUND); + public static void setClipToRoundedOutline(View view, boolean clip, int roundedCornerRadius) { + if (clip) { + if (sRoundedRectProvider == null) { + sRoundedRectProvider = new SparseArray<ViewOutlineProvider>(); + } + ViewOutlineProvider provider = sRoundedRectProvider.get(roundedCornerRadius); + if (provider == null) { + provider = new RoundedRectOutlineProvider(roundedCornerRadius); + if (sRoundedRectProvider.size() < MAX_CACHED_PROVIDER) { + sRoundedRectProvider.put(roundedCornerRadius, provider); + } + } + view.setOutlineProvider(provider); + } else { + view.setOutlineProvider(ViewOutlineProvider.BACKGROUND); + } view.setClipToOutline(clip); } } diff --git a/v17/leanback/api21/android/support/v17/leanback/widget/ShadowHelperApi21.java b/v17/leanback/api21/android/support/v17/leanback/widget/ShadowHelperApi21.java index ab4d179183..66f7687daf 100644 --- a/v17/leanback/api21/android/support/v17/leanback/widget/ShadowHelperApi21.java +++ b/v17/leanback/api21/android/support/v17/leanback/widget/ShadowHelperApi21.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * Copyright (C) 2015 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 @@ -13,20 +13,15 @@ */ package android.support.v17.leanback.widget; -import android.support.v17.leanback.R; -import android.content.res.Resources; -import android.graphics.Color; import android.graphics.Outline; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.view.ViewGroup; import android.view.View; +import android.view.ViewGroup; import android.view.ViewOutlineProvider; class ShadowHelperApi21 { static class ShadowImpl { - ViewGroup mShadowContainer; + View mShadowContainer; float mNormalZ; float mFocusedZ; } @@ -41,9 +36,10 @@ class ShadowHelperApi21 { /* add shadows and return a implementation detail object */ public static Object addDynamicShadow( - ViewGroup shadowContainer, float unfocusedZ, float focusedZ, boolean roundedCorners) { - if (roundedCorners) { - RoundedRectHelperApi21.setClipToRoundedOutline(shadowContainer, true); + View shadowContainer, float unfocusedZ, float focusedZ, int roundedCornerRadius) { + if (roundedCornerRadius > 0) { + RoundedRectHelperApi21.setClipToRoundedOutline(shadowContainer, true, + roundedCornerRadius); } else { shadowContainer.setOutlineProvider(sOutlineProvider); } @@ -52,7 +48,6 @@ class ShadowHelperApi21 { impl.mNormalZ = unfocusedZ; impl.mFocusedZ = focusedZ; shadowContainer.setZ(impl.mNormalZ); - shadowContainer.setTransitionGroup(true); return impl; } diff --git a/v17/leanback/api23/android/support/v17/leanback/widget/ForegroundHelperApi23.java b/v17/leanback/api23/android/support/v17/leanback/widget/ForegroundHelperApi23.java new file mode 100644 index 0000000000..c4760d44aa --- /dev/null +++ b/v17/leanback/api23/android/support/v17/leanback/widget/ForegroundHelperApi23.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 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.widget; + +import android.support.v17.leanback.R; +import android.content.res.Resources; +import android.graphics.Color; +import android.graphics.Outline; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.view.ViewGroup; +import android.view.View; +import android.view.ViewOutlineProvider; + +class ForegroundHelperApi23 { + + public static Drawable getForeground(View view) { + return view.getForeground(); + } + + public static void setForeground(View view, Drawable drawable) { + view.setForeground(drawable); + } +} diff --git a/v17/leanback/build.gradle b/v17/leanback/build.gradle index 8dc79e8475..401a5c4100 100644 --- a/v17/leanback/build.gradle +++ b/v17/leanback/build.gradle @@ -19,8 +19,8 @@ android { sourceSets { main.manifest.srcFile 'AndroidManifest.xml' - main.java.srcDirs = ['common', 'jbmr2', 'kitkat', 'api21', 'src'] - main.aidl.srcDirs = ['common', 'jbmr2', 'kitkat', 'api21', 'src'] + main.java.srcDirs = ['common', 'jbmr2', 'kitkat', 'api21', 'api23', 'src'] + main.aidl.srcDirs = ['common', 'jbmr2', 'kitkat', 'api21', 'api23', 'src'] main.res.srcDirs = ['res'] androidTest.setRoot('tests') diff --git a/v17/leanback/common/android/support/v17/leanback/transition/TransitionListener.java b/v17/leanback/common/android/support/v17/leanback/transition/TransitionListener.java index 80f05ede61..6a4056ea78 100644 --- a/v17/leanback/common/android/support/v17/leanback/transition/TransitionListener.java +++ b/v17/leanback/common/android/support/v17/leanback/transition/TransitionListener.java @@ -19,10 +19,20 @@ package android.support.v17.leanback.transition; */ public class TransitionListener { + protected Object mImpl; + public void onTransitionStart(Object transition) { } public void onTransitionEnd(Object transition) { } + public void onTransitionCancel(Object transition) { + } + + public void onTransitionPause(Object transition) { + } + + public void onTransitionResume(Object transition) { + } } diff --git a/v17/leanback/generatev4.py b/v17/leanback/generatev4.py index 605e9a1581..1b60b09ef4 100755 --- a/v17/leanback/generatev4.py +++ b/v17/leanback/generatev4.py @@ -20,7 +20,7 @@ import sys print "Generate v4 fragment related code for leanback" cls = ['Background', 'Base', 'BaseRow', 'Browse', 'Details', 'Error', 'Headers', - 'PlaybackOverlay', 'Rows', 'Search', 'VerticalGrid', 'Branded'] + 'PlaybackOverlay', 'Rows', 'Search', 'VerticalGrid', 'Branded', 'GuidedStep'] for w in cls: print "copy {}Fragment to {}SupportFragment".format(w, w) @@ -31,10 +31,24 @@ for w in cls: outfile.write("/* This file is auto-generated from {}Fragment.java. DO NOT MODIFY. */\n\n".format(w)) for line in file: + line = line.replace('IS_FRAMEWORK_FRAGMENT = true', 'IS_FRAMEWORK_FRAGMENT = false'); for w in cls: line = line.replace('{}Fragment'.format(w), '{}SupportFragment'.format(w)) line = line.replace('android.app.Fragment', 'android.support.v4.app.Fragment') line = line.replace('android.app.Activity', 'android.support.v4.app.FragmentActivity') + line = line.replace('activity.getFragmentManager()', 'activity.getSupportFragmentManager()') + line = line.replace('Activity activity', 'FragmentActivity activity') + line = line.replace('(Activity', '(FragmentActivity') outfile.write(line) file.close() outfile.close() + +file = open('src/android/support/v17/leanback/app/PlaybackControlGlue.java', 'r') +outfile = open('src/android/support/v17/leanback/app/PlaybackControlSupportGlue.java', 'w') +outfile.write("/* This file is auto-generated from PlaybackControlGlue.java. DO NOT MODIFY. */\n\n") +for line in file: + line = line.replace('PlaybackControlGlue', 'PlaybackControlSupportGlue'); + line = line.replace('PlaybackOverlayFragment', 'PlaybackOverlaySupportFragment'); + outfile.write(line) +file.close() +outfile.close() diff --git a/v17/leanback/kitkat/android/support/v17/leanback/transition/TransitionHelperKitkat.java b/v17/leanback/kitkat/android/support/v17/leanback/transition/TransitionHelperKitkat.java index b4b6abe633..221b84acc0 100644 --- a/v17/leanback/kitkat/android/support/v17/leanback/transition/TransitionHelperKitkat.java +++ b/v17/leanback/kitkat/android/support/v17/leanback/transition/TransitionHelperKitkat.java @@ -185,9 +185,12 @@ final class TransitionHelperKitkat { ((Transition) transition).addTarget(targetView); } - static void setTransitionListener(Object transition, final TransitionListener listener) { + static void addTransitionListener(Object transition, final TransitionListener listener) { + if (listener == null) { + return; + } Transition t = (Transition) transition; - t.addListener(new Transition.TransitionListener() { + listener.mImpl = new Transition.TransitionListener() { @Override public void onTransitionStart(Transition transition) { @@ -196,10 +199,12 @@ final class TransitionHelperKitkat { @Override public void onTransitionResume(Transition transition) { + listener.onTransitionResume(transition); } @Override public void onTransitionPause(Transition transition) { + listener.onTransitionPause(transition); } @Override @@ -209,8 +214,19 @@ final class TransitionHelperKitkat { @Override public void onTransitionCancel(Transition transition) { + listener.onTransitionCancel(transition); } - }); + }; + t.addListener((Transition.TransitionListener) listener.mImpl); + } + + static void removeTransitionListener(Object transition, final TransitionListener listener) { + if (listener == null || listener.mImpl == null) { + return; + } + Transition t = (Transition) transition; + t.removeListener((Transition.TransitionListener) listener.mImpl); + listener.mImpl = null; } static void runTransition(Object scene, Object transition) { diff --git a/v17/leanback/project.properties b/v17/leanback/project.properties index 91d2b02460..b2ef7dccc5 100644 --- a/v17/leanback/project.properties +++ b/v17/leanback/project.properties @@ -11,5 +11,5 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-19 +target=android-23 android.library=true diff --git a/v17/leanback/res/animator/lb_guidance_entry.xml b/v17/leanback/res/animator/lb_guidance_entry.xml deleted file mode 100644 index e10d2ef0bc..0000000000 --- a/v17/leanback/res/animator/lb_guidance_entry.xml +++ /dev/null @@ -1,54 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2015 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. ---> -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:ordering="sequentially"> - - <set android:ordering="together"> - <objectAnimator - android:duration="@integer/lb_guidedstep_entry_animation_delay" - android:propertyName="translationX" - android:valueFrom="@dimen/lb_guidance_entry_translationX" - android:valueTo="@dimen/lb_guidance_entry_translationX" - android:valueType="floatType" /> - - <objectAnimator - android:duration="@integer/lb_guidedstep_entry_animation_delay" - android:propertyName="alpha" - android:valueFrom="0.0" - android:valueTo="0.0" - android:valueType="floatType" /> - </set> - - <set android:ordering="together"> - <objectAnimator - android:duration="@integer/lb_guidedstep_entry_animation_duration" - android:interpolator="@android:interpolator/decelerate_quad" - android:propertyName="translationX" - android:valueFrom="@dimen/lb_guidance_entry_translationX" - android:valueTo="0.0" - android:valueType="floatType" /> - - <objectAnimator - android:duration="@integer/lb_guidedstep_entry_animation_duration" - android:interpolator="@android:interpolator/decelerate_quad" - android:propertyName="alpha" - android:valueFrom="0.0" - android:valueTo="1.0" - android:valueType="floatType" /> - </set> - -</set> diff --git a/v17/leanback/res/animator/lb_guidedactions_entry.xml b/v17/leanback/res/animator/lb_guidedactions_entry.xml deleted file mode 100644 index ec6c6551eb..0000000000 --- a/v17/leanback/res/animator/lb_guidedactions_entry.xml +++ /dev/null @@ -1,53 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2015 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. ---> -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:ordering="sequentially"> - - <set android:ordering="together"> - <objectAnimator - android:duration="@integer/lb_guidedstep_entry_animation_delay" - android:propertyName="translationX" - android:valueFrom="@dimen/lb_guidedactions_entry_translationX" - android:valueTo="@dimen/lb_guidedactions_entry_translationX" - android:valueType="floatType" /> - - <objectAnimator - android:duration="@integer/lb_guidedstep_entry_animation_delay" - android:propertyName="alpha" - android:valueFrom="0.0" - android:valueTo="0.0" - android:valueType="floatType" /> - </set> - - <set android:ordering="together"> - <objectAnimator - android:duration="@integer/lb_guidedstep_entry_animation_duration" - android:interpolator="@android:interpolator/decelerate_quad" - android:propertyName="translationX" - android:valueFrom="@dimen/lb_guidedactions_entry_translationX" - android:valueTo="0.0" - android:valueType="floatType" /> - - <objectAnimator - android:duration="@integer/lb_guidedstep_entry_animation_duration" - android:interpolator="@android:interpolator/decelerate_quad" - android:propertyName="alpha" - android:valueFrom="0.0" - android:valueTo="1.0" - android:valueType="floatType" /> - </set> -</set>
\ No newline at end of file diff --git a/v17/leanback/res/animator/lb_guidedactions_selector_hide.xml b/v17/leanback/res/animator/lb_guidedactions_selector_hide.xml index f829eb3e89..e5dafb03b1 100644 --- a/v17/leanback/res/animator/lb_guidedactions_selector_hide.xml +++ b/v17/leanback/res/animator/lb_guidedactions_selector_hide.xml @@ -17,7 +17,6 @@ <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="@integer/lb_guidedactions_animation_duration" android:propertyName="alpha" - android:valueFrom="1.0" android:valueTo="0.0" android:interpolator="@animator/lb_decelerator_2" android:valueType="floatType" /> diff --git a/v17/leanback/res/animator/lb_guidedactions_selector_show.xml b/v17/leanback/res/animator/lb_guidedactions_selector_show.xml index e8d69e570a..fcfd9fa930 100644 --- a/v17/leanback/res/animator/lb_guidedactions_selector_show.xml +++ b/v17/leanback/res/animator/lb_guidedactions_selector_show.xml @@ -20,7 +20,6 @@ <objectAnimator android:duration="@integer/lb_guidedactions_animation_duration" android:propertyName="alpha" - android:valueFrom="0" android:valueTo="1.0" android:interpolator="@animator/lb_decelerator_2" android:valueType="floatType" /> diff --git a/v17/leanback/res/animator/lb_guidedactions_item_unchecked.xml b/v17/leanback/res/animator/lb_guidedstep_slide_down.xml index 86525c8520..b31421fd53 100644 --- a/v17/leanback/res/animator/lb_guidedactions_item_unchecked.xml +++ b/v17/leanback/res/animator/lb_guidedstep_slide_down.xml @@ -15,8 +15,8 @@ limitations under the License. --> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" - android:duration="@integer/lb_guidedactions_item_animation_duration" - android:propertyName="alpha" - android:valueFrom="1.0" + android:duration="@android:integer/config_shortAnimTime" + android:propertyName="translationY" + android:valueFrom="@dimen/lb_guidedstep_slide_ime_distance" android:valueTo="0.0" android:valueType="floatType" /> diff --git a/v17/leanback/res/animator/lb_guidedstep_slide_in_from_end.xml b/v17/leanback/res/animator/lb_guidedstep_slide_in_from_end.xml deleted file mode 100644 index 1dacdbc38c..0000000000 --- a/v17/leanback/res/animator/lb_guidedstep_slide_in_from_end.xml +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2015 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. ---> -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:ordering="together" > - - <objectAnimator - android:duration="@android:integer/config_longAnimTime" - android:propertyName="translationX" - android:valueFrom="@dimen/lb_guidedstep_slide_end_distance" - android:valueTo="0.0" - android:valueType="floatType" /> - - <objectAnimator - android:duration="@android:integer/config_longAnimTime" - android:propertyName="alpha" - android:valueFrom="0.0" - android:valueTo="1.0" - android:valueType="floatType" /> - -</set> diff --git a/v17/leanback/res/animator/lb_guidedstep_slide_in_from_start.xml b/v17/leanback/res/animator/lb_guidedstep_slide_in_from_start.xml deleted file mode 100644 index 3c01324f9a..0000000000 --- a/v17/leanback/res/animator/lb_guidedstep_slide_in_from_start.xml +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2015 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. ---> -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:ordering="together" > - - <objectAnimator - android:duration="@android:integer/config_longAnimTime" - android:propertyName="translationX" - android:valueFrom="@dimen/lb_guidedstep_slide_start_distance" - android:valueTo="0.0" - android:valueType="floatType" /> - - <objectAnimator - android:duration="@android:integer/config_longAnimTime" - android:propertyName="alpha" - android:valueFrom="0.0" - android:valueTo="1.0" - android:valueType="floatType" /> - -</set> diff --git a/v17/leanback/res/animator/lb_guidedstep_slide_out_to_end.xml b/v17/leanback/res/animator/lb_guidedstep_slide_out_to_end.xml deleted file mode 100644 index 879a0cf577..0000000000 --- a/v17/leanback/res/animator/lb_guidedstep_slide_out_to_end.xml +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2015 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. ---> -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:ordering="together" > - - <objectAnimator - android:duration="@android:integer/config_longAnimTime" - android:propertyName="translationX" - android:valueFrom="0.0" - android:valueTo="@dimen/lb_guidedstep_slide_end_distance" - android:valueType="floatType" /> - - <objectAnimator - android:duration="@android:integer/config_longAnimTime" - android:propertyName="alpha" - android:valueFrom="1.0" - android:valueTo="0.0" - android:valueType="floatType" /> - -</set> diff --git a/v17/leanback/res/animator/lb_guidedstep_slide_out_to_start.xml b/v17/leanback/res/animator/lb_guidedstep_slide_out_to_start.xml deleted file mode 100644 index 4c9af82bd2..0000000000 --- a/v17/leanback/res/animator/lb_guidedstep_slide_out_to_start.xml +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2015 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. ---> -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:ordering="together" > - - <objectAnimator - android:duration="@android:integer/config_longAnimTime" - android:propertyName="translationX" - android:valueFrom="0.0" - android:valueTo="@dimen/lb_guidedstep_slide_start_distance" - android:valueType="floatType" /> - - <objectAnimator - android:duration="@android:integer/config_longAnimTime" - android:propertyName="alpha" - android:valueFrom="1.0" - android:valueTo="0.0" - android:valueType="floatType" /> - -</set> diff --git a/v17/leanback/res/animator/lb_guidedactions_item_checked.xml b/v17/leanback/res/animator/lb_guidedstep_slide_up.xml index 463b9f7bf5..165fe18bca 100644 --- a/v17/leanback/res/animator/lb_guidedactions_item_checked.xml +++ b/v17/leanback/res/animator/lb_guidedstep_slide_up.xml @@ -15,8 +15,8 @@ limitations under the License. --> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" - android:duration="@integer/lb_guidedactions_item_animation_duration" - android:propertyName="alpha" + android:duration="@android:integer/config_shortAnimTime" + android:propertyName="translationY" android:valueFrom="0.0" - android:valueTo="1.0" + android:valueTo="@dimen/lb_guidedstep_slide_ime_distance" android:valueType="floatType" /> diff --git a/v17/leanback/res/drawable/lb_headers_right_fading.xml b/v17/leanback/res/drawable/lb_headers_right_fading.xml index 96794db5a5..b20c4e84d0 100644 --- a/v17/leanback/res/drawable/lb_headers_right_fading.xml +++ b/v17/leanback/res/drawable/lb_headers_right_fading.xml @@ -20,6 +20,6 @@ <gradient android:angle="0" android:startColor="#00000000" - android:endColor="@color/lb_default_brand_color" + android:endColor="?attr/defaultBrandColor" /> </shape> diff --git a/v17/leanback/res/layout/lb_fullwidth_details_overview.xml b/v17/leanback/res/layout/lb_fullwidth_details_overview.xml index 8dbb630b46..415bd82dea 100644 --- a/v17/leanback/res/layout/lb_fullwidth_details_overview.xml +++ b/v17/leanback/res/layout/lb_fullwidth_details_overview.xml @@ -34,7 +34,7 @@ android:layout_height="@dimen/lb_details_v2_card_height" android:layout_marginTop="@dimen/lb_details_v2_blank_height" android:clipToPadding="false" - android:foreground="#ffffff" + android:background="?attr/defaultBrandColor" android:elevation="@dimen/lb_details_overview_z" > @@ -48,6 +48,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/details_overview_actions_background" + android:background="?attr/defaultBrandColorDark" android:orientation="vertical" > <android.support.v17.leanback.widget.HorizontalGridView diff --git a/v17/leanback/res/layout/lb_guidedactions.xml b/v17/leanback/res/layout/lb_guidedactions.xml index 43617c9481..f2926d361b 100644 --- a/v17/leanback/res/layout/lb_guidedactions.xml +++ b/v17/leanback/res/layout/lb_guidedactions.xml @@ -16,20 +16,36 @@ --> <!-- Layout for the settings list fragment --> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" > - - <RelativeLayout - style="?attr/guidedActionsContainerStyle" > - - <FrameLayout - android:id="@+id/guidedactions_selector" - style="?attr/guidedActionsSelectorStyle" /> - + android:id="@+id/guidedactions_root" + android:transitionName="guidedactions_root" + android:transitionGroup="false" + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="match_parent"> + + <android.support.v17.leanback.widget.NonOverlappingView + android:id="@+id/guidedactions_list_background" + android:transitionName="guidedactions_list_background" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:visibility="gone" + android:background="?attr/guidedActionsBackgroundDark" /> + + <android.support.v17.leanback.widget.NonOverlappingFrameLayout + android:id="@+id/guidedactions_content" + android:transitionName="guidedactions_content" + android:transitionGroup="false" + android:layout_width="match_parent" + android:layout_height="match_parent"> <android.support.v17.leanback.widget.VerticalGridView + android:transitionGroup="true" android:id="@+id/guidedactions_list" style="?attr/guidedActionsListStyle" /> - </RelativeLayout> + <android.support.v17.leanback.widget.NonOverlappingView + android:id="@+id/guidedactions_selector" + android:transitionName="guidedactions_selector" + style="?attr/guidedActionsSelectorStyle" /> + </android.support.v17.leanback.widget.NonOverlappingFrameLayout> </RelativeLayout> diff --git a/v17/leanback/res/layout/lb_guidedactions_item.xml b/v17/leanback/res/layout/lb_guidedactions_item.xml index 4e414549e0..831c355a16 100644 --- a/v17/leanback/res/layout/lb_guidedactions_item.xml +++ b/v17/leanback/res/layout/lb_guidedactions_item.xml @@ -20,7 +20,7 @@ xmlns:tools="http://schemas.android.com/tools" style="?attr/guidedActionItemContainerStyle" > - <ImageView + <android.support.v17.leanback.widget.CheckableImageView android:id="@+id/guidedactions_item_checkmark" style="?attr/guidedActionItemCheckmarkStyle" tools:ignore="ContentDescription" /> @@ -34,13 +34,14 @@ android:id="@+id/guidedactions_item_content" style="?attr/guidedActionItemContentStyle" > - <TextView + <android.support.v17.leanback.widget.GuidedActionEditText android:id="@+id/guidedactions_item_title" style="?attr/guidedActionItemTitleStyle" /> - <TextView + <android.support.v17.leanback.widget.GuidedActionEditText android:id="@+id/guidedactions_item_description" style="?attr/guidedActionItemDescriptionStyle" /> + </android.support.v17.leanback.widget.NonOverlappingLinearLayout> <ImageView diff --git a/v17/leanback/res/layout/lb_guidedstep_background.xml b/v17/leanback/res/layout/lb_guidedstep_background.xml new file mode 100644 index 0000000000..08ea47d519 --- /dev/null +++ b/v17/leanback/res/layout/lb_guidedstep_background.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2015 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. +--> +<android.support.v17.leanback.widget.NonOverlappingView + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/guidedstep_background" + android:transitionName="guidedstep_background" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="?attr/guidedStepBackground" /> + diff --git a/v17/leanback/res/layout/lb_guidedstep_fragment.xml b/v17/leanback/res/layout/lb_guidedstep_fragment.xml index 6e0b7adf5b..a41d47d32b 100644 --- a/v17/leanback/res/layout/lb_guidedstep_fragment.xml +++ b/v17/leanback/res/layout/lb_guidedstep_fragment.xml @@ -15,21 +15,57 @@ limitations under the License. --> <!-- Layout for the frame of a 2 pane actions fragment. --> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/content_frame" +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/guidedstep_root" + android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent" > + <LinearLayout + android:id="@+id/content_frame" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="match_parent" > - <FrameLayout - android:id="@+id/content_fragment" - android:layout_width="@dimen/lb_guidedstep_guidance_section_width" - android:layout_height="match_parent" - android:layout_alignParentStart="true" /> + <android.support.v17.leanback.widget.NonOverlappingFrameLayout + android:id="@+id/content_fragment" + android:layout_toStartOf="@+id/action_fragment" + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="match_parent" + android:layout_alignParentStart="true" /> - <FrameLayout - android:id="@+id/action_fragment" - android:layout_width="@dimen/lb_guidedactions_section_width_with_shadow" - android:layout_height="match_parent" - android:layout_alignParentEnd="true" /> + <android.support.v17.leanback.widget.NonOverlappingFrameLayout + android:id="@+id/action_fragment_root" + android:transitionName="action_fragment_root" + android:transitionGroup="false" + android:orientation="horizontal" + android:clipToPadding="false" + android:clipChildren="false" + android:paddingStart="@dimen/lb_guidedactions_section_shadow_width" + android:layout_width="0dp" + android:layout_weight="?attr/guidedActionContentWidthWeight" + android:layout_height="match_parent" + android:layout_alignParentEnd="true"> -</RelativeLayout>
\ No newline at end of file + <android.support.v17.leanback.widget.NonOverlappingView + android:id="@+id/action_fragment_background" + android:transitionName="action_fragment_background" + android:orientation="horizontal" + android:outlineProvider="paddedBounds" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="?attr/guidedActionsBackground" + android:elevation="?attr/guidedActionsElevation" /> + + <android.support.v17.leanback.widget.NonOverlappingLinearLayout + android:id="@+id/action_fragment" + android:transitionName="action_fragment" + android:transitionGroup="false" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:elevation="?attr/guidedActionsElevation" /> + </android.support.v17.leanback.widget.NonOverlappingFrameLayout> + + </LinearLayout> +</FrameLayout>
\ No newline at end of file diff --git a/v17/leanback/res/layout/lb_image_card_view.xml b/v17/leanback/res/layout/lb_image_card_view.xml index 2261965ee1..1bc23f8846 100644 --- a/v17/leanback/res/layout/lb_image_card_view.xml +++ b/v17/leanback/res/layout/lb_image_card_view.xml @@ -15,59 +15,16 @@ limitations under the License. --> -<merge - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:lb="http://schemas.android.com/apk/res-auto"> +<merge xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:lb="http://schemas.android.com/apk/res-auto" > <ImageView android:id="@+id/main_image" - lb:layout_viewType="main" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:scaleType="centerCrop" - android:adjustViewBounds="true" - android:contentDescription="@null" /> - <android.support.v17.leanback.widget.NonOverlappingRelativeLayout - lb:layout_viewType="info" - android:id="@+id/info_field" - android:layout_width="match_parent" - android:layout_height="@dimen/lb_basic_card_info_height" - android:paddingStart="@dimen/lb_basic_card_info_padding_horizontal" - android:paddingEnd="@dimen/lb_basic_card_info_padding_horizontal" - android:paddingTop="@dimen/lb_basic_card_info_padding_top" - android:layout_centerHorizontal="true" > - <TextView - android:id="@+id/title_text" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_alignParentStart="true" - android:layout_marginBottom="@dimen/lb_basic_card_info_text_margin" - android:maxLines="1" - android:fontFamily="sans-serif-condensed" - android:textColor="@color/lb_basic_card_title_text_color" - android:textSize="@dimen/lb_basic_card_title_text_size" - android:ellipsize="end" /> - <TextView - android:id="@+id/content_text" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@id/title_text" - android:layout_alignParentStart="true" - android:layout_toStartOf="@+id/extra_badge" - android:maxLines="1" - android:fontFamily="sans-serif-condensed" - android:textColor="@color/lb_basic_card_content_text_color" - android:textSize="@dimen/lb_basic_card_content_text_size" - android:ellipsize="none" /> - <ImageView - android:id="@+id/extra_badge" - android:layout_width="@dimen/lb_basic_card_info_badge_size" - android:layout_height="@dimen/lb_basic_card_info_badge_size" - android:layout_marginStart="@dimen/lb_basic_card_info_badge_margin" - android:layout_alignBottom="@id/content_text" - android:layout_alignParentEnd="true" - android:scaleType="fitCenter" - android:visibility="gone" - android:contentDescription="@null" /> - </android.support.v17.leanback.widget.NonOverlappingRelativeLayout> -</merge> + style="?attr/lbImageCardViewImageStyle" /> + + <android.support.v17.leanback.widget.NonOverlappingRelativeLayout + android:id="@+id/info_field" + style="?attr/lbImageCardViewInfoAreaStyle"> + </android.support.v17.leanback.widget.NonOverlappingRelativeLayout> + +</merge>
\ No newline at end of file diff --git a/v17/leanback/res/layout/lb_image_card_view_themed_badge_left.xml b/v17/leanback/res/layout/lb_image_card_view_themed_badge_left.xml new file mode 100644 index 0000000000..35d2da655a --- /dev/null +++ b/v17/leanback/res/layout/lb_image_card_view_themed_badge_left.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2014 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. +--> + +<ImageView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/extra_badge" + android:layout_alignBottom="@+id/content_text" + android:layout_alignParentStart="true" + android:layout_marginEnd="@dimen/lb_basic_card_info_badge_margin" + style="?attr/lbImageCardViewBadgeStyle" />
\ No newline at end of file diff --git a/v17/leanback/res/layout/lb_image_card_view_themed_badge_right.xml b/v17/leanback/res/layout/lb_image_card_view_themed_badge_right.xml new file mode 100644 index 0000000000..02dd917244 --- /dev/null +++ b/v17/leanback/res/layout/lb_image_card_view_themed_badge_right.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2014 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. +--> + +<ImageView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/extra_badge" + android:layout_alignBottom="@+id/content_text" + android:layout_alignParentEnd="true" + android:layout_marginStart="@dimen/lb_basic_card_info_badge_margin" + style="?attr/lbImageCardViewBadgeStyle" />
\ No newline at end of file diff --git a/v17/leanback/res/layout/lb_card_color_overlay.xml b/v17/leanback/res/layout/lb_image_card_view_themed_content.xml index 45a40e176c..5592371532 100644 --- a/v17/leanback/res/layout/lb_card_color_overlay.xml +++ b/v17/leanback/res/layout/lb_image_card_view_themed_content.xml @@ -15,7 +15,6 @@ limitations under the License. --> -<View - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" /> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/content_text" + style="?attr/lbImageCardViewContentStyle" /> diff --git a/v17/leanback/res/layout/lb_image_card_view_themed_title.xml b/v17/leanback/res/layout/lb_image_card_view_themed_title.xml new file mode 100644 index 0000000000..67e2493887 --- /dev/null +++ b/v17/leanback/res/layout/lb_image_card_view_themed_title.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2014 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. +--> + +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/title_text" + style="?attr/lbImageCardViewTitleStyle" /> diff --git a/v17/leanback/res/layout/lb_search_fragment.xml b/v17/leanback/res/layout/lb_search_fragment.xml index 57a46b4041..25150d3587 100644 --- a/v17/leanback/res/layout/lb_search_fragment.xml +++ b/v17/leanback/res/layout/lb_search_fragment.xml @@ -18,8 +18,6 @@ limitations under the License. android:id="@+id/lb_search_frame" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingTop="@dimen/lb_search_bar_padding_top" - android:clipToPadding="false" android:clipChildren="false" > <FrameLayout @@ -28,6 +26,7 @@ limitations under the License. android:layout_height="match_parent"/> <android.support.v17.leanback.widget.SearchBar android:id="@+id/lb_search_bar" + android:layout_marginTop="@dimen/lb_search_bar_padding_top" android:layout_width="wrap_content" android:layout_height="wrap_content" android:clipChildren="false" diff --git a/v17/leanback/res/transition-v21/lb_browse_entrance_transition.xml b/v17/leanback/res/transition-v21/lb_browse_entrance_transition.xml index e26204bc81..9dd440b1d7 100644 --- a/v17/leanback/res/transition-v21/lb_browse_entrance_transition.xml +++ b/v17/leanback/res/transition-v21/lb_browse_entrance_transition.xml @@ -23,6 +23,6 @@ <slide android:duration="350" android:interpolator="@android:interpolator/linear_out_slow_in" - android:slideEdge="right"> + android:slideEdge="@integer/slideEdgeEnd"> </slide> </transitionSet>
\ No newline at end of file diff --git a/v17/leanback/res/transition-v21/lb_browse_return_transition.xml b/v17/leanback/res/transition-v21/lb_browse_return_transition.xml index 84ae9935b8..6d341b5eeb 100644 --- a/v17/leanback/res/transition-v21/lb_browse_return_transition.xml +++ b/v17/leanback/res/transition-v21/lb_browse_return_transition.xml @@ -18,7 +18,7 @@ <slide android:interpolator="@android:interpolator/fast_out_linear_in" android:duration="350" - android:slideEdge="left"> + android:slideEdge="@integer/slideEdgeStart"> <targets> <target android:targetId="@id/browse_headers_root" /> <target android:targetId="@id/title_orb" /> @@ -27,7 +27,7 @@ <slide android:interpolator="@android:interpolator/fast_out_linear_in" android:duration="350" - android:slideEdge="right"> + android:slideEdge="@integer/slideEdgeEnd"> <targets> <target android:excludeId="@+id/browse_headers_root" /> <target android:excludeId="@+id/title_orb" /> diff --git a/v17/leanback/res/transition-v21/lb_guidedstep_activity_enter.xml b/v17/leanback/res/transition-v21/lb_guidedstep_activity_enter.xml new file mode 100644 index 0000000000..5ee74ee028 --- /dev/null +++ b/v17/leanback/res/transition-v21/lb_guidedstep_activity_enter.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2015 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. +--> + +<transitionSet xmlns:android="http://schemas.android.com/apk/res/android" > + <fade + android:interpolator="@android:interpolator/fast_out_linear_in" + android:duration="@integer/lb_guidedstep_activity_background_fade_duration_ms"> + <targets> + <target android:targetId="@id/guidedstep_background" /> + </targets> + </fade> + <slide + android:interpolator="@android:interpolator/fast_out_linear_in" + android:duration="350" + android:slideEdge="@integer/slideEdgeStart"> + <targets> + <target android:targetId="@id/guidance_icon" /> + <target android:targetId="@id/guidance_title" /> + <target android:targetId="@id/guidance_breadcrumb" /> + <target android:targetId="@id/guidance_description" /> + </targets> + </slide> + <slide + android:interpolator="@android:interpolator/fast_out_linear_in" + android:duration="350" + android:slideEdge="@integer/slideEdgeEnd"> + <targets> + <target android:targetId="@id/action_fragment_background" /> + <target android:targetId="@id/guidedactions_list_background" /> + <target android:targetId="@id/guidedactions_content" /> + <target android:targetId="@id/guidedactions_list" /> + <target android:targetId="@id/guidedactions_selector" /> + <target android:targetId="@id/guidedactions_list_background2" /> + <target android:targetId="@id/guidedactions_content2" /> + <target android:targetId="@id/guidedactions_list2" /> + <target android:targetId="@id/guidedactions_selector2" /> + </targets> + </slide> +</transitionSet>
\ No newline at end of file diff --git a/v17/leanback/res/transition-v21/lb_vertical_grid_enter_transition.xml b/v17/leanback/res/transition-v21/lb_vertical_grid_enter_transition.xml new file mode 100644 index 0000000000..00466cbbde --- /dev/null +++ b/v17/leanback/res/transition-v21/lb_vertical_grid_enter_transition.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2015 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. +--> + +<transitionSet xmlns:android="http://schemas.android.com/apk/res/android" > + <fade + android:interpolator="@android:interpolator/linear_out_slow_in" + android:duration="150"/> +</transitionSet>
\ No newline at end of file diff --git a/v17/leanback/res/transition-v22/lb_browse_entrance_transition.xml b/v17/leanback/res/transition-v21/lb_vertical_grid_entrance_transition.xml index 2068c64458..ee06953119 100644 --- a/v17/leanback/res/transition-v22/lb_browse_entrance_transition.xml +++ b/v17/leanback/res/transition-v21/lb_vertical_grid_entrance_transition.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright (C) 2014 The Android Open Source Project + Copyright (C) 2015 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. @@ -16,13 +16,9 @@ --> <transitionSet xmlns:android="http://schemas.android.com/apk/res/android" > - <changeBounds - android:duration="350" - android:interpolator="@android:interpolator/linear_out_slow_in"> - </changeBounds> <slide android:duration="350" android:interpolator="@android:interpolator/linear_out_slow_in" - android:slideEdge="end"> + android:slideEdge="bottom"> </slide> </transitionSet>
\ No newline at end of file diff --git a/v17/leanback/res/transition-v22/lb_browse_return_transition.xml b/v17/leanback/res/transition-v21/lb_vertical_grid_return_transition.xml index e8dbee0cd6..edb3816f3b 100644 --- a/v17/leanback/res/transition-v22/lb_browse_return_transition.xml +++ b/v17/leanback/res/transition-v21/lb_vertical_grid_return_transition.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright (C) 2014 The Android Open Source Project + Copyright (C) 2015 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. @@ -18,26 +18,15 @@ <slide android:interpolator="@android:interpolator/fast_out_linear_in" android:duration="350" - android:slideEdge="start"> + android:slideEdge="bottom"> <targets> - <target android:targetId="@id/browse_headers_root" /> - <target android:targetId="@id/title_orb" /> - </targets> - </slide> - <slide - android:interpolator="@android:interpolator/fast_out_linear_in" - android:duration="350" - android:slideEdge="end"> - <targets> - <target android:excludeId="@+id/browse_headers_root" /> <target android:excludeId="@+id/title_orb" /> + <target android:excludeId="@+id/title_text" /> + <target android:excludeId="@+id/title_badge" /> </targets> </slide> <fade android:interpolator="@android:interpolator/fast_out_linear_in" android:duration="350"> - <targets> - <target android:excludeId="@+id/browse_headers_root" /> - </targets> </fade> </transitionSet>
\ No newline at end of file diff --git a/v17/leanback/res/values-af/strings.xml b/v17/leanback/res/values-af/strings.xml index c7109bbf9f..03104033a3 100644 --- a/v17/leanback/res/values-af/strings.xml +++ b/v17/leanback/res/values-af/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Deaktiveer hoë gehalte"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Aktiveer onderskrifte"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Deaktiveer onderskrifte"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Voltooi"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Gaan voort"</string> </resources> diff --git a/v17/leanback/res/values-am/strings.xml b/v17/leanback/res/values-am/strings.xml index d5bf0f5cc4..77d29936af 100644 --- a/v17/leanback/res/values-am/strings.xml +++ b/v17/leanback/res/values-am/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"ከፍተኛ ጥራትን አሰናክል"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"ዝግ የምስል ስር ጽሑፍ አጻጻፍን አንቃ"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"ዝግ የምስል ስር ጽሑፍ አጻጻፍን አሰናክል"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ጨርስ"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ቀጥል"</string> </resources> diff --git a/v17/leanback/res/values-ar/strings.xml b/v17/leanback/res/values-ar/strings.xml index 31f4d1a056..c52be7fa76 100644 --- a/v17/leanback/res/values-ar/strings.xml +++ b/v17/leanback/res/values-ar/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"تعطيل الجودة العالية"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"تمكين الترجمة المصاحبة"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"تعطيل الترجمة المصاحبة"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"إنهاء"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"متابعة"</string> </resources> diff --git a/v17/leanback/res/values-az-rAZ/strings.xml b/v17/leanback/res/values-az-rAZ/strings.xml index d1e685f8a7..cb558a02d4 100644 --- a/v17/leanback/res/values-az-rAZ/strings.xml +++ b/v17/leanback/res/values-az-rAZ/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Yüksək keyfiyyəti deaktiv edin"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Qapalı çəkilişi aktiv edin"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Qapalı çəkilişi deaktiv edin"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Bitir"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Davam edin"</string> </resources> diff --git a/v17/leanback/res/values-bg/strings.xml b/v17/leanback/res/values-bg/strings.xml index 74c01b61fd..6b16775bcc 100644 --- a/v17/leanback/res/values-bg/strings.xml +++ b/v17/leanback/res/values-bg/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Деактивиране на високото качество"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Активиране на субтитрите"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Деактивиране на субтитрите"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Край"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Напред"</string> </resources> diff --git a/v17/leanback/res/values-bn-rBD/strings.xml b/v17/leanback/res/values-bn-rBD/strings.xml index 4f0526cde0..04669bd30e 100644 --- a/v17/leanback/res/values-bn-rBD/strings.xml +++ b/v17/leanback/res/values-bn-rBD/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"উচ্চ গুণমান অক্ষম করুন"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"সাবটাইটেল সক্ষম করুন"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"সাবটাইটেল অক্ষম করুন"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"শেষ করুন"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"চালিয়ে যান"</string> </resources> diff --git a/v17/leanback/res/values-ca/strings.xml b/v17/leanback/res/values-ca/strings.xml index dbf20a89bb..6578f3bafe 100644 --- a/v17/leanback/res/values-ca/strings.xml +++ b/v17/leanback/res/values-ca/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Desactiva l\'alta qualitat"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Activa els subtítols tancats"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Desactiva els subtítols tancats"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finalitza"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continua"</string> </resources> diff --git a/v17/leanback/res/values-cs/strings.xml b/v17/leanback/res/values-cs/strings.xml index 13f96893e8..8ffb4f304d 100644 --- a/v17/leanback/res/values-cs/strings.xml +++ b/v17/leanback/res/values-cs/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Vypnout vysokou kvalitu"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Zapnout titulky"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Vypnout titulky"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Dokončit"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Pokračovat"</string> </resources> diff --git a/v17/leanback/res/values-da/strings.xml b/v17/leanback/res/values-da/strings.xml index 701138f970..87c507b3c4 100644 --- a/v17/leanback/res/values-da/strings.xml +++ b/v17/leanback/res/values-da/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Deaktiver høj kvalitet"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Aktivér undertekster"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Deaktiver undertekster"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Afslut"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Fortsæt"</string> </resources> diff --git a/v17/leanback/res/values-de/strings.xml b/v17/leanback/res/values-de/strings.xml index db0c3e03da..9d018c619a 100644 --- a/v17/leanback/res/values-de/strings.xml +++ b/v17/leanback/res/values-de/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Hohe Qualität deaktivieren"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Untertitel aktivieren"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Untertitel deaktivieren"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Fertigstellen"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Weiter"</string> </resources> diff --git a/v17/leanback/res/values-el/strings.xml b/v17/leanback/res/values-el/strings.xml index 6c4f603f21..310b6a9962 100644 --- a/v17/leanback/res/values-el/strings.xml +++ b/v17/leanback/res/values-el/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Απενεργοποίηση Υψηλής ποιότητας"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Ενεργοποίηση υποτίτλων"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Απενεργοποίηση υποτίτλων"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Τέλος"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Συνέχεια"</string> </resources> diff --git a/v17/leanback/res/values-en-rAU/strings.xml b/v17/leanback/res/values-en-rAU/strings.xml index ed22ccd781..0097135d1d 100644 --- a/v17/leanback/res/values-en-rAU/strings.xml +++ b/v17/leanback/res/values-en-rAU/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Disable High Quality"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Enable Closed Captioning"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Disable Closed Captioning"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finish"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continue"</string> </resources> diff --git a/v17/leanback/res/values-en-rGB/strings.xml b/v17/leanback/res/values-en-rGB/strings.xml index ed22ccd781..0097135d1d 100644 --- a/v17/leanback/res/values-en-rGB/strings.xml +++ b/v17/leanback/res/values-en-rGB/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Disable High Quality"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Enable Closed Captioning"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Disable Closed Captioning"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finish"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continue"</string> </resources> diff --git a/v17/leanback/res/values-en-rIN/strings.xml b/v17/leanback/res/values-en-rIN/strings.xml index ed22ccd781..0097135d1d 100644 --- a/v17/leanback/res/values-en-rIN/strings.xml +++ b/v17/leanback/res/values-en-rIN/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Disable High Quality"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Enable Closed Captioning"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Disable Closed Captioning"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finish"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continue"</string> </resources> diff --git a/v17/leanback/res/values-es-rUS/strings.xml b/v17/leanback/res/values-es-rUS/strings.xml index ab05f83ad1..8341a4dc86 100644 --- a/v17/leanback/res/values-es-rUS/strings.xml +++ b/v17/leanback/res/values-es-rUS/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Inhabilitar calidad alta"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Habilitar subtítulos"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Inhabilitar subtítulos"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finalizar"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuar"</string> </resources> diff --git a/v17/leanback/res/values-es/strings.xml b/v17/leanback/res/values-es/strings.xml index 9e6c526684..9f308c03e5 100644 --- a/v17/leanback/res/values-es/strings.xml +++ b/v17/leanback/res/values-es/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Inhabilitar alta calidad"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Habilitar subtítulos"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Inhabilitar subtítulos"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finalizar"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuar"</string> </resources> diff --git a/v17/leanback/res/values-et-rEE/strings.xml b/v17/leanback/res/values-et-rEE/strings.xml index cfda00e9cb..a97c385246 100644 --- a/v17/leanback/res/values-et-rEE/strings.xml +++ b/v17/leanback/res/values-et-rEE/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Keela kõrgkvaliteetne taasesitus"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Luba subtiitrid"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Keela subtiitrid"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Lõpeta"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Jätka"</string> </resources> diff --git a/v17/leanback/res/values-eu-rES/strings.xml b/v17/leanback/res/values-eu-rES/strings.xml index d9f9bf7a43..c22f172a51 100644 --- a/v17/leanback/res/values-eu-rES/strings.xml +++ b/v17/leanback/res/values-eu-rES/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Desgaitu kalitate handiko erreprodukzioa"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Gaitu azpitituluak"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Desgaitu azpitituluak"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Amaitu"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Jarraitu"</string> </resources> diff --git a/v17/leanback/res/values-fa/strings.xml b/v17/leanback/res/values-fa/strings.xml index bb615fcd7f..58e823f5bf 100644 --- a/v17/leanback/res/values-fa/strings.xml +++ b/v17/leanback/res/values-fa/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"غیرفعال کردن کیفیت بالا"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"فعال کردن زیرنویس"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"غیرفعال کردن زیرنویس"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"پایان"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ادامه"</string> </resources> diff --git a/v17/leanback/res/values-fi/strings.xml b/v17/leanback/res/values-fi/strings.xml index 92b5f12a44..0c55e6fea6 100644 --- a/v17/leanback/res/values-fi/strings.xml +++ b/v17/leanback/res/values-fi/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Poista korkea laatu käytöstä"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Ota tekstitys käyttöön"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Poista tekstitys käytöstä"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Valmis"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Jatka"</string> </resources> diff --git a/v17/leanback/res/values-fr-rCA/strings.xml b/v17/leanback/res/values-fr-rCA/strings.xml index bbd3eea0c3..b28beae5e6 100644 --- a/v17/leanback/res/values-fr-rCA/strings.xml +++ b/v17/leanback/res/values-fr-rCA/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Désactiver la lecture haute qualité"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Activer le sous-titrage"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Désactiver le sous-titrage"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Terminer"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuer"</string> </resources> diff --git a/v17/leanback/res/values-fr/strings.xml b/v17/leanback/res/values-fr/strings.xml index 0a3e6feab4..914bf08b7b 100644 --- a/v17/leanback/res/values-fr/strings.xml +++ b/v17/leanback/res/values-fr/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Désactiver la haute qualité"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Activer les sous-titres"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Désactiver les sous-titres"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Terminer"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuer"</string> </resources> diff --git a/v17/leanback/res/values-gl-rES/strings.xml b/v17/leanback/res/values-gl-rES/strings.xml index 717b994e88..62b3a2b9c1 100644 --- a/v17/leanback/res/values-gl-rES/strings.xml +++ b/v17/leanback/res/values-gl-rES/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Desactivar alta calidade"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Activar subtítulos"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Desactivar subtítulos"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finalizar"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuar"</string> </resources> diff --git a/v17/leanback/res/values-gu-rIN/strings.xml b/v17/leanback/res/values-gu-rIN/strings.xml index 2e4f30f50c..afec9da91e 100644 --- a/v17/leanback/res/values-gu-rIN/strings.xml +++ b/v17/leanback/res/values-gu-rIN/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"ઉચ્ચ ગુણવત્તા અક્ષમ કરો"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"ઉપશીર્ષક સક્ષમ કરો"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"વિગતવાર ઉપશીર્ષકોને અક્ષમ કરો"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"સમાપ્ત કરો"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ચાલુ રાખો"</string> </resources> diff --git a/v17/leanback/res/values-hi/strings.xml b/v17/leanback/res/values-hi/strings.xml index a926396977..c243c17dba 100644 --- a/v17/leanback/res/values-hi/strings.xml +++ b/v17/leanback/res/values-hi/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"उच्च गुणवत्ता अक्षम करें"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"उपशीर्षक सक्षम करें"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"उपशीर्षक अक्षम करें"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"समाप्त करें"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"जारी रखें"</string> </resources> diff --git a/v17/leanback/res/values-hr/strings.xml b/v17/leanback/res/values-hr/strings.xml index 974de0af77..bb6ebcc6f4 100644 --- a/v17/leanback/res/values-hr/strings.xml +++ b/v17/leanback/res/values-hr/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Onemogući visoku kvalitetu"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Omogući titlove"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Onemogući titlove"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Završi"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Nastavi"</string> </resources> diff --git a/v17/leanback/res/values-hu/strings.xml b/v17/leanback/res/values-hu/strings.xml index 427f1cd6fb..cfeb2ada2e 100644 --- a/v17/leanback/res/values-hu/strings.xml +++ b/v17/leanback/res/values-hu/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Jó minőségű lejátszás letiltása"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Feliratok engedélyezése"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Feliratok letiltása"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Befejezés"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Folytatás"</string> </resources> diff --git a/v17/leanback/res/values-hy-rAM/strings.xml b/v17/leanback/res/values-hy-rAM/strings.xml index 7e8112e183..1ac5dd8113 100644 --- a/v17/leanback/res/values-hy-rAM/strings.xml +++ b/v17/leanback/res/values-hy-rAM/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Անջատել բարձր որակը"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Միացնել խորագրերը"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Անջատել խորագրերը"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Վերջ"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Շարունակել"</string> </resources> diff --git a/v17/leanback/res/values-in/strings.xml b/v17/leanback/res/values-in/strings.xml index 2dca7d3b66..6569825cdb 100644 --- a/v17/leanback/res/values-in/strings.xml +++ b/v17/leanback/res/values-in/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Nonaktifkan Kualitas Tinggi"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Aktifkan Pembuatan Teks"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Nonaktifkan Pembuatan Teks"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Selesai"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Lanjutkan"</string> </resources> diff --git a/v17/leanback/res/values-is-rIS/strings.xml b/v17/leanback/res/values-is-rIS/strings.xml index c84a4c6751..830b11e3df 100644 --- a/v17/leanback/res/values-is-rIS/strings.xml +++ b/v17/leanback/res/values-is-rIS/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Slökkva á miklum gæðum"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Kveikja á skjátextum"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Slökkva á skjátextum"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Ljúka"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Halda áfram"</string> </resources> diff --git a/v17/leanback/res/values-it/strings.xml b/v17/leanback/res/values-it/strings.xml index 1b58e0cebd..2f0ca47f2e 100644 --- a/v17/leanback/res/values-it/strings.xml +++ b/v17/leanback/res/values-it/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Disattiva alta qualità"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Attiva sottotitoli"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Disattiva sottotitoli"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Fine"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continua"</string> </resources> diff --git a/v17/leanback/res/values-iw/strings.xml b/v17/leanback/res/values-iw/strings.xml index f10249843b..f2bda5839e 100644 --- a/v17/leanback/res/values-iw/strings.xml +++ b/v17/leanback/res/values-iw/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"השבת איכות גבוהה"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"הפעל כתוביות"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"השבת כתוביות"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"סיום"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"המשך"</string> </resources> diff --git a/v17/leanback/res/values-ja/strings.xml b/v17/leanback/res/values-ja/strings.xml index 802631ce6f..09faa8b32d 100644 --- a/v17/leanback/res/values-ja/strings.xml +++ b/v17/leanback/res/values-ja/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"高品質を無効にする"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"字幕を有効にする"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"字幕を無効にする"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"完了"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"続行"</string> </resources> diff --git a/v17/leanback/res/values-ka-rGE/strings.xml b/v17/leanback/res/values-ka-rGE/strings.xml index 70aeada5c3..ac9f4cd7c3 100644 --- a/v17/leanback/res/values-ka-rGE/strings.xml +++ b/v17/leanback/res/values-ka-rGE/strings.xml @@ -50,4 +50,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"მაღალი ხარისხის გამორთვა"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"დახურული წარწერების ჩართვა"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"დახურული წარწერების გაუქმება"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"დასრულება"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"გაგრძელება"</string> </resources> diff --git a/v17/leanback/res/values-kk-rKZ/strings.xml b/v17/leanback/res/values-kk-rKZ/strings.xml index 9ed6ce240e..380695b06b 100644 --- a/v17/leanback/res/values-kk-rKZ/strings.xml +++ b/v17/leanback/res/values-kk-rKZ/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Жоғары сапаны өшіру"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Жасырын титрлерді қосу"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Жасырын титрлерді өшіру"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Аяқтау"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Жалғастыру"</string> </resources> diff --git a/v17/leanback/res/values-km-rKH/strings.xml b/v17/leanback/res/values-km-rKH/strings.xml index 7874af2f57..60f90e5a7b 100644 --- a/v17/leanback/res/values-km-rKH/strings.xml +++ b/v17/leanback/res/values-km-rKH/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"បិទគុណភាពខ្ពស់"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"បើកការដាក់ចំណងដែលបានបិទ"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"បិទការដាក់ចំណងដែលបានបិទ"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"បញ្ចប់"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"បន្ត"</string> </resources> diff --git a/v17/leanback/res/values-kn-rIN/strings.xml b/v17/leanback/res/values-kn-rIN/strings.xml index 196b154fcb..18206ed3f5 100644 --- a/v17/leanback/res/values-kn-rIN/strings.xml +++ b/v17/leanback/res/values-kn-rIN/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"ಹೆಚ್ಚು ಗುಣಮಟ್ಟವನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"ಮುಚ್ಚಿದ ಶೀರ್ಷಿಕೆಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"ಮುಚ್ಚಿದ ಶೀರ್ಷಿಕೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ಪೂರ್ಣಗೊಳಿಸು"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ಮುಂದುವರಿಸು"</string> </resources> diff --git a/v17/leanback/res/values-ko/strings.xml b/v17/leanback/res/values-ko/strings.xml index c244dbfbc7..e262b1c6fb 100644 --- a/v17/leanback/res/values-ko/strings.xml +++ b/v17/leanback/res/values-ko/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"고화질 사용 중지"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"자막 사용 설정"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"자막 사용 중지"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"완료"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"계속"</string> </resources> diff --git a/v17/leanback/res/values-ky-rKG/strings.xml b/v17/leanback/res/values-ky-rKG/strings.xml index 4ddb284364..74cc841465 100644 --- a/v17/leanback/res/values-ky-rKG/strings.xml +++ b/v17/leanback/res/values-ky-rKG/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Жогорку сапатты өчүрүү"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Жабык субтитрлерди иштетүү"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Жабык субтитрлерди өчүрүү"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Бүтүрүү"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Улантуу"</string> </resources> diff --git a/v17/leanback/res/drawable/lb_guidedactions_item_checkmark.xml b/v17/leanback/res/values-ldrtl/integers.xml index ec7903bf24..250523d3d1 100644 --- a/v17/leanback/res/drawable/lb_guidedactions_item_checkmark.xml +++ b/v17/leanback/res/values-ldrtl/integers.xml @@ -14,13 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. --> -<shape xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="oval" > +<resources> - <size - android:height="@dimen/lb_guidedactions_item_checkmark_diameter" - android:width="@dimen/lb_guidedactions_item_checkmark_diameter" /> + <!-- Gravity.RIGHT --> + <integer name="slideEdgeStart">5</integer> + <!-- Gravity.LEFT --> + <integer name="slideEdgeEnd">3</integer> - <solid android:color="@color/lb_tv_white" /> - -</shape> +</resources>
\ No newline at end of file diff --git a/v17/leanback/res/values-lo-rLA/strings.xml b/v17/leanback/res/values-lo-rLA/strings.xml index 35f519b098..d919e9341a 100644 --- a/v17/leanback/res/values-lo-rLA/strings.xml +++ b/v17/leanback/res/values-lo-rLA/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"ປິດນຳໃຊ້ການຫຼິ້ນດ້ວຍຄຸນນະພາບສູງ"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"ເປີດນຳໃຊ້ຄຳບັນຍາຍແບບປິດ"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"ປິດນຳໃຊ້ຄຳບັນຍາຍແບບປິດ"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ສໍາເລັດ"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ສືບຕໍ່"</string> </resources> diff --git a/v17/leanback/res/values-lt/strings.xml b/v17/leanback/res/values-lt/strings.xml index 6ca2bab0c2..415fc25be7 100644 --- a/v17/leanback/res/values-lt/strings.xml +++ b/v17/leanback/res/values-lt/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Išjungti aukštą kokybę"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Įgalinti subtitrus"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Išjungti subtitrus"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Baigti"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Tęsti"</string> </resources> diff --git a/v17/leanback/res/values-lv/strings.xml b/v17/leanback/res/values-lv/strings.xml index 7d3bc2be88..3979e43d1c 100644 --- a/v17/leanback/res/values-lv/strings.xml +++ b/v17/leanback/res/values-lv/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Atspējot augstas kvalitātes vienumu atskaņošanu"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Iespējot slēgtos parakstus"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Atspējot slēgtos parakstus"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Pabeigt"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Turpināt"</string> </resources> diff --git a/v17/leanback/res/values-mk-rMK/strings.xml b/v17/leanback/res/values-mk-rMK/strings.xml index 75666e0491..ccf6d62674 100644 --- a/v17/leanback/res/values-mk-rMK/strings.xml +++ b/v17/leanback/res/values-mk-rMK/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Оневозможи висок квалитет"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Овозможи затворено објаснување"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Оневозможи затворено објаснување"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Заврши"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Продолжи"</string> </resources> diff --git a/v17/leanback/res/values-ml-rIN/strings.xml b/v17/leanback/res/values-ml-rIN/strings.xml index b900f091d0..356b0e93c2 100644 --- a/v17/leanback/res/values-ml-rIN/strings.xml +++ b/v17/leanback/res/values-ml-rIN/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"ഉയർന്ന നിലവാരം പ്രവർത്തനരഹിതമാക്കുക"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"അടച്ച അടിക്കുറിപ്പ് നൽകൽ പ്രവർത്തനക്ഷമമാക്കുക"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"അടച്ച അടിക്കുറിപ്പ് നൽകൽ പ്രവർത്തനരഹിതമാക്കുക"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"പൂര്ത്തിയാക്കുക"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"തുടരുക"</string> </resources> diff --git a/v17/leanback/res/values-mn-rMN/strings.xml b/v17/leanback/res/values-mn-rMN/strings.xml index e4a8fcd8ac..a7a640fd8b 100644 --- a/v17/leanback/res/values-mn-rMN/strings.xml +++ b/v17/leanback/res/values-mn-rMN/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Өндөр чанарыг идэвхгүйжүүлэх"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Текст тайлбарыг идэвхжүүлэх"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Текст тайлбарыг идэвхгүйжүүлэх"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Дуусгах"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Үргэлжлүүлэх"</string> </resources> diff --git a/v17/leanback/res/values-mr-rIN/strings.xml b/v17/leanback/res/values-mr-rIN/strings.xml index 11748ecf5b..b9b568aacf 100644 --- a/v17/leanback/res/values-mr-rIN/strings.xml +++ b/v17/leanback/res/values-mr-rIN/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"उच्च गुणवत्ता अक्षम करा"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"उपशीर्षके सक्षम करा"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"उपशीर्षके अक्षम करा"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"समाप्त"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"सुरू ठेवा"</string> </resources> diff --git a/v17/leanback/res/values-ms-rMY/strings.xml b/v17/leanback/res/values-ms-rMY/strings.xml index c073e43509..0a5f8bd30e 100644 --- a/v17/leanback/res/values-ms-rMY/strings.xml +++ b/v17/leanback/res/values-ms-rMY/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Lumpuhkan Kualiti Tinggi"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Dayakan Kapsyen Tertutup"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Lumpuhkan Kapsyen Tertutup"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Selesai"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Teruskan"</string> </resources> diff --git a/v17/leanback/res/values-my-rMM/strings.xml b/v17/leanback/res/values-my-rMM/strings.xml index 2efaf7fc67..1b3cdeee48 100644 --- a/v17/leanback/res/values-my-rMM/strings.xml +++ b/v17/leanback/res/values-my-rMM/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"အရည်အသွေးကောင်းအား ပိတ်ထားရန်"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"စာတမ်းထိုး ဖွင့်ရန်"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"စာတမ်းထိုးအား ပိတ်ထားရန်"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ပြီးပြီ"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ဆက်လုပ်ရန်"</string> </resources> diff --git a/v17/leanback/res/values-nb/strings.xml b/v17/leanback/res/values-nb/strings.xml index f5ab2e16ad..c58544d756 100644 --- a/v17/leanback/res/values-nb/strings.xml +++ b/v17/leanback/res/values-nb/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Deaktiver høy kvalitet"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Aktivér teksting"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Deaktiver teksting"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Fullfør"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Fortsett"</string> </resources> diff --git a/v17/leanback/res/values-ne-rNP/strings.xml b/v17/leanback/res/values-ne-rNP/strings.xml index c399985fb2..54c9285697 100644 --- a/v17/leanback/res/values-ne-rNP/strings.xml +++ b/v17/leanback/res/values-ne-rNP/strings.xml @@ -48,4 +48,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"उच्च गुणस्तर असक्षम"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"बन्द क्याप्सनहरु सक्षम"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"बन्द क्याप्सनहरु असक्षम"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"समाप्त गर्नुहोस्"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"जारी राख्नुहोस्"</string> </resources> diff --git a/v17/leanback/res/values-nl/strings.xml b/v17/leanback/res/values-nl/strings.xml index 1925e1483b..941aa79bd8 100644 --- a/v17/leanback/res/values-nl/strings.xml +++ b/v17/leanback/res/values-nl/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Hoge kwaliteit uitschakelen"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Ondertiteling inschakelen"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Ondertiteling uitschakelen"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Voltooien"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Doorgaan"</string> </resources> diff --git a/v17/leanback/res/values-pa-rIN/strings.xml b/v17/leanback/res/values-pa-rIN/strings.xml index f9c7c4b259..f1976574ca 100644 --- a/v17/leanback/res/values-pa-rIN/strings.xml +++ b/v17/leanback/res/values-pa-rIN/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"ਉੱਚ ਗੁਣਵੱਤਾ ਨੂੰ ਅਸਮਰੱਥ ਬਣਾਓ"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"ਬੰਦ ਕੈਪਸ਼ਨਿੰਗ ਸਮਰੱਥ ਬਣਾਓ"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"ਬੰਦ ਕੈਪਸ਼ਨਿੰਗ ਅਸਮਰੱਥ ਬਣਾਓ"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ਖ਼ਤਮ"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ਜਾਰੀ ਰੱਖੋ"</string> </resources> diff --git a/v17/leanback/res/values-pl/strings.xml b/v17/leanback/res/values-pl/strings.xml index 18b8bf67c8..7598c73c22 100644 --- a/v17/leanback/res/values-pl/strings.xml +++ b/v17/leanback/res/values-pl/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Wyłącz wysoką jakość"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Włącz napisy"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Wyłącz napisy"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Zakończ"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Dalej"</string> </resources> diff --git a/v17/leanback/res/values-pt-rBR/strings.xml b/v17/leanback/res/values-pt-rBR/strings.xml index 79b9d402ea..12c9533c68 100644 --- a/v17/leanback/res/values-pt-rBR/strings.xml +++ b/v17/leanback/res/values-pt-rBR/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Desativar alta qualidade"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Ativar closed captioning"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Desativar closed captioning"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Concluir"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuar"</string> </resources> diff --git a/v17/leanback/res/values-pt-rPT/strings.xml b/v17/leanback/res/values-pt-rPT/strings.xml index f269712c2f..7b12f5e6e1 100644 --- a/v17/leanback/res/values-pt-rPT/strings.xml +++ b/v17/leanback/res/values-pt-rPT/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Desativar alta qualidade"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Ativar legendas"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Desativar legendas"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Concluir"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuar"</string> </resources> diff --git a/v17/leanback/res/values-pt/strings.xml b/v17/leanback/res/values-pt/strings.xml index 79b9d402ea..12c9533c68 100644 --- a/v17/leanback/res/values-pt/strings.xml +++ b/v17/leanback/res/values-pt/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Desativar alta qualidade"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Ativar closed captioning"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Desativar closed captioning"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Concluir"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuar"</string> </resources> diff --git a/v17/leanback/res/values-ro/strings.xml b/v17/leanback/res/values-ro/strings.xml index cb6aa4aeb4..b4f9ee3e6d 100644 --- a/v17/leanback/res/values-ro/strings.xml +++ b/v17/leanback/res/values-ro/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Dezactivează calitatea înaltă"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Activează subtitrările"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Dezactivează subtitrările"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finalizați"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuați"</string> </resources> diff --git a/v17/leanback/res/values-ru/strings.xml b/v17/leanback/res/values-ru/strings.xml index fdb453b33c..864054aa14 100644 --- a/v17/leanback/res/values-ru/strings.xml +++ b/v17/leanback/res/values-ru/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Отключить высокое качество."</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Включить субтитры."</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Отключить субтитры."</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Готово"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Далее"</string> </resources> diff --git a/v17/leanback/res/values-si-rLK/strings.xml b/v17/leanback/res/values-si-rLK/strings.xml index e5c0cf481e..3db129389f 100644 --- a/v17/leanback/res/values-si-rLK/strings.xml +++ b/v17/leanback/res/values-si-rLK/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"උපරිම ගුණත්වය අබල කරන ලදි"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"වැසුණු ශිර්ෂ කිරීම සබල කරන ලදි"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"වැසුණු ශිර්ෂ කිරීම අබල කරන ලදි"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"අවසානය"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"දිගටම කර ගෙන යන්න"</string> </resources> diff --git a/v17/leanback/res/values-sk/strings.xml b/v17/leanback/res/values-sk/strings.xml index 74a9044e5e..a317d18479 100644 --- a/v17/leanback/res/values-sk/strings.xml +++ b/v17/leanback/res/values-sk/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Zakázať médiá vo vysokej kvalite"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Zapnúť skryté titulky"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Vypnúť skryté titulky"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Dokončiť"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Pokračovať"</string> </resources> diff --git a/v17/leanback/res/values-sl/strings.xml b/v17/leanback/res/values-sl/strings.xml index b5d0e1d54f..7b169521a5 100644 --- a/v17/leanback/res/values-sl/strings.xml +++ b/v17/leanback/res/values-sl/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Onemogoči visoko kakovost"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Omogoči podnapise"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Onemogoči podnapise"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Dokončaj"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Naprej"</string> </resources> diff --git a/v17/leanback/res/values-sq-rAL/strings.xml b/v17/leanback/res/values-sq-rAL/strings.xml index 28c313f6f1..723b90cfb9 100644 --- a/v17/leanback/res/values-sq-rAL/strings.xml +++ b/v17/leanback/res/values-sq-rAL/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Çaktivizo \"Cilësinë e lartë\""</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Aktivizo titrat"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Çaktivizo titrat me sekuencë kohore"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Përfundo"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Vazhdo"</string> </resources> diff --git a/v17/leanback/res/values-sr/strings.xml b/v17/leanback/res/values-sr/strings.xml index bb5c32d02a..df6c6b1530 100644 --- a/v17/leanback/res/values-sr/strings.xml +++ b/v17/leanback/res/values-sr/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Онемогући висок квалитет"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Омогући титлове"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Онемогући титлове"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Доврши"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Настави"</string> </resources> diff --git a/v17/leanback/res/values-sv/strings.xml b/v17/leanback/res/values-sv/strings.xml index 1a8e757137..9b874ca9f1 100644 --- a/v17/leanback/res/values-sv/strings.xml +++ b/v17/leanback/res/values-sv/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Inaktivera hög kvalitet"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Aktivera textning"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Inaktivera textning"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Slutför"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Fortsätt"</string> </resources> diff --git a/v17/leanback/res/values-sw/strings.xml b/v17/leanback/res/values-sw/strings.xml index 0ad04d1a25..53ef95ae5e 100644 --- a/v17/leanback/res/values-sw/strings.xml +++ b/v17/leanback/res/values-sw/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Zima Ubora wa Juu"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Washa manukuu"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Zima manukuu"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Kamilisha"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Endelea"</string> </resources> diff --git a/v17/leanback/res/values-ta-rIN/strings.xml b/v17/leanback/res/values-ta-rIN/strings.xml index 9472522b06..1cc2eea9de 100644 --- a/v17/leanback/res/values-ta-rIN/strings.xml +++ b/v17/leanback/res/values-ta-rIN/strings.xml @@ -20,7 +20,7 @@ limitations under the License. <string name="orb_search_action" msgid="5651268540267663887">"செயலைத் தேடுக"</string> <string name="lb_search_bar_hint" msgid="8325490927970116252">"தேடு"</string> <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"தேட, பேசவும்"</string> - <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> ஐத் தேடுக"</string> + <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> இல் தேடுக"</string> <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"<xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g> ஐத் தேட, பேசவும்"</string> <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string> <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string> @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"உயர் தரத்தை முடக்கு"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"விரிவான வசனங்களை இயக்கு"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"விரிவான வசனங்களை முடக்கு"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"முடி"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"தொடர்க"</string> </resources> diff --git a/v17/leanback/res/values-te-rIN/strings.xml b/v17/leanback/res/values-te-rIN/strings.xml index f71e8cb771..32d311d7fd 100644 --- a/v17/leanback/res/values-te-rIN/strings.xml +++ b/v17/leanback/res/values-te-rIN/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"అధిక నాణ్యతను నిలిపివేయి"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"సంవృత శీర్షికలను ప్రారంభించు"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"సంవృత శీర్షికలను నిలిపివేయి"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ముగించు"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"కొనసాగించు"</string> </resources> diff --git a/v17/leanback/res/values-th/strings.xml b/v17/leanback/res/values-th/strings.xml index 093a529280..d3eb2a3ba5 100644 --- a/v17/leanback/res/values-th/strings.xml +++ b/v17/leanback/res/values-th/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"ปิดใช้คุณภาพสูง"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"เปิดใช้คำบรรยาย"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"ปิดใช้คำบรรยาย"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"เสร็จสิ้น"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ต่อไป"</string> </resources> diff --git a/v17/leanback/res/values-tl/strings.xml b/v17/leanback/res/values-tl/strings.xml index a536298741..f50b4d15ec 100644 --- a/v17/leanback/res/values-tl/strings.xml +++ b/v17/leanback/res/values-tl/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"I-disable ang Mataas na Kalidad"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"I-enable ang Paglalagay ng Subtitle"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"I-disable ang Paglalagay ng Subtitle"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Tapusin"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Magpatuloy"</string> </resources> diff --git a/v17/leanback/res/values-tr/strings.xml b/v17/leanback/res/values-tr/strings.xml index 800436c4ad..814cb295ab 100644 --- a/v17/leanback/res/values-tr/strings.xml +++ b/v17/leanback/res/values-tr/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Yüksek Kalitede Oynatmayı Devre Dışı Bırak"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Altyazıları Etkinleştir"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Altyazıları Devre Dışı Bırak"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Son"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Devam"</string> </resources> diff --git a/v17/leanback/res/values-uk/strings.xml b/v17/leanback/res/values-uk/strings.xml index 79b2782089..a38db308b2 100644 --- a/v17/leanback/res/values-uk/strings.xml +++ b/v17/leanback/res/values-uk/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Вимкнути високу якість"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Увімкнути субтитри"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Вимкнути субтитри"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Закінчити"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Продовжити"</string> </resources> diff --git a/v17/leanback/res/values-ur-rPK/strings.xml b/v17/leanback/res/values-ur-rPK/strings.xml index b670251479..666cf7191a 100644 --- a/v17/leanback/res/values-ur-rPK/strings.xml +++ b/v17/leanback/res/values-ur-rPK/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"اعلی معیار کو غیر فعال کریں"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"سب ٹائٹلز کو فعال کریں"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"سب ٹائٹلز کو غیر فعال کریں"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"مکمل کریں"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"جاری رکھیں"</string> </resources> diff --git a/v17/leanback/res/values-uz-rUZ/strings.xml b/v17/leanback/res/values-uz-rUZ/strings.xml index c7612449a8..d81d8dea8b 100644 --- a/v17/leanback/res/values-uz-rUZ/strings.xml +++ b/v17/leanback/res/values-uz-rUZ/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Yuqori sifatni o‘chirib qo‘yish"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Taglavhalarni yoqish"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Taglavhalarni o‘chirib qo‘yish"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Tugatish"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Davom etish"</string> </resources> diff --git a/v17/leanback/res/values-v19/themes.xml b/v17/leanback/res/values-v19/themes.xml index 53befec16b..74e0e2e931 100644 --- a/v17/leanback/res/values-v19/themes.xml +++ b/v17/leanback/res/values-v19/themes.xml @@ -20,6 +20,7 @@ <item name="playbackProgressPrimaryColor">@color/lb_playback_progress_color_no_theme</item> <item name="playbackControlsIconHighlightColor">@color/lb_playback_icon_highlight_no_theme</item> <item name="defaultBrandColor">@color/lb_default_brand_color</item> + <item name="defaultBrandColorDark">@color/lb_default_brand_color_dark</item> <item name="android:windowOverscan">true</item> <item name="guidedStepTheme">@style/Theme.Leanback.GuidedStep</item> diff --git a/v17/leanback/res/values-v21/themes.xml b/v17/leanback/res/values-v21/themes.xml index 3b48dae00e..1072b2bce6 100644 --- a/v17/leanback/res/values-v21/themes.xml +++ b/v17/leanback/res/values-v21/themes.xml @@ -21,6 +21,8 @@ <item name="playbackControlsIconHighlightColor">?android:attr/colorAccent</item> <item name="defaultBrandColor">?android:attr/colorPrimary</item> <item name="android:colorPrimary">@color/lb_default_brand_color</item> + <item name="defaultBrandColorDark">?android:attr/colorPrimaryDark</item> + <item name="android:colorPrimaryDark">@color/lb_default_brand_color_dark</item> <item name="android:windowOverscan">true</item> <item name="guidedStepTheme">@style/Theme.Leanback.GuidedStep</item> diff --git a/v17/leanback/res/values-v22/integers.xml b/v17/leanback/res/values-v22/integers.xml new file mode 100644 index 0000000000..fdd7792e88 --- /dev/null +++ b/v17/leanback/res/values-v22/integers.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2015 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. +--> +<resources> + + <!-- Gravity.START --> + <integer name="slideEdgeStart">0x800003</integer> + <!-- Gravity.END --> + <integer name="slideEdgeEnd">0x800005</integer> + +</resources>
\ No newline at end of file diff --git a/v17/leanback/res/values-vi/strings.xml b/v17/leanback/res/values-vi/strings.xml index baf1c44dc8..881734b9d5 100644 --- a/v17/leanback/res/values-vi/strings.xml +++ b/v17/leanback/res/values-vi/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Tắt chế độ chất lượng cao"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Bật phụ đề"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Tắt phụ đề"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Hoàn tất"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Tiếp tục"</string> </resources> diff --git a/v17/leanback/res/values-zh-rCN/strings.xml b/v17/leanback/res/values-zh-rCN/strings.xml index 17c52b6a67..fc3fa103ca 100644 --- a/v17/leanback/res/values-zh-rCN/strings.xml +++ b/v17/leanback/res/values-zh-rCN/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"关闭高画质模式"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"开启字幕"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"关闭字幕"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"完成"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"继续"</string> </resources> diff --git a/v17/leanback/res/values-zh-rHK/strings.xml b/v17/leanback/res/values-zh-rHK/strings.xml index 5e87989b17..7cba4b58c3 100644 --- a/v17/leanback/res/values-zh-rHK/strings.xml +++ b/v17/leanback/res/values-zh-rHK/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"停用高畫質"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"啟用字幕"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"停用字幕"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"完成"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"繼續"</string> </resources> diff --git a/v17/leanback/res/values-zh-rTW/strings.xml b/v17/leanback/res/values-zh-rTW/strings.xml index 67efc4026d..dcca2db98f 100644 --- a/v17/leanback/res/values-zh-rTW/strings.xml +++ b/v17/leanback/res/values-zh-rTW/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"停用高品質播放"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"啟用字幕"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"停用字幕"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"完成"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"繼續"</string> </resources> diff --git a/v17/leanback/res/values-zu/strings.xml b/v17/leanback/res/values-zu/strings.xml index f17455de39..f4c589d2b1 100644 --- a/v17/leanback/res/values-zu/strings.xml +++ b/v17/leanback/res/values-zu/strings.xml @@ -46,4 +46,6 @@ limitations under the License. <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Khubaza ikhwalithi ephezulu"</string> <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Nika amandla imibhalo engezansi"</string> <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Khubaza imihbalo engezansi"</string> + <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Qeda"</string> + <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Qhubeka"</string> </resources> diff --git a/v17/leanback/res/values/attrs.xml b/v17/leanback/res/values/attrs.xml index e7da321249..90f010ae87 100644 --- a/v17/leanback/res/values/attrs.xml +++ b/v17/leanback/res/values/attrs.xml @@ -49,6 +49,10 @@ </declare-styleable> <declare-styleable name="lbBaseCardView"> + <!-- Defines the background of card --> + <attr name="cardForeground" format="reference|color"/> + <!-- Defines the background of card --> + <attr name="cardBackground" format="reference|color"/> <!-- Defines the type of the card layout --> <attr name="cardType" format="enum"> <!-- A simple card layout with a single layout region. --> @@ -116,7 +120,23 @@ </declare-styleable> <declare-styleable name="lbImageCardView"> + <!-- Deprecated. Use 'lbImageCardViewInfoAreaStyle' instead. --> <attr name="infoAreaBackground" format="reference|color"/> + <!-- Use these attributes to override a ImageCardView's component style. --> + <attr name="lbImageCardViewImageStyle" format="reference" /> + <attr name="lbImageCardViewTitleStyle" format="reference" /> + <attr name="lbImageCardViewContentStyle" format="reference" /> + <attr name="lbImageCardViewBadgeStyle" format="reference" /> + <attr name="lbImageCardViewInfoAreaStyle" format="reference" /> + <!-- Defines what components the ImageCardView will use. --> + <attr name="lbImageCardViewType"> + <flag name="Title" value="1" /> + <flag name="Content" value="2" /> + <flag name="IconOnRight" value="4" /> + <flag name="IconOnLeft" value="8" /> + <!-- Only display the main image. --> + <flag name="ImageOnly" value="0" /> + </attr> </declare-styleable> <declare-styleable name="lbSearchOrbView"> @@ -258,6 +278,12 @@ b) SDK < 21: set the brand color explicitly via defaultBrandColor, or programatically. --> <attr name="defaultBrandColor" format="reference|color" /> + <!-- Default dark brand color used for the background of certain leanback visual elements + such as the actions background. If your app runs on: + a) SDK 21+: set colorPrimaryDark, used by the leanback launcher and elsewhere, and defaultBrandColorDark will inherit it. + b) SDK < 21: set the brand color explicitly via defaultBrandColorDark, or programatically. + --> + <attr name="defaultBrandColorDark" format="reference|color" /> <!-- Default colors --> <attr name="defaultSearchColor" format="reference|color" /> @@ -292,27 +318,18 @@ Theme attribute used to inspect theme inheritance. --> <attr name="guidedStepThemeFlag" format="boolean" /> - <!-- Theme attribute for the animation used when a guided step element is animated in on - fragment stack push. Default is {@link - android.support.v17.leanback.R.animator#lb_guidedstep_slide_in_from_end}. --> - <attr name="guidedStepEntryAnimation" format="reference" /> - <!-- Theme attribute for the animation used when a guided step element is animated out on - fragment stack push. Default is {@link - android.support.v17.leanback.R.animator#lb_guidedstep_slide_out_to_start}. --> - <attr name="guidedStepExitAnimation" format="reference" /> - <!-- Theme attribute for the animation used when a guided step element is animated in on - fragment stack pop. Default is {@link - android.support.v17.leanback.R.animator#lb_guidedstep_slide_in_from_start}. --> - <attr name="guidedStepReentryAnimation" format="reference" /> - <!-- Theme attribute for the animation used when a guided step element is animated out on - fragment stack pop. Default is {@link - android.support.v17.leanback.R.animator#lb_guidedstep_slide_out_to_end}. --> - <attr name="guidedStepReturnAnimation" format="reference" /> - - <!-- Theme attribute for the animation used when the guidance is animated in at activity - start. Default is {@link android.support.v17.leanback.R.animator#lb_guidance_entry}. - --> - <attr name="guidanceEntryAnimation" format="reference" /> + <!-- Theme attribute of background drawable used by GuidedStepFragment. --> + <attr name="guidedStepBackground" format="reference|color" /> + + <!-- Theme attribute for the animation used when a guided step element is animated in + response to the IME appearing. Default is {@link + android.support.v17.leanback.R.animator#lb_guidedstep_slide_up}. --> + <attr name="guidedStepImeAppearingAnimation" format="reference" /> + <!-- Theme attribute for the animation used when a guided step element is animated in + response to the IME disappearing. Default is {@link + android.support.v17.leanback.R.animator#lb_guidedstep_slide_down}. --> + <attr name="guidedStepImeDisappearingAnimation" format="reference" /> + <!-- Theme attribute for the style of the main container in a GuidanceStylist. Default is {@link android.support.v17.leanback.R.style#Widget_Leanback_GuidanceContainerStyle}.--> <attr name="guidanceContainerStyle" format="reference" /> @@ -329,10 +346,6 @@ {@link android.support.v17.leanback.R.style#Widget_Leanback_GuidanceIconStyle}. --> <attr name="guidanceIconStyle" format="reference" /> - <!-- Theme attribute for the animation used in a GuidedActionsPresenter when the actions - list is animated in at activity start. Default is {@link - android.support.v17.leanback.R.animator#lb_guidedactions_entry}. --> - <attr name="guidedActionsEntryAnimation" format="reference" /> <!-- Theme attribute for the animation used in a GuidedActionsPresenter when the action selector is animated in at activity start. Default is {@link android.support.v17.leanback.R.animator#lb_guidedactions_selector_show}. --> @@ -341,12 +354,22 @@ selector is animated in at activity start. Default is {@link android.support.v17.leanback.R.animator#lb_guidedactions_selector_hide}. --> <attr name="guidedActionsSelectorHideAnimation" format="reference" /> - <!-- Theme attribute for the style of the container in a GuidedActionsPresenter. Default is - {@link android.support.v17.leanback.R.style#Widget_Leanback_GuidedActionsContainerStyle}. --> - <attr name="guidedActionsContainerStyle" format="reference" /> <!-- Theme attribute for the style of the item selector in a GuidedActionsPresenter. Default is {@link android.support.v17.leanback.R.style#Widget_Leanback_GuidedActionsSelectorStyle}. --> <attr name="guidedActionsSelectorStyle" format="reference" /> + + <!-- Theme attribute for the shadow elevation of GuidedActions. Default is + {@link android.support.v17.leanback.R.dimen#lb_guidedactions_elevation}.--> + <attr name="guidedActionsElevation" format="dimension|reference" /> + + <!-- Theme attribute for the background of GuidedActions. Default is + {@link android.support.v17.leanback.R.color#lb_guidedactions_background}.--> + <attr name="guidedActionsBackground" format="reference" /> + + <!-- Theme attribute for the dark version background of GuidedActions. Default is + {@link android.support.v17.leanback.R.color#lb_guidedactions_background_dark}.--> + <attr name="guidedActionsBackgroundDark" format="reference" /> + <!-- Theme attribute for the style of the list in a GuidedActionsPresenter. Default is {@link android.support.v17.leanback.R.style#Widget_Leanback_GuidedActionsListStyle}.--> <attr name="guidedActionsListStyle" format="reference" /> @@ -381,14 +404,6 @@ <attr name="guidedActionItemChevronStyle" format="reference" /> <!-- Theme attribute for the animation used in a GuidedActionsPresenter when an action - is checked. Default is {@link - android.support.v17.leanback.R.animator#lb_guidedactions_item_checked}. --> - <attr name="guidedActionCheckedAnimation" format="reference" /> - <!-- Theme attribute for the animation used in a GuidedActionsPresenter when an action - is unchecked. Default is {@link - android.support.v17.leanback.R.animator#lb_guidedactions_item_unchecked}. --> - <attr name="guidedActionUncheckedAnimation" format="reference" /> - <!-- Theme attribute for the animation used in a GuidedActionsPresenter when an action is pressed. Default is {@link android.support.v17.leanback.R.animator#lb_guidedactions_item_pressed}. --> <attr name="guidedActionPressedAnimation" format="reference" /> @@ -404,14 +419,12 @@ decoration when its action is disabled. Default is {@link android.support.v17.leanback.R.string#lb_guidedactions_item_disabled_chevron_alpha}. --> <attr name="guidedActionDisabledChevronAlpha" format="reference" /> - <!-- Theme attribute used in a GuidedActionsPresenter for the width of the text area of - a single action when there is an icon present. Default is {@link - android.support.v17.leanback.R.dimen#lb_guidedactions_item_text_width}. --> - <attr name="guidedActionContentWidth" format="reference" /> - <!-- Theme attribute used in a GuidedActionsPresenter for the width of the text area of - a single action when there is no icon present. Default is {@link - android.support.v17.leanback.R.dimen#lb_guidedactions_item_text_width_no_icon}. --> - <attr name="guidedActionContentWidthNoIcon" format="reference" /> + <!-- Theme attribute used for the weight of actions. Default is {@link + android.support.v17.leanback.R.string#lb_guidedactions_width_weight}. --> + <attr name="guidedActionContentWidthWeight" format="reference" /> + <!-- Theme attribute used for the weight of actions when there are two panels. Default is {@link + android.support.v17.leanback.R.string#lb_guidedactions_width_weight_two_panels}. --> + <attr name="guidedActionContentWidthWeightTwoPanels" format="reference" /> <!-- Theme attribute used in a GuidedActionsPresenter for the max lines of the title text view when the action's isMultilineDescription is set to false. Default is {@link android.support.v17.leanback.R.integer#lb_guidedactions_item_title_min_lines}. --> @@ -431,4 +444,4 @@ </declare-styleable> -</resources> +</resources>
\ No newline at end of file diff --git a/v17/leanback/res/values/colors.xml b/v17/leanback/res/values/colors.xml index e63c58b0f4..858ace53c3 100644 --- a/v17/leanback/res/values/colors.xml +++ b/v17/leanback/res/values/colors.xml @@ -52,6 +52,7 @@ <color name="lb_basic_card_content_text_color">#B3EEEEEE</color> <color name="lb_default_brand_color">#FF455A64</color> + <color name="lb_default_brand_color_dark">#FF222D32</color> <color name="lb_default_search_color">#FFFFAA3F</color> <color name="lb_control_button_color">#66EEEEEE</color> @@ -71,6 +72,7 @@ <!-- refactor naming here --> <color name="lb_guidedactions_background">#FF111111</color> + <color name="lb_guidedactions_background_dark">#FF080808</color> <color name="lb_guidedactions_selector_color">#26FFFFFF</color> <color name="lb_guidedactions_item_unselected_text_color">#FFF1F1F1</color> <!-- end refactor naming --> diff --git a/v17/leanback/res/values/dimens.xml b/v17/leanback/res/values/dimens.xml index 275612e453..053c7e0229 100644 --- a/v17/leanback/res/values/dimens.xml +++ b/v17/leanback/res/values/dimens.xml @@ -180,7 +180,7 @@ <!-- Search Fragment --> - <dimen name="lb_search_browse_rows_align_top">120dp</dimen> + <dimen name="lb_search_browse_rows_align_top">147dp</dimen> <dimen name="lb_search_browse_row_padding_start">56dp</dimen> <dimen name="lb_search_orb_size">52dp</dimen> @@ -203,6 +203,7 @@ <dimen name="lb_basic_card_info_height">52dp</dimen> <dimen name="lb_basic_card_info_height_no_content">34dp</dimen> <dimen name="lb_basic_card_info_padding_top">7dp</dimen> + <dimen name="lb_basic_card_info_padding_bottom">8dp</dimen> <dimen name="lb_basic_card_info_padding_horizontal">11dp</dimen> <dimen name="lb_basic_card_info_text_margin">1dp</dimen> <dimen name="lb_basic_card_title_text_size">14sp</dimen> @@ -223,19 +224,22 @@ <dimen name="lb_rounded_rect_corner_radius">2dp</dimen> <!-- GuidedStepFragment --> - <dimen name="lb_guidedstep_guidance_section_width">576dp</dimen> <dimen name="lb_guidedstep_slide_start_distance">-200dp</dimen> <dimen name="lb_guidedstep_slide_end_distance">200dp</dimen> + <dimen name="lb_guidedstep_slide_ime_distance">-100dp</dimen> <dimen name="lb_guidance_entry_translationX">-120dp</dimen> <dimen name="lb_guidedactions_entry_translationX">384dp</dimen> - <dimen name="lb_guidedactions_section_width">384dp</dimen> - <dimen name="lb_guidedactions_section_width_with_shadow">400dp</dimen> + <item name="lb_guidedactions_width_weight" format="float" type="string">0.666666667</item> + <item name="lb_guidedactions_width_weight_two_panels" format="float" type="string">1</item> + <dimen name="lb_guidedactions_section_shadow_width">16dp</dimen> <dimen name="lb_guidedactions_elevation">12dp</dimen> <dimen name="lb_guidedactions_selector_min_height">8dp</dimen> <dimen name="lb_guidedactions_vertical_padding">12dp</dimen> + <item name="lb_guidedactions_item_disabled_text_alpha" format="float" type="string">0.25</item> + <item name="lb_guidedactions_item_disabled_description_text_alpha" format="float" type="string">0.25</item> <item name="lb_guidedactions_item_unselected_text_alpha" format="float" type="string">1.00</item> <item name="lb_guidedactions_item_unselected_description_text_alpha" format="float" type="string">0.50</item> <item name="lb_guidedactions_item_enabled_chevron_alpha" format="float" type="string">1.00</item> @@ -244,10 +248,10 @@ <dimen name="lb_guidedactions_item_text_width">248dp</dimen> <dimen name="lb_guidedactions_item_text_width_no_icon">284dp</dimen> <dimen name="lb_guidedactions_item_min_height">64dp</dimen> - <dimen name="lb_guidedactions_item_start_padding">20dp</dimen> + <dimen name="lb_guidedactions_item_start_padding">28dp</dimen> <dimen name="lb_guidedactions_item_end_padding">28dp</dimen> <dimen name="lb_guidedactions_item_delimiter_padding">4dp</dimen> - <dimen name="lb_guidedactions_item_checkmark_diameter">8dp</dimen> + <dimen name="lb_guidedactions_item_checkmark_diameter">16dp</dimen> <dimen name="lb_guidedactions_item_icon_width">32dp</dimen> <dimen name="lb_guidedactions_item_icon_height">32dp</dimen> <dimen name="lb_guidedactions_item_title_font_size">18sp</dimen> diff --git a/v17/leanback/res/values/ids.xml b/v17/leanback/res/values/ids.xml index 6b679198e2..ca84efc8a8 100644 --- a/v17/leanback/res/values/ids.xml +++ b/v17/leanback/res/values/ids.xml @@ -16,7 +16,11 @@ --> <resources> <item type="id" name="lb_focus_animator" /> + <item type="id" name="lb_shadow_impl" /> <item type="id" name="lb_slide_transition_value" /> + <item type="id" name="transitionPosition" /> + + <item type="id" name="lb_guidedstep_background" /> <item type="id" name="lb_control_play_pause" /> <item type="id" name="lb_control_fast_forward" /> @@ -31,4 +35,10 @@ <item type="id" name="lb_control_high_quality" /> <item type="id" name="lb_control_closed_captioning" /> - </resources> + <item type="id" name="guidedactions_root2" /> + <item type="id" name="guidedactions_list_background2" /> + <item type="id" name="guidedactions_list2" /> + <item type="id" name="guidedactions_selector2" /> + <item type="id" name="guidedactions_content2" /> + +</resources> diff --git a/v17/leanback/res/values/integers.xml b/v17/leanback/res/values/integers.xml index 8547e22f0d..c9f33842de 100644 --- a/v17/leanback/res/values/integers.xml +++ b/v17/leanback/res/values/integers.xml @@ -29,4 +29,11 @@ <integer name="lb_playback_rows_fade_out_ms">250</integer> <integer name="lb_playback_rows_fade_delay_ms">100</integer> <integer name="lb_playback_controls_show_time_ms">3000</integer> + + <!-- Gravity.LEFT --> + <integer name="slideEdgeStart">3</integer> + <!-- Gravity.RIGHT --> + <integer name="slideEdgeEnd">5</integer> + + <integer name="lb_guidedstep_activity_background_fade_duration_ms">350</integer> </resources> diff --git a/v17/leanback/res/values/strings.xml b/v17/leanback/res/values/strings.xml index 2cd0ff119e..3d93db2abe 100644 --- a/v17/leanback/res/values/strings.xml +++ b/v17/leanback/res/values/strings.xml @@ -75,4 +75,8 @@ limitations under the License. <!-- Talkback label for the control button to disable closed captioning --> <string name="lb_playback_controls_closed_captioning_disable">Disable Closed Captioning</string> + <!-- Title of standard Finish action for GuidedStepFragment --> + <string name="lb_guidedaction_finish_title">Finish</string> + <!-- Title of standard Continue action for GuidedStepFragment --> + <string name="lb_guidedaction_continue_title">Continue</string> </resources> diff --git a/v17/leanback/res/values/styles.xml b/v17/leanback/res/values/styles.xml index 3ee2821aaf..390446780d 100644 --- a/v17/leanback/res/values/styles.xml +++ b/v17/leanback/res/values/styles.xml @@ -87,14 +87,8 @@ <style name="Widget.Leanback" parent="Widget.LeanbackBase" /> <style name="Widget.Leanback.BaseCardViewStyle"> - <item name="android:foreground">@drawable/lb_card_foreground</item> - </style> - - <style name="Widget.Leanback.ImageCardViewStyle" parent="Widget.Leanback.BaseCardViewStyle"> - <item name="cardType">infoUnder</item> - <item name="infoVisibility">activated</item> - <item name="android:background">@color/lb_basic_card_bg_color</item> - <item name="infoAreaBackground">@color/lb_basic_card_info_bg_color</item> + <item name="cardForeground">@drawable/lb_card_foreground</item> + <item name="cardBackground">@color/lb_basic_card_bg_color</item> </style> <style name="Widget.Leanback.TitleView" > @@ -104,6 +98,77 @@ <item name="android:paddingEnd">?attr/browsePaddingEnd</item> </style> + <style name="Widget.Leanback.ImageCardViewStyle" parent="Widget.Leanback.BaseCardViewStyle"> + <item name="cardType">infoUnder</item> + <item name="infoVisibility">activated</item> + <!-- In order to keep backward compatibility we have to create an icon on right. --> + <item name="lbImageCardViewType">Title|Content|IconOnRight</item> + <item name="lbImageCardViewImageStyle">@style/Widget.Leanback.ImageCardView.ImageStyle</item> + <item name="lbImageCardViewTitleStyle">@style/Widget.Leanback.ImageCardView.TitleStyle</item> + <item name="lbImageCardViewContentStyle">@style/Widget.Leanback.ImageCardView.ContentStyle</item> + <item name="lbImageCardViewBadgeStyle">@style/Widget.Leanback.ImageCardView.BadgeStyle</item> + <item name="lbImageCardViewInfoAreaStyle">@style/Widget.Leanback.ImageCardView.InfoAreaStyle</item> + <!-- Deprecated. Use 'Widget.Leanback.ImageCardView.InfoAreaStyle' instead. --> + <item name="infoAreaBackground">@null</item> + </style> + + <style name="Widget.Leanback.ImageCardView" /> + + <style name="Widget.Leanback.ImageCardView.ImageStyle"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:adjustViewBounds">true</item> + <item name="android:contentDescription">@null</item> + <item name="android:scaleType">centerCrop</item> + <item name="layout_viewType">main</item> + </style> + + <style name="Widget.Leanback.ImageCardView.InfoAreaStyle"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_centerHorizontal">true</item> + <item name="layout_viewType">info</item> + <item name="android:paddingBottom">@dimen/lb_basic_card_info_padding_bottom</item> + <item name="android:paddingEnd">@dimen/lb_basic_card_info_padding_horizontal</item> + <item name="android:paddingStart">@dimen/lb_basic_card_info_padding_horizontal</item> + <item name="android:paddingTop">@dimen/lb_basic_card_info_padding_top</item> + <item name="android:background">@color/lb_basic_card_info_bg_color</item> + </style> + + <style name="Widget.Leanback.ImageCardView.TitleStyle"> + <item name="android:id">@id/title_text</item> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:maxLines">1</item> + <item name="android:layout_marginBottom">@dimen/lb_basic_card_info_text_margin</item> + <item name="android:fontFamily">sans-serif-condensed</item> + <item name="android:textColor">@color/lb_basic_card_title_text_color</item> + <item name="android:textSize">@dimen/lb_basic_card_title_text_size</item> + <item name="android:ellipsize">end</item> + </style> + + <style name="Widget.Leanback.ImageCardView.ContentStyle"> + <item name="android:id">@id/content_text</item> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_alignParentStart">true</item> + <item name="android:layout_below">@+id/title_text</item> + <item name="android:layout_toStartOf">@+id/extra_badge</item> + <item name="android:maxLines">1</item> + <item name="android:fontFamily">sans-serif-condensed</item> + <item name="android:textColor">@color/lb_basic_card_content_text_color</item> + <item name="android:textSize">@dimen/lb_basic_card_content_text_size</item> + <item name="android:ellipsize">none</item> + </style> + + <style name="Widget.Leanback.ImageCardView.BadgeStyle"> + <item name="android:id">@id/extra_badge</item> + <item name="android:layout_width">@dimen/lb_basic_card_info_badge_size</item> + <item name="android:layout_height">@dimen/lb_basic_card_info_badge_size</item> + <item name="android:contentDescription">@null</item> + <item name="android:scaleType">fitCenter</item> + </style> + <style name="Widget.Leanback.Title" /> <style name="Widget.Leanback.Title.Text"> @@ -130,6 +195,7 @@ <style name="Widget.Leanback.GridItems" /> <style name="Widget.Leanback.Headers.VerticalGridView" > + <item name="android:background">?attr/defaultBrandColor</item> <item name="android:paddingStart">?attr/browsePaddingStart</item> <item name="focusOutFront">true</item> <item name="focusOutEnd">true</item> @@ -357,21 +423,11 @@ <item name="android:scaleType">fitCenter</item> </style> - <!-- Style for the container view in a GuidedActionsStylist's default layout. --> - <style name="Widget.Leanback.GuidedActionsContainerStyle"> - <item name="android:layout_width">@dimen/lb_guidedactions_section_width</item> - <item name="android:layout_height">match_parent</item> - <item name="android:layout_alignParentEnd">true</item> - <item name="android:background">@color/lb_guidedactions_background</item> - <item name="android:elevation">@dimen/lb_guidedactions_elevation</item> - </style> - <!-- Style for the selector view in a GuidedActionsStylist's default layout. --> <style name="Widget.Leanback.GuidedActionsSelectorStyle"> <item name="android:layout_width">match_parent</item> <item name="android:layout_height">@dimen/lb_guidedactions_selector_min_height</item> <item name="android:layout_centerVertical">true</item> - <item name="android:alpha">0</item> <item name="android:background">@color/lb_guidedactions_selector_color</item> </style> @@ -401,9 +457,8 @@ <item name="android:layout_height">@dimen/lb_guidedactions_item_checkmark_diameter</item> <item name="android:layout_gravity">center</item> <item name="android:layout_marginEnd">@dimen/lb_guidedactions_item_delimiter_padding</item> - <item name="android:scaleType">center</item> - <item name="android:src">@drawable/lb_guidedactions_item_checkmark</item> - <item name="android:visibility">invisible</item> + <item name="android:scaleType">centerInside</item> + <item name="android:visibility">gone</item> </style> <!-- Style for an action's icon in a GuidedActionsStylist's default item layout. --> @@ -418,7 +473,7 @@ <!-- Style for an action's text content in a GuidedActionsStylist's default item layout. --> <style name="Widget.Leanback.GuidedActionItemContentStyle"> - <item name="android:layout_width">0dp</item> + <item name="android:layout_width">match_parent</item> <item name="android:layout_height">wrap_content</item> <item name="android:layout_gravity">start|center_vertical</item> <item name="android:layout_weight">1</item> @@ -430,11 +485,12 @@ <item name="android:layout_width">match_parent</item> <item name="android:layout_height">wrap_content</item> <item name="android:alpha">@string/lb_guidedactions_item_unselected_text_alpha</item> - <item name="android:ellipsize">marquee</item> + <item name="android:ellipsize">end</item> <item name="android:fontFamily">sans-serif-condensed</item> <item name="android:maxLines">@integer/lb_guidedactions_item_title_min_lines</item> <item name="android:textColor">@color/lb_guidedactions_item_unselected_text_color</item> <item name="android:textSize">@dimen/lb_guidedactions_item_title_font_size</item> + <item name="android:background">@null</item> </style> <!-- Style for an action's description in a GuidedActionsStylist's default item layout. --> @@ -442,12 +498,13 @@ <item name="android:layout_width">match_parent</item> <item name="android:layout_height">wrap_content</item> <item name="android:alpha">@string/lb_guidedactions_item_unselected_description_text_alpha</item> - <item name="android:ellipsize">marquee</item> + <item name="android:ellipsize">end</item> <item name="android:fontFamily">sans-serif-condensed</item> <item name="android:maxLines">@integer/lb_guidedactions_item_description_min_lines</item> <item name="android:textColor">@color/lb_guidedactions_item_unselected_text_color</item> <item name="android:textSize">@dimen/lb_guidedactions_item_description_font_size</item> <item name="android:visibility">gone</item> + <item name="android:background">@null</item> </style> <!-- Style for an action's chevron in a GuidedActionsStylist's default item layout. --> diff --git a/v17/leanback/res/values/themes.xml b/v17/leanback/res/values/themes.xml index 8178c50f8c..36362ddddc 100644 --- a/v17/leanback/res/values/themes.xml +++ b/v17/leanback/res/values/themes.xml @@ -15,13 +15,14 @@ limitations under the License. --> -<resources> +<resources xmlns:android="http://schemas.android.com/apk/res/android"> <!-- LeanbackBase may be overridden for specific api levels --> <style name="Theme.LeanbackBase" parent="android:Theme.Holo.NoActionBar"> <item name="playbackProgressPrimaryColor">@color/lb_playback_progress_color_no_theme</item> <item name="playbackControlsIconHighlightColor">@color/lb_playback_icon_highlight_no_theme</item> <item name="defaultBrandColor">@color/lb_default_brand_color</item> + <item name="defaultBrandColorDark">@color/lb_default_brand_color_dark</item> <item name="android:windowOverscan">true</item> <item name="guidedStepTheme">@style/Theme.Leanback.GuidedStep</item> @@ -95,6 +96,11 @@ <item name="android:windowReturnTransition">@transition/lb_browse_return_transition</item> </style> + <style name="Theme.Leanback.VerticalGrid" parent="Theme.Leanback"> + <item name="android:windowEnterTransition">@transition/lb_vertical_grid_enter_transition</item> + <item name="android:windowReturnTransition">@transition/lb_vertical_grid_return_transition</item> + </style> + <style name="Theme.Leanback.Details" parent="Theme.Leanback"> <item name="android:windowEnterTransition">@transition/lb_details_enter_transition</item> <item name="android:windowReturnTransition">@transition/lb_details_return_transition</item> @@ -111,12 +117,18 @@ <style name="Theme.Leanback.GuidedStep" parent="Theme.LeanbackBase"> <item name="guidedStepThemeFlag">true</item> - <item name="guidedStepEntryAnimation">@animator/lb_guidedstep_slide_in_from_end</item> - <item name="guidedStepExitAnimation">@animator/lb_guidedstep_slide_out_to_start</item> - <item name="guidedStepReentryAnimation">@animator/lb_guidedstep_slide_in_from_start</item> - <item name="guidedStepReturnAnimation">@animator/lb_guidedstep_slide_out_to_end</item> - <item name="guidanceEntryAnimation">@animator/lb_guidance_entry</item> - <item name="guidedActionsEntryAnimation">@animator/lb_guidedactions_entry</item> + <item name="android:windowEnterTransition">@transition/lb_guidedstep_activity_enter</item> + + <!-- background applied to each GuidedStepFragment by default--> + <item name="guidedStepBackground">?android:attr/colorBackground</item> + <!-- Each GuidedStepFragment has a background so activity does not need a background. + But We still need a dumb background to keep the temporary translucent state last + as long as the background view fade-in transition --> + <item name="android:windowBackground">@android:color/transparent</item> + <item name="android:windowTransitionBackgroundFadeDuration">@integer/lb_guidedstep_activity_background_fade_duration_ms</item> + + <item name="guidedStepImeAppearingAnimation">@animator/lb_guidedstep_slide_up</item> + <item name="guidedStepImeDisappearingAnimation">@animator/lb_guidedstep_slide_down</item> <item name="guidanceContainerStyle">@style/Widget.Leanback.GuidanceContainerStyle</item> <item name="guidanceIconStyle">@style/Widget.Leanback.GuidanceIconStyle</item> @@ -124,7 +136,9 @@ <item name="guidanceBreadcrumbStyle">@style/Widget.Leanback.GuidanceBreadcrumbStyle</item> <item name="guidanceDescriptionStyle">@style/Widget.Leanback.GuidanceDescriptionStyle</item> - <item name="guidedActionsContainerStyle">@style/Widget.Leanback.GuidedActionsContainerStyle</item> + <item name="guidedActionsElevation">@dimen/lb_guidedactions_elevation</item> + <item name="guidedActionsBackground">@color/lb_guidedactions_background</item> + <item name="guidedActionsBackgroundDark">@color/lb_guidedactions_background_dark</item> <item name="guidedActionsSelectorStyle">@style/Widget.Leanback.GuidedActionsSelectorStyle</item> <item name="guidedActionsListStyle">@style/Widget.Leanback.GuidedActionsListStyle</item> <item name="guidedActionsSelectorShowAnimation">@animator/lb_guidedactions_selector_show</item> @@ -138,14 +152,12 @@ <item name="guidedActionItemDescriptionStyle">@style/Widget.Leanback.GuidedActionItemDescriptionStyle</item> <item name="guidedActionItemChevronStyle">@style/Widget.Leanback.GuidedActionItemChevronStyle</item> - <item name="guidedActionCheckedAnimation">@animator/lb_guidedactions_item_checked</item> - <item name="guidedActionUncheckedAnimation">@animator/lb_guidedactions_item_unchecked</item> <item name="guidedActionPressedAnimation">@animator/lb_guidedactions_item_pressed</item> <item name="guidedActionUnpressedAnimation">@animator/lb_guidedactions_item_unpressed</item> <item name="guidedActionEnabledChevronAlpha">@string/lb_guidedactions_item_enabled_chevron_alpha</item> <item name="guidedActionDisabledChevronAlpha">@string/lb_guidedactions_item_disabled_chevron_alpha</item> - <item name="guidedActionContentWidth">@dimen/lb_guidedactions_item_text_width</item> - <item name="guidedActionContentWidthNoIcon">@dimen/lb_guidedactions_item_text_width_no_icon</item> + <item name="guidedActionContentWidthWeight">@string/lb_guidedactions_width_weight</item> + <item name="guidedActionContentWidthWeightTwoPanels">@string/lb_guidedactions_width_weight_two_panels</item> <item name="guidedActionTitleMinLines">@integer/lb_guidedactions_item_title_min_lines</item> <item name="guidedActionTitleMaxLines">@integer/lb_guidedactions_item_title_max_lines</item> <item name="guidedActionDescriptionMinLines">@integer/lb_guidedactions_item_description_min_lines</item> diff --git a/v17/leanback/src/android/support/v17/leanback/animation/UntargetableAnimatorSet.java b/v17/leanback/src/android/support/v17/leanback/animation/UntargetableAnimatorSet.java deleted file mode 100644 index 9cdbc0cbfd..0000000000 --- a/v17/leanback/src/android/support/v17/leanback/animation/UntargetableAnimatorSet.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2015 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.animation; - -import android.animation.Animator; -import android.animation.AnimatorSet; -import android.animation.TimeInterpolator; - -import java.util.ArrayList; - -/** - * Custom fragment animations supplied by Fragment.onCreateAnimator have their targets set to the - * fragment's main view by the fragment manager. Sometimes, this isn't what you want; you may be - * supplying a heterogeneous collection of animations that already have targets. This class helps - * you return such a collection of animations from onCreateAnimator without having their targets - * reset. - * - * Note that one does not simply subclass AnimatorSet and override setTarget() because AnimatorSet - * is final. - * @hide - */ -public class UntargetableAnimatorSet extends Animator { - - private final AnimatorSet mAnimatorSet; - - public UntargetableAnimatorSet(AnimatorSet animatorSet) { - mAnimatorSet = animatorSet; - } - - @Override - public void addListener(Animator.AnimatorListener listener) { - mAnimatorSet.addListener(listener); - } - - @Override - public void cancel() { - mAnimatorSet.cancel(); - } - - @Override - public Animator clone() { - return mAnimatorSet.clone(); - } - - @Override - public void end() { - mAnimatorSet.end(); - } - - @Override - public long getDuration() { - return mAnimatorSet.getDuration(); - } - - @Override - public ArrayList<Animator.AnimatorListener> getListeners() { - return mAnimatorSet.getListeners(); - } - - @Override - public long getStartDelay() { - return mAnimatorSet.getStartDelay(); - } - - @Override - public boolean isRunning() { - return mAnimatorSet.isRunning(); - } - - @Override - public boolean isStarted() { - return mAnimatorSet.isStarted(); - } - - @Override - public void removeAllListeners() { - mAnimatorSet.removeAllListeners(); - } - - @Override - public void removeListener(Animator.AnimatorListener listener) { - mAnimatorSet.removeListener(listener); - } - - @Override - public Animator setDuration(long duration) { - return mAnimatorSet.setDuration(duration); - } - - @Override - public void setInterpolator(TimeInterpolator value) { - mAnimatorSet.setInterpolator(value); - } - - @Override - public void setStartDelay(long startDelay) { - mAnimatorSet.setStartDelay(startDelay); - } - - @Override - public void setTarget(Object target) { - // ignore - } - - @Override - public void setupEndValues() { - mAnimatorSet.setupEndValues(); - } - - @Override - public void setupStartValues() { - mAnimatorSet.setupStartValues(); - } - - @Override - public void start() { - mAnimatorSet.start(); - } -} - diff --git a/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java index 5b2deac348..c878a86060 100644 --- a/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java +++ b/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java @@ -30,8 +30,6 @@ class BaseFragment extends BrandedFragment { private boolean mEntranceTransitionPreparePending = false; private Object mEntranceTransition; - static TransitionHelper sTransitionHelper = TransitionHelper.getInstance(); - @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); @@ -176,7 +174,7 @@ class BaseFragment extends BrandedFragment { if (mEntranceTransition == null) { return; } - sTransitionHelper.setTransitionListener(mEntranceTransition, new TransitionListener() { + TransitionHelper.addTransitionListener(mEntranceTransition, new TransitionListener() { @Override public void onTransitionEnd(Object transition) { mEntranceTransition = null; diff --git a/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java index a72bb5b55d..b9c7d58429 100644 --- a/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java +++ b/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java @@ -32,8 +32,6 @@ class BaseSupportFragment extends BrandedSupportFragment { private boolean mEntranceTransitionPreparePending = false; private Object mEntranceTransition; - static TransitionHelper sTransitionHelper = TransitionHelper.getInstance(); - @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); @@ -178,7 +176,7 @@ class BaseSupportFragment extends BrandedSupportFragment { if (mEntranceTransition == null) { return; } - sTransitionHelper.setTransitionListener(mEntranceTransition, new TransitionListener() { + TransitionHelper.addTransitionListener(mEntranceTransition, new TransitionListener() { @Override public void onTransitionEnd(Object transition) { mEntranceTransition = null; 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 7e8f02fc40..8c417a4c2a 100644 --- a/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java +++ b/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java @@ -425,7 +425,7 @@ public class BrowseFragment extends BaseFragment { if (mBrowseTransitionListener != null) { mBrowseTransitionListener.onHeadersTransitionStart(withHeaders); } - sTransitionHelper.runTransition(withHeaders ? mSceneWithHeaders : mSceneWithoutHeaders, + TransitionHelper.runTransition(withHeaders ? mSceneWithHeaders : mSceneWithoutHeaders, mHeadersTransition); if (mHeadersBackStackEnabled) { if (!withHeaders) { @@ -622,19 +622,19 @@ public class BrowseFragment extends BaseFragment { mHeadersFragment.setBackgroundColor(mBrandColor); } - mSceneWithHeaders = sTransitionHelper.createScene(mBrowseFrame, new Runnable() { + mSceneWithHeaders = TransitionHelper.createScene(mBrowseFrame, new Runnable() { @Override public void run() { showHeaders(true); } }); - mSceneWithoutHeaders = sTransitionHelper.createScene(mBrowseFrame, new Runnable() { + mSceneWithoutHeaders = TransitionHelper.createScene(mBrowseFrame, new Runnable() { @Override public void run() { showHeaders(false); } }); - mSceneAfterEntranceTransition = sTransitionHelper.createScene(mBrowseFrame, new Runnable() { + mSceneAfterEntranceTransition = TransitionHelper.createScene(mBrowseFrame, new Runnable() { @Override public void run() { setEntranceTransitionEndState(); @@ -644,11 +644,11 @@ public class BrowseFragment extends BaseFragment { } private void createHeadersTransition() { - mHeadersTransition = sTransitionHelper.loadTransition(getActivity(), + mHeadersTransition = TransitionHelper.loadTransition(getActivity(), mShowingHeaders ? R.transition.lb_browse_headers_in : R.transition.lb_browse_headers_out); - sTransitionHelper.setTransitionListener(mHeadersTransition, new TransitionListener() { + TransitionHelper.addTransitionListener(mHeadersTransition, new TransitionListener() { @Override public void onTransitionStart(Object transition) { } @@ -891,14 +891,13 @@ public class BrowseFragment extends BaseFragment { @Override protected Object createEntranceTransition() { - return sTransitionHelper.loadTransition(getActivity(), + return TransitionHelper.loadTransition(getActivity(), R.transition.lb_browse_entrance_transition); } @Override protected void runEntranceTransition(Object entranceTransition) { - sTransitionHelper.runTransition(mSceneAfterEntranceTransition, - entranceTransition); + TransitionHelper.runTransition(mSceneAfterEntranceTransition, entranceTransition); } @Override 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 44deec44c1..fa7f61ec9f 100644 --- a/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java +++ b/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java @@ -427,7 +427,7 @@ public class BrowseSupportFragment extends BaseSupportFragment { if (mBrowseTransitionListener != null) { mBrowseTransitionListener.onHeadersTransitionStart(withHeaders); } - sTransitionHelper.runTransition(withHeaders ? mSceneWithHeaders : mSceneWithoutHeaders, + TransitionHelper.runTransition(withHeaders ? mSceneWithHeaders : mSceneWithoutHeaders, mHeadersTransition); if (mHeadersBackStackEnabled) { if (!withHeaders) { @@ -624,19 +624,19 @@ public class BrowseSupportFragment extends BaseSupportFragment { mHeadersSupportFragment.setBackgroundColor(mBrandColor); } - mSceneWithHeaders = sTransitionHelper.createScene(mBrowseFrame, new Runnable() { + mSceneWithHeaders = TransitionHelper.createScene(mBrowseFrame, new Runnable() { @Override public void run() { showHeaders(true); } }); - mSceneWithoutHeaders = sTransitionHelper.createScene(mBrowseFrame, new Runnable() { + mSceneWithoutHeaders = TransitionHelper.createScene(mBrowseFrame, new Runnable() { @Override public void run() { showHeaders(false); } }); - mSceneAfterEntranceTransition = sTransitionHelper.createScene(mBrowseFrame, new Runnable() { + mSceneAfterEntranceTransition = TransitionHelper.createScene(mBrowseFrame, new Runnable() { @Override public void run() { setEntranceTransitionEndState(); @@ -646,11 +646,11 @@ public class BrowseSupportFragment extends BaseSupportFragment { } private void createHeadersTransition() { - mHeadersTransition = sTransitionHelper.loadTransition(getActivity(), + mHeadersTransition = TransitionHelper.loadTransition(getActivity(), mShowingHeaders ? R.transition.lb_browse_headers_in : R.transition.lb_browse_headers_out); - sTransitionHelper.setTransitionListener(mHeadersTransition, new TransitionListener() { + TransitionHelper.addTransitionListener(mHeadersTransition, new TransitionListener() { @Override public void onTransitionStart(Object transition) { } @@ -893,14 +893,13 @@ public class BrowseSupportFragment extends BaseSupportFragment { @Override protected Object createEntranceTransition() { - return sTransitionHelper.loadTransition(getActivity(), + return TransitionHelper.loadTransition(getActivity(), R.transition.lb_browse_entrance_transition); } @Override protected void runEntranceTransition(Object entranceTransition) { - sTransitionHelper.runTransition(mSceneAfterEntranceTransition, - entranceTransition); + TransitionHelper.runTransition(mSceneAfterEntranceTransition, entranceTransition); } @Override diff --git a/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java b/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java index d415de054e..ddafd5fd1c 100644 --- a/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java +++ b/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java @@ -14,6 +14,7 @@ package android.support.v17.leanback.app; import android.support.v17.leanback.R; +import android.support.v17.leanback.transition.TransitionHelper; import android.support.v17.leanback.widget.BrowseFrameLayout; import android.support.v17.leanback.widget.DetailsOverviewRow; import android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter; @@ -200,7 +201,7 @@ public class DetailsFragment extends BaseFragment { } } - mSceneAfterEntranceTransition = sTransitionHelper.createScene( + mSceneAfterEntranceTransition = TransitionHelper.createScene( (ViewGroup) view, new Runnable() { @Override public void run() { @@ -394,25 +395,20 @@ public class DetailsFragment extends BaseFragment { super.onStart(); setupChildFragmentLayout(); setupFocusSearchListener(); - mRowsFragment.getView().requestFocus(); if (isEntranceTransitionEnabled()) { - // make sure recycler view animation is disabled - mRowsFragment.onTransitionPrepare(); - mRowsFragment.onTransitionStart(); mRowsFragment.setEntranceTransitionState(false); } } @Override protected Object createEntranceTransition() { - return sTransitionHelper.loadTransition(getActivity(), + return TransitionHelper.loadTransition(getActivity(), R.transition.lb_details_enter_transition); } @Override protected void runEntranceTransition(Object entranceTransition) { - sTransitionHelper.runTransition(mSceneAfterEntranceTransition, - entranceTransition); + TransitionHelper.runTransition(mSceneAfterEntranceTransition, entranceTransition); } @Override @@ -420,4 +416,13 @@ public class DetailsFragment extends BaseFragment { mRowsFragment.onTransitionEnd(); } + @Override + protected void onEntranceTransitionPrepare() { + mRowsFragment.onTransitionPrepare(); + } + + @Override + protected void onEntranceTransitionStart() { + mRowsFragment.onTransitionStart(); + } } diff --git a/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java index afaf5a81b0..7532d79dd1 100644 --- a/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java +++ b/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java @@ -16,6 +16,7 @@ package android.support.v17.leanback.app; import android.support.v17.leanback.R; +import android.support.v17.leanback.transition.TransitionHelper; import android.support.v17.leanback.widget.BrowseFrameLayout; import android.support.v17.leanback.widget.DetailsOverviewRow; import android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter; @@ -202,7 +203,7 @@ public class DetailsSupportFragment extends BaseSupportFragment { } } - mSceneAfterEntranceTransition = sTransitionHelper.createScene( + mSceneAfterEntranceTransition = TransitionHelper.createScene( (ViewGroup) view, new Runnable() { @Override public void run() { @@ -396,25 +397,20 @@ public class DetailsSupportFragment extends BaseSupportFragment { super.onStart(); setupChildFragmentLayout(); setupFocusSearchListener(); - mRowsSupportFragment.getView().requestFocus(); if (isEntranceTransitionEnabled()) { - // make sure recycler view animation is disabled - mRowsSupportFragment.onTransitionPrepare(); - mRowsSupportFragment.onTransitionStart(); mRowsSupportFragment.setEntranceTransitionState(false); } } @Override protected Object createEntranceTransition() { - return sTransitionHelper.loadTransition(getActivity(), + return TransitionHelper.loadTransition(getActivity(), R.transition.lb_details_enter_transition); } @Override protected void runEntranceTransition(Object entranceTransition) { - sTransitionHelper.runTransition(mSceneAfterEntranceTransition, - entranceTransition); + TransitionHelper.runTransition(mSceneAfterEntranceTransition, entranceTransition); } @Override @@ -422,4 +418,13 @@ public class DetailsSupportFragment extends BaseSupportFragment { mRowsSupportFragment.onTransitionEnd(); } + @Override + protected void onEntranceTransitionPrepare() { + mRowsSupportFragment.onTransitionPrepare(); + } + + @Override + protected void onEntranceTransitionStart() { + mRowsSupportFragment.onTransitionStart(); + } } diff --git a/v17/leanback/src/android/support/v17/leanback/app/GuidedActionAdapter.java b/v17/leanback/src/android/support/v17/leanback/app/GuidedActionAdapter.java index 41ecac9983..142e9718e6 100644 --- a/v17/leanback/src/android/support/v17/leanback/app/GuidedActionAdapter.java +++ b/v17/leanback/src/android/support/v17/leanback/app/GuidedActionAdapter.java @@ -19,6 +19,7 @@ import android.media.AudioManager; import android.support.v17.leanback.R; import android.support.v17.leanback.widget.GuidedAction; import android.support.v17.leanback.widget.GuidedActionsStylist; +import android.support.v17.leanback.widget.ImeKeyMonitor; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.ViewHolder; import android.util.Log; @@ -26,9 +27,14 @@ import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.ViewParent; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.EditText; import android.widget.ImageView; import android.widget.TextView; +import android.widget.TextView.OnEditorActionListener; import java.util.ArrayList; import java.util.List; @@ -43,6 +49,9 @@ class GuidedActionAdapter extends RecyclerView.Adapter { private static final String TAG = "GuidedActionAdapter"; private static final boolean DEBUG = false; + private static final String TAG_EDIT = "EditableAction"; + private static final boolean DEBUG_EDIT = false; + /** * Object listening for click events within a {@link GuidedActionAdapter}. */ @@ -66,11 +75,32 @@ class GuidedActionAdapter extends RecyclerView.Adapter { } /** + * Object listening for edit events within a {@link GuidedActionAdapter}. + */ + public interface EditListener { + + /** + * Called when the user exits edit mode on an action. + */ + public long onGuidedActionEdited(GuidedAction action); + + /** + * Called when Ime Open + */ + public void onImeOpen(); + + /** + * Called when Ime Close + */ + public void onImeClose(); + } + + /** * View holder containing a {@link GuidedAction}. */ - private static class ActionViewHolder extends ViewHolder { + static class ActionViewHolder extends ViewHolder { - private final GuidedActionsStylist.ViewHolder mStylistViewHolder; + final GuidedActionsStylist.ViewHolder mStylistViewHolder; private GuidedAction mAction; /** @@ -98,30 +128,36 @@ class GuidedActionAdapter extends RecyclerView.Adapter { } } - private RecyclerView mRecyclerView; private final ActionOnKeyListener mActionOnKeyListener; private final ActionOnFocusListener mActionOnFocusListener; + private final ActionEditListener mActionEditListener; private final List<GuidedAction> mActions; private ClickListener mClickListener; - private GuidedActionsStylist mStylist; + private final GuidedActionsStylist mStylist; private final View.OnClickListener mOnClickListener = new View.OnClickListener() { @Override public void onClick(View v) { - if (v != null && v.getWindowToken() != null && mClickListener != null) { - ActionViewHolder avh = (ActionViewHolder)mRecyclerView.getChildViewHolder(v); + if (v != null && v.getWindowToken() != null && getRecyclerView() != null) { + ActionViewHolder avh = (ActionViewHolder)getRecyclerView().getChildViewHolder(v); GuidedAction action = avh.getAction(); - if (action.isEnabled() && !action.infoOnly()) { - mClickListener.onGuidedActionClicked(action); + if (action.isEditable() || action.isDescriptionEditable()) { + if (DEBUG_EDIT) Log.v(TAG_EDIT, "openIme by click"); + mGroup.openIme(GuidedActionAdapter.this, avh); + } else { + handleCheckedActions(avh); + if (action.isEnabled() && !action.infoOnly()) { + performOnActionClick(avh); + } } } } }; + GuidedActionAdapterGroup mGroup; /** * Constructs a GuidedActionAdapter with the given list of guided actions, the given click and * focus listeners, and the given presenter. * @param actions The list of guided actions this adapter will manage. - * @param clickListener The click listener for items in this adapter. * @param focusListener The focus listener for items in this adapter. * @param presenter The presenter that will manage the display of items in this adapter. */ @@ -131,8 +167,9 @@ class GuidedActionAdapter extends RecyclerView.Adapter { mActions = new ArrayList<GuidedAction>(actions); mClickListener = clickListener; mStylist = presenter; - mActionOnKeyListener = new ActionOnKeyListener(clickListener, mActions); + mActionOnKeyListener = new ActionOnKeyListener(); mActionOnFocusListener = new ActionOnFocusListener(focusListener); + mActionEditListener = new ActionEditListener(); } /** @@ -164,12 +201,27 @@ class GuidedActionAdapter extends RecyclerView.Adapter { } /** + * Return index of action in array + * @param action Action to search index. + * @return Index of Action in array. + */ + public int indexOf(GuidedAction action) { + return mActions.indexOf(action); + } + + /** + * @return GuidedActionsStylist used to build the actions list UI. + */ + public GuidedActionsStylist getGuidedActionsStylist() { + return mStylist; + } + + /** * Sets the click listener for items managed by this adapter. * @param clickListener The click listener for this adapter. */ public void setClickListener(ClickListener clickListener) { mClickListener = clickListener; - mActionOnKeyListener.setListener(clickListener); } /** @@ -192,16 +244,12 @@ class GuidedActionAdapter extends RecyclerView.Adapter { * {@inheritDoc} */ @Override - public void onAttachedToRecyclerView(RecyclerView recyclerView) { - mRecyclerView = recyclerView; + public int getItemViewType(int position) { + return mStylist.getItemViewType(mActions.get(position)); } - /** - * {@inheritDoc} - */ - @Override - public void onDetachedFromRecyclerView(RecyclerView recyclerView) { - mRecyclerView = null; + private RecyclerView getRecyclerView() { + return mStylist.getActionsGridView(); } /** @@ -209,15 +257,29 @@ class GuidedActionAdapter extends RecyclerView.Adapter { */ @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - GuidedActionsStylist.ViewHolder vh = mStylist.onCreateViewHolder(parent); + GuidedActionsStylist.ViewHolder vh = mStylist.onCreateViewHolder(parent, viewType); View v = vh.view; v.setOnKeyListener(mActionOnKeyListener); v.setOnClickListener(mOnClickListener); v.setOnFocusChangeListener(mActionOnFocusListener); + setupListeners(vh.getEditableTitleView()); + setupListeners(vh.getEditableDescriptionView()); + return new ActionViewHolder(v, vh); } + private void setupListeners(EditText edit) { + if (edit != null) { + edit.setPrivateImeOptions("EscapeNorth=1;"); + edit.setOnEditorActionListener(mActionEditListener); + if (edit instanceof ImeKeyMonitor) { + ImeKeyMonitor monitor = (ImeKeyMonitor)edit; + monitor.setImeKeyListener(mActionEditListener); + } + } + } + /** * {@inheritDoc} */ @@ -226,7 +288,7 @@ class GuidedActionAdapter extends RecyclerView.Adapter { if (position >= mActions.size()) { return; } - ActionViewHolder avh = (ActionViewHolder)holder; + final ActionViewHolder avh = (ActionViewHolder)holder; GuidedAction action = mActions.get(position); avh.setAction(action); mStylist.onBindViewHolder(avh.mStylistViewHolder, action); @@ -254,8 +316,8 @@ class GuidedActionAdapter extends RecyclerView.Adapter { } public void unFocus() { - if (mSelectedView != null) { - ViewHolder vh = mRecyclerView.getChildViewHolder(mSelectedView); + if (mSelectedView != null && getRecyclerView() != null) { + ViewHolder vh = getRecyclerView().getChildViewHolder(mSelectedView); if (vh != null) { ActionViewHolder avh = (ActionViewHolder)vh; mStylist.onAnimateItemFocused(avh.mStylistViewHolder, false); @@ -268,8 +330,10 @@ class GuidedActionAdapter extends RecyclerView.Adapter { @Override public void onFocusChange(View v, boolean hasFocus) { - ActionViewHolder avh = (ActionViewHolder)mRecyclerView.getChildViewHolder(v); - mStylist.onAnimateItemFocused(avh.mStylistViewHolder, hasFocus); + if (getRecyclerView() == null) { + return; + } + ActionViewHolder avh = (ActionViewHolder) getRecyclerView().getChildViewHolder(v); if (hasFocus) { mSelectedView = v; if (mFocusListener != null) { @@ -279,27 +343,74 @@ class GuidedActionAdapter extends RecyclerView.Adapter { } } else { if (mSelectedView == v) { + mStylist.onAnimateItemPressedCancelled(avh.mStylistViewHolder); mSelectedView = null; } } + mStylist.onAnimateItemFocused(avh.mStylistViewHolder, hasFocus); } } - private class ActionOnKeyListener implements View.OnKeyListener { + public ActionViewHolder findSubChildViewHolder(View v) { + // Needed because RecyclerView.getChildViewHolder does not traverse the hierarchy + if (getRecyclerView() == null) { + return null; + } + ActionViewHolder result = null; + ViewParent parent = v.getParent(); + while (parent != getRecyclerView() && parent != null && v != null) { + v = (View)parent; + parent = parent.getParent(); + } + if (parent != null && v != null) { + result = (ActionViewHolder)getRecyclerView().getChildViewHolder(v); + } + return result; + } - private final List<GuidedAction> mActions; - private boolean mKeyPressed = false; - private ClickListener mClickListener; + public void handleCheckedActions(ActionViewHolder avh) { + GuidedAction action = avh.getAction(); + int actionCheckSetId = action.getCheckSetId(); + if (getRecyclerView() != null && actionCheckSetId != GuidedAction.NO_CHECK_SET) { + // Find any actions that are checked and are in the same group + // as the selected action. Fade their checkmarks out. + if (actionCheckSetId != GuidedAction.CHECKBOX_CHECK_SET_ID) { + for (int i = 0, size = mActions.size(); i < size; i++) { + GuidedAction a = mActions.get(i); + if (a != action && a.getCheckSetId() == actionCheckSetId && a.isChecked()) { + a.setChecked(false); + ViewHolder vh = getRecyclerView().findViewHolderForPosition(i); + if (vh != null) { + GuidedActionsStylist.ViewHolder subViewHolder = + ((ActionViewHolder)vh).mStylistViewHolder; + mStylist.onAnimateItemChecked(subViewHolder, false); + } + } + } + } - public ActionOnKeyListener(ClickListener listener, - List<GuidedAction> actions) { - mClickListener = listener; - mActions = actions; + // If we we'ren't already checked, fade our checkmark in. + if (!action.isChecked()) { + action.setChecked(true); + mStylist.onAnimateItemChecked(avh.mStylistViewHolder, true); + } else { + if (actionCheckSetId == GuidedAction.CHECKBOX_CHECK_SET_ID) { + action.setChecked(false); + mStylist.onAnimateItemChecked(avh.mStylistViewHolder, false); + } + } } + } - public void setListener(ClickListener listener) { - mClickListener = listener; + public void performOnActionClick(ActionViewHolder avh) { + if (mClickListener != null) { + mClickListener.onGuidedActionClicked(avh.getAction()); } + } + + private class ActionOnKeyListener implements View.OnKeyListener { + + private boolean mKeyPressed = false; private void playSound(View v, int soundEffect) { if (v.isSoundEffectsEnabled()) { @@ -314,7 +425,7 @@ class GuidedActionAdapter extends RecyclerView.Adapter { */ @Override public boolean onKey(View v, int keyCode, KeyEvent event) { - if (v == null || event == null) { + if (v == null || event == null || getRecyclerView() == null) { return false; } boolean handled = false; @@ -325,7 +436,8 @@ class GuidedActionAdapter extends RecyclerView.Adapter { case KeyEvent.KEYCODE_BUTTON_Y: case KeyEvent.KEYCODE_ENTER: - ActionViewHolder avh = (ActionViewHolder)mRecyclerView.getChildViewHolder(v); + ActionViewHolder avh = (ActionViewHolder) getRecyclerView() + .getChildViewHolder(v); GuidedAction action = avh.getAction(); if (!action.isEnabled() || action.infoOnly()) { @@ -338,33 +450,25 @@ class GuidedActionAdapter extends RecyclerView.Adapter { switch (event.getAction()) { case KeyEvent.ACTION_DOWN: + if (DEBUG) { + Log.d(TAG, "Enter Key down"); + } if (!mKeyPressed) { mKeyPressed = true; - playSound(v, AudioManager.FX_KEY_CLICK); - - if (DEBUG) { - Log.d(TAG, "Enter Key down"); - } - mStylist.onAnimateItemPressed(avh.mStylistViewHolder, mKeyPressed); - handled = true; } break; case KeyEvent.ACTION_UP: + if (DEBUG) { + Log.d(TAG, "Enter Key up"); + } + // Sometimes we are losing ACTION_DOWN for the first ENTER after pressed + // Escape in IME. if (mKeyPressed) { mKeyPressed = false; - - if (DEBUG) { - Log.d(TAG, "Enter Key up"); - } - - mStylist.onAnimateItemPressed(avh.mStylistViewHolder, - mKeyPressed); - handleCheckedActions(avh, action); - mClickListener.onGuidedActionClicked(action); - handled = true; + mStylist.onAnimateItemPressed(avh.mStylistViewHolder, mKeyPressed); } break; default: @@ -377,30 +481,40 @@ class GuidedActionAdapter extends RecyclerView.Adapter { return handled; } - private void handleCheckedActions(ActionViewHolder avh, GuidedAction action) { - int actionCheckSetId = action.getCheckSetId(); - if (actionCheckSetId != GuidedAction.NO_CHECK_SET) { - // Find any actions that are checked and are in the same group - // as the selected action. Fade their checkmarks out. - for (int i = 0, size = mActions.size(); i < size; i++) { - GuidedAction a = mActions.get(i); - if (a != action && a.getCheckSetId() == actionCheckSetId && a.isChecked()) { - a.setChecked(false); - ViewHolder vh = mRecyclerView.findViewHolderForPosition(i); - if (vh != null) { - GuidedActionsStylist.ViewHolder subViewHolder = - ((ActionViewHolder)vh).mStylistViewHolder; - mStylist.onAnimateItemChecked(subViewHolder, false); - } - } - } + } - // If we we'ren't already checked, fade our checkmark in. - if (!action.isChecked()) { - action.setChecked(true); - mStylist.onAnimateItemChecked(avh.mStylistViewHolder, true); - } + private class ActionEditListener implements OnEditorActionListener, + ImeKeyMonitor.ImeKeyListener { + + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (DEBUG_EDIT) Log.v(TAG_EDIT, "IME action: " + actionId); + boolean handled = false; + if (actionId == EditorInfo.IME_ACTION_NEXT || + actionId == EditorInfo.IME_ACTION_DONE) { + mGroup.fillAndGoNext(GuidedActionAdapter.this, v); + handled = true; + } else if (actionId == EditorInfo.IME_ACTION_NONE) { + if (DEBUG_EDIT) Log.v(TAG_EDIT, "closeIme escape north"); + // Escape north handling: stay on current item, but close editor + handled = true; + mGroup.fillAndStay(GuidedActionAdapter.this, v); } + return handled; } + + @Override + public boolean onKeyPreIme(EditText editText, int keyCode, KeyEvent event) { + if (DEBUG_EDIT) Log.v(TAG_EDIT, "IME key: " + keyCode); + if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) { + mGroup.fillAndStay(GuidedActionAdapter.this, editText); + } else if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == + KeyEvent.ACTION_UP) { + mGroup.fillAndGoNext(GuidedActionAdapter.this, editText); + } + return false; + } + } + } diff --git a/v17/leanback/src/android/support/v17/leanback/app/GuidedActionAdapterGroup.java b/v17/leanback/src/android/support/v17/leanback/app/GuidedActionAdapterGroup.java new file mode 100644 index 0000000000..8f8951c5e8 --- /dev/null +++ b/v17/leanback/src/android/support/v17/leanback/app/GuidedActionAdapterGroup.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2015 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.content.Context; +import android.support.v17.leanback.app.GuidedActionAdapter.ActionViewHolder; +import android.support.v17.leanback.app.GuidedActionAdapter.ClickListener; +import android.support.v17.leanback.app.GuidedActionAdapter.EditListener; +import android.support.v17.leanback.widget.GuidedAction; +import android.support.v17.leanback.widget.ImeKeyMonitor; +import android.util.Log; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewParent; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.TextView.OnEditorActionListener; + +import java.util.ArrayList; + +/** + * Internal implementation manages a group of GuidedActionAdapters, control the next action after + * editing finished, maintain the Ime open/close status. + */ +class GuidedActionAdapterGroup { + + private static final String TAG_EDIT = "EditableAction"; + private static final boolean DEBUG_EDIT = false; + + ArrayList<GuidedActionAdapter> mAdapters = new ArrayList<GuidedActionAdapter>(); + private boolean mImeOpened; + private EditListener mEditListener; + + GuidedActionAdapterGroup() { + } + + public void addAdpter(GuidedActionAdapter adapter) { + mAdapters.add(adapter); + adapter.mGroup = this; + } + + public void setEditListener(EditListener listener) { + mEditListener = listener; + } + + boolean focusToNextAction(GuidedActionAdapter adapter, GuidedAction action, long nextActionId) { + // for ACTION_ID_NEXT, we first find out the matching index in Actions list. + int index = 0; + if (nextActionId == GuidedAction.ACTION_ID_NEXT) { + index = adapter.indexOf(action); + if (index < 0) { + return false; + } + // start from next, if reach end, will go next Adapter below + index++; + } + + int adapterIndex = mAdapters.indexOf(adapter); + do { + int size = adapter.getCount(); + if (nextActionId == GuidedAction.ACTION_ID_NEXT) { + while (index < size && !adapter.getItem(index).isFocusable()) { + index++; + } + } else { + while (index < size && adapter.getItem(index).getId() != nextActionId) { + index++; + } + } + if (index < size) { + ActionViewHolder vh = (ActionViewHolder) adapter.getGuidedActionsStylist() + .getActionsGridView().findViewHolderForPosition(index); + if (vh != null) { + if (vh.getAction().isEditable() || vh.getAction().isDescriptionEditable()) { + if (DEBUG_EDIT) Log.v(TAG_EDIT, "openIme of next Action"); + // open Ime on next action. + openIme(adapter, vh); + } else { + if (DEBUG_EDIT) Log.v(TAG_EDIT, "closeIme and focus to next Action"); + // close IME and focus to next (not editable) action + closeIme(vh.mStylistViewHolder.view); + vh.mStylistViewHolder.view.requestFocus(); + } + return true; + } + return false; + } + // search from index 0 of next Adapter + adapterIndex++; + if (adapterIndex >= mAdapters.size()) { + break; + } + adapter = mAdapters.get(adapterIndex); + index = 0; + } while (true); + return false; + } + + public void openIme(GuidedActionAdapter adapter, ActionViewHolder avh) { + adapter.getGuidedActionsStylist().setEditingMode(avh.mStylistViewHolder, avh.getAction(), + true); + View v = avh.mStylistViewHolder.getEditingView(); + if (v == null) { + return; + } + InputMethodManager mgr = (InputMethodManager) + v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + v.requestFocus(); + mgr.showSoftInput(v, 0); + if (!mImeOpened) { + mImeOpened = true; + mEditListener.onImeOpen(); + } + } + + public void closeIme(View v) { + if (mImeOpened) { + mImeOpened = false; + InputMethodManager mgr = (InputMethodManager) + v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + mgr.hideSoftInputFromWindow(v.getWindowToken(), 0); + mEditListener.onImeClose(); + } + } + + private long finishEditing(GuidedActionAdapter adapter, ActionViewHolder avh) { + long nextActionId = mEditListener.onGuidedActionEdited(avh.getAction()); + adapter.getGuidedActionsStylist().setEditingMode(avh.mStylistViewHolder, avh.getAction(), + false); + return nextActionId; + } + + public void fillAndStay(GuidedActionAdapter adapter, TextView v) { + ActionViewHolder avh = adapter.findSubChildViewHolder(v); + updateTextIntoAction(avh, v); + finishEditing(adapter, avh); + closeIme(v); + avh.mStylistViewHolder.view.requestFocus(); + } + + public void fillAndGoNext(GuidedActionAdapter adapter, TextView v) { + boolean handled = false; + ActionViewHolder avh = adapter.findSubChildViewHolder(v); + updateTextIntoAction(avh, v); + adapter.performOnActionClick(avh); + long nextActionId = finishEditing(adapter, avh); + if (nextActionId != GuidedAction.ACTION_ID_CURRENT + && nextActionId != avh.getAction().getId()) { + handled = focusToNextAction(adapter, avh.getAction(), nextActionId); + } + if (!handled) { + if (DEBUG_EDIT) Log.v(TAG_EDIT, "closeIme no next action"); + handled = true; + closeIme(v); + avh.mStylistViewHolder.view.requestFocus(); + } + } + + private void updateTextIntoAction(ActionViewHolder avh, TextView v) { + GuidedAction action = avh.getAction(); + if (v == avh.mStylistViewHolder.getDescriptionView()) { + if (action.getEditDescription() != null) { + action.setEditDescription(v.getText()); + } else { + action.setDescription(v.getText()); + } + } else if (v == avh.mStylistViewHolder.getTitleView()) { + if (action.getEditTitle() != null) { + action.setEditTitle(v.getText()); + } else { + action.setTitle(v.getText()); + } + } + } + +} diff --git a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java index b1e87a1e4d..8be8f24f47 100644 --- a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java +++ b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java @@ -18,28 +18,34 @@ import android.animation.AnimatorSet; import android.app.Activity; import android.app.Fragment; import android.app.FragmentManager; +import android.app.FragmentManager.BackStackEntry; import android.app.FragmentTransaction; import android.content.Context; import android.content.res.TypedArray; +import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; -import android.support.v17.leanback.animation.UntargetableAnimatorSet; +import android.support.v17.leanback.transition.TransitionHelper; import android.support.v17.leanback.R; import android.support.v17.leanback.widget.GuidanceStylist; import android.support.v17.leanback.widget.GuidanceStylist.Guidance; import android.support.v17.leanback.widget.GuidedAction; import android.support.v17.leanback.widget.GuidedActionsStylist; import android.support.v17.leanback.widget.VerticalGridView; +import android.support.v4.app.ActivityCompat; +import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.view.ContextThemeWrapper; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.ImageView; import android.widget.RelativeLayout; +import android.widget.LinearLayout; import android.widget.TextView; import java.util.ArrayList; @@ -52,7 +58,7 @@ import java.util.List; * <p> * <h3>Basic Usage</h3> * <p> - * Clients of GuidedStepFragment typically create a custom subclass to attach to their Activities. + * Clients of GuidedStepFragment must create a custom subclass to attach to their Activities. * This custom subclass provides the information necessary to construct the user interface and * respond to user actions. At a minimum, subclasses should override: * <ul> @@ -61,6 +67,19 @@ import java.util.List; * <li>{@link #onGuidedActionClicked}, to respond to those actions</li> * </ul> * <p> + * Clients use following helper functions to add GuidedStepFragment to Activity or FragmentManager: + * <ul> + * <li>{@link #addAsRoot(Activity, GuidedStepFragment, int)}, to be called during Activity onCreate, + * adds GuidedStepFragment as the first Fragment in activity.</li> + * <li>{@link #add(FragmentManager, GuidedStepFragment)} or {@link #add(FragmentManager, + * GuidedStepFragment, int)}, to add GuidedStepFragment on top of existing Fragments or + * replacing existing GuidedStepFragment when moving forward to next step.</li> + * <li>{@link #finishGuidedStepFragments()} can either finish the activity or pop all + * GuidedStepFragment from stack. + * <li>If app chooses not to use the helper function, it is the app's responsibility to call + * {@link #setUiStyle(int)} to select fragment transition and remember the stack entry where it + * need pops to. + * </ul> * <h3>Theming and Stylists</h3> * <p> * GuidedStepFragment delegates its visual styling to classes called stylists. The {@link @@ -106,8 +125,14 @@ import java.util.List; * <p> * <i>Note: Currently GuidedStepFragments grouped in this way must all be defined programmatically, * rather than in XML. This restriction may be removed in the future.</i> - * <p> + * * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepTheme + * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepBackground + * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionContentWidthWeight + * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionContentWidthWeightTwoPanels + * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsBackground + * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsBackgroundDark + * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsElevation * @see GuidanceStylist * @see GuidanceStylist.Guidance * @see GuidedAction @@ -118,24 +143,81 @@ public class GuidedStepFragment extends Fragment implements GuidedActionAdapter. private static final String TAG_LEAN_BACK_ACTIONS_FRAGMENT = "leanBackGuidedStepFragment"; private static final String EXTRA_ACTION_SELECTED_INDEX = "selectedIndex"; - private static final String EXTRA_ACTION_ENTRY_TRANSITION_ENABLED = "entryTransitionEnabled"; - private static final String EXTRA_ENTRY_TRANSITION_PERFORMED = "entryTransitionPerformed"; + + private static final String ENTRY_NAME_REPLACE = "GuidedStepDefault"; + + private static final String ENTRY_NAME_ENTRANCE = "GuidedStepEntrance"; + + /** + * Fragment argument name for UI style. The argument value is persisted in fragment state. + * The value is initially {@link #UI_STYLE_ENTRANCE} and might be changed in one of the three + * helper functions: + * <ul> + * <li>{@link #addAsRoot(Activity, GuidedStepFragment, int)}</li> + * <li>{@link #add(FragmentManager, GuidedStepFragment)} or {@link #add(FragmentManager, + * GuidedStepFragment, int)}</li> + * </ul> + * <p> + * Argument value can be either: + * <ul> + * <li>{@link #UI_STYLE_REPLACE}</li> + * <li>{@link #UI_STYLE_ENTRANCE}</li> + * <li>{@link #UI_STYLE_ACTIVITY_ROOT}</li> + * </ul> + */ + public static final String EXTRA_UI_STYLE = "uiStyle"; + + /** + * This is the case that we use GuidedStepFragment to replace another existing + * GuidedStepFragment when moving forward to next step. Default behavior of this style is: + * <ul> + * <li>Enter transition slides in from END(right), exit transition same as + * {@link #UI_STYLE_ENTRANCE}. + * </li> + * </ul> + */ + public static final int UI_STYLE_REPLACE = 0; + + /** + * Default value for argument {@link #EXTRA_UI_STYLE}. The default value is assigned in + * GuidedStepFragment constructor. This is the case that we show GuidedStepFragment on top of + * other content. The default behavior of this style: + * <ul> + * <li>Enter transition slides in from two sides, exit transition slide out to START(left). + * Background will be faded in. Note: Changing exit transition by UI style is not working + * because fragment transition asks for exit transition before UI style is restored in Fragment + * .onCreate().</li> + * </ul> + */ + public static final int UI_STYLE_ENTRANCE = 1; + + /** + * One possible value of argument {@link #EXTRA_UI_STYLE}. This is the case that we show first + * GuidedStepFragment in a separate activity. The default behavior of this style: + * <ul> + * <li>Enter transition is assigned null (will rely on activity transition), exit transition is + * same as {@link #UI_STYLE_ENTRANCE}. Note: Changing exit transition by UI style is not working + * because fragment transition asks for exit transition before UI style is restored in + * Fragment.onCreate().</li> + * </ul> + */ + public static final int UI_STYLE_ACTIVITY_ROOT = 2; + private static final String TAG = "GuidedStepFragment"; - private static final boolean DEBUG = true; - private static final int ANIMATION_FRAGMENT_ENTER = 1; - private static final int ANIMATION_FRAGMENT_EXIT = 2; - private static final int ANIMATION_FRAGMENT_ENTER_POP = 3; - private static final int ANIMATION_FRAGMENT_EXIT_POP = 4; + private static final boolean DEBUG = false; private int mTheme; + private ContextThemeWrapper mThemeWrapper; private GuidanceStylist mGuidanceStylist; private GuidedActionsStylist mActionsStylist; + private GuidedActionsStylist mButtonActionsStylist; private GuidedActionAdapter mAdapter; - private VerticalGridView mListView; + private GuidedActionAdapter mButtonAdapter; + private GuidedActionAdapterGroup mAdapterGroup; private List<GuidedAction> mActions = new ArrayList<GuidedAction>(); + private List<GuidedAction> mButtonActions = new ArrayList<GuidedAction>(); private int mSelectedIndex = -1; - private boolean mEntryTransitionPerformed; - private boolean mEntryTransitionEnabled = true; + private int mButtonSelectedIndex = -1; public GuidedStepFragment() { // We need to supply the theme before any potential call to onInflate in order @@ -143,6 +225,8 @@ public class GuidedStepFragment extends Fragment implements GuidedActionAdapter. mTheme = onProvideTheme(); mGuidanceStylist = onCreateGuidanceStylist(); mActionsStylist = onCreateActionsStylist(); + mButtonActionsStylist = onCreateButtonActionsStylist(); + onProvideFragmentTransitions(); } /** @@ -164,6 +248,15 @@ public class GuidedStepFragment extends Fragment implements GuidedActionAdapter. } /** + * Creates the presenter used to style a sided actions panel for button only. + * The default implementation returns a basic GuidedActionsStylist. + * @return The GuidedActionsStylist used in this fragment. + */ + public GuidedActionsStylist onCreateButtonActionsStylist() { + return new GuidedActionsStylist(); + } + + /** * Returns the theme used for styling the fragment. The default returns -1, indicating that the * host Activity's theme should be used. * @return The theme resource ID of the theme to use in this fragment, or -1 to use the @@ -195,6 +288,16 @@ public class GuidedStepFragment extends Fragment implements GuidedActionAdapter. } /** + * Fills out the set of actions shown at right available to the user. This hook is called during + * {@link #onCreate}. The default leaves the list of actions empty; subclasses may override. + * @param actions A non-null, empty list ready to be populated. + * @param savedInstanceState The saved instance state from onCreate. + */ + public void onCreateButtonActions(@NonNull List<GuidedAction> actions, + Bundle savedInstanceState) { + } + + /** * Callback invoked when an action is taken by the user. Subclasses should override in * order to act on the user's decisions. * @param action The chosen action. @@ -211,8 +314,34 @@ public class GuidedStepFragment extends Fragment implements GuidedActionAdapter. } /** + * Callback invoked when an action's title or description has been edited. + * Override {@link #onGuidedActionEditedAndProceed(GuidedAction)} instead of app wants to + * control the next action to focus on. + */ + public void onGuidedActionEdited(GuidedAction action) { + } + + /** + * Callback invoked when an action's title or description has been edited. Default + * implementation calls {@link #onGuidedActionEdited(GuidedAction)} and returns + * {@link GuidedAction#ACTION_ID_NEXT}. + * + * @param action The action that has been edited. + * @return ID of the action will be focused or {@link GuidedAction#ACTION_ID_NEXT}, + * {@link GuidedAction#ACTION_ID_CURRENT}. + */ + public long onGuidedActionEditedAndProceed(GuidedAction action) { + onGuidedActionEdited(action); + return GuidedAction.ACTION_ID_NEXT; + } + + /** * Adds the specified GuidedStepFragment to the fragment stack, replacing any existing - * GuidedStepFragments in the stack, and configuring the fragment-to-fragment custom animations. + * GuidedStepFragments in the stack, and configuring the fragment-to-fragment custom + * transitions. A backstack entry is added, so the fragment will be dismissed when BACK key + * is pressed. + * <li>If current fragment on stack is GuidedStepFragment: assign {@link #UI_STYLE_REPLACE} + * <li>If current fragment on stack is not GuidedStepFragment: assign {@link #UI_STYLE_ENTRANCE} * <p> * Note: currently fragments added using this method must be created programmatically rather * than via XML. @@ -224,19 +353,165 @@ public class GuidedStepFragment extends Fragment implements GuidedActionAdapter. return add(fragmentManager, fragment, android.R.id.content); } - // Note, this method used to be public, but I haven't found a good way for a client - // to specify an id. - private static int add(FragmentManager fm, GuidedStepFragment f, int id) { - boolean inGuidedStep = getCurrentGuidedStepFragment(fm) != null; - FragmentTransaction ft = fm.beginTransaction(); + /** + * Adds the specified GuidedStepFragment to the fragment stack, replacing any existing + * GuidedStepFragments in the stack, and configuring the fragment-to-fragment custom + * transitions. A backstack entry is added, so the fragment will be dismissed when BACK key + * is pressed. + * <li>If current fragment on stack is GuidedStepFragment: assign {@link #UI_STYLE_REPLACE} and + * {@link #onAddSharedElementTransition(FragmentTransaction, GuidedStepFragment)} will be called + * to perform shared element transition between GuidedStepFragments. + * <li>If current fragment on stack is not GuidedStepFragment: assign {@link #UI_STYLE_ENTRANCE} + * <p> + * Note: currently fragments added using this method must be created programmatically rather + * than via XML. + * @param fragmentManager The FragmentManager to be used in the transaction. + * @param fragment The GuidedStepFragment to be inserted into the fragment stack. + * @param id The id of container to add GuidedStepFragment, can be android.R.id.content. + * @return The ID returned by the call FragmentTransaction.replace. + */ + public static int add(FragmentManager fragmentManager, GuidedStepFragment fragment, int id) { + GuidedStepFragment current = getCurrentGuidedStepFragment(fragmentManager); + boolean inGuidedStep = current != null; + FragmentTransaction ft = fragmentManager.beginTransaction(); + + fragment.setUiStyle(inGuidedStep ? UI_STYLE_REPLACE : UI_STYLE_ENTRANCE); + ft.addToBackStack(fragment.generateStackEntryName()); + if (current != null) { + fragment.onAddSharedElementTransition(ft, current); + } + return ft.replace(id, fragment, TAG_LEAN_BACK_ACTIONS_FRAGMENT).commit(); + } + + /** + * Called when this fragment is added to FragmentTransaction with {@link #UI_STYLE_REPLACE} (aka + * when the GuidedStepFragment replacing an existing GuidedStepFragment). Default implementation + * establishes connections between action background views to morph action background bounds + * change from disappearing GuidedStepFragment into this GuidedStepFragment. The default + * implementation heavily relies on {@link GuidedActionsStylist}'s layout, app may override this + * method when modifying the default layout of {@link GuidedActionsStylist}. + * + * @see GuidedActionsStylist + * @see #onProvideFragmentTransitions() + * @param ft The FragmentTransaction to add shared element. + * @param disappearing The disappearing fragment. + */ + protected void onAddSharedElementTransition(FragmentTransaction ft, GuidedStepFragment + disappearing) { + TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById( + R.id.action_fragment_root), "action_fragment_root"); + TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById( + R.id.action_fragment_background), "action_fragment_background"); + TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById( + R.id.action_fragment), "action_fragment"); + TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById( + R.id.guidedactions_root), "guidedactions_root"); + TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById( + R.id.guidedactions_selector), "guidedactions_selector"); + TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById( + R.id.guidedactions_content), "guidedactions_content"); + TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById( + R.id.guidedactions_list_background), "guidedactions_list_background"); + TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById( + R.id.guidedactions_root2), "guidedactions_root2"); + TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById( + R.id.guidedactions_selector2), "guidedactions_selector2"); + TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById( + R.id.guidedactions_content2), "guidedactions_content2"); + TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById( + R.id.guidedactions_list_background2), "guidedactions_list_background2"); + } + + /** + * Returns BackStackEntry name for the GuidedStepFragment or empty String if no entry is + * associated. Note {@link #UI_STYLE_ACTIVITY_ROOT} will return empty String. The method + * returns undefined value if the fragment is not in FragmentManager. + * @return BackStackEntry name for the GuidedStepFragment or empty String if no entry is + * associated. + */ + public String generateStackEntryName() { + return generateStackEntryName(getUiStyle(), getClass()); + } + + /** + * Generates BackStackEntry name for GuidedStepFragment class or empty String if no entry is + * associated. Note {@link #UI_STYLE_ACTIVITY_ROOT} is not allowed and returns empty String. + * @param uiStyle {@link #UI_STYLE_REPLACE} or {@link #UI_STYLE_ENTRANCE} + * @return BackStackEntry name for the GuidedStepFragment or empty String if no entry is + * associated. + */ + public static String generateStackEntryName(int uiStyle, Class guidedStepFragmentClass) { + if (!GuidedStepFragment.class.isAssignableFrom(guidedStepFragmentClass)) { + return ""; + } + switch (uiStyle) { + case UI_STYLE_REPLACE: + return ENTRY_NAME_REPLACE + guidedStepFragmentClass.getName(); + case UI_STYLE_ENTRANCE: + return ENTRY_NAME_ENTRANCE + guidedStepFragmentClass.getName(); + case UI_STYLE_ACTIVITY_ROOT: + default: + return ""; + } + } + + /** + * Returns true if the backstack represents GuidedStepFragment with {@link #UI_STYLE_ENTRANCE}; + * false otherwise. + * @param backStackEntryName Name of BackStackEntry. + * @return True if the backstack represents GuidedStepFragment with {@link #UI_STYLE_ENTRANCE}; + * false otherwise. + */ + public static boolean isUiStyleEntrance(String backStackEntryName) { + return backStackEntryName != null && backStackEntryName.startsWith(ENTRY_NAME_ENTRANCE); + } + + /** + * Returns true if the backstack represents GuidedStepFragment with {@link #UI_STYLE_REPLACE}; + * false otherwise. + * @param backStackEntryName Name of BackStackEntry. + * @return True if the backstack represents GuidedStepFragment with {@link #UI_STYLE_REPLACE}; + * false otherwise. + */ + public static boolean isUiStyleDefault(String backStackEntryName) { + return backStackEntryName != null && backStackEntryName.startsWith(ENTRY_NAME_REPLACE); + } - if (inGuidedStep) { - ft.setCustomAnimations(ANIMATION_FRAGMENT_ENTER, - ANIMATION_FRAGMENT_EXIT, ANIMATION_FRAGMENT_ENTER_POP, - ANIMATION_FRAGMENT_EXIT_POP); - ft.addToBackStack(null); + /** + * Extract Class name from BackStackEntry name. + * @param backStackEntryName Name of BackStackEntry. + * @return Class name of GuidedStepFragment. + */ + public static String getGuidedStepFragmentClassName(String backStackEntryName) { + if (backStackEntryName.startsWith(ENTRY_NAME_REPLACE)) { + return backStackEntryName.substring(ENTRY_NAME_REPLACE.length()); + } else if (backStackEntryName.startsWith(ENTRY_NAME_ENTRANCE)) { + return backStackEntryName.substring(ENTRY_NAME_ENTRANCE.length()); + } else { + return ""; } - return ft.replace(id, f, TAG_LEAN_BACK_ACTIONS_FRAGMENT).commit(); + } + + /** + * Adds the specified GuidedStepFragment as content of Activity; no backstack entry is added so + * the activity will be dismissed when BACK key is pressed. + * {@link #UI_STYLE_ACTIVITY_ROOT} is assigned. + * + * Note: currently fragments added using this method must be created programmatically rather + * than via XML. + * @param activity The Activity to be used to insert GuidedstepFragment. + * @param fragment The GuidedStepFragment to be inserted into the fragment stack. + * @param id The id of container to add GuidedStepFragment, can be android.R.id.content. + * @return The ID returned by the call FragmentTransaction.replace. + */ + public static int addAsRoot(Activity activity, GuidedStepFragment fragment, int id) { + // Workaround b/23764120: call getDecorView() to force requestFeature of ActivityTransition. + activity.getWindow().getDecorView(); + + FragmentManager fragmentManager = activity.getFragmentManager(); + FragmentTransaction ft = fragmentManager.beginTransaction(); + fragment.setUiStyle(UI_STYLE_ACTIVITY_ROOT); + return ft.replace(id, fragment, TAG_LEAN_BACK_ACTIONS_FRAGMENT).commit(); } /** @@ -268,6 +543,99 @@ public class GuidedStepFragment extends Fragment implements GuidedActionAdapter. } /** + * Returns the list of button GuidedActions that the user may take in this fragment. + * @return The list of button GuidedActions for this fragment. + */ + public List<GuidedAction> getButtonActions() { + return mButtonActions; + } + + /** + * Find button GuidedAction by Id. + * @param id Id of the button action to search. + * @return GuidedAction object or null if not found. + */ + public GuidedAction findButtonActionById(long id) { + int index = findButtonActionPositionById(id); + return index >= 0 ? mButtonActions.get(index) : null; + } + + /** + * Find button GuidedAction position in array by Id. + * @param id Id of the button action to search. + * @return position of GuidedAction object in array or -1 if not found. + */ + public int findButtonActionPositionById(long id) { + if (mButtonActions != null) { + for (int i = 0; i < mButtonActions.size(); i++) { + GuidedAction action = mButtonActions.get(i); + if (mButtonActions.get(i).getId() == id) { + return i; + } + } + } + return -1; + } + + /** + * Returns the GuidedActionsStylist that displays the button actions the user may take. + * @return The GuidedActionsStylist for this fragment. + */ + public GuidedActionsStylist getGuidedButtonActionsStylist() { + return mButtonActionsStylist; + } + + /** + * Sets the list of button GuidedActions that the user may take in this fragment. + * @param actions The list of button GuidedActions for this fragment. + */ + public void setButtonActions(List<GuidedAction> actions) { + mButtonActions = actions; + if (mButtonAdapter != null) { + mButtonAdapter.setActions(mButtonActions); + } + } + + /** + * Notify an button action has changed and update its UI. + * @param position Position of the button GuidedAction in array. + */ + public void notifyButtonActionChanged(int position) { + if (mButtonAdapter != null) { + mButtonAdapter.notifyItemChanged(position); + } + } + + /** + * Returns the view corresponding to the button action at the indicated position in the list of + * actions for this fragment. + * @param position The integer position of the button action of interest. + * @return The View corresponding to the button action at the indicated position, or null if + * that action is not currently onscreen. + */ + public View getButtonActionItemView(int position) { + final RecyclerView.ViewHolder holder = mButtonActionsStylist.getActionsGridView() + .findViewHolderForPosition(position); + return holder == null ? null : holder.itemView; + } + + /** + * Scrolls the action list to the position indicated, selecting that button action's view. + * @param position The integer position of the button action of interest. + */ + public void setSelectedButtonActionPosition(int position) { + mButtonActionsStylist.getActionsGridView().setSelectedPosition(position); + } + + /** + * Returns the position if the currently selected button GuidedAction. + * @return position The integer position of the currently selected button action. + */ + public int getSelectedButtonActionPosition() { + return mButtonActionsStylist.getActionsGridView().getSelectedPosition(); + } + + /** * Returns the list of GuidedActions that the user may take in this fragment. * @return The list of GuidedActions for this fragment. */ @@ -276,6 +644,33 @@ public class GuidedStepFragment extends Fragment implements GuidedActionAdapter. } /** + * Find GuidedAction by Id. + * @param id Id of the action to search. + * @return GuidedAction object or null if not found. + */ + public GuidedAction findActionById(long id) { + int index = findActionPositionById(id); + return index >= 0 ? mActions.get(index) : null; + } + + /** + * Find GuidedAction position in array by Id. + * @param id Id of the action to search. + * @return position of GuidedAction object in array or -1 if not found. + */ + public int findActionPositionById(long id) { + if (mActions != null) { + for (int i = 0; i < mActions.size(); i++) { + GuidedAction action = mActions.get(i); + if (mActions.get(i).getId() == id) { + return i; + } + } + } + return -1; + } + + /** * Sets the list of GuidedActions that the user may take in this fragment. * @param actions The list of GuidedActions for this fragment. */ @@ -287,6 +682,16 @@ public class GuidedStepFragment extends Fragment implements GuidedActionAdapter. } /** + * Notify an action has changed and update its UI. + * @param position Position of the GuidedAction in array. + */ + public void notifyActionChanged(int position) { + if (mAdapter != null) { + mAdapter.notifyItemChanged(position); + } + } + + /** * Returns the view corresponding to the action at the indicated position in the list of * actions for this fragment. * @param position The integer position of the action of interest. @@ -294,7 +699,9 @@ public class GuidedStepFragment extends Fragment implements GuidedActionAdapter. * action is not currently onscreen. */ public View getActionItemView(int position) { - return mListView.findViewHolderForPosition(position).itemView; + final RecyclerView.ViewHolder holder = mActionsStylist.getActionsGridView() + .findViewHolderForPosition(position); + return holder == null ? null : holder.itemView; } /** @@ -302,7 +709,7 @@ public class GuidedStepFragment extends Fragment implements GuidedActionAdapter. * @param position The integer position of the action of interest. */ public void setSelectedActionPosition(int position) { - mListView.setSelectedPosition(position); + mActionsStylist.getActionsGridView().setSelectedPosition(position); } /** @@ -310,7 +717,126 @@ public class GuidedStepFragment extends Fragment implements GuidedActionAdapter. * @return position The integer position of the currently selected action. */ public int getSelectedActionPosition() { - return mListView.getSelectedPosition(); + return mActionsStylist.getActionsGridView().getSelectedPosition(); + } + + /** + * Called by Constructor to provide fragment transitions. The default implementation assigns + * transitions based on {@link #getUiStyle()}: + * <ul> + * <li> {@link #UI_STYLE_REPLACE} Slide from/to end(right) for enter transition, slide from/to + * start(left) for exit transition, shared element enter transition is set to ChangeBounds. + * <li> {@link #UI_STYLE_ENTRANCE} Enter transition is set to slide from both sides, exit + * transition is same as {@link #UI_STYLE_REPLACE}, no shared element enter transition. + * <li> {@link #UI_STYLE_ACTIVITY_ROOT} Enter transition is set to null and app should rely on + * activity transition, exit transition is same as {@link #UI_STYLE_REPLACE}, no shared element + * enter transition. + * </ul> + * <p> + * The default implementation heavily relies on {@link GuidedActionsStylist} and + * {@link GuidanceStylist} layout, app may override this method when modifying the default + * layout of {@link GuidedActionsStylist} or {@link GuidanceStylist}. + * <p> + * TIP: because the fragment view is removed during fragment transition, in general app cannot + * use two Visibility transition together. Workaround is to create your own Visibility + * transition that controls multiple animators (e.g. slide and fade animation in one Transition + * class). + */ + protected void onProvideFragmentTransitions() { + if (Build.VERSION.SDK_INT >= 21) { + final int uiStyle = getUiStyle(); + if (uiStyle == UI_STYLE_REPLACE) { + Object enterTransition = TransitionHelper.createFadeAndShortSlide(Gravity.END); + TransitionHelper.exclude(enterTransition, R.id.guidedstep_background, true); + TransitionHelper.setEnterTransition(this, enterTransition); + + Object changeBounds = TransitionHelper.createChangeBounds(false); + TransitionHelper.setSharedElementEnterTransition(this, changeBounds); + } else if (uiStyle == UI_STYLE_ENTRANCE) { + Object fade = TransitionHelper.createFadeTransition(TransitionHelper.FADE_IN | + TransitionHelper.FADE_OUT); + TransitionHelper.include(fade, R.id.guidedstep_background); + Object slide = TransitionHelper.createFadeAndShortSlide(Gravity.END | + Gravity.START); + TransitionHelper.include(slide, R.id.content_fragment); + TransitionHelper.include(slide, R.id.action_fragment_root); + Object enterTransition = TransitionHelper.createTransitionSet(false); + TransitionHelper.addTransition(enterTransition, fade); + TransitionHelper.addTransition(enterTransition, slide); + TransitionHelper.setEnterTransition(this, enterTransition); + + // No shared element transition + TransitionHelper.setSharedElementEnterTransition(this, null); + } else if (uiStyle == UI_STYLE_ACTIVITY_ROOT) { + // for Activity root, we dont need enter transition, use activity transition + TransitionHelper.setEnterTransition(this, null); + // No shared element transition + TransitionHelper.setSharedElementEnterTransition(this, null); + } + // exitTransition is same for all style + Object exitTransition = TransitionHelper.createFadeAndShortSlide(Gravity.START); + TransitionHelper.exclude(exitTransition, R.id.guidedstep_background, true); + TransitionHelper.setExitTransition(this, exitTransition); + } + } + + /** + * Called by onCreateView to inflate background view. Default implementation loads view + * from {@link R.layout#lb_guidedstep_background} which holds a reference to + * guidedStepBackground. + * @param inflater LayoutInflater to load background view. + * @param container Parent view of background view. + * @param savedInstanceState + * @return Created background view or null if no background. + */ + public View onCreateBackgroundView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.lb_guidedstep_background, container, false); + } + + /** + * Set UI style to fragment arguments. Default value is {@link #UI_STYLE_ENTRANCE} when fragment + * is first initialized. UI style is used to choose different fragment transition animations and + * determine if this is the first GuidedStepFragment on backstack. In most cases app does not + * directly call this method, app calls helper function + * {@link #add(FragmentManager, GuidedStepFragment, int)}. However if the app creates Fragment + * transaction and controls backstack by itself, it would need call setUiStyle() to select the + * fragment transition to use. + * + * @param style {@link #UI_STYLE_ACTIVITY_ROOT} {@link #UI_STYLE_REPLACE} or + * {@link #UI_STYLE_ENTRANCE}. + */ + public void setUiStyle(int style) { + int oldStyle = getUiStyle(); + Bundle arguments = getArguments(); + boolean isNew = false; + if (arguments == null) { + arguments = new Bundle(); + isNew = true; + } + arguments.putInt(EXTRA_UI_STYLE, style); + // call setArgument() will validate if the fragment is already added. + if (isNew) { + setArguments(arguments); + } + if (style != oldStyle) { + onProvideFragmentTransitions(); + } + } + + /** + * Read UI style from fragment arguments. Default value is {@link #UI_STYLE_ENTRANCE} when + * fragment is first initialized. UI style is used to choose different fragment transition + * animations and determine if this is the first GuidedStepFragment on backstack. + * + * @return {@link #UI_STYLE_ACTIVITY_ROOT} {@link #UI_STYLE_REPLACE} or + * {@link #UI_STYLE_ENTRANCE}. + * @see #onProvideFragmentTransitions() + */ + public int getUiStyle() { + Bundle b = getArguments(); + if (b == null) return UI_STYLE_ENTRANCE; + return b.getInt(EXTRA_UI_STYLE, UI_STYLE_ENTRANCE); } /** @@ -320,16 +846,34 @@ public class GuidedStepFragment extends Fragment implements GuidedActionAdapter. public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (DEBUG) Log.v(TAG, "onCreate"); + // Set correct transition from saved arguments. + onProvideFragmentTransitions(); Bundle state = (savedInstanceState != null) ? savedInstanceState : getArguments(); if (state != null) { if (mSelectedIndex == -1) { mSelectedIndex = state.getInt(EXTRA_ACTION_SELECTED_INDEX, -1); } - mEntryTransitionEnabled = state.getBoolean(EXTRA_ACTION_ENTRY_TRANSITION_ENABLED, true); - mEntryTransitionPerformed = state.getBoolean(EXTRA_ENTRY_TRANSITION_PERFORMED, false); } - mActions.clear(); - onCreateActions(mActions, savedInstanceState); + ArrayList<GuidedAction> actions = new ArrayList<GuidedAction>(); + onCreateActions(actions, savedInstanceState); + setActions(actions); + ArrayList<GuidedAction> buttonActions = new ArrayList<GuidedAction>(); + onCreateButtonActions(buttonActions, savedInstanceState); + setButtonActions(buttonActions); + } + + /** + * {@inheritDoc} + */ + @Override + public void onDestroyView() { + mGuidanceStylist.onDestroyView(); + mActionsStylist.onDestroyView(); + mButtonActionsStylist.onDestroyView(); + mAdapter = null; + mButtonAdapter = null; + mAdapterGroup = null; + super.onDestroyView(); } /** @@ -343,9 +887,10 @@ public class GuidedStepFragment extends Fragment implements GuidedActionAdapter. resolveTheme(); inflater = getThemeInflater(inflater); - View v = inflater.inflate(R.layout.lb_guidedstep_fragment, container, false); - ViewGroup guidanceContainer = (ViewGroup) v.findViewById(R.id.content_fragment); - ViewGroup actionContainer = (ViewGroup) v.findViewById(R.id.action_fragment); + ViewGroup root = (ViewGroup) inflater.inflate(R.layout.lb_guidedstep_fragment, + container, false); + ViewGroup guidanceContainer = (ViewGroup) root.findViewById(R.id.content_fragment); + ViewGroup actionContainer = (ViewGroup) root.findViewById(R.id.action_fragment); Guidance guidance = onCreateGuidance(savedInstanceState); View guidanceView = mGuidanceStylist.onCreateView(inflater, guidanceContainer, guidance); @@ -354,15 +899,77 @@ public class GuidedStepFragment extends Fragment implements GuidedActionAdapter. View actionsView = mActionsStylist.onCreateView(inflater, actionContainer); actionContainer.addView(actionsView); + View buttonActionsView = mButtonActionsStylist.onCreateView(inflater, actionContainer); + mButtonActionsStylist.setAsButtonActions(); + actionContainer.addView(buttonActionsView); + + GuidedActionAdapter.EditListener editListener = new GuidedActionAdapter.EditListener() { + + @Override + public void onImeOpen() { + runImeAnimations(true); + } + + @Override + public void onImeClose() { + runImeAnimations(false); + } + + @Override + public long onGuidedActionEdited(GuidedAction action) { + return GuidedStepFragment.this.onGuidedActionEditedAndProceed(action); + } + }; + mAdapter = new GuidedActionAdapter(mActions, this, this, mActionsStylist); + mButtonAdapter = new GuidedActionAdapter(mButtonActions, this, this, mButtonActionsStylist); + mAdapterGroup = new GuidedActionAdapterGroup(); + mAdapterGroup.addAdpter(mAdapter); + mAdapterGroup.addAdpter(mButtonAdapter); + mAdapterGroup.setEditListener(editListener); + + mActionsStylist.getActionsGridView().setAdapter(mAdapter); + mButtonActionsStylist.getActionsGridView().setAdapter(mButtonAdapter); + if (mButtonActions.size() == 0) { + // when there is no button actions, we dont need show the second panel, but keep + // the width zero to run ChangeBounds transition. + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) + buttonActionsView.getLayoutParams(); + lp.weight = 0; + buttonActionsView.setLayoutParams(lp); + } else { + // when there are two actions panel, we need adjust the weight of action to + // guidedActionContentWidthWeightTwoPanels. + Context ctx = mThemeWrapper != null ? mThemeWrapper : getActivity(); + TypedValue typedValue = new TypedValue(); + if (ctx.getTheme().resolveAttribute(R.attr.guidedActionContentWidthWeightTwoPanels, + typedValue, true)) { + View actionsRoot = root.findViewById(R.id.action_fragment_root); + float weight = typedValue.getFloat(); + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) actionsRoot + .getLayoutParams(); + lp.weight = weight; + actionsRoot.setLayoutParams(lp); + } + } - mListView = mActionsStylist.getActionsGridView(); - mListView.setAdapter(mAdapter); int pos = (mSelectedIndex >= 0 && mSelectedIndex < mActions.size()) ? mSelectedIndex : getFirstCheckedAction(); - mListView.setSelectedPosition(pos); + setSelectedActionPosition(pos); + + setSelectedButtonActionPosition(0); - return v; + View backgroundView = onCreateBackgroundView(inflater, root, savedInstanceState); + if (backgroundView != null) { + root.addView(backgroundView, 0); + } + return root; + } + + @Override + public void onResume() { + super.onResume(); + mActionsStylist.getActionsGridView().requestFocus(); } /** @@ -372,84 +979,70 @@ public class GuidedStepFragment extends Fragment implements GuidedActionAdapter. public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt(EXTRA_ACTION_SELECTED_INDEX, - (mListView != null) ? getSelectedActionPosition() : mSelectedIndex); - outState.putBoolean(EXTRA_ACTION_ENTRY_TRANSITION_ENABLED, mEntryTransitionEnabled); - outState.putBoolean(EXTRA_ENTRY_TRANSITION_PERFORMED, mEntryTransitionPerformed); + (mActionsStylist.getActionsGridView() != null) ? + getSelectedActionPosition() : mSelectedIndex); } - /** - * {@inheritDoc} - */ - @Override - public void onStart() { - if (DEBUG) Log.v(TAG, "onStart"); - super.onStart(); - if (isEntryTransitionEnabled() && !mEntryTransitionPerformed) { - mEntryTransitionPerformed = true; - performEntryTransition(); - } + private static boolean isGuidedStepTheme(Context context) { + int resId = R.attr.guidedStepThemeFlag; + TypedValue typedValue = new TypedValue(); + boolean found = context.getTheme().resolveAttribute(resId, typedValue, true); + if (DEBUG) Log.v(TAG, "Found guided step theme flag? " + found); + return found && typedValue.type == TypedValue.TYPE_INT_BOOLEAN && typedValue.data != 0; } /** - * {@inheritDoc} + * Convenient method to close GuidedStepFragments on top of other content or finish Activity if + * GuidedStepFragments were started in a separate activity. Pops all stack entries including + * {@link #UI_STYLE_ENTRANCE}; if {@link #UI_STYLE_ENTRANCE} is not found, finish the activity. + * Note that this method must be paired with {@link #add(FragmentManager, GuidedStepFragment, + * int)} which sets up the stack entry name for finding which fragment we need to pop back to. */ - @Override - public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) { - if (DEBUG) Log.v(TAG, "onCreateAnimator: " + transit + " " + enter + " " + nextAnim); - View mainView = getView(); - - ArrayList<Animator> animators = new ArrayList<Animator>(); - switch (nextAnim) { - case ANIMATION_FRAGMENT_ENTER: - mGuidanceStylist.onFragmentEnter(animators); - mActionsStylist.onFragmentEnter(animators); - break; - case ANIMATION_FRAGMENT_EXIT: - mGuidanceStylist.onFragmentExit(animators); - mActionsStylist.onFragmentExit(animators); - break; - case ANIMATION_FRAGMENT_ENTER_POP: - mGuidanceStylist.onFragmentReenter(animators); - mActionsStylist.onFragmentReenter(animators); - break; - case ANIMATION_FRAGMENT_EXIT_POP: - mGuidanceStylist.onFragmentReturn(animators); - mActionsStylist.onFragmentReturn(animators); - break; - default: - return super.onCreateAnimator(transit, enter, nextAnim); + public void finishGuidedStepFragments() { + final FragmentManager fragmentManager = getFragmentManager(); + final int entryCount = fragmentManager.getBackStackEntryCount(); + if (entryCount > 0) { + for (int i = entryCount - 1; i >= 0; i--) { + BackStackEntry entry = fragmentManager.getBackStackEntryAt(i); + if (isUiStyleEntrance(entry.getName())) { + GuidedStepFragment top = getCurrentGuidedStepFragment(fragmentManager); + if (top != null) { + top.setUiStyle(UI_STYLE_ENTRANCE); + } + fragmentManager.popBackStack(entry.getId(), + FragmentManager.POP_BACK_STACK_INCLUSIVE); + return; + } + } } - - mEntryTransitionPerformed = true; - return createDummyAnimator(mainView, animators); + ActivityCompat.finishAfterTransition(getActivity()); } /** - * Returns whether entry transitions are enabled for this fragment. - * @return Whether entry transitions are enabled for this fragment. + * Convenient method to pop to fragment with Given class. + * @param guidedStepFragmentClass Name of the Class of GuidedStepFragment to pop to. + * @param flags Either 0 or {@link FragmentManager#POP_BACK_STACK_INCLUSIVE}. */ - protected boolean isEntryTransitionEnabled() { - return mEntryTransitionEnabled; - } - - /** - * Sets whether entry transitions are enabled for this fragment. - * @param enabled Whether to enable entry transitions for this fragment. - */ - protected void setEntryTransitionEnabled(boolean enabled) { - mEntryTransitionEnabled = enabled; - } - - private boolean isGuidedStepTheme(Context context) { - int resId = R.attr.guidedStepThemeFlag; - TypedValue typedValue = new TypedValue(); - boolean found = context.getTheme().resolveAttribute(resId, typedValue, true); - if (DEBUG) Log.v(TAG, "Found guided step theme flag? " + found); - return found && typedValue.type == TypedValue.TYPE_INT_BOOLEAN && typedValue.data != 0; + public void popBackStackToGuidedStepFragment(Class guidedStepFragmentClass, int flags) { + if (!GuidedStepFragment.class.isAssignableFrom(guidedStepFragmentClass)) { + return; + } + final FragmentManager fragmentManager = getFragmentManager(); + final int entryCount = fragmentManager.getBackStackEntryCount(); + String className = guidedStepFragmentClass.getName(); + if (entryCount > 0) { + for (int i = entryCount - 1; i >= 0; i--) { + BackStackEntry entry = fragmentManager.getBackStackEntryAt(i); + String entryClassName = getGuidedStepFragmentClassName(entry.getName()); + if (className.equals(entryClassName)) { + fragmentManager.popBackStack(entry.getId(), flags); + return; + } + } + } } private void resolveTheme() { - boolean hasThemeReference = true; // Look up the guidedStepTheme in the currently specified theme. If it exists, // replace the theme with its value. Activity activity = getActivity(); @@ -461,15 +1054,21 @@ public class GuidedStepFragment extends Fragment implements GuidedActionAdapter. boolean found = activity.getTheme().resolveAttribute(resId, typedValue, true); if (DEBUG) Log.v(TAG, "Found guided step theme reference? " + found); if (found) { - if (isGuidedStepTheme(new ContextThemeWrapper(activity, typedValue.resourceId))) { + ContextThemeWrapper themeWrapper = + new ContextThemeWrapper(activity, typedValue.resourceId); + if (isGuidedStepTheme(themeWrapper)) { mTheme = typedValue.resourceId; + mThemeWrapper = themeWrapper; } else { found = false; + mThemeWrapper = null; } } if (!found) { Log.e(TAG, "GuidedStepFragment does not have an appropriate theme set."); } + } else if (mTheme != -1) { + mThemeWrapper = new ContextThemeWrapper(activity, mTheme); } } @@ -477,8 +1076,7 @@ public class GuidedStepFragment extends Fragment implements GuidedActionAdapter. if (mTheme == -1) { return inflater; } else { - Context ctw = new ContextThemeWrapper(getActivity(), mTheme); - return inflater.cloneInContext(ctw); + return inflater.cloneInContext(mThemeWrapper); } } @@ -491,41 +1089,20 @@ public class GuidedStepFragment extends Fragment implements GuidedActionAdapter. return 0; } - private void performEntryTransition() { - if (DEBUG) Log.v(TAG, "performEntryTransition"); - final View mainView = getView(); - - mainView.setVisibility(View.INVISIBLE); - + private void runImeAnimations(boolean entering) { ArrayList<Animator> animators = new ArrayList<Animator>(); - mGuidanceStylist.onActivityEnter(animators); - mActionsStylist.onActivityEnter(animators); - - final Animator animator = createDummyAnimator(mainView, animators); - - // We need to defer the animation until the first layout has occurred, as we don't yet - // know the final locations of views. - mainView.getViewTreeObserver().addOnGlobalLayoutListener( - new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - mainView.getViewTreeObserver().removeOnGlobalLayoutListener(this); - if (!isAdded()) { - // We have been detached before this could run, - // so just bail - return; - } - - mainView.setVisibility(View.VISIBLE); - animator.start(); - } - }); - } - - private Animator createDummyAnimator(final View v, ArrayList<Animator> animators) { - final AnimatorSet animatorSet = new AnimatorSet(); - animatorSet.playTogether(animators); - return new UntargetableAnimatorSet(animatorSet); + if (entering) { + mGuidanceStylist.onImeAppearing(animators); + mActionsStylist.onImeAppearing(animators); + mButtonActionsStylist.onImeAppearing(animators); + } else { + mGuidanceStylist.onImeDisappearing(animators); + mActionsStylist.onImeDisappearing(animators); + mButtonActionsStylist.onImeDisappearing(animators); + } + AnimatorSet set = new AnimatorSet(); + set.playTogether(animators); + set.start(); } } diff --git a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java new file mode 100644 index 0000000000..ad186ff1b7 --- /dev/null +++ b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java @@ -0,0 +1,1110 @@ +/* This file is auto-generated from GuidedStepFragment.java. DO NOT MODIFY. */ + +/* + * Copyright (C) 2015 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.animation.Animator; +import android.animation.AnimatorSet; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentManager.BackStackEntry; +import android.support.v4.app.FragmentTransaction; +import android.content.Context; +import android.content.res.TypedArray; +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v17.leanback.transition.TransitionHelper; +import android.support.v17.leanback.R; +import android.support.v17.leanback.widget.GuidanceStylist; +import android.support.v17.leanback.widget.GuidanceStylist.Guidance; +import android.support.v17.leanback.widget.GuidedAction; +import android.support.v17.leanback.widget.GuidedActionsStylist; +import android.support.v17.leanback.widget.VerticalGridView; +import android.support.v4.app.ActivityCompat; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.util.Log; +import android.util.TypedValue; +import android.view.ContextThemeWrapper; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.LinearLayout; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.List; + +/** + * A GuidedStepSupportFragment is used to guide the user through a decision or series of decisions. + * It is composed of a guidance view on the left and a view on the right containing a list of + * possible actions. + * <p> + * <h3>Basic Usage</h3> + * <p> + * Clients of GuidedStepSupportFragment must create a custom subclass to attach to their Activities. + * This custom subclass provides the information necessary to construct the user interface and + * respond to user actions. At a minimum, subclasses should override: + * <ul> + * <li>{@link #onCreateGuidance}, to provide instructions to the user</li> + * <li>{@link #onCreateActions}, to provide a set of {@link GuidedAction}s the user can take</li> + * <li>{@link #onGuidedActionClicked}, to respond to those actions</li> + * </ul> + * <p> + * Clients use following helper functions to add GuidedStepSupportFragment to Activity or FragmentManager: + * <ul> + * <li>{@link #addAsRoot(FragmentActivity, GuidedStepSupportFragment, int)}, to be called during Activity onCreate, + * adds GuidedStepSupportFragment as the first Fragment in activity.</li> + * <li>{@link #add(FragmentManager, GuidedStepSupportFragment)} or {@link #add(FragmentManager, + * GuidedStepSupportFragment, int)}, to add GuidedStepSupportFragment on top of existing Fragments or + * replacing existing GuidedStepSupportFragment when moving forward to next step.</li> + * <li>{@link #finishGuidedStepSupportFragments()} can either finish the activity or pop all + * GuidedStepSupportFragment from stack. + * <li>If app chooses not to use the helper function, it is the app's responsibility to call + * {@link #setUiStyle(int)} to select fragment transition and remember the stack entry where it + * need pops to. + * </ul> + * <h3>Theming and Stylists</h3> + * <p> + * GuidedStepSupportFragment delegates its visual styling to classes called stylists. The {@link + * GuidanceStylist} is responsible for the left guidance view, while the {@link + * GuidedActionsStylist} is responsible for the right actions view. The stylists use theme + * attributes to derive values associated with the presentation, such as colors, animations, etc. + * Most simple visual aspects of GuidanceStylist and GuidedActionsStylist can be customized + * via theming; see their documentation for more information. + * <p> + * GuidedStepSupportFragments must have access to an appropriate theme in order for the stylists to + * function properly. Specifically, the fragment must receive {@link + * android.support.v17.leanback.R.style#Theme_Leanback_GuidedStep}, or a theme whose parent is + * is set to that theme. Themes can be provided in one of three ways: + * <ul> + * <li>The simplest way is to set the theme for the host Activity to the GuidedStep theme or a + * theme that derives from it.</li> + * <li>If the Activity already has a theme and setting its parent theme is inconvenient, the + * existing Activity theme can have an entry added for the attribute {@link + * android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepTheme}. If present, + * this theme will be used by GuidedStepSupportFragment as an overlay to the Activity's theme.</li> + * <li>Finally, custom subclasses of GuidedStepSupportFragment may provide a theme through the {@link + * #onProvideTheme} method. This can be useful if a subclass is used across multiple + * Activities.</li> + * </ul> + * <p> + * If the theme is provided in multiple ways, the onProvideTheme override has priority, followed by + * the Activty's theme. (Themes whose parent theme is already set to the guided step theme do not + * need to set the guidedStepTheme attribute; if set, it will be ignored.) + * <p> + * If themes do not provide enough customizability, the stylists themselves may be subclassed and + * provided to the GuidedStepSupportFragment through the {@link #onCreateGuidanceStylist} and {@link + * #onCreateActionsStylist} methods. The stylists have simple hooks so that subclasses + * may override layout files; subclasses may also have more complex logic to determine styling. + * <p> + * <h3>Guided sequences</h3> + * <p> + * GuidedStepSupportFragments can be grouped together to provide a guided sequence. GuidedStepSupportFragments + * grouped as a sequence use custom animations provided by {@link GuidanceStylist} and + * {@link GuidedActionsStylist} (or subclasses) during transitions between steps. Clients + * should use {@link #add} to place subsequent GuidedFragments onto the fragment stack so that + * custom animations are properly configured. (Custom animations are triggered automatically when + * the fragment stack is subsequently popped by any normal mechanism.) + * <p> + * <i>Note: Currently GuidedStepSupportFragments grouped in this way must all be defined programmatically, + * rather than in XML. This restriction may be removed in the future.</i> + * + * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepTheme + * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepBackground + * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionContentWidthWeight + * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionContentWidthWeightTwoPanels + * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsBackground + * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsBackgroundDark + * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsElevation + * @see GuidanceStylist + * @see GuidanceStylist.Guidance + * @see GuidedAction + * @see GuidedActionsStylist + */ +public class GuidedStepSupportFragment extends Fragment implements GuidedActionAdapter.ClickListener, + GuidedActionAdapter.FocusListener { + + private static final String TAG_LEAN_BACK_ACTIONS_FRAGMENT = "leanBackGuidedStepSupportFragment"; + private static final String EXTRA_ACTION_SELECTED_INDEX = "selectedIndex"; + + private static final String ENTRY_NAME_REPLACE = "GuidedStepDefault"; + + private static final String ENTRY_NAME_ENTRANCE = "GuidedStepEntrance"; + + /** + * Fragment argument name for UI style. The argument value is persisted in fragment state. + * The value is initially {@link #UI_STYLE_ENTRANCE} and might be changed in one of the three + * helper functions: + * <ul> + * <li>{@link #addAsRoot(FragmentActivity, GuidedStepSupportFragment, int)}</li> + * <li>{@link #add(FragmentManager, GuidedStepSupportFragment)} or {@link #add(FragmentManager, + * GuidedStepSupportFragment, int)}</li> + * </ul> + * <p> + * Argument value can be either: + * <ul> + * <li>{@link #UI_STYLE_REPLACE}</li> + * <li>{@link #UI_STYLE_ENTRANCE}</li> + * <li>{@link #UI_STYLE_ACTIVITY_ROOT}</li> + * </ul> + */ + public static final String EXTRA_UI_STYLE = "uiStyle"; + + /** + * This is the case that we use GuidedStepSupportFragment to replace another existing + * GuidedStepSupportFragment when moving forward to next step. Default behavior of this style is: + * <ul> + * <li>Enter transition slides in from END(right), exit transition same as + * {@link #UI_STYLE_ENTRANCE}. + * </li> + * </ul> + */ + public static final int UI_STYLE_REPLACE = 0; + + /** + * Default value for argument {@link #EXTRA_UI_STYLE}. The default value is assigned in + * GuidedStepSupportFragment constructor. This is the case that we show GuidedStepSupportFragment on top of + * other content. The default behavior of this style: + * <ul> + * <li>Enter transition slides in from two sides, exit transition slide out to START(left). + * Background will be faded in. Note: Changing exit transition by UI style is not working + * because fragment transition asks for exit transition before UI style is restored in Fragment + * .onCreate().</li> + * </ul> + */ + public static final int UI_STYLE_ENTRANCE = 1; + + /** + * One possible value of argument {@link #EXTRA_UI_STYLE}. This is the case that we show first + * GuidedStepSupportFragment in a separate activity. The default behavior of this style: + * <ul> + * <li>Enter transition is assigned null (will rely on activity transition), exit transition is + * same as {@link #UI_STYLE_ENTRANCE}. Note: Changing exit transition by UI style is not working + * because fragment transition asks for exit transition before UI style is restored in + * Fragment.onCreate().</li> + * </ul> + */ + public static final int UI_STYLE_ACTIVITY_ROOT = 2; + + private static final String TAG = "GuidedStepSupportFragment"; + private static final boolean DEBUG = false; + + private int mTheme; + private ContextThemeWrapper mThemeWrapper; + private GuidanceStylist mGuidanceStylist; + private GuidedActionsStylist mActionsStylist; + private GuidedActionsStylist mButtonActionsStylist; + private GuidedActionAdapter mAdapter; + private GuidedActionAdapter mButtonAdapter; + private GuidedActionAdapterGroup mAdapterGroup; + private List<GuidedAction> mActions = new ArrayList<GuidedAction>(); + private List<GuidedAction> mButtonActions = new ArrayList<GuidedAction>(); + private int mSelectedIndex = -1; + private int mButtonSelectedIndex = -1; + + public GuidedStepSupportFragment() { + // We need to supply the theme before any potential call to onInflate in order + // for the defaulting to work properly. + mTheme = onProvideTheme(); + mGuidanceStylist = onCreateGuidanceStylist(); + mActionsStylist = onCreateActionsStylist(); + mButtonActionsStylist = onCreateButtonActionsStylist(); + onProvideFragmentTransitions(); + } + + /** + * Creates the presenter used to style the guidance panel. The default implementation returns + * a basic GuidanceStylist. + * @return The GuidanceStylist used in this fragment. + */ + public GuidanceStylist onCreateGuidanceStylist() { + return new GuidanceStylist(); + } + + /** + * Creates the presenter used to style the guided actions panel. The default implementation + * returns a basic GuidedActionsStylist. + * @return The GuidedActionsStylist used in this fragment. + */ + public GuidedActionsStylist onCreateActionsStylist() { + return new GuidedActionsStylist(); + } + + /** + * Creates the presenter used to style a sided actions panel for button only. + * The default implementation returns a basic GuidedActionsStylist. + * @return The GuidedActionsStylist used in this fragment. + */ + public GuidedActionsStylist onCreateButtonActionsStylist() { + return new GuidedActionsStylist(); + } + + /** + * Returns the theme used for styling the fragment. The default returns -1, indicating that the + * host Activity's theme should be used. + * @return The theme resource ID of the theme to use in this fragment, or -1 to use the + * host Activity's theme. + */ + public int onProvideTheme() { + return -1; + } + + /** + * Returns the information required to provide guidance to the user. This hook is called during + * {@link #onCreateView}. May be overridden to return a custom subclass of {@link + * GuidanceStylist.Guidance} for use in a subclass of {@link GuidanceStylist}. The default + * returns a Guidance object with empty fields; subclasses should override. + * @param savedInstanceState The saved instance state from onCreateView. + * @return The Guidance object representing the information used to guide the user. + */ + public @NonNull Guidance onCreateGuidance(Bundle savedInstanceState) { + return new Guidance("", "", "", null); + } + + /** + * Fills out the set of actions available to the user. This hook is called during {@link + * #onCreate}. The default leaves the list of actions empty; subclasses should override. + * @param actions A non-null, empty list ready to be populated. + * @param savedInstanceState The saved instance state from onCreate. + */ + public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) { + } + + /** + * Fills out the set of actions shown at right available to the user. This hook is called during + * {@link #onCreate}. The default leaves the list of actions empty; subclasses may override. + * @param actions A non-null, empty list ready to be populated. + * @param savedInstanceState The saved instance state from onCreate. + */ + public void onCreateButtonActions(@NonNull List<GuidedAction> actions, + Bundle savedInstanceState) { + } + + /** + * Callback invoked when an action is taken by the user. Subclasses should override in + * order to act on the user's decisions. + * @param action The chosen action. + */ + @Override + public void onGuidedActionClicked(GuidedAction action) { + } + + /** + * Callback invoked when an action is focused (made to be the current selection) by the user. + */ + @Override + public void onGuidedActionFocused(GuidedAction action) { + } + + /** + * Callback invoked when an action's title or description has been edited. + * Override {@link #onGuidedActionEditedAndProceed(GuidedAction)} instead of app wants to + * control the next action to focus on. + */ + public void onGuidedActionEdited(GuidedAction action) { + } + + /** + * Callback invoked when an action's title or description has been edited. Default + * implementation calls {@link #onGuidedActionEdited(GuidedAction)} and returns + * {@link GuidedAction#ACTION_ID_NEXT}. + * + * @param action The action that has been edited. + * @return ID of the action will be focused or {@link GuidedAction#ACTION_ID_NEXT}, + * {@link GuidedAction#ACTION_ID_CURRENT}. + */ + public long onGuidedActionEditedAndProceed(GuidedAction action) { + onGuidedActionEdited(action); + return GuidedAction.ACTION_ID_NEXT; + } + + /** + * Adds the specified GuidedStepSupportFragment to the fragment stack, replacing any existing + * GuidedStepSupportFragments in the stack, and configuring the fragment-to-fragment custom + * transitions. A backstack entry is added, so the fragment will be dismissed when BACK key + * is pressed. + * <li>If current fragment on stack is GuidedStepSupportFragment: assign {@link #UI_STYLE_REPLACE} + * <li>If current fragment on stack is not GuidedStepSupportFragment: assign {@link #UI_STYLE_ENTRANCE} + * <p> + * Note: currently fragments added using this method must be created programmatically rather + * than via XML. + * @param fragmentManager The FragmentManager to be used in the transaction. + * @param fragment The GuidedStepSupportFragment to be inserted into the fragment stack. + * @return The ID returned by the call FragmentTransaction.replace. + */ + public static int add(FragmentManager fragmentManager, GuidedStepSupportFragment fragment) { + return add(fragmentManager, fragment, android.R.id.content); + } + + /** + * Adds the specified GuidedStepSupportFragment to the fragment stack, replacing any existing + * GuidedStepSupportFragments in the stack, and configuring the fragment-to-fragment custom + * transitions. A backstack entry is added, so the fragment will be dismissed when BACK key + * is pressed. + * <li>If current fragment on stack is GuidedStepSupportFragment: assign {@link #UI_STYLE_REPLACE} and + * {@link #onAddSharedElementTransition(FragmentTransaction, GuidedStepSupportFragment)} will be called + * to perform shared element transition between GuidedStepSupportFragments. + * <li>If current fragment on stack is not GuidedStepSupportFragment: assign {@link #UI_STYLE_ENTRANCE} + * <p> + * Note: currently fragments added using this method must be created programmatically rather + * than via XML. + * @param fragmentManager The FragmentManager to be used in the transaction. + * @param fragment The GuidedStepSupportFragment to be inserted into the fragment stack. + * @param id The id of container to add GuidedStepSupportFragment, can be android.R.id.content. + * @return The ID returned by the call FragmentTransaction.replace. + */ + public static int add(FragmentManager fragmentManager, GuidedStepSupportFragment fragment, int id) { + GuidedStepSupportFragment current = getCurrentGuidedStepSupportFragment(fragmentManager); + boolean inGuidedStep = current != null; + FragmentTransaction ft = fragmentManager.beginTransaction(); + + fragment.setUiStyle(inGuidedStep ? UI_STYLE_REPLACE : UI_STYLE_ENTRANCE); + ft.addToBackStack(fragment.generateStackEntryName()); + if (current != null) { + fragment.onAddSharedElementTransition(ft, current); + } + return ft.replace(id, fragment, TAG_LEAN_BACK_ACTIONS_FRAGMENT).commit(); + } + + /** + * Called when this fragment is added to FragmentTransaction with {@link #UI_STYLE_REPLACE} (aka + * when the GuidedStepSupportFragment replacing an existing GuidedStepSupportFragment). Default implementation + * establishes connections between action background views to morph action background bounds + * change from disappearing GuidedStepSupportFragment into this GuidedStepSupportFragment. The default + * implementation heavily relies on {@link GuidedActionsStylist}'s layout, app may override this + * method when modifying the default layout of {@link GuidedActionsStylist}. + * + * @see GuidedActionsStylist + * @see #onProvideFragmentTransitions() + * @param ft The FragmentTransaction to add shared element. + * @param disappearing The disappearing fragment. + */ + protected void onAddSharedElementTransition(FragmentTransaction ft, GuidedStepSupportFragment + disappearing) { + TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById( + R.id.action_fragment_root), "action_fragment_root"); + TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById( + R.id.action_fragment_background), "action_fragment_background"); + TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById( + R.id.action_fragment), "action_fragment"); + TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById( + R.id.guidedactions_root), "guidedactions_root"); + TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById( + R.id.guidedactions_selector), "guidedactions_selector"); + TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById( + R.id.guidedactions_content), "guidedactions_content"); + TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById( + R.id.guidedactions_list_background), "guidedactions_list_background"); + TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById( + R.id.guidedactions_root2), "guidedactions_root2"); + TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById( + R.id.guidedactions_selector2), "guidedactions_selector2"); + TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById( + R.id.guidedactions_content2), "guidedactions_content2"); + TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById( + R.id.guidedactions_list_background2), "guidedactions_list_background2"); + } + + /** + * Returns BackStackEntry name for the GuidedStepSupportFragment or empty String if no entry is + * associated. Note {@link #UI_STYLE_ACTIVITY_ROOT} will return empty String. The method + * returns undefined value if the fragment is not in FragmentManager. + * @return BackStackEntry name for the GuidedStepSupportFragment or empty String if no entry is + * associated. + */ + public String generateStackEntryName() { + return generateStackEntryName(getUiStyle(), getClass()); + } + + /** + * Generates BackStackEntry name for GuidedStepSupportFragment class or empty String if no entry is + * associated. Note {@link #UI_STYLE_ACTIVITY_ROOT} is not allowed and returns empty String. + * @param uiStyle {@link #UI_STYLE_REPLACE} or {@link #UI_STYLE_ENTRANCE} + * @return BackStackEntry name for the GuidedStepSupportFragment or empty String if no entry is + * associated. + */ + public static String generateStackEntryName(int uiStyle, Class guidedStepFragmentClass) { + if (!GuidedStepSupportFragment.class.isAssignableFrom(guidedStepFragmentClass)) { + return ""; + } + switch (uiStyle) { + case UI_STYLE_REPLACE: + return ENTRY_NAME_REPLACE + guidedStepFragmentClass.getName(); + case UI_STYLE_ENTRANCE: + return ENTRY_NAME_ENTRANCE + guidedStepFragmentClass.getName(); + case UI_STYLE_ACTIVITY_ROOT: + default: + return ""; + } + } + + /** + * Returns true if the backstack represents GuidedStepSupportFragment with {@link #UI_STYLE_ENTRANCE}; + * false otherwise. + * @param backStackEntryName Name of BackStackEntry. + * @return True if the backstack represents GuidedStepSupportFragment with {@link #UI_STYLE_ENTRANCE}; + * false otherwise. + */ + public static boolean isUiStyleEntrance(String backStackEntryName) { + return backStackEntryName != null && backStackEntryName.startsWith(ENTRY_NAME_ENTRANCE); + } + + /** + * Returns true if the backstack represents GuidedStepSupportFragment with {@link #UI_STYLE_REPLACE}; + * false otherwise. + * @param backStackEntryName Name of BackStackEntry. + * @return True if the backstack represents GuidedStepSupportFragment with {@link #UI_STYLE_REPLACE}; + * false otherwise. + */ + public static boolean isUiStyleDefault(String backStackEntryName) { + return backStackEntryName != null && backStackEntryName.startsWith(ENTRY_NAME_REPLACE); + } + + /** + * Extract Class name from BackStackEntry name. + * @param backStackEntryName Name of BackStackEntry. + * @return Class name of GuidedStepSupportFragment. + */ + public static String getGuidedStepSupportFragmentClassName(String backStackEntryName) { + if (backStackEntryName.startsWith(ENTRY_NAME_REPLACE)) { + return backStackEntryName.substring(ENTRY_NAME_REPLACE.length()); + } else if (backStackEntryName.startsWith(ENTRY_NAME_ENTRANCE)) { + return backStackEntryName.substring(ENTRY_NAME_ENTRANCE.length()); + } else { + return ""; + } + } + + /** + * Adds the specified GuidedStepSupportFragment as content of Activity; no backstack entry is added so + * the activity will be dismissed when BACK key is pressed. + * {@link #UI_STYLE_ACTIVITY_ROOT} is assigned. + * + * Note: currently fragments added using this method must be created programmatically rather + * than via XML. + * @param activity The Activity to be used to insert GuidedstepFragment. + * @param fragment The GuidedStepSupportFragment to be inserted into the fragment stack. + * @param id The id of container to add GuidedStepSupportFragment, can be android.R.id.content. + * @return The ID returned by the call FragmentTransaction.replace. + */ + public static int addAsRoot(FragmentActivity activity, GuidedStepSupportFragment fragment, int id) { + // Workaround b/23764120: call getDecorView() to force requestFeature of ActivityTransition. + activity.getWindow().getDecorView(); + + FragmentManager fragmentManager = activity.getSupportFragmentManager(); + FragmentTransaction ft = fragmentManager.beginTransaction(); + fragment.setUiStyle(UI_STYLE_ACTIVITY_ROOT); + return ft.replace(id, fragment, TAG_LEAN_BACK_ACTIONS_FRAGMENT).commit(); + } + + /** + * Returns the current GuidedStepSupportFragment on the fragment transaction stack. + * @return The current GuidedStepSupportFragment, if any, on the fragment transaction stack. + */ + public static GuidedStepSupportFragment getCurrentGuidedStepSupportFragment(FragmentManager fm) { + Fragment f = fm.findFragmentByTag(TAG_LEAN_BACK_ACTIONS_FRAGMENT); + if (f instanceof GuidedStepSupportFragment) { + return (GuidedStepSupportFragment) f; + } + return null; + } + + /** + * Returns the GuidanceStylist that displays guidance information for the user. + * @return The GuidanceStylist for this fragment. + */ + public GuidanceStylist getGuidanceStylist() { + return mGuidanceStylist; + } + + /** + * Returns the GuidedActionsStylist that displays the actions the user may take. + * @return The GuidedActionsStylist for this fragment. + */ + public GuidedActionsStylist getGuidedActionsStylist() { + return mActionsStylist; + } + + /** + * Returns the list of button GuidedActions that the user may take in this fragment. + * @return The list of button GuidedActions for this fragment. + */ + public List<GuidedAction> getButtonActions() { + return mButtonActions; + } + + /** + * Find button GuidedAction by Id. + * @param id Id of the button action to search. + * @return GuidedAction object or null if not found. + */ + public GuidedAction findButtonActionById(long id) { + int index = findButtonActionPositionById(id); + return index >= 0 ? mButtonActions.get(index) : null; + } + + /** + * Find button GuidedAction position in array by Id. + * @param id Id of the button action to search. + * @return position of GuidedAction object in array or -1 if not found. + */ + public int findButtonActionPositionById(long id) { + if (mButtonActions != null) { + for (int i = 0; i < mButtonActions.size(); i++) { + GuidedAction action = mButtonActions.get(i); + if (mButtonActions.get(i).getId() == id) { + return i; + } + } + } + return -1; + } + + /** + * Returns the GuidedActionsStylist that displays the button actions the user may take. + * @return The GuidedActionsStylist for this fragment. + */ + public GuidedActionsStylist getGuidedButtonActionsStylist() { + return mButtonActionsStylist; + } + + /** + * Sets the list of button GuidedActions that the user may take in this fragment. + * @param actions The list of button GuidedActions for this fragment. + */ + public void setButtonActions(List<GuidedAction> actions) { + mButtonActions = actions; + if (mButtonAdapter != null) { + mButtonAdapter.setActions(mButtonActions); + } + } + + /** + * Notify an button action has changed and update its UI. + * @param position Position of the button GuidedAction in array. + */ + public void notifyButtonActionChanged(int position) { + if (mButtonAdapter != null) { + mButtonAdapter.notifyItemChanged(position); + } + } + + /** + * Returns the view corresponding to the button action at the indicated position in the list of + * actions for this fragment. + * @param position The integer position of the button action of interest. + * @return The View corresponding to the button action at the indicated position, or null if + * that action is not currently onscreen. + */ + public View getButtonActionItemView(int position) { + final RecyclerView.ViewHolder holder = mButtonActionsStylist.getActionsGridView() + .findViewHolderForPosition(position); + return holder == null ? null : holder.itemView; + } + + /** + * Scrolls the action list to the position indicated, selecting that button action's view. + * @param position The integer position of the button action of interest. + */ + public void setSelectedButtonActionPosition(int position) { + mButtonActionsStylist.getActionsGridView().setSelectedPosition(position); + } + + /** + * Returns the position if the currently selected button GuidedAction. + * @return position The integer position of the currently selected button action. + */ + public int getSelectedButtonActionPosition() { + return mButtonActionsStylist.getActionsGridView().getSelectedPosition(); + } + + /** + * Returns the list of GuidedActions that the user may take in this fragment. + * @return The list of GuidedActions for this fragment. + */ + public List<GuidedAction> getActions() { + return mActions; + } + + /** + * Find GuidedAction by Id. + * @param id Id of the action to search. + * @return GuidedAction object or null if not found. + */ + public GuidedAction findActionById(long id) { + int index = findActionPositionById(id); + return index >= 0 ? mActions.get(index) : null; + } + + /** + * Find GuidedAction position in array by Id. + * @param id Id of the action to search. + * @return position of GuidedAction object in array or -1 if not found. + */ + public int findActionPositionById(long id) { + if (mActions != null) { + for (int i = 0; i < mActions.size(); i++) { + GuidedAction action = mActions.get(i); + if (mActions.get(i).getId() == id) { + return i; + } + } + } + return -1; + } + + /** + * Sets the list of GuidedActions that the user may take in this fragment. + * @param actions The list of GuidedActions for this fragment. + */ + public void setActions(List<GuidedAction> actions) { + mActions = actions; + if (mAdapter != null) { + mAdapter.setActions(mActions); + } + } + + /** + * Notify an action has changed and update its UI. + * @param position Position of the GuidedAction in array. + */ + public void notifyActionChanged(int position) { + if (mAdapter != null) { + mAdapter.notifyItemChanged(position); + } + } + + /** + * Returns the view corresponding to the action at the indicated position in the list of + * actions for this fragment. + * @param position The integer position of the action of interest. + * @return The View corresponding to the action at the indicated position, or null if that + * action is not currently onscreen. + */ + public View getActionItemView(int position) { + final RecyclerView.ViewHolder holder = mActionsStylist.getActionsGridView() + .findViewHolderForPosition(position); + return holder == null ? null : holder.itemView; + } + + /** + * Scrolls the action list to the position indicated, selecting that action's view. + * @param position The integer position of the action of interest. + */ + public void setSelectedActionPosition(int position) { + mActionsStylist.getActionsGridView().setSelectedPosition(position); + } + + /** + * Returns the position if the currently selected GuidedAction. + * @return position The integer position of the currently selected action. + */ + public int getSelectedActionPosition() { + return mActionsStylist.getActionsGridView().getSelectedPosition(); + } + + /** + * Called by Constructor to provide fragment transitions. The default implementation assigns + * transitions based on {@link #getUiStyle()}: + * <ul> + * <li> {@link #UI_STYLE_REPLACE} Slide from/to end(right) for enter transition, slide from/to + * start(left) for exit transition, shared element enter transition is set to ChangeBounds. + * <li> {@link #UI_STYLE_ENTRANCE} Enter transition is set to slide from both sides, exit + * transition is same as {@link #UI_STYLE_REPLACE}, no shared element enter transition. + * <li> {@link #UI_STYLE_ACTIVITY_ROOT} Enter transition is set to null and app should rely on + * activity transition, exit transition is same as {@link #UI_STYLE_REPLACE}, no shared element + * enter transition. + * </ul> + * <p> + * The default implementation heavily relies on {@link GuidedActionsStylist} and + * {@link GuidanceStylist} layout, app may override this method when modifying the default + * layout of {@link GuidedActionsStylist} or {@link GuidanceStylist}. + * <p> + * TIP: because the fragment view is removed during fragment transition, in general app cannot + * use two Visibility transition together. Workaround is to create your own Visibility + * transition that controls multiple animators (e.g. slide and fade animation in one Transition + * class). + */ + protected void onProvideFragmentTransitions() { + if (Build.VERSION.SDK_INT >= 21) { + final int uiStyle = getUiStyle(); + if (uiStyle == UI_STYLE_REPLACE) { + Object enterTransition = TransitionHelper.createFadeAndShortSlide(Gravity.END); + TransitionHelper.exclude(enterTransition, R.id.guidedstep_background, true); + TransitionHelper.setEnterTransition(this, enterTransition); + + Object changeBounds = TransitionHelper.createChangeBounds(false); + TransitionHelper.setSharedElementEnterTransition(this, changeBounds); + } else if (uiStyle == UI_STYLE_ENTRANCE) { + Object fade = TransitionHelper.createFadeTransition(TransitionHelper.FADE_IN | + TransitionHelper.FADE_OUT); + TransitionHelper.include(fade, R.id.guidedstep_background); + Object slide = TransitionHelper.createFadeAndShortSlide(Gravity.END | + Gravity.START); + TransitionHelper.include(slide, R.id.content_fragment); + TransitionHelper.include(slide, R.id.action_fragment_root); + Object enterTransition = TransitionHelper.createTransitionSet(false); + TransitionHelper.addTransition(enterTransition, fade); + TransitionHelper.addTransition(enterTransition, slide); + TransitionHelper.setEnterTransition(this, enterTransition); + + // No shared element transition + TransitionHelper.setSharedElementEnterTransition(this, null); + } else if (uiStyle == UI_STYLE_ACTIVITY_ROOT) { + // for Activity root, we dont need enter transition, use activity transition + TransitionHelper.setEnterTransition(this, null); + // No shared element transition + TransitionHelper.setSharedElementEnterTransition(this, null); + } + // exitTransition is same for all style + Object exitTransition = TransitionHelper.createFadeAndShortSlide(Gravity.START); + TransitionHelper.exclude(exitTransition, R.id.guidedstep_background, true); + TransitionHelper.setExitTransition(this, exitTransition); + } + } + + /** + * Called by onCreateView to inflate background view. Default implementation loads view + * from {@link R.layout#lb_guidedstep_background} which holds a reference to + * guidedStepBackground. + * @param inflater LayoutInflater to load background view. + * @param container Parent view of background view. + * @param savedInstanceState + * @return Created background view or null if no background. + */ + public View onCreateBackgroundView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.lb_guidedstep_background, container, false); + } + + /** + * Set UI style to fragment arguments. Default value is {@link #UI_STYLE_ENTRANCE} when fragment + * is first initialized. UI style is used to choose different fragment transition animations and + * determine if this is the first GuidedStepSupportFragment on backstack. In most cases app does not + * directly call this method, app calls helper function + * {@link #add(FragmentManager, GuidedStepSupportFragment, int)}. However if the app creates Fragment + * transaction and controls backstack by itself, it would need call setUiStyle() to select the + * fragment transition to use. + * + * @param style {@link #UI_STYLE_ACTIVITY_ROOT} {@link #UI_STYLE_REPLACE} or + * {@link #UI_STYLE_ENTRANCE}. + */ + public void setUiStyle(int style) { + int oldStyle = getUiStyle(); + Bundle arguments = getArguments(); + boolean isNew = false; + if (arguments == null) { + arguments = new Bundle(); + isNew = true; + } + arguments.putInt(EXTRA_UI_STYLE, style); + // call setArgument() will validate if the fragment is already added. + if (isNew) { + setArguments(arguments); + } + if (style != oldStyle) { + onProvideFragmentTransitions(); + } + } + + /** + * Read UI style from fragment arguments. Default value is {@link #UI_STYLE_ENTRANCE} when + * fragment is first initialized. UI style is used to choose different fragment transition + * animations and determine if this is the first GuidedStepSupportFragment on backstack. + * + * @return {@link #UI_STYLE_ACTIVITY_ROOT} {@link #UI_STYLE_REPLACE} or + * {@link #UI_STYLE_ENTRANCE}. + * @see #onProvideFragmentTransitions() + */ + public int getUiStyle() { + Bundle b = getArguments(); + if (b == null) return UI_STYLE_ENTRANCE; + return b.getInt(EXTRA_UI_STYLE, UI_STYLE_ENTRANCE); + } + + /** + * {@inheritDoc} + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (DEBUG) Log.v(TAG, "onCreate"); + // Set correct transition from saved arguments. + onProvideFragmentTransitions(); + Bundle state = (savedInstanceState != null) ? savedInstanceState : getArguments(); + if (state != null) { + if (mSelectedIndex == -1) { + mSelectedIndex = state.getInt(EXTRA_ACTION_SELECTED_INDEX, -1); + } + } + ArrayList<GuidedAction> actions = new ArrayList<GuidedAction>(); + onCreateActions(actions, savedInstanceState); + setActions(actions); + ArrayList<GuidedAction> buttonActions = new ArrayList<GuidedAction>(); + onCreateButtonActions(buttonActions, savedInstanceState); + setButtonActions(buttonActions); + } + + /** + * {@inheritDoc} + */ + @Override + public void onDestroyView() { + mGuidanceStylist.onDestroyView(); + mActionsStylist.onDestroyView(); + mButtonActionsStylist.onDestroyView(); + mAdapter = null; + mButtonAdapter = null; + mAdapterGroup = null; + super.onDestroyView(); + } + + /** + * {@inheritDoc} + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + if (DEBUG) Log.v(TAG, "onCreateView"); + + resolveTheme(); + inflater = getThemeInflater(inflater); + + ViewGroup root = (ViewGroup) inflater.inflate(R.layout.lb_guidedstep_fragment, + container, false); + ViewGroup guidanceContainer = (ViewGroup) root.findViewById(R.id.content_fragment); + ViewGroup actionContainer = (ViewGroup) root.findViewById(R.id.action_fragment); + + Guidance guidance = onCreateGuidance(savedInstanceState); + View guidanceView = mGuidanceStylist.onCreateView(inflater, guidanceContainer, guidance); + guidanceContainer.addView(guidanceView); + + View actionsView = mActionsStylist.onCreateView(inflater, actionContainer); + actionContainer.addView(actionsView); + + View buttonActionsView = mButtonActionsStylist.onCreateView(inflater, actionContainer); + mButtonActionsStylist.setAsButtonActions(); + actionContainer.addView(buttonActionsView); + + GuidedActionAdapter.EditListener editListener = new GuidedActionAdapter.EditListener() { + + @Override + public void onImeOpen() { + runImeAnimations(true); + } + + @Override + public void onImeClose() { + runImeAnimations(false); + } + + @Override + public long onGuidedActionEdited(GuidedAction action) { + return GuidedStepSupportFragment.this.onGuidedActionEditedAndProceed(action); + } + }; + + mAdapter = new GuidedActionAdapter(mActions, this, this, mActionsStylist); + mButtonAdapter = new GuidedActionAdapter(mButtonActions, this, this, mButtonActionsStylist); + mAdapterGroup = new GuidedActionAdapterGroup(); + mAdapterGroup.addAdpter(mAdapter); + mAdapterGroup.addAdpter(mButtonAdapter); + mAdapterGroup.setEditListener(editListener); + + mActionsStylist.getActionsGridView().setAdapter(mAdapter); + mButtonActionsStylist.getActionsGridView().setAdapter(mButtonAdapter); + if (mButtonActions.size() == 0) { + // when there is no button actions, we dont need show the second panel, but keep + // the width zero to run ChangeBounds transition. + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) + buttonActionsView.getLayoutParams(); + lp.weight = 0; + buttonActionsView.setLayoutParams(lp); + } else { + // when there are two actions panel, we need adjust the weight of action to + // guidedActionContentWidthWeightTwoPanels. + Context ctx = mThemeWrapper != null ? mThemeWrapper : getActivity(); + TypedValue typedValue = new TypedValue(); + if (ctx.getTheme().resolveAttribute(R.attr.guidedActionContentWidthWeightTwoPanels, + typedValue, true)) { + View actionsRoot = root.findViewById(R.id.action_fragment_root); + float weight = typedValue.getFloat(); + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) actionsRoot + .getLayoutParams(); + lp.weight = weight; + actionsRoot.setLayoutParams(lp); + } + } + + int pos = (mSelectedIndex >= 0 && mSelectedIndex < mActions.size()) ? + mSelectedIndex : getFirstCheckedAction(); + setSelectedActionPosition(pos); + + setSelectedButtonActionPosition(0); + + View backgroundView = onCreateBackgroundView(inflater, root, savedInstanceState); + if (backgroundView != null) { + root.addView(backgroundView, 0); + } + return root; + } + + @Override + public void onResume() { + super.onResume(); + mActionsStylist.getActionsGridView().requestFocus(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt(EXTRA_ACTION_SELECTED_INDEX, + (mActionsStylist.getActionsGridView() != null) ? + getSelectedActionPosition() : mSelectedIndex); + } + + private static boolean isGuidedStepTheme(Context context) { + int resId = R.attr.guidedStepThemeFlag; + TypedValue typedValue = new TypedValue(); + boolean found = context.getTheme().resolveAttribute(resId, typedValue, true); + if (DEBUG) Log.v(TAG, "Found guided step theme flag? " + found); + return found && typedValue.type == TypedValue.TYPE_INT_BOOLEAN && typedValue.data != 0; + } + + /** + * Convenient method to close GuidedStepSupportFragments on top of other content or finish Activity if + * GuidedStepSupportFragments were started in a separate activity. Pops all stack entries including + * {@link #UI_STYLE_ENTRANCE}; if {@link #UI_STYLE_ENTRANCE} is not found, finish the activity. + * Note that this method must be paired with {@link #add(FragmentManager, GuidedStepSupportFragment, + * int)} which sets up the stack entry name for finding which fragment we need to pop back to. + */ + public void finishGuidedStepSupportFragments() { + final FragmentManager fragmentManager = getFragmentManager(); + final int entryCount = fragmentManager.getBackStackEntryCount(); + if (entryCount > 0) { + for (int i = entryCount - 1; i >= 0; i--) { + BackStackEntry entry = fragmentManager.getBackStackEntryAt(i); + if (isUiStyleEntrance(entry.getName())) { + GuidedStepSupportFragment top = getCurrentGuidedStepSupportFragment(fragmentManager); + if (top != null) { + top.setUiStyle(UI_STYLE_ENTRANCE); + } + fragmentManager.popBackStack(entry.getId(), + FragmentManager.POP_BACK_STACK_INCLUSIVE); + return; + } + } + } + ActivityCompat.finishAfterTransition(getActivity()); + } + + /** + * Convenient method to pop to fragment with Given class. + * @param guidedStepFragmentClass Name of the Class of GuidedStepSupportFragment to pop to. + * @param flags Either 0 or {@link FragmentManager#POP_BACK_STACK_INCLUSIVE}. + */ + public void popBackStackToGuidedStepSupportFragment(Class guidedStepFragmentClass, int flags) { + if (!GuidedStepSupportFragment.class.isAssignableFrom(guidedStepFragmentClass)) { + return; + } + final FragmentManager fragmentManager = getFragmentManager(); + final int entryCount = fragmentManager.getBackStackEntryCount(); + String className = guidedStepFragmentClass.getName(); + if (entryCount > 0) { + for (int i = entryCount - 1; i >= 0; i--) { + BackStackEntry entry = fragmentManager.getBackStackEntryAt(i); + String entryClassName = getGuidedStepSupportFragmentClassName(entry.getName()); + if (className.equals(entryClassName)) { + fragmentManager.popBackStack(entry.getId(), flags); + return; + } + } + } + } + + private void resolveTheme() { + // Look up the guidedStepTheme in the currently specified theme. If it exists, + // replace the theme with its value. + FragmentActivity activity = getActivity(); + if (mTheme == -1 && !isGuidedStepTheme(activity)) { + // Look up the guidedStepTheme in the activity's currently specified theme. If it + // exists, replace the theme with its value. + int resId = R.attr.guidedStepTheme; + TypedValue typedValue = new TypedValue(); + boolean found = activity.getTheme().resolveAttribute(resId, typedValue, true); + if (DEBUG) Log.v(TAG, "Found guided step theme reference? " + found); + if (found) { + ContextThemeWrapper themeWrapper = + new ContextThemeWrapper(activity, typedValue.resourceId); + if (isGuidedStepTheme(themeWrapper)) { + mTheme = typedValue.resourceId; + mThemeWrapper = themeWrapper; + } else { + found = false; + mThemeWrapper = null; + } + } + if (!found) { + Log.e(TAG, "GuidedStepSupportFragment does not have an appropriate theme set."); + } + } else if (mTheme != -1) { + mThemeWrapper = new ContextThemeWrapper(activity, mTheme); + } + } + + private LayoutInflater getThemeInflater(LayoutInflater inflater) { + if (mTheme == -1) { + return inflater; + } else { + return inflater.cloneInContext(mThemeWrapper); + } + } + + private int getFirstCheckedAction() { + for (int i = 0, size = mActions.size(); i < size; i++) { + if (mActions.get(i).isChecked()) { + return i; + } + } + return 0; + } + + private void runImeAnimations(boolean entering) { + ArrayList<Animator> animators = new ArrayList<Animator>(); + if (entering) { + mGuidanceStylist.onImeAppearing(animators); + mActionsStylist.onImeAppearing(animators); + mButtonActionsStylist.onImeAppearing(animators); + } else { + mGuidanceStylist.onImeDisappearing(animators); + mActionsStylist.onImeDisappearing(animators); + mButtonActionsStylist.onImeDisappearing(animators); + } + AnimatorSet set = new AnimatorSet(); + set.playTogether(animators); + set.start(); + } + +} diff --git a/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java b/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java index b2c9b1ccdb..219bb9898d 100644 --- a/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java +++ b/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java @@ -138,8 +138,10 @@ public class HeadersFragment extends BaseRowFragment { if (getBridgeAdapter() != null) { FocusHighlightHelper.setupHeaderItemFocusHighlight(listView); } - view.setBackgroundColor(getBackgroundColor()); - updateFadingEdgeToBrandColor(getBackgroundColor()); + if (mBackgroundColorSet) { + view.setBackgroundColor(mBackgroundColor); + updateFadingEdgeToBrandColor(mBackgroundColor); + } updateListViewVisibility(); } @@ -228,22 +230,6 @@ public class HeadersFragment extends BaseRowFragment { } } - int getBackgroundColor() { - if (getActivity() == null) { - throw new IllegalStateException("Activity must be attached"); - } - - if (mBackgroundColorSet) { - return mBackgroundColor; - } - - TypedValue outValue = new TypedValue(); - if (getActivity().getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true)) { - return getResources().getColor(outValue.resourceId); - } - return getResources().getColor(R.color.lb_default_brand_color); - } - @Override void onTransitionStart() { super.onTransitionStart(); diff --git a/v17/leanback/src/android/support/v17/leanback/app/HeadersSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/HeadersSupportFragment.java index 9c66714810..ecf04d87ee 100644 --- a/v17/leanback/src/android/support/v17/leanback/app/HeadersSupportFragment.java +++ b/v17/leanback/src/android/support/v17/leanback/app/HeadersSupportFragment.java @@ -140,8 +140,10 @@ public class HeadersSupportFragment extends BaseRowSupportFragment { if (getBridgeAdapter() != null) { FocusHighlightHelper.setupHeaderItemFocusHighlight(listView); } - view.setBackgroundColor(getBackgroundColor()); - updateFadingEdgeToBrandColor(getBackgroundColor()); + if (mBackgroundColorSet) { + view.setBackgroundColor(mBackgroundColor); + updateFadingEdgeToBrandColor(mBackgroundColor); + } updateListViewVisibility(); } @@ -230,22 +232,6 @@ public class HeadersSupportFragment extends BaseRowSupportFragment { } } - int getBackgroundColor() { - if (getActivity() == null) { - throw new IllegalStateException("Activity must be attached"); - } - - if (mBackgroundColorSet) { - return mBackgroundColor; - } - - TypedValue outValue = new TypedValue(); - if (getActivity().getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true)) { - return getResources().getColor(outValue.resourceId); - } - return getResources().getColor(R.color.lb_default_brand_color); - } - @Override void onTransitionStart() { super.onTransitionStart(); diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java index 8c56925298..e3af40320d 100644 --- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java +++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java @@ -74,7 +74,7 @@ import android.view.View; */ public abstract class PlaybackControlGlue implements OnActionClickedListener, View.OnKeyListener { /** - * The adapter key for the first custom control on the right side + * The adapter key for the first custom control on the left side * of the predefined primary controls. */ public static final int ACTION_CUSTOM_LEFT_FIRST = 0x1; diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlSupportGlue.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlSupportGlue.java new file mode 100644 index 0000000000..5857e65dce --- /dev/null +++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlSupportGlue.java @@ -0,0 +1,858 @@ +/* This file is auto-generated from PlaybackControlGlue.java. DO NOT MODIFY. */ + +package android.support.v17.leanback.app; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Message; +import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter; +import android.support.v17.leanback.widget.Action; +import android.support.v17.leanback.widget.ControlButtonPresenterSelector; +import android.support.v17.leanback.widget.OnActionClickedListener; +import android.support.v17.leanback.widget.OnItemViewClickedListener; +import android.support.v17.leanback.widget.PlaybackControlsRow; +import android.support.v17.leanback.widget.PlaybackControlsRowPresenter; +import android.support.v17.leanback.widget.Presenter; +import android.support.v17.leanback.widget.PresenterSelector; +import android.support.v17.leanback.widget.Row; +import android.support.v17.leanback.widget.RowPresenter; +import android.support.v17.leanback.widget.SparseArrayObjectAdapter; +import android.util.Log; +import android.view.InputEvent; +import android.view.KeyEvent; +import android.view.View; + + +/** + * A helper class for managing a {@link android.support.v17.leanback.widget.PlaybackControlsRow} and + * {@link PlaybackOverlaySupportFragment} that implements a recommended approach to handling standard + * playback control actions such as play/pause, fast forward/rewind at progressive speed levels, + * and skip to next/previous. This helper class is a glue layer in that it manages the + * configuration of and interaction between the leanback UI components by defining a functional + * interface to the media player. + * + * <p>You can instantiate a concrete subclass such as {@link MediaControllerGlue} or you must + * subclass this abstract helper. To create a subclass you must implement all of the + * abstract methods and the subclass must invoke {@link #onMetadataChanged()} and + * {@link #onStateChanged()} appropriately. + * </p> + * + * <p>To use an instance of the glue layer, first construct an instance. Constructor parameters + * inform the glue what speed levels are supported for fast forward/rewind. Providing a + * {@link android.support.v17.leanback.app.PlaybackOverlaySupportFragment} is optional. + * </p> + * + * <p>If you have your own controls row you must pass it to {@link #setControlsRow}. + * The row will be updated by the glue layer based on the media metadata and playback state. + * Alternatively, you may call {@link #createControlsRowAndPresenter()} which will set a controls + * row and return a row presenter you can use to present the row. + * </p> + * + * <p>The helper sets a {@link android.support.v17.leanback.widget.SparseArrayObjectAdapter} + * on the controls row as the primary actions adapter, and adds actions to it. You can provide + * additional actions by overriding {@link #createPrimaryActionsAdapter}. This helper does not + * deal in secondary actions so those you may add separately. + * </p> + * + * <p>Provide a click listener on your fragment and if an action is clicked, call + * {@link #onActionClicked}. There is no need to call {@link #setOnItemViewClickedListener} + * but if you do a click listener will be installed on the fragment and recognized action clicks + * will be handled. Your listener will be called only for unhandled actions. + * </p> + * + * <p>The helper implements a key event handler. If you pass a + * {@link android.support.v17.leanback.app.PlaybackOverlaySupportFragment} the fragment's input event + * handler will be set. Otherwise, you should set the glue object as key event handler to the + * ViewHolder when bound by your row presenter; see + * {@link RowPresenter.ViewHolder#setOnKeyListener(android.view.View.OnKeyListener)}. + * </p> + * + * <p>To update the controls row progress during playback, override {@link #enableProgressUpdating} + * to manage the lifecycle of a periodic callback to {@link #updateProgress()}. + * {@link #getUpdatePeriod()} provides a recommended update period. + * </p> + * + */ +public abstract class PlaybackControlSupportGlue implements OnActionClickedListener, View.OnKeyListener { + /** + * The adapter key for the first custom control on the left side + * of the predefined primary controls. + */ + public static final int ACTION_CUSTOM_LEFT_FIRST = 0x1; + + /** + * The adapter key for the skip to previous control. + */ + public static final int ACTION_SKIP_TO_PREVIOUS = 0x10; + + /** + * The adapter key for the rewind control. + */ + public static final int ACTION_REWIND = 0x20; + + /** + * The adapter key for the play/pause control. + */ + public static final int ACTION_PLAY_PAUSE = 0x40; + + /** + * The adapter key for the fast forward control. + */ + public static final int ACTION_FAST_FORWARD = 0x80; + + /** + * The adapter key for the skip to next control. + */ + public static final int ACTION_SKIP_TO_NEXT = 0x100; + + /** + * The adapter key for the first custom control on the right side + * of the predefined primary controls. + */ + public static final int ACTION_CUSTOM_RIGHT_FIRST = 0x1000; + + /** + * Invalid playback speed. + */ + public static final int PLAYBACK_SPEED_INVALID = -1; + + /** + * Speed representing playback state that is paused. + */ + public static final int PLAYBACK_SPEED_PAUSED = 0; + + /** + * Speed representing playback state that is playing normally. + */ + public static final int PLAYBACK_SPEED_NORMAL = 1; + + /** + * The initial (level 0) fast forward playback speed. + * The negative of this value is for rewind at the same speed. + */ + public static final int PLAYBACK_SPEED_FAST_L0 = 10; + + /** + * The level 1 fast forward playback speed. + * The negative of this value is for rewind at the same speed. + */ + public static final int PLAYBACK_SPEED_FAST_L1 = 11; + + /** + * The level 2 fast forward playback speed. + * The negative of this value is for rewind at the same speed. + */ + public static final int PLAYBACK_SPEED_FAST_L2 = 12; + + /** + * The level 3 fast forward playback speed. + * The negative of this value is for rewind at the same speed. + */ + public static final int PLAYBACK_SPEED_FAST_L3 = 13; + + /** + * The level 4 fast forward playback speed. + * The negative of this value is for rewind at the same speed. + */ + public static final int PLAYBACK_SPEED_FAST_L4 = 14; + + private static final String TAG = "PlaybackControlSupportGlue"; + private static final boolean DEBUG = false; + + private static final int MSG_UPDATE_PLAYBACK_STATE = 100; + private static final int UPDATE_PLAYBACK_STATE_DELAY_MS = 2000; + private static final int NUMBER_OF_SEEK_SPEEDS = PLAYBACK_SPEED_FAST_L4 - + PLAYBACK_SPEED_FAST_L0 + 1; + + private final PlaybackOverlaySupportFragment mFragment; + private final Context mContext; + private final int[] mFastForwardSpeeds; + private final int[] mRewindSpeeds; + private PlaybackControlsRow mControlsRow; + private SparseArrayObjectAdapter mPrimaryActionsAdapter; + private PlaybackControlsRow.PlayPauseAction mPlayPauseAction; + private PlaybackControlsRow.SkipNextAction mSkipNextAction; + private PlaybackControlsRow.SkipPreviousAction mSkipPreviousAction; + private PlaybackControlsRow.FastForwardAction mFastForwardAction; + private PlaybackControlsRow.RewindAction mRewindAction; + private OnItemViewClickedListener mExternalOnItemViewClickedListener; + private int mPlaybackSpeed = PLAYBACK_SPEED_NORMAL; + private boolean mFadeWhenPlaying = true; + + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + if (msg.what == MSG_UPDATE_PLAYBACK_STATE) { + updatePlaybackState(); + } + } + }; + + private final OnItemViewClickedListener mOnItemViewClickedListener = + new OnItemViewClickedListener() { + @Override + public void onItemClicked(Presenter.ViewHolder viewHolder, Object object, + RowPresenter.ViewHolder viewHolder2, Row row) { + if (DEBUG) Log.v(TAG, "onItemClicked " + object); + boolean handled = false; + if (object instanceof Action) { + handled = dispatchAction((Action) object, null); + } + if (!handled && mExternalOnItemViewClickedListener != null) { + mExternalOnItemViewClickedListener.onItemClicked(viewHolder, object, + viewHolder2, row); + } + } + }; + + /** + * Constructor for the glue. + * + * @param context + * @param seekSpeeds Array of seek speeds for fast forward and rewind. + */ + public PlaybackControlSupportGlue(Context context, int[] seekSpeeds) { + this(context, null, seekSpeeds, seekSpeeds); + } + + /** + * Constructor for the glue. + * + * @param context + * @param fastForwardSpeeds Array of seek speeds for fast forward. + * @param rewindSpeeds Array of seek speeds for rewind. + */ + public PlaybackControlSupportGlue(Context context, + int[] fastForwardSpeeds, + int[] rewindSpeeds) { + this(context, null, fastForwardSpeeds, rewindSpeeds); + } + + /** + * Constructor for the glue. + * + * @param context + * @param fragment Optional; if using a {@link PlaybackOverlaySupportFragment}, pass it in. + * @param seekSpeeds Array of seek speeds for fast forward and rewind. + */ + public PlaybackControlSupportGlue(Context context, + PlaybackOverlaySupportFragment fragment, + int[] seekSpeeds) { + this(context, fragment, seekSpeeds, seekSpeeds); + } + + /** + * Constructor for the glue. + * + * @param context + * @param fragment Optional; if using a {@link PlaybackOverlaySupportFragment}, pass it in. + * @param fastForwardSpeeds Array of seek speeds for fast forward. + * @param rewindSpeeds Array of seek speeds for rewind. + */ + public PlaybackControlSupportGlue(Context context, + PlaybackOverlaySupportFragment fragment, + int[] fastForwardSpeeds, + int[] rewindSpeeds) { + mContext = context; + mFragment = fragment; + if (fragment != null) { + attachToFragment(); + } + if (fastForwardSpeeds.length == 0 || fastForwardSpeeds.length > NUMBER_OF_SEEK_SPEEDS) { + throw new IllegalStateException("invalid fastForwardSpeeds array size"); + } + mFastForwardSpeeds = fastForwardSpeeds; + if (rewindSpeeds.length == 0 || rewindSpeeds.length > NUMBER_OF_SEEK_SPEEDS) { + throw new IllegalStateException("invalid rewindSpeeds array size"); + } + mRewindSpeeds = rewindSpeeds; + } + + private final PlaybackOverlaySupportFragment.InputEventHandler mOnInputEventHandler = + new PlaybackOverlaySupportFragment.InputEventHandler() { + @Override + public boolean handleInputEvent(InputEvent event) { + if (event instanceof KeyEvent) { + KeyEvent keyEvent = (KeyEvent) event; + return onKey(null, keyEvent.getKeyCode(), keyEvent); + } + return false; + } + }; + + private void attachToFragment() { + mFragment.setInputEventHandler(mOnInputEventHandler); + } + + /** + * Helper method for instantiating a + * {@link android.support.v17.leanback.widget.PlaybackControlsRow} and corresponding + * {@link android.support.v17.leanback.widget.PlaybackControlsRowPresenter}. + */ + public PlaybackControlsRowPresenter createControlsRowAndPresenter() { + PlaybackControlsRow controlsRow = new PlaybackControlsRow(this); + setControlsRow(controlsRow); + + AbstractDetailsDescriptionPresenter detailsPresenter = + new AbstractDetailsDescriptionPresenter() { + @Override + protected void onBindDescription(AbstractDetailsDescriptionPresenter.ViewHolder + viewHolder, Object object) { + PlaybackControlSupportGlue glue = (PlaybackControlSupportGlue) object; + if (glue.hasValidMedia()) { + viewHolder.getTitle().setText(glue.getMediaTitle()); + viewHolder.getSubtitle().setText(glue.getMediaSubtitle()); + } else { + viewHolder.getTitle().setText(""); + viewHolder.getSubtitle().setText(""); + } + } + }; + return new PlaybackControlsRowPresenter(detailsPresenter) { + @Override + protected void onBindRowViewHolder(RowPresenter.ViewHolder vh, Object item) { + super.onBindRowViewHolder(vh, item); + vh.setOnKeyListener(PlaybackControlSupportGlue.this); + } + @Override + protected void onUnbindRowViewHolder(RowPresenter.ViewHolder vh) { + super.onUnbindRowViewHolder(vh); + vh.setOnKeyListener(null); + } + }; + } + + /** + * Returns the fragment. + */ + public PlaybackOverlaySupportFragment getFragment() { + return mFragment; + } + + /** + * Returns the context. + */ + public Context getContext() { + return mContext; + } + + /** + * Returns the fast forward speeds. + */ + public int[] getFastForwardSpeeds() { + return mFastForwardSpeeds; + } + + /** + * Returns the rewind speeds. + */ + public int[] getRewindSpeeds() { + return mRewindSpeeds; + } + + /** + * Sets the controls to fade after a timeout when media is playing. + */ + public void setFadingEnabled(boolean enable) { + mFadeWhenPlaying = enable; + if (!mFadeWhenPlaying && mFragment != null) { + mFragment.setFadingEnabled(false); + } + } + + /** + * Returns true if controls are set to fade when media is playing. + */ + public boolean isFadingEnabled() { + return mFadeWhenPlaying; + } + + /** + * Set the {@link OnItemViewClickedListener} to be called if the click event + * is not handled internally. + * @param listener + * @deprecated Don't call this. Instead set the listener on the fragment yourself, + * and call {@link #onActionClicked} to handle clicks. + */ + public void setOnItemViewClickedListener(OnItemViewClickedListener listener) { + mExternalOnItemViewClickedListener = listener; + if (mFragment != null) { + mFragment.setOnItemViewClickedListener(mOnItemViewClickedListener); + } + } + + /** + * Returns the {@link OnItemViewClickedListener}. + */ + public OnItemViewClickedListener getOnItemViewClickedListener() { + return mExternalOnItemViewClickedListener; + } + + /** + * Sets the controls row to be managed by the glue layer. + * The primary actions and playback state related aspects of the row + * are updated by the glue. + */ + public void setControlsRow(PlaybackControlsRow controlsRow) { + mControlsRow = controlsRow; + mPrimaryActionsAdapter = createPrimaryActionsAdapter( + new ControlButtonPresenterSelector()); + mControlsRow.setPrimaryActionsAdapter(mPrimaryActionsAdapter); + updateControlsRow(); + } + + /** + * Returns the playback controls row managed by the glue layer. + */ + public PlaybackControlsRow getControlsRow() { + return mControlsRow; + } + + /** + * Override this to start/stop a runnable to call {@link #updateProgress} at + * an interval such as {@link #getUpdatePeriod}. + */ + public void enableProgressUpdating(boolean enable) { + } + + /** + * Returns the time period in milliseconds that should be used + * to update the progress. See {@link #updateProgress()}. + */ + public int getUpdatePeriod() { + // TODO: calculate a better update period based on total duration and screen size + return 500; + } + + /** + * Updates the progress bar based on the current media playback position. + */ + public void updateProgress() { + int position = getCurrentPosition(); + if (DEBUG) Log.v(TAG, "updateProgress " + position); + mControlsRow.setCurrentTime(position); + } + + /** + * Handles action clicks. A subclass may override this add support for additional actions. + */ + @Override + public void onActionClicked(Action action) { + dispatchAction(action, null); + } + + /** + * Handles key events and returns true if handled. A subclass may override this to provide + * additional support. + */ + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + switch (keyCode) { + case KeyEvent.KEYCODE_DPAD_UP: + case KeyEvent.KEYCODE_DPAD_DOWN: + case KeyEvent.KEYCODE_DPAD_RIGHT: + case KeyEvent.KEYCODE_DPAD_LEFT: + case KeyEvent.KEYCODE_BACK: + case KeyEvent.KEYCODE_ESCAPE: + boolean abortSeek = mPlaybackSpeed >= PLAYBACK_SPEED_FAST_L0 || + mPlaybackSpeed <= -PLAYBACK_SPEED_FAST_L0; + if (abortSeek) { + mPlaybackSpeed = PLAYBACK_SPEED_NORMAL; + startPlayback(mPlaybackSpeed); + updatePlaybackStatusAfterUserAction(); + return keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE; + } + return false; + } + Action action = mControlsRow.getActionForKeyCode(mPrimaryActionsAdapter, keyCode); + if (action != null) { + if (action == mPrimaryActionsAdapter.lookup(ACTION_PLAY_PAUSE) || + action == mPrimaryActionsAdapter.lookup(ACTION_REWIND) || + action == mPrimaryActionsAdapter.lookup(ACTION_FAST_FORWARD) || + action == mPrimaryActionsAdapter.lookup(ACTION_SKIP_TO_PREVIOUS) || + action == mPrimaryActionsAdapter.lookup(ACTION_SKIP_TO_NEXT)) { + if (((KeyEvent) event).getAction() == KeyEvent.ACTION_DOWN) { + dispatchAction(action, (KeyEvent) event); + } + return true; + } + } + return false; + } + + /** + * Called when the given action is invoked, either by click or keyevent. + */ + private boolean dispatchAction(Action action, KeyEvent keyEvent) { + boolean handled = false; + if (action == mPlayPauseAction) { + boolean canPlay = keyEvent == null || + keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE || + keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY; + boolean canPause = keyEvent == null || + keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE || + keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PAUSE; + if (mPlaybackSpeed != PLAYBACK_SPEED_NORMAL) { + if (canPlay) { + mPlaybackSpeed = PLAYBACK_SPEED_NORMAL; + startPlayback(mPlaybackSpeed); + } + } else if (canPause) { + mPlaybackSpeed = PLAYBACK_SPEED_PAUSED; + pausePlayback(); + } + updatePlaybackStatusAfterUserAction(); + handled = true; + } else if (action == mSkipNextAction) { + skipToNext(); + handled = true; + } else if (action == mSkipPreviousAction) { + skipToPrevious(); + handled = true; + } else if (action == mFastForwardAction) { + if (mPlaybackSpeed < getMaxForwardSpeedId()) { + switch (mPlaybackSpeed) { + case PLAYBACK_SPEED_FAST_L0: + case PLAYBACK_SPEED_FAST_L1: + case PLAYBACK_SPEED_FAST_L2: + case PLAYBACK_SPEED_FAST_L3: + mPlaybackSpeed++; + break; + default: + mPlaybackSpeed = PLAYBACK_SPEED_FAST_L0; + break; + } + startPlayback(mPlaybackSpeed); + updatePlaybackStatusAfterUserAction(); + } + handled = true; + } else if (action == mRewindAction) { + if (mPlaybackSpeed > -getMaxRewindSpeedId()) { + switch (mPlaybackSpeed) { + case -PLAYBACK_SPEED_FAST_L0: + case -PLAYBACK_SPEED_FAST_L1: + case -PLAYBACK_SPEED_FAST_L2: + case -PLAYBACK_SPEED_FAST_L3: + mPlaybackSpeed--; + break; + default: + mPlaybackSpeed = -PLAYBACK_SPEED_FAST_L0; + break; + } + startPlayback(mPlaybackSpeed); + updatePlaybackStatusAfterUserAction(); + } + handled = true; + } + return handled; + } + + private int getMaxForwardSpeedId() { + return PLAYBACK_SPEED_FAST_L0 + (mFastForwardSpeeds.length - 1); + } + + private int getMaxRewindSpeedId() { + return PLAYBACK_SPEED_FAST_L0 + (mRewindSpeeds.length - 1); + } + + private void updateControlsRow() { + updateRowMetadata(); + mHandler.removeMessages(MSG_UPDATE_PLAYBACK_STATE); + updatePlaybackState(); + } + + private void updatePlaybackStatusAfterUserAction() { + updatePlaybackState(mPlaybackSpeed); + // Sync playback state after a delay + mHandler.removeMessages(MSG_UPDATE_PLAYBACK_STATE); + mHandler.sendEmptyMessageDelayed(MSG_UPDATE_PLAYBACK_STATE, + UPDATE_PLAYBACK_STATE_DELAY_MS); + } + + private void updateRowMetadata() { + if (mControlsRow == null) { + return; + } + + if (DEBUG) Log.v(TAG, "updateRowMetadata hasValidMedia " + hasValidMedia()); + + if (!hasValidMedia()) { + mControlsRow.setImageDrawable(null); + mControlsRow.setTotalTime(0); + mControlsRow.setCurrentTime(0); + } else { + mControlsRow.setImageDrawable(getMediaArt()); + mControlsRow.setTotalTime(getMediaDuration()); + mControlsRow.setCurrentTime(getCurrentPosition()); + } + + onRowChanged(mControlsRow); + } + + private void updatePlaybackState() { + if (hasValidMedia()) { + mPlaybackSpeed = getCurrentSpeedId(); + updatePlaybackState(mPlaybackSpeed); + } + } + + private void updatePlaybackState(int playbackSpeed) { + if (mControlsRow == null) { + return; + } + + final long actions = getSupportedActions(); + if ((actions & ACTION_SKIP_TO_PREVIOUS) != 0) { + if (mSkipPreviousAction == null) { + mSkipPreviousAction = new PlaybackControlsRow.SkipPreviousAction(mContext); + } + mPrimaryActionsAdapter.set(ACTION_SKIP_TO_PREVIOUS, mSkipPreviousAction); + } else { + mPrimaryActionsAdapter.clear(ACTION_SKIP_TO_PREVIOUS); + mSkipPreviousAction = null; + } + if ((actions & ACTION_REWIND) != 0) { + if (mRewindAction == null) { + mRewindAction = new PlaybackControlsRow.RewindAction(mContext, + mRewindSpeeds.length); + } + mPrimaryActionsAdapter.set(ACTION_REWIND, mRewindAction); + } else { + mPrimaryActionsAdapter.clear(ACTION_REWIND); + mRewindAction = null; + } + if ((actions & ACTION_PLAY_PAUSE) != 0) { + if (mPlayPauseAction == null) { + mPlayPauseAction = new PlaybackControlsRow.PlayPauseAction(mContext); + } + mPrimaryActionsAdapter.set(ACTION_PLAY_PAUSE, mPlayPauseAction); + } else { + mPrimaryActionsAdapter.clear(ACTION_PLAY_PAUSE); + mPlayPauseAction = null; + } + if ((actions & ACTION_FAST_FORWARD) != 0) { + if (mFastForwardAction == null) { + mFastForwardAction = new PlaybackControlsRow.FastForwardAction(mContext, + mFastForwardSpeeds.length); + } + mPrimaryActionsAdapter.set(ACTION_FAST_FORWARD, mFastForwardAction); + } else { + mPrimaryActionsAdapter.clear(ACTION_FAST_FORWARD); + mFastForwardAction = null; + } + if ((actions & ACTION_SKIP_TO_NEXT) != 0) { + if (mSkipNextAction == null) { + mSkipNextAction = new PlaybackControlsRow.SkipNextAction(mContext); + } + mPrimaryActionsAdapter.set(ACTION_SKIP_TO_NEXT, mSkipNextAction); + } else { + mPrimaryActionsAdapter.clear(ACTION_SKIP_TO_NEXT); + mSkipNextAction = null; + } + + if (mFastForwardAction != null) { + int index = 0; + if (playbackSpeed >= PLAYBACK_SPEED_FAST_L0) { + index = playbackSpeed - PLAYBACK_SPEED_FAST_L0; + if (playbackSpeed < getMaxForwardSpeedId()) { + index++; + } + } + if (mFastForwardAction.getIndex() != index) { + mFastForwardAction.setIndex(index); + notifyItemChanged(mPrimaryActionsAdapter, mFastForwardAction); + } + } + if (mRewindAction != null) { + int index = 0; + if (playbackSpeed <= -PLAYBACK_SPEED_FAST_L0) { + index = -playbackSpeed - PLAYBACK_SPEED_FAST_L0; + if (-playbackSpeed < getMaxRewindSpeedId()) { + index++; + } + } + if (mRewindAction.getIndex() != index) { + mRewindAction.setIndex(index); + notifyItemChanged(mPrimaryActionsAdapter, mRewindAction); + } + } + + if (playbackSpeed == PLAYBACK_SPEED_PAUSED) { + updateProgress(); + enableProgressUpdating(false); + } else { + enableProgressUpdating(true); + } + + if (mFadeWhenPlaying && mFragment != null) { + mFragment.setFadingEnabled(playbackSpeed == PLAYBACK_SPEED_NORMAL); + } + + if (mPlayPauseAction != null) { + int index = playbackSpeed == PLAYBACK_SPEED_PAUSED ? + PlaybackControlsRow.PlayPauseAction.PLAY : + PlaybackControlsRow.PlayPauseAction.PAUSE; + if (mPlayPauseAction.getIndex() != index) { + mPlayPauseAction.setIndex(index); + notifyItemChanged(mPrimaryActionsAdapter, mPlayPauseAction); + } + } + } + + private static void notifyItemChanged(SparseArrayObjectAdapter adapter, Object object) { + int index = adapter.indexOf(object); + if (index >= 0) { + adapter.notifyArrayItemRangeChanged(index, 1); + } + } + + private static String getSpeedString(int speed) { + switch (speed) { + case PLAYBACK_SPEED_INVALID: + return "PLAYBACK_SPEED_INVALID"; + case PLAYBACK_SPEED_PAUSED: + return "PLAYBACK_SPEED_PAUSED"; + case PLAYBACK_SPEED_NORMAL: + return "PLAYBACK_SPEED_NORMAL"; + case PLAYBACK_SPEED_FAST_L0: + return "PLAYBACK_SPEED_FAST_L0"; + case PLAYBACK_SPEED_FAST_L1: + return "PLAYBACK_SPEED_FAST_L1"; + case PLAYBACK_SPEED_FAST_L2: + return "PLAYBACK_SPEED_FAST_L2"; + case PLAYBACK_SPEED_FAST_L3: + return "PLAYBACK_SPEED_FAST_L3"; + case PLAYBACK_SPEED_FAST_L4: + return "PLAYBACK_SPEED_FAST_L4"; + case -PLAYBACK_SPEED_FAST_L0: + return "-PLAYBACK_SPEED_FAST_L0"; + case -PLAYBACK_SPEED_FAST_L1: + return "-PLAYBACK_SPEED_FAST_L1"; + case -PLAYBACK_SPEED_FAST_L2: + return "-PLAYBACK_SPEED_FAST_L2"; + case -PLAYBACK_SPEED_FAST_L3: + return "-PLAYBACK_SPEED_FAST_L3"; + case -PLAYBACK_SPEED_FAST_L4: + return "-PLAYBACK_SPEED_FAST_L4"; + } + return null; + } + + /** + * Returns true if there is a valid media item. + */ + public abstract boolean hasValidMedia(); + + /** + * Returns true if media is currently playing. + */ + public abstract boolean isMediaPlaying(); + + /** + * Returns the title of the media item. + */ + public abstract CharSequence getMediaTitle(); + + /** + * Returns the subtitle of the media item. + */ + public abstract CharSequence getMediaSubtitle(); + + /** + * Returns the duration of the media item in milliseconds. + */ + public abstract int getMediaDuration(); + + /** + * Returns a bitmap of the art for the media item. + */ + public abstract Drawable getMediaArt(); + + /** + * Returns a bitmask of actions supported by the media player. + */ + public abstract long getSupportedActions(); + + /** + * Returns the current playback speed. When playing normally, + * {@link #PLAYBACK_SPEED_NORMAL} should be returned. + */ + public abstract int getCurrentSpeedId(); + + /** + * Returns the current position of the media item in milliseconds. + */ + public abstract int getCurrentPosition(); + + /** + * Start playback at the given speed. + * @param speed The desired playback speed. For normal playback this will be + * {@link #PLAYBACK_SPEED_NORMAL}; higher positive values for fast forward, + * and negative values for rewind. + */ + protected abstract void startPlayback(int speed); + + /** + * Pause playback. + */ + protected abstract void pausePlayback(); + + /** + * Skip to the next track. + */ + protected abstract void skipToNext(); + + /** + * Skip to the previous track. + */ + protected abstract void skipToPrevious(); + + /** + * Invoked when the playback controls row has changed. The adapter containing this row + * should be notified. + */ + protected abstract void onRowChanged(PlaybackControlsRow row); + + /** + * Creates the primary action adapter. May be overridden to add additional primary + * actions to the adapter. + */ + protected SparseArrayObjectAdapter createPrimaryActionsAdapter( + PresenterSelector presenterSelector) { + return new SparseArrayObjectAdapter(presenterSelector); + } + + /** + * Must be called appropriately by a subclass when the playback state has changed. + */ + protected void onStateChanged() { + if (DEBUG) Log.v(TAG, "onStateChanged"); + // If a pending control button update is present, delay + // the update until the state settles. + if (!hasValidMedia()) { + return; + } + if (mHandler.hasMessages(MSG_UPDATE_PLAYBACK_STATE)) { + mHandler.removeMessages(MSG_UPDATE_PLAYBACK_STATE); + if (getCurrentSpeedId() != mPlaybackSpeed) { + if (DEBUG) Log.v(TAG, "Status expectation mismatch, delaying update"); + mHandler.sendEmptyMessageDelayed(MSG_UPDATE_PLAYBACK_STATE, + UPDATE_PLAYBACK_STATE_DELAY_MS); + } else { + if (DEBUG) Log.v(TAG, "Update state matches expectation"); + updatePlaybackState(); + } + } else { + updatePlaybackState(); + } + } + + /** + * Must be called appropriately by a subclass when the metadata state has changed. + */ + protected void onMetadataChanged() { + if (DEBUG) Log.v(TAG, "onMetadataChanged"); + updateRowMetadata(); + } +} 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 71a95e8223..a5a7ccd891 100644 --- a/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java +++ b/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java @@ -448,6 +448,10 @@ public class RowsFragment extends BaseRowFragment { @Override public boolean onPreDraw() { + if (getView() == null || getActivity() == null) { + mVerticalView.getViewTreeObserver().removeOnPreDrawListener(this); + return true; + } if (mState == STATE_INIT) { setExpand(true); mState = STATE_FIRST_DRAW; 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 7f12aacbc5..7390acfc7f 100644 --- a/v17/leanback/src/android/support/v17/leanback/app/RowsSupportFragment.java +++ b/v17/leanback/src/android/support/v17/leanback/app/RowsSupportFragment.java @@ -450,6 +450,10 @@ public class RowsSupportFragment extends BaseRowSupportFragment { @Override public boolean onPreDraw() { + if (getView() == null || getActivity() == null) { + mVerticalView.getViewTreeObserver().removeOnPreDrawListener(this); + return true; + } if (mState == STATE_INIT) { setExpand(true); mState = STATE_FIRST_DRAW; diff --git a/v17/leanback/src/android/support/v17/leanback/app/SearchFragment.java b/v17/leanback/src/android/support/v17/leanback/app/SearchFragment.java index fc93625f4d..d828963cf4 100644 --- a/v17/leanback/src/android/support/v17/leanback/app/SearchFragment.java +++ b/v17/leanback/src/android/support/v17/leanback/app/SearchFragment.java @@ -34,6 +34,7 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.inputmethod.CompletionInfo; import android.widget.FrameLayout; import android.support.v17.leanback.R; @@ -478,6 +479,17 @@ public class SearchFragment extends Fragment { } /** + * Displays the completions shown by the IME. An application may provide + * a list of query completions that the system will show in the IME. + * + * @param completions A list of completions to show in the IME. Setting to + * null or empty will clear the list. + */ + public void displayCompletions(CompletionInfo[] completions) { + mSearchBar.displayCompletions(completions); + } + + /** * Sets this callback to have the fragment pass speech recognition requests * to the activity rather than using an internal recognizer. */ diff --git a/v17/leanback/src/android/support/v17/leanback/app/SearchSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/SearchSupportFragment.java index e06f7e8da9..7ff364e478 100644 --- a/v17/leanback/src/android/support/v17/leanback/app/SearchSupportFragment.java +++ b/v17/leanback/src/android/support/v17/leanback/app/SearchSupportFragment.java @@ -36,6 +36,7 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.inputmethod.CompletionInfo; import android.widget.FrameLayout; import android.support.v17.leanback.R; @@ -480,6 +481,17 @@ public class SearchSupportFragment extends Fragment { } /** + * Displays the completions shown by the IME. An application may provide + * a list of query completions that the system will show in the IME. + * + * @param completions A list of completions to show in the IME. Setting to + * null or empty will clear the list. + */ + public void displayCompletions(CompletionInfo[] completions) { + mSearchBar.displayCompletions(completions); + } + + /** * Sets this callback to have the fragment pass speech recognition requests * to the activity rather than using an internal recognizer. */ diff --git a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java index 6b6cc2e273..3e51989e31 100644 --- a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java +++ b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java @@ -15,6 +15,7 @@ package android.support.v17.leanback.app; import android.support.annotation.ColorInt; import android.support.v17.leanback.R; +import android.support.v17.leanback.transition.TransitionHelper; import android.support.v17.leanback.widget.BrowseFrameLayout; import android.support.v17.leanback.widget.OnChildLaidOutListener; import android.support.v17.leanback.widget.OnItemViewClickedListener; @@ -38,7 +39,7 @@ import android.view.ViewGroup; * <p>Renders a vertical grid of objects given a {@link VerticalGridPresenter} and * an {@link ObjectAdapter}. */ -public class VerticalGridFragment extends BrandedFragment { +public class VerticalGridFragment extends BaseFragment { private static final String TAG = "VerticalGridFragment"; private static boolean DEBUG = false; @@ -47,6 +48,7 @@ public class VerticalGridFragment extends BrandedFragment { private VerticalGridPresenter.ViewHolder mGridViewHolder; private OnItemViewSelectedListener mOnItemViewSelectedListener; private OnItemViewClickedListener mOnItemViewClickedListener; + private Object mSceneAfterEntranceTransition; private int mSelectedPosition = -1; /** @@ -170,6 +172,13 @@ public class VerticalGridFragment extends BrandedFragment { gridDock.addView(mGridViewHolder.view); mGridViewHolder.getGridView().setOnChildLaidOutListener(mChildLaidOutListener); + mSceneAfterEntranceTransition = TransitionHelper.createScene(gridDock, new Runnable() { + @Override + public void run() { + setEntranceTransitionState(true); + } + }); + updateAdapter(); } @@ -183,7 +192,9 @@ public class VerticalGridFragment extends BrandedFragment { public void onStart() { super.onStart(); setupFocusSearchListener(); - mGridViewHolder.getGridView().requestFocus(); + if (isEntranceTransitionEnabled()) { + setEntranceTransitionState(false); + } } @Override @@ -210,4 +221,19 @@ public class VerticalGridFragment extends BrandedFragment { } } } + + @Override + protected Object createEntranceTransition() { + return TransitionHelper.loadTransition(getActivity(), + R.transition.lb_vertical_grid_entrance_transition); + } + + @Override + protected void runEntranceTransition(Object entranceTransition) { + TransitionHelper.runTransition(mSceneAfterEntranceTransition, entranceTransition); + } + + void setEntranceTransitionState(boolean afterTransition) { + mGridPresenter.setEntranceTransitionState(mGridViewHolder, afterTransition); + } } diff --git a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java index 0770761399..eb0b3372d6 100644 --- a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java +++ b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java @@ -17,6 +17,7 @@ package android.support.v17.leanback.app; import android.support.annotation.ColorInt; import android.support.v17.leanback.R; +import android.support.v17.leanback.transition.TransitionHelper; import android.support.v17.leanback.widget.BrowseFrameLayout; import android.support.v17.leanback.widget.OnChildLaidOutListener; import android.support.v17.leanback.widget.OnItemViewClickedListener; @@ -40,7 +41,7 @@ import android.view.ViewGroup; * <p>Renders a vertical grid of objects given a {@link VerticalGridPresenter} and * an {@link ObjectAdapter}. */ -public class VerticalGridSupportFragment extends BrandedSupportFragment { +public class VerticalGridSupportFragment extends BaseSupportFragment { private static final String TAG = "VerticalGridSupportFragment"; private static boolean DEBUG = false; @@ -49,6 +50,7 @@ public class VerticalGridSupportFragment extends BrandedSupportFragment { private VerticalGridPresenter.ViewHolder mGridViewHolder; private OnItemViewSelectedListener mOnItemViewSelectedListener; private OnItemViewClickedListener mOnItemViewClickedListener; + private Object mSceneAfterEntranceTransition; private int mSelectedPosition = -1; /** @@ -172,6 +174,13 @@ public class VerticalGridSupportFragment extends BrandedSupportFragment { gridDock.addView(mGridViewHolder.view); mGridViewHolder.getGridView().setOnChildLaidOutListener(mChildLaidOutListener); + mSceneAfterEntranceTransition = TransitionHelper.createScene(gridDock, new Runnable() { + @Override + public void run() { + setEntranceTransitionState(true); + } + }); + updateAdapter(); } @@ -185,7 +194,9 @@ public class VerticalGridSupportFragment extends BrandedSupportFragment { public void onStart() { super.onStart(); setupFocusSearchListener(); - mGridViewHolder.getGridView().requestFocus(); + if (isEntranceTransitionEnabled()) { + setEntranceTransitionState(false); + } } @Override @@ -212,4 +223,19 @@ public class VerticalGridSupportFragment extends BrandedSupportFragment { } } } + + @Override + protected Object createEntranceTransition() { + return TransitionHelper.loadTransition(getActivity(), + R.transition.lb_vertical_grid_entrance_transition); + } + + @Override + protected void runEntranceTransition(Object entranceTransition) { + TransitionHelper.runTransition(mSceneAfterEntranceTransition, entranceTransition); + } + + void setEntranceTransitionState(boolean afterTransition) { + mGridPresenter.setEntranceTransitionState(mGridViewHolder, afterTransition); + } } diff --git a/v17/leanback/src/android/support/v17/leanback/transition/LeanbackTransitionHelper.java b/v17/leanback/src/android/support/v17/leanback/transition/LeanbackTransitionHelper.java index f7451d426a..47495cb866 100644 --- a/v17/leanback/src/android/support/v17/leanback/transition/LeanbackTransitionHelper.java +++ b/v17/leanback/src/android/support/v17/leanback/transition/LeanbackTransitionHelper.java @@ -30,9 +30,9 @@ public class LeanbackTransitionHelper { static interface LeanbackTransitionHelperVersion { - public Object loadTitleInTransition(Context context, TransitionHelper helper); + public Object loadTitleInTransition(Context context); - public Object loadTitleOutTransition(Context context, TransitionHelper helper); + public Object loadTitleOutTransition(Context context); } /* @@ -42,12 +42,12 @@ public class LeanbackTransitionHelper { static class LeanbackTransitionHelperKitKatImpl implements LeanbackTransitionHelperVersion { @Override - public Object loadTitleInTransition(Context context, TransitionHelper helper) { + public Object loadTitleInTransition(Context context) { return LeanbackTransitionHelperKitKat.loadTitleInTransition(context); } @Override - public Object loadTitleOutTransition(Context context, TransitionHelper helper) { + public Object loadTitleOutTransition(Context context) { return LeanbackTransitionHelperKitKat.loadTitleOutTransition(context); } } @@ -58,13 +58,13 @@ public class LeanbackTransitionHelper { static class LeanbackTransitionHelperDefault implements LeanbackTransitionHelperVersion { @Override - public Object loadTitleInTransition(Context context, TransitionHelper helper) { - return helper.loadTransition(context, R.transition.lb_title_in); + public Object loadTitleInTransition(Context context) { + return TransitionHelper.loadTransition(context, R.transition.lb_title_in); } @Override - public Object loadTitleOutTransition(Context context, TransitionHelper helper) { - return helper.loadTransition(context, R.transition.lb_title_out); + public Object loadTitleOutTransition(Context context) { + return TransitionHelper.loadTransition(context, R.transition.lb_title_out); } } @@ -81,11 +81,11 @@ public class LeanbackTransitionHelper { } } - static public Object loadTitleInTransition(Context context, TransitionHelper helper) { - return sImpl.loadTitleInTransition(context, helper); + static public Object loadTitleInTransition(Context context) { + return sImpl.loadTitleInTransition(context); } - static public Object loadTitleOutTransition(Context context, TransitionHelper helper) { - return sImpl.loadTitleOutTransition(context, helper); + static public Object loadTitleOutTransition(Context context) { + return sImpl.loadTitleOutTransition(context); } } diff --git a/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java b/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java index 1c66d0309b..9420154f7c 100644 --- a/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java +++ b/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java @@ -20,6 +20,8 @@ import android.view.View; import android.view.ViewGroup; import android.view.Window; +import java.util.ArrayList; + /** * Helper for view transitions. * @hide @@ -34,8 +36,7 @@ public final class TransitionHelper { public static final int SLIDE_RIGHT = Gravity.RIGHT; public static final int SLIDE_BOTTOM = Gravity.BOTTOM; - private final static TransitionHelper sHelper = new TransitionHelper(); - TransitionHelperVersionImpl mImpl; + private static TransitionHelperVersionImpl sImpl; /** * Gets whether the system supports Transition animations. @@ -62,6 +63,16 @@ public final class TransitionHelper { */ static interface TransitionHelperVersionImpl { + public void setEnterTransition(android.app.Fragment fragment, Object transition); + + public void setExitTransition(android.app.Fragment fragment, Object transition); + + public void setSharedElementEnterTransition(android.app.Fragment fragment, + Object transition); + + public void addSharedElement(android.app.FragmentTransaction ft, + View view, String transitionName); + public Object getSharedElementEnterTransition(Window window); public Object getSharedElementReturnTransition(Window window); @@ -90,6 +101,8 @@ public final class TransitionHelper { public Object createChangeBounds(boolean reparent); + public Object createFadeAndShortSlide(int edge); + public void setChangeBoundsStartDelay(Object changeBounds, View view, int startDelay); public void setChangeBoundsStartDelay(Object changeBounds, int viewId, int startDelay); @@ -103,7 +116,9 @@ public final class TransitionHelper { public void addTransition(Object transitionSet, Object transition); - public void setTransitionListener(Object transition, TransitionListener listener); + public void addTransitionListener(Object transition, TransitionListener listener); + + public void removeTransitionListener(Object transition, TransitionListener listener); public void runTransition(Object scene, Object transition); @@ -130,15 +145,31 @@ public final class TransitionHelper { public Object createDefaultInterpolator(Context context); public Object loadTransition(Context context, int resId); + + public void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup); } /** * Interface used when we do not support Transition animations. */ - private static final class TransitionHelperStubImpl implements TransitionHelperVersionImpl { + static class TransitionHelperStubImpl implements TransitionHelperVersionImpl { private static class TransitionStub { - TransitionListener mTransitionListener; + ArrayList<TransitionListener> mTransitionListeners; + } + + public void setEnterTransition(android.app.Fragment fragment, Object transition) { + } + + public void setExitTransition(android.app.Fragment fragment, Object transition) { + } + + public void setSharedElementEnterTransition(android.app.Fragment fragment, + Object transition) { + } + + public void addSharedElement(android.app.FragmentTransaction ft, + View view, String transitionName) { } @Override @@ -202,6 +233,11 @@ public final class TransitionHelper { } @Override + public Object createFadeAndShortSlide(int edge) { + return new TransitionStub(); + } + + @Override public Object createSlide(int slideEdge) { return new TransitionStub(); } @@ -270,22 +306,38 @@ public final class TransitionHelper { } @Override - public void setTransitionListener(Object transition, TransitionListener listener) { - ((TransitionStub) transition).mTransitionListener = listener; + public void addTransitionListener(Object transition, TransitionListener listener) { + TransitionStub stub = (TransitionStub) transition; + if (stub.mTransitionListeners == null) { + stub.mTransitionListeners = new ArrayList<TransitionListener>(); + } + stub.mTransitionListeners.add(listener); + } + + @Override + public void removeTransitionListener(Object transition, TransitionListener listener) { + TransitionStub stub = (TransitionStub) transition; + if (stub.mTransitionListeners != null) { + stub.mTransitionListeners.remove(listener); + } } @Override public void runTransition(Object scene, Object transition) { TransitionStub transitionStub = (TransitionStub) transition; - if (transitionStub != null && transitionStub.mTransitionListener != null) { - transitionStub.mTransitionListener.onTransitionStart(transition); + if (transitionStub != null && transitionStub.mTransitionListeners != null) { + for (int i = 0, size = transitionStub.mTransitionListeners.size(); i < size; i++) { + transitionStub.mTransitionListeners.get(i).onTransitionStart(transition); + } } Runnable r = ((Runnable) scene); if (r != null) { r.run(); } - if (transitionStub != null && transitionStub.mTransitionListener != null) { - transitionStub.mTransitionListener.onTransitionEnd(transition); + if (transitionStub != null && transitionStub.mTransitionListeners != null) { + for (int i = 0, size = transitionStub.mTransitionListeners.size(); i < size; i++) { + transitionStub.mTransitionListeners.get(i).onTransitionEnd(transition); + } } } @@ -306,52 +358,16 @@ public final class TransitionHelper { public Object loadTransition(Context context, int resId) { return new TransitionStub(); } + + @Override + public void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup) { + } } /** * Implementation used on KitKat (and above). */ - private static class TransitionHelperKitkatImpl implements TransitionHelperVersionImpl { - - @Override - public Object getSharedElementEnterTransition(Window window) { - return null; - } - - @Override - public Object getSharedElementReturnTransition(Window window) { - return null; - } - - @Override - public Object getSharedElementExitTransition(Window window) { - return null; - } - - @Override - public Object getSharedElementReenterTransition(Window window) { - return null; - } - - @Override - public Object getEnterTransition(Window window) { - return null; - } - - @Override - public Object getReturnTransition(Window window) { - return null; - } - - @Override - public Object getExitTransition(Window window) { - return null; - } - - @Override - public Object getReenterTransition(Window window) { - return null; - } + static class TransitionHelperKitkatImpl extends TransitionHelperStubImpl { @Override public Object createScene(ViewGroup sceneRoot, Runnable r) { @@ -455,8 +471,13 @@ public final class TransitionHelper { } @Override - public void setTransitionListener(Object transition, TransitionListener listener) { - TransitionHelperKitkat.setTransitionListener(transition, listener); + public void addTransitionListener(Object transition, TransitionListener listener) { + TransitionHelperKitkat.addTransitionListener(transition, listener); + } + + @Override + public void removeTransitionListener(Object transition, TransitionListener listener) { + TransitionHelperKitkat.removeTransitionListener(transition, listener); } @Override @@ -485,7 +506,25 @@ public final class TransitionHelper { } } - private static final class TransitionHelperApi21Impl extends TransitionHelperKitkatImpl { + static final class TransitionHelperApi21Impl extends TransitionHelperKitkatImpl { + + public void setEnterTransition(android.app.Fragment fragment, Object transition) { + TransitionHelperApi21.setEnterTransition(fragment, transition); + } + + public void setExitTransition(android.app.Fragment fragment, Object transition) { + TransitionHelperApi21.setExitTransition(fragment, transition); + } + + public void setSharedElementEnterTransition(android.app.Fragment fragment, + Object transition) { + TransitionHelperApi21.setSharedElementEnterTransition(fragment, transition); + } + + public void addSharedElement(android.app.FragmentTransaction ft, + View view, String transitionName) { + TransitionHelperApi21.addSharedElement(ft, view, transitionName); + } @Override public Object getSharedElementEnterTransition(Window window) { @@ -508,6 +547,11 @@ public final class TransitionHelper { } @Override + public Object createFadeAndShortSlide(int edge) { + return TransitionHelperApi21.createFadeAndShortSlide(edge); + } + + @Override public Object getEnterTransition(Window window) { return TransitionHelperApi21.getEnterTransition(window); } @@ -536,159 +580,207 @@ public final class TransitionHelper { public Object createDefaultInterpolator(Context context) { return TransitionHelperApi21.createDefaultInterpolator(context); } - } - /** - * Returns the TransitionHelper that can be used to perform Transition - * animations. - */ - public static TransitionHelper getInstance() { - return sHelper; + @Override + public void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup) { + TransitionHelperApi21.setTransitionGroup(viewGroup, transitionGroup); + } } - private TransitionHelper() { + static { if (Build.VERSION.SDK_INT >= 21) { - mImpl = new TransitionHelperApi21Impl(); + sImpl = new TransitionHelperApi21Impl(); } else if (systemSupportsTransitions()) { - mImpl = new TransitionHelperKitkatImpl(); + sImpl = new TransitionHelperKitkatImpl(); } else { - mImpl = new TransitionHelperStubImpl(); + sImpl = new TransitionHelperStubImpl(); } } - public Object getSharedElementEnterTransition(Window window) { - return mImpl.getSharedElementEnterTransition(window); + public static Object getSharedElementEnterTransition(Window window) { + return sImpl.getSharedElementEnterTransition(window); + } + + public static Object getSharedElementReturnTransition(Window window) { + return sImpl.getSharedElementReturnTransition(window); + } + + public static Object getSharedElementExitTransition(Window window) { + return sImpl.getSharedElementExitTransition(window); + } + + public static Object getSharedElementReenterTransition(Window window) { + return sImpl.getSharedElementReenterTransition(window); + } + + public static Object getEnterTransition(Window window) { + return sImpl.getEnterTransition(window); + } + + public static Object getReturnTransition(Window window) { + return sImpl.getReturnTransition(window); + } + + public static Object getExitTransition(Window window) { + return sImpl.getExitTransition(window); + } + + public static Object getReenterTransition(Window window) { + return sImpl.getReenterTransition(window); + } + + public static Object createScene(ViewGroup sceneRoot, Runnable r) { + return sImpl.createScene(sceneRoot, r); + } + + public static Object createChangeBounds(boolean reparent) { + return sImpl.createChangeBounds(reparent); + } + + public static void setChangeBoundsStartDelay(Object changeBounds, View view, int startDelay) { + sImpl.setChangeBoundsStartDelay(changeBounds, view, startDelay); + } + + public static void setChangeBoundsStartDelay(Object changeBounds, int viewId, int startDelay) { + sImpl.setChangeBoundsStartDelay(changeBounds, viewId, startDelay); } - public Object getSharedElementReturnTransition(Window window) { - return mImpl.getSharedElementReturnTransition(window); + public static void setChangeBoundsStartDelay(Object changeBounds, String className, + int startDelay) { + sImpl.setChangeBoundsStartDelay(changeBounds, className, startDelay); } - public Object getSharedElementExitTransition(Window window) { - return mImpl.getSharedElementExitTransition(window); + public static void setChangeBoundsDefaultStartDelay(Object changeBounds, int startDelay) { + sImpl.setChangeBoundsDefaultStartDelay(changeBounds, startDelay); } - public Object getSharedElementReenterTransition(Window window) { - return mImpl.getSharedElementReenterTransition(window); + public static Object createTransitionSet(boolean sequential) { + return sImpl.createTransitionSet(sequential); } - public Object getEnterTransition(Window window) { - return mImpl.getEnterTransition(window); + public static Object createSlide(int slideEdge) { + return sImpl.createSlide(slideEdge); } - public Object getReturnTransition(Window window) { - return mImpl.getReturnTransition(window); + public static Object createScale() { + return sImpl.createScale(); } - public Object getExitTransition(Window window) { - return mImpl.getExitTransition(window); + public static void addTransition(Object transitionSet, Object transition) { + sImpl.addTransition(transitionSet, transition); } - public Object getReenterTransition(Window window) { - return mImpl.getReenterTransition(window); + public static void exclude(Object transition, int targetId, boolean exclude) { + sImpl.exclude(transition, targetId, exclude); } - public Object createScene(ViewGroup sceneRoot, Runnable r) { - return mImpl.createScene(sceneRoot, r); + public static void exclude(Object transition, View targetView, boolean exclude) { + sImpl.exclude(transition, targetView, exclude); } - public Object createChangeBounds(boolean reparent) { - return mImpl.createChangeBounds(reparent); + public static void excludeChildren(Object transition, int targetId, boolean exclude) { + sImpl.excludeChildren(transition, targetId, exclude); } - public void setChangeBoundsStartDelay(Object changeBounds, View view, int startDelay) { - mImpl.setChangeBoundsStartDelay(changeBounds, view, startDelay); + public static void excludeChildren(Object transition, View targetView, boolean exclude) { + sImpl.excludeChildren(transition, targetView, exclude); } - public void setChangeBoundsStartDelay(Object changeBounds, int viewId, int startDelay) { - mImpl.setChangeBoundsStartDelay(changeBounds, viewId, startDelay); + public static void include(Object transition, int targetId) { + sImpl.include(transition, targetId); } - public void setChangeBoundsStartDelay(Object changeBounds, String className, int startDelay) { - mImpl.setChangeBoundsStartDelay(changeBounds, className, startDelay); + public static void include(Object transition, View targetView) { + sImpl.include(transition, targetView); } - public void setChangeBoundsDefaultStartDelay(Object changeBounds, int startDelay) { - mImpl.setChangeBoundsDefaultStartDelay(changeBounds, startDelay); + public static void setStartDelay(Object transition, long startDelay) { + sImpl.setStartDelay(transition, startDelay); } - public Object createTransitionSet(boolean sequential) { - return mImpl.createTransitionSet(sequential); + public static void setDuration(Object transition, long duration) { + sImpl.setDuration(transition, duration); } - public Object createSlide(int slideEdge) { - return mImpl.createSlide(slideEdge); + public static Object createAutoTransition() { + return sImpl.createAutoTransition(); } - public Object createScale() { - return mImpl.createScale(); + public static Object createFadeTransition(int fadeMode) { + return sImpl.createFadeTransition(fadeMode); } - public void addTransition(Object transitionSet, Object transition) { - mImpl.addTransition(transitionSet, transition); + public static void addTransitionListener(Object transition, TransitionListener listener) { + sImpl.addTransitionListener(transition, listener); } - public void exclude(Object transition, int targetId, boolean exclude) { - mImpl.exclude(transition, targetId, exclude); + public static void removeTransitionListener(Object transition, TransitionListener listener) { + sImpl.removeTransitionListener(transition, listener); } - public void exclude(Object transition, View targetView, boolean exclude) { - mImpl.exclude(transition, targetView, exclude); + public static void runTransition(Object scene, Object transition) { + sImpl.runTransition(scene, transition); } - public void excludeChildren(Object transition, int targetId, boolean exclude) { - mImpl.excludeChildren(transition, targetId, exclude); + public static void setInterpolator(Object transition, Object timeInterpolator) { + sImpl.setInterpolator(transition, timeInterpolator); } - public void excludeChildren(Object transition, View targetView, boolean exclude) { - mImpl.excludeChildren(transition, targetView, exclude); + public static void addTarget(Object transition, View view) { + sImpl.addTarget(transition, view); } - public void include(Object transition, int targetId) { - mImpl.include(transition, targetId); + public static Object createDefaultInterpolator(Context context) { + return sImpl.createDefaultInterpolator(context); } - public void include(Object transition, View targetView) { - mImpl.include(transition, targetView); + public static Object loadTransition(Context context, int resId) { + return sImpl.loadTransition(context, resId); } - public void setStartDelay(Object transition, long startDelay) { - mImpl.setStartDelay(transition, startDelay); + public static void setEnterTransition(android.app.Fragment fragment, Object transition) { + sImpl.setEnterTransition(fragment, transition); } - public void setDuration(Object transition, long duration) { - mImpl.setDuration(transition, duration); + public static void setExitTransition(android.app.Fragment fragment, Object transition) { + sImpl.setExitTransition(fragment, transition); } - public Object createAutoTransition() { - return mImpl.createAutoTransition(); + public static void setSharedElementEnterTransition(android.app.Fragment fragment, + Object transition) { + sImpl.setSharedElementEnterTransition(fragment, transition); } - public Object createFadeTransition(int fadeMode) { - return mImpl.createFadeTransition(fadeMode); + public static void addSharedElement(android.app.FragmentTransaction ft, + View view, String transitionName) { + sImpl.addSharedElement(ft, view, transitionName); } - public void setTransitionListener(Object transition, TransitionListener listener) { - mImpl.setTransitionListener(transition, listener); + public static void setEnterTransition(android.support.v4.app.Fragment fragment, + Object transition) { + fragment.setEnterTransition(transition); } - public void runTransition(Object scene, Object transition) { - mImpl.runTransition(scene, transition); + public static void setExitTransition(android.support.v4.app.Fragment fragment, + Object transition) { + fragment.setExitTransition(transition); } - public void setInterpolator(Object transition, Object timeInterpolator) { - mImpl.setInterpolator(transition, timeInterpolator); + public static void setSharedElementEnterTransition(android.support.v4.app.Fragment fragment, + Object transition) { + fragment.setSharedElementEnterTransition(transition); } - public void addTarget(Object transition, View view) { - mImpl.addTarget(transition, view); + public static void addSharedElement(android.support.v4.app.FragmentTransaction ft, + View view, String transitionName) { + ft.addSharedElement(view, transitionName); } - public Object createDefaultInterpolator(Context context) { - return mImpl.createDefaultInterpolator(context); + public static Object createFadeAndShortSlide(int edge) { + return sImpl.createFadeAndShortSlide(edge); } - public Object loadTransition(Context context, int resId) { - return mImpl.loadTransition(context, resId); + public static void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup) { + sImpl.setTransitionGroup(viewGroup, transitionGroup); } } diff --git a/v17/leanback/src/android/support/v17/leanback/widget/BaseCardView.java b/v17/leanback/src/android/support/v17/leanback/widget/BaseCardView.java index 614f12b4dc..085aac3399 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/BaseCardView.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/BaseCardView.java @@ -18,6 +18,7 @@ package android.support.v17.leanback.widget; import android.content.Context; import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; import android.support.v17.leanback.R; import android.util.AttributeSet; import android.util.Log; @@ -154,6 +155,14 @@ public class BaseCardView extends FrameLayout { try { mCardType = a.getInteger(R.styleable.lbBaseCardView_cardType, CARD_TYPE_MAIN_ONLY); + Drawable cardForeground = a.getDrawable(R.styleable.lbBaseCardView_cardForeground); + if (cardForeground != null) { + setForeground(cardForeground); + } + Drawable cardBackground = a.getDrawable(R.styleable.lbBaseCardView_cardBackground); + if (cardBackground != null) { + setBackground(cardBackground); + } mInfoVisibility = a.getInteger(R.styleable.lbBaseCardView_infoVisibility, CARD_REGION_VISIBLE_ACTIVATED); mExtraVisibility = a.getInteger(R.styleable.lbBaseCardView_extraVisibility, diff --git a/v17/leanback/src/android/support/v17/leanback/widget/BaseGridView.java b/v17/leanback/src/android/support/v17/leanback/widget/BaseGridView.java index 93a454e3a0..73e5b4071c 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/BaseGridView.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/BaseGridView.java @@ -23,6 +23,7 @@ import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; +import android.support.v7.widget.SimpleItemAnimator; /** * An abstract base class for vertically and horizontally scrolling lists. The items come @@ -201,7 +202,7 @@ abstract class BaseGridView extends RecyclerView { // Disable change animation by default on leanback. // Change animation will create a new view and cause undesired // focus animation between the old view and new view. - getItemAnimator().setSupportsChangeAnimations(false); + ((SimpleItemAnimator)getItemAnimator()).setSupportsChangeAnimations(false); super.setRecyclerListener(new RecyclerView.RecyclerListener() { @Override public void onViewRecycled(RecyclerView.ViewHolder holder) { diff --git a/v17/leanback/src/android/support/v17/leanback/widget/CheckableImageView.java b/v17/leanback/src/android/support/v17/leanback/widget/CheckableImageView.java new file mode 100644 index 0000000000..627bbd4fc8 --- /dev/null +++ b/v17/leanback/src/android/support/v17/leanback/widget/CheckableImageView.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2015 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.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.Checkable; +import android.widget.ImageView; + +/** + * ImageView that supports Checkable states. + */ +class CheckableImageView extends ImageView implements Checkable { + + private boolean mChecked; + + private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked }; + + public CheckableImageView(Context context) { + this(context, null); + } + + public CheckableImageView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public CheckableImageView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + public int[] onCreateDrawableState(final int extraSpace) { + final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); + if (isChecked()) { + mergeDrawableStates(drawableState, CHECKED_STATE_SET); + } + return drawableState; + } + + @Override + public void toggle() { + setChecked(!mChecked); + } + + @Override + public boolean isChecked() { + return mChecked; + } + + @Override + public void setChecked(final boolean checked) { + if (mChecked != checked) { + mChecked = checked; + refreshDrawableState(); + } + } + +} diff --git a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java index 410aa28d65..cac7d9eb41 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java @@ -232,11 +232,10 @@ final class DetailsOverviewSharedElementHelper extends SharedElementCallback { Log.d(TAG, "setTransitionName "+mViewHolder.mOverviewFrame); } ViewCompat.setTransitionName(mViewHolder.mOverviewFrame, mSharedElementName); - final TransitionHelper transitionHelper = TransitionHelper.getInstance(); - Object transition = transitionHelper.getSharedElementEnterTransition( + Object transition = TransitionHelper.getSharedElementEnterTransition( mActivityToRunTransition.getWindow()); if (transition != null) { - transitionHelper.setTransitionListener(transition, new TransitionListener() { + TransitionHelper.addTransitionListener(transition, new TransitionListener() { @Override public void onTransitionEnd(Object transition) { if (DEBUG) { @@ -247,7 +246,7 @@ final class DetailsOverviewSharedElementHelper extends SharedElementCallback { if (mViewHolder.mActionsRow.isFocused()) { mViewHolder.mActionsRow.requestFocus(); } - transitionHelper.setTransitionListener(transition, null); + TransitionHelper.removeTransitionListener(transition, this); } }); } diff --git a/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java index 48829eb72f..87bbbd47af 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java @@ -85,7 +85,7 @@ public class FocusHighlightHelper { mWrapper = null; } mAnimator.setTimeListener(this); - if (mWrapper != null && useDimmer) { + if (useDimmer) { mDimmer = ColorOverlayDimmer.createDefault(view.getContext()); } else { mDimmer = null; @@ -99,9 +99,16 @@ public class FocusHighlightHelper { mView.setScaleY(scale); if (mWrapper != null) { mWrapper.setShadowFocusLevel(level); - if (mDimmer != null) { - mDimmer.setActiveLevel(level); - mWrapper.setOverlayColor(mDimmer.getPaint().getColor()); + } else { + ShadowOverlayHelper.setNoneWrapperShadowFocusLevel(mView, level); + } + if (mDimmer != null) { + mDimmer.setActiveLevel(level); + int color = mDimmer.getPaint().getColor(); + if (mWrapper != null) { + mWrapper.setOverlayColor(color); + } else { + ShadowOverlayHelper.setNoneWrapperOverlayColor(mView, color); } } } diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ForegroundHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/ForegroundHelper.java new file mode 100644 index 0000000000..c9bed58c4c --- /dev/null +++ b/v17/leanback/src/android/support/v17/leanback/widget/ForegroundHelper.java @@ -0,0 +1,78 @@ +package android.support.v17.leanback.widget; + +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.view.View; +import android.view.ViewGroup; + +final class ForegroundHelper { + + final static ForegroundHelper sInstance = new ForegroundHelper(); + ForegroundHelperVersionImpl mImpl; + + /** + * Interface implemented by classes that support Shadow. + */ + static interface ForegroundHelperVersionImpl { + + public void setForeground(View view, Drawable drawable); + + public Drawable getForeground(View view); + } + + /** + * Implementation used on api 23 (and above). + */ + private static final class ForegroundHelperApi23Impl implements ForegroundHelperVersionImpl { + @Override + public void setForeground(View view, Drawable drawable) { + ForegroundHelperApi23.setForeground(view, drawable); + } + + @Override + public Drawable getForeground(View view) { + return ForegroundHelperApi23.getForeground(view); + } + } + + /** + * Stub implementation + */ + private static final class ForegroundHelperStubImpl implements ForegroundHelperVersionImpl { + @Override + public void setForeground(View view, Drawable drawable) { + } + + @Override + public Drawable getForeground(View view) { + return null; + } + } + + private ForegroundHelper() { + if (supportsForeground()) { + mImpl = new ForegroundHelperApi23Impl(); + } else { + mImpl = new ForegroundHelperStubImpl(); + } + } + + public static ForegroundHelper getInstance() { + return sInstance; + } + + /** + * Returns true if view.setForeground() is supported. + */ + public static boolean supportsForeground() { + return Build.VERSION.SDK_INT >= 23; + } + + public Drawable getForeground(View view) { + return mImpl.getForeground(view); + } + + public void setForeground(View view, Drawable drawable) { + mImpl.setForeground(view, drawable); + } +} diff --git a/v17/leanback/src/android/support/v17/leanback/widget/FragmentAnimationProvider.java b/v17/leanback/src/android/support/v17/leanback/widget/FragmentAnimationProvider.java index 8bd0007c59..1c5dcb5f42 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/FragmentAnimationProvider.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/FragmentAnimationProvider.java @@ -26,47 +26,15 @@ import java.util.List; public interface FragmentAnimationProvider { /** - * Animates the entry of the fragment in the case where the activity is first being presented. + * Animates the fragment in response to the IME appearing. * @param animators A list of animations to which this provider's animations should be added. */ - public abstract void onActivityEnter(@NonNull List<Animator> animators); + public abstract void onImeAppearing(@NonNull List<Animator> animators); /** - * Animates the exit of the fragment in the case where the activity is about to pause. + * Animates the fragment in response to the IME disappearing. * @param animators A list of animations to which this provider's animations should be added. */ - public abstract void onActivityExit(@NonNull List<Animator> animators); - - /** - * Animates the entry of the fragment in the case where there is a previous step fragment - * participating in the animation. Entry occurs when the fragment is preparing to be shown - * as it is pushed onto the back stack. - * @param animators A list of animations to which this provider's animations should be added. - */ - public abstract void onFragmentEnter(@NonNull List<Animator> animators); - - /** - * Animates the exit of the fragment in the case where there is a previous step fragment - * participating in the animation. Exit occurs when the fragment is preparing to be removed, - * hidden, or detached due to pushing another fragment onto the back stack. - * @param animators A list of animations to which this provider's animations should be added. - */ - public abstract void onFragmentExit(@NonNull List<Animator> animators); - - /** - * Animates the re-entry of the fragment in the case where there is a previous step fragment - * participating in the animation. Re-entry occurs when the fragment is preparing to be shown - * due to popping the back stack. - * @param animators A list of animations to which this provider's animations should be added. - */ - public abstract void onFragmentReenter(@NonNull List<Animator> animators); - - /** - * Animates the return of the fragment in the case where there is a previous step fragment - * participating in the animation. Return occurs when the fragment is preparing to be removed, - * hidden, or detached due to popping the back stack. - * @param animators A list of animations to which this provider's animations should be added. - */ - public abstract void onFragmentReturn(@NonNull List<Animator> animators); + public abstract void onImeDisappearing(@NonNull List<Animator> animators); } diff --git a/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewRowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewRowPresenter.java index c4418c1644..7c9d5db2ff 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewRowPresenter.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewRowPresenter.java @@ -528,19 +528,6 @@ public class FullWidthDetailsOverviewRowPresenter extends RowPresenter { mListener = listener; } - private int getDefaultBackgroundColor(Context context) { - TypedValue outValue = new TypedValue(); - if (context.getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true)) { - return context.getResources().getColor(outValue.resourceId); - } - return context.getResources().getColor(R.color.lb_default_brand_color); - } - - private int getDefaultActionsBackgroundColor(Context context) { - int c = getDefaultBackgroundColor(context); - return Color.argb(Color.alpha(c), Color.red(c) / 2, Color.green(c) / 2, Color.blue(c) / 2); - } - /** * Get resource id to inflate the layout. The layout must match {@link #STATE_HALF} */ @@ -558,13 +545,13 @@ public class FullWidthDetailsOverviewRowPresenter extends RowPresenter { vh.mActionBridgeAdapter = new ActionsItemBridgeAdapter(vh); final View overview = vh.mOverviewFrame; - final int bgColor = mBackgroundColorSet ? mBackgroundColor : - getDefaultBackgroundColor(overview.getContext()); - overview.setBackgroundColor(bgColor); - final int actionBgColor = mActionsBackgroundColorSet ? mActionsBackgroundColor : - getDefaultActionsBackgroundColor(overview.getContext()); - overview.findViewById(R.id.details_overview_actions_background) - .setBackgroundColor(actionBgColor); + if (mBackgroundColorSet) { + overview.setBackgroundColor(mBackgroundColor); + } + if (mActionsBackgroundColorSet) { + overview.findViewById(R.id.details_overview_actions_background) + .setBackgroundColor(mActionsBackgroundColor); + } RoundedRectHelper.getInstance().setClipToRoundedOutline(overview, true); if (!getSelectEffectEnabled()) { diff --git a/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper.java index 857c4d922c..a9fe9ecd2a 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper.java @@ -76,8 +76,7 @@ public class FullWidthDetailsOverviewSharedElementHelper extends if (DEBUG) { Log.d(TAG, "postponeEnterTransition " + mActivityToRunTransition); } - Object transition = TransitionHelper.getInstance() - .getSharedElementEnterTransition(activity.getWindow()); + Object transition = TransitionHelper.getSharedElementEnterTransition(activity.getWindow()); setAutoStartSharedElementTransition(transition != null); ActivityCompat.postponeEnterTransition(mActivityToRunTransition); if (timeoutMs > 0) { @@ -140,11 +139,10 @@ public class FullWidthDetailsOverviewSharedElementHelper extends } ViewCompat.setTransitionName(mViewHolder.getLogoViewHolder().view, mSharedElementName); - final TransitionHelper transitionHelper = TransitionHelper.getInstance(); - Object transition = transitionHelper.getSharedElementEnterTransition( + Object transition = TransitionHelper.getSharedElementEnterTransition( mActivityToRunTransition.getWindow()); if (transition != null) { - transitionHelper.setTransitionListener(transition, new TransitionListener() { + TransitionHelper.addTransitionListener(transition, new TransitionListener() { @Override public void onTransitionEnd(Object transition) { if (DEBUG) { @@ -155,7 +153,7 @@ public class FullWidthDetailsOverviewSharedElementHelper extends if (mViewHolder.getActionsRow().isFocused()) { mViewHolder.getActionsRow().requestFocus(); } - transitionHelper.setTransitionListener(transition, null); + TransitionHelper.removeTransitionListener(transition, this); } }); } diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java index 10c7e3efd6..20d54e24bb 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java @@ -58,7 +58,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager { * - Saves optical bounds insets. * - Caches focus align view center. */ - static class LayoutParams extends RecyclerView.LayoutParams { + final static class LayoutParams extends RecyclerView.LayoutParams { // For placement private int mLeftInset; @@ -128,6 +128,32 @@ final class GridLayoutManager extends RecyclerView.LayoutManager { return view.getHeight() - mTopInset - mBottomInset; } + int getDecoratedOpticalLeftWithMargin(RecyclerView.LayoutManager lm, View view) { + return lm.getDecoratedLeft(view) + mLeftInset - leftMargin; + } + + int getDecoratedOpticalTopWithMargin(RecyclerView.LayoutManager lm, View view) { + return lm.getDecoratedTop(view) + mTopInset - topMargin; + } + + int getDecoratedOpticalRightWithMargin(RecyclerView.LayoutManager lm, View view) { + return lm.getDecoratedRight(view) - mRightInset + rightMargin; + } + + int getDecoratedOpticalBottomWithMargin(RecyclerView.LayoutManager lm, View view) { + return lm.getDecoratedBottom(view) - mBottomInset + bottomMargin; + } + + int getDecoratedOpticalWidthWithMargin(RecyclerView.LayoutManager lm, View view) { + return lm.getDecoratedRight(view) - lm.getDecoratedLeft(view) + - mLeftInset - mRightInset + leftMargin + rightMargin; + } + + int getDecoratedOpticalHeightWithMargin(RecyclerView.LayoutManager lm, View view) { + return lm.getDecoratedBottom(view) - lm.getDecoratedTop(view) + - mTopInset - mBottomInset + topMargin + bottomMargin; + } + int getOpticalLeftInset() { return mLeftInset; } @@ -199,9 +225,18 @@ final class GridLayoutManager extends RecyclerView.LayoutManager { @Override protected void onStop() { - // onTargetFound() may not be called if we hit the "wall" first. + // onTargetFound() may not be called if we hit the "wall" first or get cancelled. View targetView = findViewByPosition(getTargetPosition()); - if (hasFocus() && targetView != null) { + if (targetView == null) { + if (getTargetPosition() >= 0) { + // if smooth scroller is stopped without target, immediately jumps + // to the target position. + scrollToSelection(mBaseGridView, getTargetPosition(), 0, false, 0); + } + super.onStop(); + return; + } + if (hasFocus()) { mInSelection = true; targetView.requestFocus(); mInSelection = false; @@ -391,6 +426,8 @@ final class GridLayoutManager extends RecyclerView.LayoutManager { private RecyclerView.State mState; private RecyclerView.Recycler mRecycler; + private static final Rect sTempRect = new Rect(); + private boolean mInLayout; private boolean mInScroll; private boolean mInFastRelayout; @@ -913,16 +950,21 @@ final class GridLayoutManager extends RecyclerView.LayoutManager { } private int getViewMin(View v) { - return (mOrientation == HORIZONTAL) ? getOpticalLeft(v) : getOpticalTop(v); + LayoutParams lp = (LayoutParams) v.getLayoutParams(); + return (mOrientation == HORIZONTAL) ? lp.getDecoratedOpticalLeftWithMargin(this, v) + : lp.getDecoratedOpticalTopWithMargin(this, v); } private int getViewMax(View v) { - return (mOrientation == HORIZONTAL) ? getOpticalRight(v) : getOpticalBottom(v); + LayoutParams lp = (LayoutParams) v.getLayoutParams(); + return (mOrientation == HORIZONTAL) ? lp.getDecoratedOpticalRightWithMargin(this, v) + : lp.getDecoratedOpticalBottomWithMargin(this, v); } private int getViewPrimarySize(View view) { LayoutParams p = (LayoutParams) view.getLayoutParams(); - return mOrientation == HORIZONTAL ? p.getOpticalWidth(view) : p.getOpticalHeight(view); + return mOrientation == HORIZONTAL ? p.getDecoratedOpticalWidthWithMargin(this, view) + : p.getDecoratedOpticalHeightWithMargin(this, view); } private int getViewCenter(View view) { @@ -1056,18 +1098,33 @@ final class GridLayoutManager extends RecyclerView.LayoutManager { return getRowStartSecondary(rightmostIndex) + getRowSizeSecondary(rightmostIndex); } + int getDecoratedMeasuredWidthWithMargin(View v) { + final LayoutParams lp = (LayoutParams) v.getLayoutParams(); + return getDecoratedMeasuredWidth(v) + lp.leftMargin + lp.rightMargin; + } + + int getDecoratedMeasuredHeightWithMargin(View v) { + final LayoutParams lp = (LayoutParams) v.getLayoutParams(); + return getDecoratedMeasuredHeight(v) + lp.topMargin + lp.bottomMargin; + } + private void measureScrapChild(int position, int widthSpec, int heightSpec, int[] measuredDimension) { View view = mRecycler.getViewForPosition(position); if (view != null) { - LayoutParams p = (LayoutParams) view.getLayoutParams(); + final LayoutParams p = (LayoutParams) view.getLayoutParams(); + calculateItemDecorationsForChild(view, sTempRect); + int widthUsed = p.leftMargin + p.rightMargin + sTempRect.left + sTempRect.right; + int heightUsed = p.topMargin + p.bottomMargin + sTempRect.top + sTempRect.bottom; + int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, - getPaddingLeft() + getPaddingRight(), p.width); + getPaddingLeft() + getPaddingRight() + widthUsed, p.width); int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, - getPaddingTop() + getPaddingBottom(), p.height); + getPaddingTop() + getPaddingBottom() + heightUsed, p.height); view.measure(childWidthSpec, childHeightSpec); - measuredDimension[0] = view.getMeasuredWidth(); - measuredDimension[1] = view.getMeasuredHeight(); + + measuredDimension[0] = getDecoratedMeasuredWidthWithMargin(view); + measuredDimension[1] = getDecoratedMeasuredHeightWithMargin(view); mRecycler.recycleView(view); } } @@ -1100,7 +1157,8 @@ final class GridLayoutManager extends RecyclerView.LayoutManager { measureChild(view); } final int secondarySize = mOrientation == HORIZONTAL ? - view.getMeasuredHeight() : view.getMeasuredWidth(); + getDecoratedMeasuredHeightWithMargin(view) + : getDecoratedMeasuredWidthWithMargin(view); if (secondarySize > rowSize) { rowSize = secondarySize; } @@ -1229,14 +1287,8 @@ final class GridLayoutManager extends RecyclerView.LayoutManager { } else { switch (modeSecondary) { case MeasureSpec.UNSPECIFIED: - if (mRowSizeSecondaryRequested == 0) { - if (mOrientation == HORIZONTAL) { - throw new IllegalStateException("Must specify rowHeight or view height"); - } else { - throw new IllegalStateException("Must specify columnWidth or view width"); - } - } - mFixedRowSizeSecondary = mRowSizeSecondaryRequested; + mFixedRowSizeSecondary = mRowSizeSecondaryRequested == 0 ? + sizeSecondary - paddingSecondary: mRowSizeSecondaryRequested; mNumRows = mNumRowsRequested == 0 ? 1 : mNumRowsRequested; measuredSizeSecondary = mFixedRowSizeSecondary * mNumRows + mMarginSecondary * (mNumRows - 1) + paddingSecondary; @@ -1287,7 +1339,11 @@ final class GridLayoutManager extends RecyclerView.LayoutManager { private void measureChild(View child) { if (TRACE) TraceHelper.beginSection("measureChild"); - final ViewGroup.LayoutParams lp = child.getLayoutParams(); + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + calculateItemDecorationsForChild(child, sTempRect); + int widthUsed = lp.leftMargin + lp.rightMargin + sTempRect.left + sTempRect.right; + int heightUsed = lp.topMargin + lp.bottomMargin + sTempRect.top + sTempRect.bottom; + final int secondarySpec = (mRowSizeSecondaryRequested == ViewGroup.LayoutParams.WRAP_CONTENT) ? MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED) : MeasureSpec.makeMeasureSpec(mFixedRowSizeSecondary, MeasureSpec.EXACTLY); @@ -1295,14 +1351,12 @@ final class GridLayoutManager extends RecyclerView.LayoutManager { if (mOrientation == HORIZONTAL) { widthSpec = ViewGroup.getChildMeasureSpec( - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), - 0, lp.width); - heightSpec = ViewGroup.getChildMeasureSpec(secondarySpec, 0, lp.height); + MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), widthUsed, lp.width); + heightSpec = ViewGroup.getChildMeasureSpec(secondarySpec, heightUsed, lp.height); } else { heightSpec = ViewGroup.getChildMeasureSpec( - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), - 0, lp.height); - widthSpec = ViewGroup.getChildMeasureSpec(secondarySpec, 0, lp.width); + MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), heightUsed, lp.height); + widthSpec = ViewGroup.getChildMeasureSpec(secondarySpec, widthUsed, lp.width); } child.measure(widthSpec, heightSpec); if (DEBUG) Log.v(getTag(), "measureChild secondarySpec " + Integer.toHexString(secondarySpec) + @@ -1395,7 +1449,8 @@ final class GridLayoutManager extends RecyclerView.LayoutManager { measureChild(v); } item[0] = v; - return mOrientation == HORIZONTAL ? v.getMeasuredWidth() : v.getMeasuredHeight(); + return mOrientation == HORIZONTAL ? getDecoratedMeasuredWidthWithMargin(v) + : getDecoratedMeasuredHeightWithMargin(v); } @Override @@ -1476,8 +1531,8 @@ final class GridLayoutManager extends RecyclerView.LayoutManager { private void layoutChild(int rowIndex, View v, int start, int end, int startSecondary) { if (TRACE) TraceHelper.beginSection("layoutChild"); - int sizeSecondary = mOrientation == HORIZONTAL ? v.getMeasuredHeight() - : v.getMeasuredWidth(); + int sizeSecondary = mOrientation == HORIZONTAL ? getDecoratedMeasuredHeightWithMargin(v) + : getDecoratedMeasuredWidthWithMargin(v); if (mFixedRowSizeSecondary > 0) { sizeSecondary = Math.min(sizeSecondary, mFixedRowSizeSecondary); } @@ -1507,7 +1562,9 @@ final class GridLayoutManager extends RecyclerView.LayoutManager { bottom = end; right = startSecondary + sizeSecondary; } - v.layout(left, top, right, bottom); + LayoutParams params = (LayoutParams) v.getLayoutParams(); + layoutDecorated(v, left + params.leftMargin, top + params.topMargin, + right - params.rightMargin, bottom - params.bottomMargin); updateChildOpticalInsets(v, left, top, right, bottom); updateChildAlignments(v); if (TRACE) TraceHelper.endSection(); @@ -1624,10 +1681,10 @@ final class GridLayoutManager extends RecyclerView.LayoutManager { measureChild(view); } if (mOrientation == HORIZONTAL) { - primarySize = view.getMeasuredWidth(); + primarySize = getDecoratedMeasuredWidthWithMargin(view); end = start + primarySize; } else { - primarySize = view.getMeasuredHeight(); + primarySize = getDecoratedMeasuredHeightWithMargin(view); end = start + primarySize; } layoutChild(location.row, view, start, end, startSecondary); @@ -1716,6 +1773,11 @@ final class GridLayoutManager extends RecyclerView.LayoutManager { mFocusPositionOffset = 0; saveContext(recycler, state); + View savedFocusView = findViewByPosition(mFocusPosition); + int savedFocusPos = mFocusPosition; + int savedSubFocusPos = mSubFocusPosition; + boolean hadFocus = mBaseGridView.hasFocus(); + // Track the old focus view so we can adjust our system scroll position // so that any scroll animations happening now will remain valid. // We must use same delta in Pre Layout (if prelayout exists) and second layout. @@ -1724,17 +1786,14 @@ final class GridLayoutManager extends RecyclerView.LayoutManager { if (mFocusPosition != NO_POSITION && scrollToFocus && mBaseGridView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) { // FIXME: we should get the remaining scroll animation offset from RecyclerView - View focusView = findViewByPosition(mFocusPosition); - if (focusView != null) { - if (getScrollPosition(focusView, focusView.findFocus(), sTwoInts)) { + if (savedFocusView != null) { + if (getScrollPosition(savedFocusView, savedFocusView.findFocus(), sTwoInts)) { delta = sTwoInts[0]; deltaSecondary = sTwoInts[1]; } } } - boolean hadFocus = mBaseGridView.hasFocus(); - int savedFocusPos = mFocusPosition; if (mInFastRelayout = layoutInit()) { fastRelayout(); // appends items till focus position. @@ -1802,7 +1861,8 @@ final class GridLayoutManager extends RecyclerView.LayoutManager { } // For fastRelayout, only dispatch event when focus position changes. - if (mInFastRelayout && mFocusPosition != savedFocusPos) { + if (mInFastRelayout && (mFocusPosition != savedFocusPos || mSubFocusPosition != + savedFocusPos || findViewByPosition(mFocusPosition) != savedFocusView)) { dispatchChildSelected(); } else if (!mInFastRelayout && mInLayoutSearchFocus) { // For full layout we dispatchChildSelected() in createItem() unless searched all @@ -1968,7 +2028,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager { int pos = sTwoInts[1]; int savedMaxEdge = mWindowAlignment.mainAxis().getMaxEdge(); mWindowAlignment.mainAxis().setMaxEdge(maxEdge); - int maxScroll = getPrimarySystemScrollPosition(findViewByPosition(pos)); + int maxScroll = getPrimarySystemScrollPositionOfChildMax(findViewByPosition(pos)); mWindowAlignment.mainAxis().setMaxEdge(savedMaxEdge); if (highAvailable) { @@ -2319,6 +2379,16 @@ final class GridLayoutManager extends RecyclerView.LayoutManager { return mWindowAlignment.mainAxis().getSystemScrollPos(viewCenterPrimary, isMin, isMax); } + private int getPrimarySystemScrollPositionOfChildMax(View view) { + int scrollPosition = getPrimarySystemScrollPosition(view); + final LayoutParams lp = (LayoutParams) view.getLayoutParams(); + int[] multipleAligns = lp.getAlignMultiple(); + if (multipleAligns != null && multipleAligns.length > 0) { + scrollPosition += multipleAligns[multipleAligns.length - 1] - multipleAligns[0]; + } + return scrollPosition; + } + /** * Get adjusted primary position for a given childView (if there is multiple ItemAlignment defined * on the view). diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java index 8d125102f0..3fcdbba524 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java @@ -54,11 +54,8 @@ import java.util.List; * </ul><p> * View IDs are allowed to be missing, in which case the corresponding views will be null. * - * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceEntryAnimation - * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepEntryAnimation - * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepExitAnimation - * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepReentryAnimation - * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepReturnAnimation + * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeAppearingAnimation + * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeDisappearingAnimation * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceContainerStyle * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceTitleStyle * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceDescriptionStyle @@ -176,6 +173,16 @@ public class GuidanceStylist implements FragmentAnimationProvider { } /** + * Called when destroy the View created by GuidanceStylist. + */ + public void onDestroyView() { + mBreadcrumbView = null; + mDescriptionView = null; + mIconView = null; + mTitleView = null; + } + + /** * Provides the resource ID of the layout defining the guidance view. Subclasses may override * to provide their own customized layouts. The base implementation returns * {@link android.support.v17.leanback.R.layout#lb_guidance}. If overridden, the substituted @@ -223,61 +230,22 @@ public class GuidanceStylist implements FragmentAnimationProvider { * {@inheritDoc} */ @Override - public void onActivityEnter(@NonNull List<Animator> animators) { - addAnimator(animators, mTitleView, R.attr.guidanceEntryAnimation); - addAnimator(animators, mBreadcrumbView, R.attr.guidanceEntryAnimation); - addAnimator(animators, mDescriptionView, R.attr.guidanceEntryAnimation); - addAnimator(animators, mIconView, R.attr.guidanceEntryAnimation); - } - - /** - * {@inheritDoc} - */ - @Override - public void onActivityExit(@NonNull List<Animator> animators) {} - - /** - * {@inheritDoc} - */ - @Override - public void onFragmentEnter(@NonNull List<Animator> animators) { - addAnimator(animators, mTitleView, R.attr.guidedStepEntryAnimation); - addAnimator(animators, mBreadcrumbView, R.attr.guidedStepEntryAnimation); - addAnimator(animators, mDescriptionView, R.attr.guidedStepEntryAnimation); - addAnimator(animators, mIconView, R.attr.guidedStepEntryAnimation); - } - - /** - * {@inheritDoc} - */ - @Override - public void onFragmentExit(@NonNull List<Animator> animators) { - addAnimator(animators, mTitleView, R.attr.guidedStepExitAnimation); - addAnimator(animators, mBreadcrumbView, R.attr.guidedStepExitAnimation); - addAnimator(animators, mDescriptionView, R.attr.guidedStepExitAnimation); - addAnimator(animators, mIconView, R.attr.guidedStepExitAnimation); - } - - /** - * {@inheritDoc} - */ - @Override - public void onFragmentReenter(@NonNull List<Animator> animators) { - addAnimator(animators, mTitleView, R.attr.guidedStepReentryAnimation); - addAnimator(animators, mBreadcrumbView, R.attr.guidedStepReentryAnimation); - addAnimator(animators, mDescriptionView, R.attr.guidedStepReentryAnimation); - addAnimator(animators, mIconView, R.attr.guidedStepReentryAnimation); + public void onImeAppearing(@NonNull List<Animator> animators) { + addAnimator(animators, mTitleView, R.attr.guidedStepImeAppearingAnimation); + addAnimator(animators, mBreadcrumbView, R.attr.guidedStepImeAppearingAnimation); + addAnimator(animators, mDescriptionView, R.attr.guidedStepImeAppearingAnimation); + addAnimator(animators, mIconView, R.attr.guidedStepImeAppearingAnimation); } /** * {@inheritDoc} */ @Override - public void onFragmentReturn(@NonNull List<Animator> animators) { - addAnimator(animators, mTitleView, R.attr.guidedStepReturnAnimation); - addAnimator(animators, mBreadcrumbView, R.attr.guidedStepReturnAnimation); - addAnimator(animators, mDescriptionView, R.attr.guidedStepReturnAnimation); - addAnimator(animators, mIconView, R.attr.guidedStepReturnAnimation); + public void onImeDisappearing(@NonNull List<Animator> animators) { + addAnimator(animators, mTitleView, R.attr.guidedStepImeDisappearingAnimation); + addAnimator(animators, mBreadcrumbView, R.attr.guidedStepImeDisappearingAnimation); + addAnimator(animators, mDescriptionView, R.attr.guidedStepImeDisappearingAnimation); + addAnimator(animators, mIconView, R.attr.guidedStepImeDisappearingAnimation); } private void addAnimator(List<Animator> animators, View v, int attrId) { diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidedAction.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidedAction.java index e4db2eba68..21986d51e0 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/GuidedAction.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidedAction.java @@ -13,9 +13,12 @@ */ package android.support.v17.leanback.widget; +import android.support.v17.leanback.R; + import android.content.Context; import android.content.Intent; import android.graphics.drawable.Drawable; +import android.text.InputType; import android.util.Log; /** @@ -34,47 +37,189 @@ public class GuidedAction extends Action { private static final String TAG = "GuidedAction"; - public static final int NO_DRAWABLE = 0; + /** + * Special check set Id that is neither checkbox nor radio. + */ public static final int NO_CHECK_SET = 0; + /** + * Default checkset Id for radio. + */ public static final int DEFAULT_CHECK_SET_ID = 1; + /** + * Checkset Id for checkbox. + */ + public static final int CHECKBOX_CHECK_SET_ID = -1; + + /** + * When finishing editing, goes to next action. + */ + public static final long ACTION_ID_NEXT = -2; + /** + * When finishing editing, stay on current action. + */ + public static final long ACTION_ID_CURRENT = -3; + + /** + * Id of standard OK action. + */ + public static final long ACTION_ID_OK = -4; + + /** + * Id of standard Cancel action. + */ + public static final long ACTION_ID_CANCEL = -5; + + /** + * Id of standard Finish action. + */ + public static final long ACTION_ID_FINISH = -6; + + /** + * Id of standard Finish action. + */ + public static final long ACTION_ID_CONTINUE = -7; + + /** + * Id of standard Yes action. + */ + public static final long ACTION_ID_YES = -8; /** - * Builds a {@link GuidedAction} object. + * Id of standard No action. + */ + public static final long ACTION_ID_NO = -9; + + /** + * Builds a {@link GuidedAction} object. When subclass GuidedAction, you may override this + * Builder class and call {@link #applyValues(GuidedAction)}. */ public static class Builder { private long mId; - private String mTitle; - private String mDescription; + private CharSequence mTitle; + private CharSequence mEditTitle; + private CharSequence mDescription; + private CharSequence mEditDescription; private Drawable mIcon; private boolean mChecked; private boolean mMultilineDescription; private boolean mHasNext; private boolean mInfoOnly; + private boolean mEditable = false; + private boolean mDescriptionEditable = false; + private int mInputType = InputType.TYPE_CLASS_TEXT; + private int mDescriptionInputType = InputType.TYPE_CLASS_TEXT; + private int mEditInputType = InputType.TYPE_CLASS_TEXT; + private int mDescriptionEditInputType = InputType.TYPE_CLASS_TEXT; private int mCheckSetId = NO_CHECK_SET; private boolean mEnabled = true; + private boolean mFocusable = true; private Intent mIntent; /** * Builds the GuidedAction corresponding to this Builder. * @return the GuidedAction as configured through this Builder. */ - public GuidedAction build() { + public final GuidedAction build() { GuidedAction action = new GuidedAction(); + applyValues(action); + return action; + } + + /** + * Subclass Builder may call this function to apply values. + * @param action GuidedAction to apply Builder values. + */ + protected final void applyValues(GuidedAction action) { // Base Action values action.setId(mId); action.setLabel1(mTitle); + action.setEditTitle(mEditTitle); action.setLabel2(mDescription); + action.setEditDescription(mEditDescription); action.setIcon(mIcon); // Subclass values action.mIntent = mIntent; + action.mEditable = mEditable; + action.mDescriptionEditable = mDescriptionEditable; + action.mInputType = mInputType; + action.mDescriptionInputType = mDescriptionInputType; + action.mEditInputType = mEditInputType; + action.mDescriptionEditInputType = mDescriptionEditInputType; action.mChecked = mChecked; action.mCheckSetId = mCheckSetId; action.mMultilineDescription = mMultilineDescription; action.mHasNext = mHasNext; action.mInfoOnly = mInfoOnly; action.mEnabled = mEnabled; - return action; + action.mFocusable = mFocusable; + } + + /** + * Construct a standard "OK" action with {@link GuidedAction#ACTION_ID_OK}. + * @param context Context for loading action title. + * @return The same Builder object. + */ + public Builder constructOK(Context context) { + mId = ACTION_ID_OK; + mTitle = context.getString(android.R.string.ok); + return this; + } + + /** + * Construct a standard "Cancel" action with {@link GuidedAction#ACTION_ID_CANCEL}. + * @param context Context for loading action title. + * @return The same Builder object. + */ + public Builder constructCancel(Context context) { + mId = ACTION_ID_CANCEL; + mTitle = context.getString(android.R.string.cancel); + return this; + } + + /** + * Construct a standard "Finish" action with {@link GuidedAction#ACTION_ID_FINISH}. + * @param context Context for loading action title. + * @return The same Builder object. + */ + public Builder constructFinish(Context context) { + mId = ACTION_ID_FINISH; + mTitle = context.getString(R.string.lb_guidedaction_finish_title); + return this; + } + + /** + * Construct a standard "Continue" action with {@link GuidedAction#ACTION_ID_CONTINUE}. + * @param context Context for loading action title. + * @return The same Builder object. + */ + public Builder constructContinue(Context context) { + mId = ACTION_ID_CONTINUE; + mHasNext = true; + mTitle = context.getString(R.string.lb_guidedaction_continue_title); + return this; + } + + /** + * Construct a standard "Yes" action with {@link GuidedAction#ACTION_ID_YES}. + * @param context Context for loading action title. + * @return The same Builder object. + */ + public Builder constructYes(Context context) { + mId = ACTION_ID_YES; + mTitle = context.getString(android.R.string.yes); + return this; + } + + /** + * Construct a standard "No" action with {@link GuidedAction#ACTION_ID_NO}. + * @param context Context for loading action title. + * @return The same Builder object. + */ + public Builder constructNo(Context context) { + mId = ACTION_ID_NO; + mTitle = context.getString(android.R.string.no); + return this; } /** @@ -92,22 +237,41 @@ public class GuidedAction extends Action { * action to be taken on click, e.g. "Continue" or "Cancel". * @param title The title for this action. */ - public Builder title(String title) { + public Builder title(CharSequence title) { mTitle = title; return this; } /** + * Sets the optional title text to edit. When TextView is activated, the edit title + * replaces the string of title. + */ + public Builder editTitle(CharSequence editTitle) { + mEditTitle = editTitle; + return this; + } + + /** * Sets the description for this action. The description is typically a longer string * providing extra information on what the action will do. * @param description The description for this action. */ - public Builder description(String description) { + public Builder description(CharSequence description) { mDescription = description; return this; } /** + * Sets the optional description text to edit. When TextView is activated, the edit + * description replaces the string of description. + * @param description The description to edit for this action. + */ + public Builder editDescription(CharSequence description) { + mEditDescription = description; + return this; + } + + /** * Sets the intent associated with this action. Clients would typically fire this intent * directly when the action is clicked. * @param intent The intent associated with this action. @@ -138,22 +302,96 @@ public class GuidedAction extends Action { } /** + * Indicates whether this action title is editable. Note: Editable actions cannot also be + * checked, or belong to a check set. + * @param editable Whether this action is editable. + */ + public Builder editable(boolean editable) { + mEditable = editable; + if (mChecked || mCheckSetId != NO_CHECK_SET) { + throw new IllegalArgumentException("Editable actions cannot also be checked"); + } + return this; + } + + /** + * Indicates whether this action's description is editable + * @param editable Whether this action description is editable. + */ + public Builder descriptionEditable(boolean editable) { + mDescriptionEditable = editable; + if (mChecked || mCheckSetId != NO_CHECK_SET) { + throw new IllegalArgumentException("Editable actions cannot also be checked"); + } + return this; + } + + /** + * Sets {@link InputType} of this action title not in editing. + * + * @param inputType InputType for the action title not in editing. + */ + public Builder inputType(int inputType) { + mInputType = inputType; + return this; + } + + /** + * Sets {@link InputType} of this action description not in editing. + * + * @param inputType InputType for the action description not in editing. + */ + public Builder descriptionInputType(int inputType) { + mDescriptionInputType = inputType; + return this; + } + + + /** + * Sets {@link InputType} of this action title in editing. + * + * @param inputType InputType for the action title in editing. + */ + public Builder editInputType(int inputType) { + mEditInputType = inputType; + return this; + } + + /** + * Sets {@link InputType} of this action description in editing. + * + * @param inputType InputType for the action description in editing. + */ + public Builder descriptionEditInputType(int inputType) { + mDescriptionEditInputType = inputType; + return this; + } + + + /** * Indicates whether this action is initially checked. * @param checked Whether this action is checked. */ public Builder checked(boolean checked) { mChecked = checked; + if (mEditable || mDescriptionEditable) { + throw new IllegalArgumentException("Editable actions cannot also be checked"); + } return this; } /** - * Indicates whether this action is part of a single-select group similar to radio buttons. - * When one item in a check set is checked, all others with the same check set ID will be - * unchecked automatically. - * @param checkSetId The check set ID, or {@link #NO_CHECK_SET) to indicate no check set. + * Indicates whether this action is part of a single-select group similar to radio buttons + * or this action is a checkbox. When one item in a check set is checked, all others with + * the same check set ID will be nchecked automatically. + * @param checkSetId The check set ID, or {@link GuidedAction#NO_CHECK_SET} to indicate not + * radio or checkbox, or {@link GuidedAction#CHECKBOX_CHECK_SET_ID} to indicate a checkbox. */ public Builder checkSetId(int checkSetId) { mCheckSetId = checkSetId; + if (mEditable || mDescriptionEditable) { + throw new IllegalArgumentException("Editable actions cannot also be in check sets"); + } return this; } @@ -193,18 +431,37 @@ public class GuidedAction extends Action { mEnabled = enabled; return this; } + + /** + * Indicates whether this action can take focus. + * @param focusable + * @return The same Builder object. + */ + public Builder focusable(boolean focusable) { + mFocusable = focusable; + return this; + } } - private boolean mChecked; + private CharSequence mEditTitle; + private CharSequence mEditDescription; + private boolean mEditable; + private boolean mDescriptionEditable; + private int mInputType; + private int mDescriptionInputType; + private int mEditInputType; + private int mDescriptionEditInputType; private boolean mMultilineDescription; private boolean mHasNext; + private boolean mChecked; private boolean mInfoOnly; private int mCheckSetId; private boolean mEnabled; + private boolean mFocusable; private Intent mIntent; - private GuidedAction() { + protected GuidedAction() { super(0); } @@ -217,14 +474,74 @@ public class GuidedAction extends Action { } /** + * Sets the title of this action. + * @param title The title set when this action was built. + */ + public void setTitle(CharSequence title) { + setLabel1(title); + } + + /** + * Returns the optional title text to edit. When not null, it is being edited instead of + * {@link #getTitle()}. + * @return Optional title text to edit instead of {@link #getTitle()}. + */ + public CharSequence getEditTitle() { + return mEditTitle; + } + + /** + * Sets the optional title text to edit instead of {@link #setTitle(CharSequence)}. + * @param editTitle Optional title text to edit instead of {@link #setTitle(CharSequence)}. + */ + public void setEditTitle(CharSequence editTitle) { + mEditTitle = editTitle; + } + + /** + * Returns the optional description text to edit. When not null, it is being edited instead of + * {@link #getDescription()}. + * @return Optional description text to edit instead of {@link #getDescription()}. + */ + public CharSequence getEditDescription() { + return mEditDescription; + } + + /** + * Sets the optional description text to edit instead of {@link #setDescription(CharSequence)}. + * @param editDescription Optional description text to edit instead of + * {@link #setDescription(CharSequence)}. + */ + public void setEditDescription(CharSequence editDescription) { + mEditDescription = editDescription; + } + + /** + * Returns true if {@link #getEditTitle()} is not null. When true, the {@link #getEditTitle()} + * is being edited instead of {@link #getTitle()}. + * @return true if {@link #getEditTitle()} is not null. + */ + public boolean isEditTitleUsed() { + return mEditTitle != null; + } + + /** * Returns the description of this action. - * @return The description set when this action was built. + * @return The description of this action. */ public CharSequence getDescription() { return getLabel2(); } /** + * Sets the description of this action. + * @param description The description of the action. + */ + public void setDescription(CharSequence description) { + setLabel2(description); + } + + /** * Returns the intent associated with this action. * @return The intent set when this action was built. */ @@ -233,6 +550,55 @@ public class GuidedAction extends Action { } /** + * Returns whether this action title is editable. + * @return true if the action title is editable, false otherwise. + */ + public boolean isEditable() { + return mEditable; + } + + /** + * Returns whether this action description is editable. + * @return true if the action description is editable, false otherwise. + */ + public boolean isDescriptionEditable() { + return mDescriptionEditable; + } + + /** + * Returns InputType of action title in editing; only valid when {@link #isEditable()} is true. + * @return InputType of action title in editing. + */ + public int getEditInputType() { + return mEditInputType; + } + + /** + * Returns InputType of action description in editing; only valid when + * {@link #isDescriptionEditable()} is true. + * @return InputType of action description in editing. + */ + public int getDescriptionEditInputType() { + return mDescriptionEditInputType; + } + + /** + * Returns InputType of action title not in editing. + * @return InputType of action title not in editing. + */ + public int getInputType() { + return mInputType; + } + + /** + * Returns InputType of action description not in editing. + * @return InputType of action description not in editing. + */ + public int getDescriptionInputType() { + return mDescriptionInputType; + } + + /** * Returns whether this action is checked. * @return true if the action is currently checked, false otherwise. */ @@ -249,13 +615,13 @@ public class GuidedAction extends Action { } /** - * Returns the check set id this action is a part of. All actions in the - * same list with the same check set id are considered linked. When one - * of the actions within that set is selected, that action becomes - * checked, while all the other actions become unchecked. + * Returns the check set id this action is a part of. All actions in the same list with the same + * check set id are considered linked. When one of the actions within that set is selected, that + * action becomes checked, while all the other actions become unchecked. * * @return an integer representing the check set this action is a part of, or - * {@link #NO_CHECK_SET} if this action isn't a part of a check set. + * {@link #CHECKBOX_CHECK_SET_ID} if this is a checkbox, or {@link #NO_CHECK_SET} if + * this action is not a checkbox or radiobutton. */ public int getCheckSetId() { return mCheckSetId; @@ -287,6 +653,22 @@ public class GuidedAction extends Action { } /** + * Returns whether this action is focusable. + * @return true if the action is currently focusable, false otherwise. + */ + public boolean isFocusable() { + return mFocusable; + } + + /** + * Sets whether this action is focusable. + * @param focusable Whether this action should be focusable. + */ + public void setFocusable(boolean focusable) { + mFocusable = focusable; + } + + /** * Returns whether this action will request further user input when selected, such as showing * another GuidedStepFragment or launching a new activity. Configured during construction. * @return true if the action will request further user input when selected, false otherwise. diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionEditText.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionEditText.java new file mode 100644 index 0000000000..8e052fb248 --- /dev/null +++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionEditText.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2014 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.widget; + +import android.content.Context; +import android.support.v17.leanback.widget.ImeKeyMonitor.ImeKeyListener; +import android.util.AttributeSet; +import android.widget.EditText; +import android.view.KeyEvent; + +/** + * A custom EditText that satisfies the IME key monitoring requirements of GuidedStepFragment. + */ +public class GuidedActionEditText extends EditText implements ImeKeyMonitor { + + private ImeKeyListener mKeyListener; + + public GuidedActionEditText(Context ctx) { + this(ctx, null); + } + + public GuidedActionEditText(Context ctx, AttributeSet attrs) { + this(ctx, attrs, android.R.attr.editTextStyle); + } + + public GuidedActionEditText(Context ctx, AttributeSet attrs, int defStyleAttr) { + super(ctx, attrs, defStyleAttr); + } + + @Override + public void setImeKeyListener(ImeKeyListener listener) { + mKeyListener = listener; + } + + @Override + public boolean onKeyPreIme(int keyCode, KeyEvent event) { + boolean result = false; + if (mKeyListener != null) { + result = mKeyListener.onKeyPreIme(this, keyCode, event); + } + if (!result) { + result = super.onKeyPreIme(keyCode, event); + } + return result; + } + +} diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java index 15943b423a..6e5d5067db 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java @@ -22,17 +22,21 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Resources; import android.content.res.TypedArray; +import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.net.Uri; import android.support.annotation.NonNull; import android.support.v17.leanback.R; import android.support.v17.leanback.widget.VerticalGridView; +import android.support.v4.content.ContextCompat; +import android.support.v4.view.ViewCompat; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.ViewHolder; import android.text.TextUtils; import android.util.Log; import android.util.TypedValue; import android.view.animation.DecelerateInterpolator; +import android.view.inputmethod.EditorInfo; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -40,6 +44,8 @@ import android.view.ViewGroup.LayoutParams; import android.view.ViewPropertyAnimator; import android.view.ViewTreeObserver; import android.view.WindowManager; +import android.widget.Checkable; +import android.widget.EditText; import android.widget.ImageView; import android.widget.TextView; @@ -81,11 +87,15 @@ import java.util.List; * </ul><p> * These view IDs are allowed to be missing, in which case the corresponding views in {@link * GuidedActionsStylist.ViewHolder} will be null. + * <p> + * In order to support editable actions, the view associated with guidedactions_item_title should + * be a subclass of {@link android.widget.EditText}, and should satisfy the {@link + * ImeKeyMonitor} interface. * - * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsEntryAnimation + * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeAppearingAnimation + * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeDisappearingAnimation * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsSelectorShowAnimation * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsSelectorHideAnimation - * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsContainerStyle * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsSelectorStyle * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsListStyle * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemContainerStyle @@ -95,24 +105,30 @@ import java.util.List; * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemTitleStyle * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemDescriptionStyle * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemChevronStyle - * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionCheckedAnimation - * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionUncheckedAnimation * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionPressedAnimation * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionUnpressedAnimation * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionEnabledChevronAlpha * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionDisabledChevronAlpha - * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionContentWidth - * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionContentWidthNoIcon * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionTitleMinLines * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionTitleMaxLines * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionDescriptionMinLines * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionVerticalPadding + * @see android.R.styleable#Theme_listChoiceIndicatorSingle + * @see android.R.styleable#Theme_listChoiceIndicatorMultiple * @see android.support.v17.leanback.app.GuidedStepFragment * @see GuidedAction */ public class GuidedActionsStylist implements FragmentAnimationProvider { /** + * Default viewType that associated with default layout Id for the action item. + * @see #getItemViewType(GuidedAction) + * @see #onProvideItemLayoutId(int) + * @see #onCreateViewHolder(ViewGroup, int) + */ + public static final int VIEW_TYPE_DEFAULT = 0; + + /** * ViewHolder caches information about the action item layouts' subviews. Subclasses of {@link * GuidedActionsStylist} may also wish to subclass this in order to add fields. * @see GuidedAction @@ -127,6 +143,8 @@ public class GuidedActionsStylist implements FragmentAnimationProvider { private ImageView mIconView; private ImageView mCheckmarkView; private ImageView mChevronView; + private boolean mInEditing; + private boolean mInEditingDescription; /** * Constructs an ViewHolder and caches the relevant subviews. @@ -158,6 +176,14 @@ public class GuidedActionsStylist implements FragmentAnimationProvider { } /** + * Convenience method to return an editable version of the title, if possible, + * or null if the title view isn't an EditText. + */ + public EditText getEditableTitleView() { + return (mTitleView instanceof EditText) ? (EditText)mTitleView : null; + } + + /** * Returns the description view within this view holder's view. */ public TextView getDescriptionView() { @@ -165,6 +191,14 @@ public class GuidedActionsStylist implements FragmentAnimationProvider { } /** + * Convenience method to return an editable version of the description, if possible, + * or null if the description view isn't an EditText. + */ + public EditText getEditableDescriptionView() { + return (mDescriptionView instanceof EditText) ? (EditText)mDescriptionView : null; + } + + /** * Returns the icon view within this view holder's view. */ public ImageView getIconView() { @@ -185,19 +219,47 @@ public class GuidedActionsStylist implements FragmentAnimationProvider { return mChevronView; } + /** + * Returns true if the TextView is in editing title or description, false otherwise. + */ + public boolean isInEditing() { + return mInEditing; + } + + /** + * Returns true if the TextView is in editing description, false otherwise. + */ + public boolean isInEditingDescription() { + return mInEditingDescription; + } + + public View getEditingView() { + if (mInEditing) { + return mInEditingDescription ? mDescriptionView : mTitleView; + } else { + return null; + } + } } private static String TAG = "GuidedActionsStylist"; - protected View mMainView; - protected VerticalGridView mActionsGridView; - protected View mSelectorView; + private ViewGroup mMainView; + private VerticalGridView mActionsGridView; + private View mBgView; + private View mSelectorView; + private View mContentView; + private boolean mButtonActions; + + private Animator mSelectorAnimator; // Cached values from resources + private float mEnabledTextAlpha; + private float mDisabledTextAlpha; + private float mEnabledDescriptionAlpha; + private float mDisabledDescriptionAlpha; private float mEnabledChevronAlpha; private float mDisabledChevronAlpha; - private int mContentWidth; - private int mContentWidthNoIcon; private int mTitleMinLines; private int mTitleMaxLines; private int mDescriptionMinLines; @@ -216,8 +278,17 @@ public class GuidedActionsStylist implements FragmentAnimationProvider { * @return The view to be added to the caller's view hierarchy. */ public View onCreateView(LayoutInflater inflater, ViewGroup container) { - mMainView = inflater.inflate(onProvideLayoutId(), container, false); + mMainView = (ViewGroup) inflater.inflate(onProvideLayoutId(), container, false); + mContentView = mMainView.findViewById(R.id.guidedactions_content); mSelectorView = mMainView.findViewById(R.id.guidedactions_selector); + mSelectorView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom) { + updateSelectorView(false); + } + }); + mBgView = mMainView.findViewById(R.id.guidedactions_list_background); if (mMainView instanceof VerticalGridView) { mActionsGridView = (VerticalGridView) mMainView; } else { @@ -229,32 +300,23 @@ public class GuidedActionsStylist implements FragmentAnimationProvider { mActionsGridView.setWindowAlignmentOffsetPercent(50f); mActionsGridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE); if (mSelectorView != null) { - mActionsGridView.setOnScrollListener(new - SelectorAnimator(mSelectorView, mActionsGridView)); + mActionsGridView.setOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + if (mSelectorView.getAlpha() != 1f) { + updateSelectorView(true); + } + } + } + }); } } - mActionsGridView.requestFocusFromTouch(); - if (mSelectorView != null) { // ALlow focus to move to other views mActionsGridView.getViewTreeObserver().addOnGlobalFocusChangeListener( - new ViewTreeObserver.OnGlobalFocusChangeListener() { - private boolean mChildFocused; - - @Override - public void onGlobalFocusChanged(View oldFocus, View newFocus) { - View focusedChild = mActionsGridView.getFocusedChild(); - if (focusedChild == null) { - mSelectorView.setVisibility(View.INVISIBLE); - mChildFocused = false; - } else if (!mChildFocused) { - mChildFocused = true; - mSelectorView.setVisibility(View.VISIBLE); - updateSelectorView(focusedChild); - } - } - }); + mGlobalFocusChangeListener); } // Cache widths, chevron alpha values, max and min text lines, etc @@ -262,8 +324,6 @@ public class GuidedActionsStylist implements FragmentAnimationProvider { TypedValue val = new TypedValue(); mEnabledChevronAlpha = getFloat(ctx, val, R.attr.guidedActionEnabledChevronAlpha); mDisabledChevronAlpha = getFloat(ctx, val, R.attr.guidedActionDisabledChevronAlpha); - mContentWidth = getDimension(ctx, val, R.attr.guidedActionContentWidth); - mContentWidthNoIcon = getDimension(ctx, val, R.attr.guidedActionContentWidthNoIcon); mTitleMinLines = getInteger(ctx, val, R.attr.guidedActionTitleMinLines); mTitleMaxLines = getInteger(ctx, val, R.attr.guidedActionTitleMaxLines); mDescriptionMinLines = getInteger(ctx, val, R.attr.guidedActionDescriptionMinLines); @@ -271,10 +331,79 @@ public class GuidedActionsStylist implements FragmentAnimationProvider { mDisplayHeight = ((WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay().getHeight(); + mEnabledTextAlpha = Float.valueOf(ctx.getResources().getString(R.string + .lb_guidedactions_item_unselected_text_alpha)); + mDisabledTextAlpha = Float.valueOf(ctx.getResources().getString(R.string + .lb_guidedactions_item_disabled_text_alpha)); + mEnabledDescriptionAlpha = Float.valueOf(ctx.getResources().getString(R.string + .lb_guidedactions_item_unselected_description_text_alpha)); + mDisabledDescriptionAlpha = Float.valueOf(ctx.getResources().getString(R.string + .lb_guidedactions_item_disabled_description_text_alpha)); return mMainView; } /** + * Default implementation turns on background for actions and applies different Ids to views so + * that GuidedStepFragment could run transitions against two action lists. The method is called + * by GuidedStepFragment, app may override this function when replacing default layout file + * provided by {@link #onProvideLayoutId()} + */ + public void setAsButtonActions() { + mButtonActions = true; + mMainView.setId(R.id.guidedactions_root2); + ViewCompat.setTransitionName(mMainView, "guidedactions_root2"); + if (mActionsGridView != null) { + mActionsGridView.setId(R.id.guidedactions_list2); + } + if (mSelectorView != null) { + mSelectorView.setId(R.id.guidedactions_selector2); + ViewCompat.setTransitionName(mSelectorView, "guidedactions_selector2"); + } + if (mContentView != null) { + mContentView.setId(R.id.guidedactions_content2); + ViewCompat.setTransitionName(mContentView, "guidedactions_content2"); + } + if (mBgView != null) { + mBgView.setId(R.id.guidedactions_list_background2); + ViewCompat.setTransitionName(mBgView, "guidedactions_list_background2"); + mBgView.setVisibility(View.VISIBLE); + } + } + + /** + * Returns true if {@link #setAsButtonActions()} was called, false otherwise. + * @return True if {@link #setAsButtonActions()} was called, false otherwise. + */ + public boolean isButtonActions() { + return mButtonActions; + } + + final ViewTreeObserver.OnGlobalFocusChangeListener mGlobalFocusChangeListener = + new ViewTreeObserver.OnGlobalFocusChangeListener() { + + @Override + public void onGlobalFocusChanged(View oldFocus, View newFocus) { + updateSelectorView(false); + } + }; + + /** + * Called when destroy the View created by GuidedActionsStylist. + */ + public void onDestroyView() { + if (mSelectorView != null) { + mActionsGridView.getViewTreeObserver().removeOnGlobalFocusChangeListener( + mGlobalFocusChangeListener); + } + endSelectorAnimator(); + mActionsGridView = null; + mSelectorView = null; + mContentView = null; + mBgView = null; + mMainView = null; + } + + /** * Returns the VerticalGridView that displays the list of GuidedActions. * @return The VerticalGridView for this presenter. */ @@ -296,11 +425,25 @@ public class GuidedActionsStylist implements FragmentAnimationProvider { } /** + * Return view type of action, each different type can have differently associated layout Id. + * Default implementation returns {@link #VIEW_TYPE_DEFAULT}. + * @param action The action object. + * @return View type that used in {@link #onProvideItemLayoutId(int)}. + */ + public int getItemViewType(GuidedAction action) { + return VIEW_TYPE_DEFAULT; + } + + /** * Provides the resource ID of the layout defining the view for an individual guided actions. * Subclasses may override to provide their own customized layouts. The base implementation * returns {@link android.support.v17.leanback.R.layout#lb_guidedactions_item}. If overridden, * the substituted layout should contain matching IDs for any views that should be managed by - * the base class; this can be achieved by starting with a copy of the base layout file. + * the base class; this can be achieved by starting with a copy of the base layout file. Note + * that in order for the item to support editing, the title view should both subclass {@link + * android.widget.EditText} and implement {@link ImeKeyMonitor}; see {@link + * GuidedActionEditText}. To support different types of Layouts, override {@link + * #onProvideItemLayoutId(int)}. * @return The resource ID of the layout to be inflated to define the view to display an * individual GuidedAction. */ @@ -309,8 +452,31 @@ public class GuidedActionsStylist implements FragmentAnimationProvider { } /** + * Provides the resource ID of the layout defining the view for an individual guided actions. + * Subclasses may override to provide their own customized layouts. The base implementation + * returns {@link android.support.v17.leanback.R.layout#lb_guidedactions_item}. If overridden, + * the substituted layout should contain matching IDs for any views that should be managed by + * the base class; this can be achieved by starting with a copy of the base layout file. Note + * that in order for the item to support editing, the title view should both subclass {@link + * android.widget.EditText} and implement {@link ImeKeyMonitor}; see {@link + * GuidedActionEditText}. + * @param viewType View type returned by {@link #getItemViewType(GuidedAction)} + * @return The resource ID of the layout to be inflated to define the view to display an + * individual GuidedAction. + */ + public int onProvideItemLayoutId(int viewType) { + if (viewType == VIEW_TYPE_DEFAULT) { + return onProvideItemLayoutId(); + } else { + throw new RuntimeException("ViewType " + viewType + + " not supported in GuidedActionsStylist"); + } + } + + /** * Constructs a {@link ViewHolder} capable of representing {@link GuidedAction}s. Subclasses - * may choose to return a subclass of ViewHolder. + * may choose to return a subclass of ViewHolder. To support different view types, override + * {@link #onCreateViewHolder(ViewGroup, int)} * <p> * <i>Note: Should not actually add the created view to the parent; the caller will do * this.</i> @@ -324,6 +490,25 @@ public class GuidedActionsStylist implements FragmentAnimationProvider { } /** + * Constructs a {@link ViewHolder} capable of representing {@link GuidedAction}s. Subclasses + * may choose to return a subclass of ViewHolder. + * <p> + * <i>Note: Should not actually add the created view to the parent; the caller will do + * this.</i> + * @param parent The view group to be used as the parent of the new view. + * @param viewType The viewType returned by {@link #getItemViewType(GuidedAction)} + * @return The view to be added to the caller's view hierarchy. + */ + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + if (viewType == VIEW_TYPE_DEFAULT) { + return onCreateViewHolder(parent); + } + LayoutInflater inflater = LayoutInflater.from(parent.getContext()); + View v = inflater.inflate(onProvideItemLayoutId(viewType), parent, false); + return new ViewHolder(v); + } + + /** * Binds a {@link ViewHolder} to a particular {@link GuidedAction}. * @param vh The view holder to be associated with the given action. * @param action The guided action to be displayed by the view holder's view. @@ -333,31 +518,24 @@ public class GuidedActionsStylist implements FragmentAnimationProvider { if (vh.mTitleView != null) { vh.mTitleView.setText(action.getTitle()); + vh.mTitleView.setAlpha(action.isEnabled() ? mEnabledTextAlpha : mDisabledTextAlpha); + vh.mTitleView.setFocusable(action.isEditable()); } if (vh.mDescriptionView != null) { vh.mDescriptionView.setText(action.getDescription()); vh.mDescriptionView.setVisibility(TextUtils.isEmpty(action.getDescription()) ? View.GONE : View.VISIBLE); + vh.mDescriptionView.setAlpha(action.isEnabled() ? mEnabledDescriptionAlpha : + mDisabledDescriptionAlpha); + vh.mDescriptionView.setFocusable(action.isDescriptionEditable()); } // Clients might want the check mark view to be gone entirely, in which case, ignore it. - if (vh.mCheckmarkView != null && vh.mCheckmarkView.getVisibility() != View.GONE) { - vh.mCheckmarkView.setVisibility(action.isChecked() ? View.VISIBLE : View.INVISIBLE); - } - - if (vh.mContentView != null) { - ViewGroup.LayoutParams contentLp = vh.mContentView.getLayoutParams(); - if (setIcon(vh.mIconView, action)) { - contentLp.width = mContentWidth; - } else { - contentLp.width = mContentWidthNoIcon; - } - vh.mContentView.setLayoutParams(contentLp); + if (vh.mCheckmarkView != null) { + onBindCheckMarkView(vh, action); } if (vh.mChevronView != null) { - vh.mChevronView.setVisibility(action.hasNext() ? View.VISIBLE : View.INVISIBLE); - vh.mChevronView.setAlpha(action.isEnabled() ? mEnabledChevronAlpha : - mDisabledChevronAlpha); + onBindChevronView(vh, action); } if (action.hasMultilineDescription()) { @@ -376,6 +554,89 @@ public class GuidedActionsStylist implements FragmentAnimationProvider { vh.mDescriptionView.setMaxLines(mDescriptionMinLines); } } + setEditingMode(vh, action, false); + if (action.isFocusable()) { + vh.view.setFocusable(true); + if (vh.view instanceof ViewGroup) { + ((ViewGroup) vh.view).setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS); + } + } else { + vh.view.setFocusable(false); + if (vh.view instanceof ViewGroup) { + ((ViewGroup) vh.view).setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); + } + } + setupImeOptions(vh, action); + } + + /** + * Called by {@link #onBindViewHolder(ViewHolder, GuidedAction)} to setup IME options. Default + * implementation assigns {@link EditorInfo#IME_ACTION_DONE}. Subclass may override. + * @param vh The view holder to be associated with the given action. + * @param action The guided action to be displayed by the view holder's view. + */ + protected void setupImeOptions(ViewHolder vh, GuidedAction action) { + setupNextImeOptions(vh.getEditableTitleView()); + setupNextImeOptions(vh.getEditableDescriptionView()); + } + + private void setupNextImeOptions(EditText edit) { + if (edit != null) { + edit.setImeOptions(EditorInfo.IME_ACTION_NEXT); + } + } + + public void setEditingMode(ViewHolder vh, GuidedAction action, boolean editing) { + if (editing != vh.mInEditing) { + vh.mInEditing = editing; + onEditingModeChange(vh, action, editing); + } + } + + protected void onEditingModeChange(ViewHolder vh, GuidedAction action, boolean editing) { + TextView titleView = vh.getTitleView(); + TextView descriptionView = vh.getDescriptionView(); + if (editing) { + CharSequence editTitle = action.getEditTitle(); + if (titleView != null && editTitle != null) { + titleView.setText(editTitle); + } + CharSequence editDescription = action.getEditDescription(); + if (descriptionView != null && editDescription != null) { + descriptionView.setText(editDescription); + } + if (action.isDescriptionEditable()) { + if (descriptionView != null) { + descriptionView.setVisibility(View.VISIBLE); + descriptionView.setInputType(action.getDescriptionEditInputType()); + } + vh.mInEditingDescription = true; + } else { + vh.mInEditingDescription = false; + if (titleView != null) { + titleView.setInputType(action.getEditInputType()); + } + } + } else { + if (titleView != null) { + titleView.setText(action.getTitle()); + } + if (descriptionView != null) { + descriptionView.setText(action.getDescription()); + } + if (vh.mInEditingDescription) { + if (descriptionView != null) { + descriptionView.setVisibility(TextUtils.isEmpty(action.getDescription()) ? + View.GONE : View.VISIBLE); + descriptionView.setInputType(action.getDescriptionInputType()); + } + vh.mInEditingDescription = false; + } else { + if (titleView != null) { + titleView.setInputType(action.getInputType()); + } + } + } } /** @@ -402,85 +663,97 @@ public class GuidedActionsStylist implements FragmentAnimationProvider { } /** - * Animates the view holder's view (or subviews thereof) when the action has had its check - * state changed. + * Resets the view holder's view to unpressed state. * @param vh The view holder associated with the relevant action. - * @param checked True if the action has become checked, false if it has become unchecked. */ - public void onAnimateItemChecked(ViewHolder vh, boolean checked) { - final View checkView = vh.mCheckmarkView; - if (checkView != null) { - if (checked) { - checkView.setVisibility(View.VISIBLE); - createAnimator(checkView, R.attr.guidedActionCheckedAnimation).start(); - } else { - Animator animator = createAnimator(checkView, - R.attr.guidedActionUncheckedAnimation); - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - checkView.setVisibility(View.INVISIBLE); - } - }); - animator.start(); - } - } + public void onAnimateItemPressedCancelled(ViewHolder vh) { + createAnimator(vh.view, R.attr.guidedActionUnpressedAnimation).end(); } - /* - * ========================================== - * FragmentAnimationProvider overrides - * ========================================== - */ - /** - * {@inheritDoc} + * Animates the view holder's view (or subviews thereof) when the action has had its check state + * changed. Default implementation calls setChecked() if {@link ViewHolder#getCheckmarkView()} + * is instance of {@link Checkable}. + * + * @param vh The view holder associated with the relevant action. + * @param checked True if the action has become checked, false if it has become unchecked. + * @see #onBindCheckMarkView(ViewHolder, GuidedAction) */ - @Override - public void onActivityEnter(@NonNull List<Animator> animators) { - animators.add(createAnimator(mMainView, R.attr.guidedActionsEntryAnimation)); + public void onAnimateItemChecked(ViewHolder vh, boolean checked) { + if (vh.mCheckmarkView instanceof Checkable) { + ((Checkable) vh.mCheckmarkView).setChecked(checked); + } } /** - * {@inheritDoc} + * Sets states of check mark view, called by {@link #onBindViewHolder(ViewHolder, GuidedAction)} + * when action's checkset Id is other than {@link GuidedAction#NO_CHECK_SET}. Default + * implementation assigns drawable loaded from theme attribute + * {@link android.R.attr#listChoiceIndicatorMultiple} for checkbox or + * {@link android.R.attr#listChoiceIndicatorSingle} for radio button. Subclass rarely needs + * override the method, instead app can provide its own drawable that supports transition + * animations, change theme attributes {@link android.R.attr#listChoiceIndicatorMultiple} and + * {@link android.R.attr#listChoiceIndicatorSingle} in {android.support.v17.leanback.R. + * styleable#LeanbackGuidedStepTheme}. + * + * @param vh The view holder associated with the relevant action. + * @param action The GuidedAction object to bind to. + * @see #onAnimateItemChecked(ViewHolder, boolean) */ - @Override - public void onActivityExit(@NonNull List<Animator> animators) {} + public void onBindCheckMarkView(ViewHolder vh, GuidedAction action) { + if (action.getCheckSetId() != GuidedAction.NO_CHECK_SET) { + vh.mCheckmarkView.setVisibility(View.VISIBLE); + int attrId = action.getCheckSetId() == GuidedAction.CHECKBOX_CHECK_SET_ID ? + android.R.attr.listChoiceIndicatorMultiple : + android.R.attr.listChoiceIndicatorSingle; + final Context context = vh.mCheckmarkView.getContext(); + Drawable drawable = null; + TypedValue typedValue = new TypedValue(); + if (context.getTheme().resolveAttribute(attrId, typedValue, true)) { + drawable = ContextCompat.getDrawable(context, typedValue.resourceId); + } + vh.mCheckmarkView.setImageDrawable(drawable); + if (vh.mCheckmarkView instanceof Checkable) { + ((Checkable) vh.mCheckmarkView).setChecked(action.isChecked()); + } + } else { + vh.mCheckmarkView.setVisibility(View.GONE); + } + } /** - * {@inheritDoc} + * Sets states of chevron view, called by {@link #onBindViewHolder(ViewHolder, GuidedAction)}. + * Subclass may override. + * + * @param vh The view holder associated with the relevant action. + * @param action The GuidedAction object to bind to. */ - @Override - public void onFragmentEnter(@NonNull List<Animator> animators) { - animators.add(createAnimator(mActionsGridView, R.attr.guidedStepEntryAnimation)); - animators.add(createAnimator(mSelectorView, R.attr.guidedStepEntryAnimation)); + public void onBindChevronView(ViewHolder vh, GuidedAction action) { + vh.mChevronView.setVisibility(action.hasNext() ? View.VISIBLE : View.GONE); + vh.mChevronView.setAlpha(action.isEnabled() ? mEnabledChevronAlpha : + mDisabledChevronAlpha); } - /** - * {@inheritDoc} + /* + * ========================================== + * FragmentAnimationProvider overrides + * ========================================== */ - @Override - public void onFragmentExit(@NonNull List<Animator> animators) { - animators.add(createAnimator(mActionsGridView, R.attr.guidedStepExitAnimation)); - animators.add(createAnimator(mSelectorView, R.attr.guidedStepExitAnimation)); - } /** * {@inheritDoc} */ @Override - public void onFragmentReenter(@NonNull List<Animator> animators) { - animators.add(createAnimator(mActionsGridView, R.attr.guidedStepReentryAnimation)); - animators.add(createAnimator(mSelectorView, R.attr.guidedStepReentryAnimation)); + public void onImeAppearing(@NonNull List<Animator> animators) { + animators.add(createAnimator(mContentView, R.attr.guidedStepImeAppearingAnimation)); } /** * {@inheritDoc} */ @Override - public void onFragmentReturn(@NonNull List<Animator> animators) { - animators.add(createAnimator(mActionsGridView, R.attr.guidedStepReturnAnimation)); - animators.add(createAnimator(mSelectorView, R.attr.guidedStepReturnAnimation)); + public void onImeDisappearing(@NonNull List<Animator> animators) { + animators.add(createAnimator(mContentView, R.attr.guidedStepImeDisappearingAnimation)); } /* @@ -489,15 +762,6 @@ public class GuidedActionsStylist implements FragmentAnimationProvider { * ========================================== */ - private void updateSelectorView(View focusedChild) { - // Display the selector view. - int height = focusedChild.getHeight(); - LayoutParams lp = mSelectorView.getLayoutParams(); - lp.height = height; - mSelectorView.setLayoutParams(lp); - mSelectorView.setAlpha(1f); - } - private float getFloat(Context ctx, TypedValue typedValue, int attrId) { ctx.getTheme().resolveAttribute(attrId, typedValue, true); // Android resources don't have a native float type, so we have to use strings. @@ -551,96 +815,43 @@ public class GuidedActionsStylist implements FragmentAnimationProvider { return (int)(mDisplayHeight - 2*mVerticalPadding - 2*mTitleMaxLines*title.getLineHeight()); } - /** - * SelectorAnimator - * Controls animation for selected item backgrounds - * TODO: Move into focus animation override? - */ - private static class SelectorAnimator extends RecyclerView.OnScrollListener { - - private final View mSelectorView; - private final ViewGroup mParentView; - private volatile boolean mFadedOut = true; - - SelectorAnimator(View selectorView, ViewGroup parentView) { - mSelectorView = selectorView; - mParentView = parentView; + private void endSelectorAnimator() { + if (mSelectorAnimator != null) { + mSelectorAnimator.end(); + mSelectorAnimator = null; } + } - // We want to fade in the selector if we've stopped scrolling on it. If - // we're scrolling, we want to ensure to dim the selector if we haven't - // already. We dim the last highlighted view so that while a user is - // scrolling, nothing is highlighted. - @Override - public void onScrollStateChanged(RecyclerView recyclerView, int newState) { - Animator animator = null; - boolean fadingOut = false; - if (newState == RecyclerView.SCROLL_STATE_IDLE) { - // The selector starts with a height of 0. In order to scale up from - // 0, we first need the set the height to 1 and scale from there. - View focusedChild = mParentView.getFocusedChild(); - if (focusedChild != null) { - int selectorHeight = mSelectorView.getHeight(); - float scaleY = (float) focusedChild.getHeight() / selectorHeight; - AnimatorSet animators = (AnimatorSet)createAnimator(mSelectorView, - R.attr.guidedActionsSelectorShowAnimation); - if (mFadedOut) { - // selector is completely faded out, so we can just scale before fading in. - mSelectorView.setScaleY(scaleY); - animator = animators.getChildAnimations().get(0); - } else { - // selector is not faded out, so we must animate the scale as we fade in. - ((ObjectAnimator)animators.getChildAnimations().get(1)) - .setFloatValues(scaleY); - animator = animators; - } - } - } else { - animator = createAnimator(mSelectorView, R.attr.guidedActionsSelectorHideAnimation); - fadingOut = true; - } - if (animator != null) { - animator.addListener(new Listener(fadingOut)); - animator.start(); - } + private void updateSelectorView(boolean animate) { + if (mActionsGridView == null || mSelectorView == null || mSelectorView.getHeight() <= 0) { + return; } - - /** - * Sets {@link BaseScrollAdapterFragment#mFadedOut} - * {@link BaseScrollAdapterFragment#mFadedOut} is true, iff - * {@link BaseScrollAdapterFragment#mSelectorView} has an alpha of 0 - * (faded out). If false the view either has an alpha of 1 (visible) or - * is in the process of animating. - */ - private class Listener implements Animator.AnimatorListener { - private boolean mFadingOut; - private boolean mCanceled; - - public Listener(boolean fadingOut) { - mFadingOut = fadingOut; - } - - @Override - public void onAnimationStart(Animator animation) { - if (!mFadingOut) { - mFadedOut = false; - } - } - - @Override - public void onAnimationEnd(Animator animation) { - if (!mCanceled && mFadingOut) { - mFadedOut = true; - } - } - - @Override - public void onAnimationCancel(Animator animation) { - mCanceled = true; + final View focusedChild = mActionsGridView.getFocusedChild(); + endSelectorAnimator(); + if (focusedChild == null || !mActionsGridView.hasFocus() + || mActionsGridView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) { + if (animate) { + mSelectorAnimator = createAnimator(mSelectorView, + R.attr.guidedActionsSelectorHideAnimation); + mSelectorAnimator.start(); + } else { + mSelectorView.setAlpha(0f); } - - @Override - public void onAnimationRepeat(Animator animation) { + } else { + final float scaleY = (float) focusedChild.getHeight() / mSelectorView.getHeight(); + Rect r = new Rect(0, 0, focusedChild.getWidth(), focusedChild.getHeight()); + mMainView.offsetDescendantRectToMyCoords(focusedChild, r); + mMainView.offsetRectIntoDescendantCoords(mSelectorView, r); + mSelectorView.setTranslationY(r.exactCenterY() - mSelectorView.getHeight() * 0.5f); + if (animate) { + mSelectorAnimator = createAnimator(mSelectorView, + R.attr.guidedActionsSelectorShowAnimation); + ((ObjectAnimator) ((AnimatorSet) mSelectorAnimator).getChildAnimations().get(1)) + .setFloatValues(scaleY); + mSelectorAnimator.start(); + } else { + mSelectorView.setAlpha(1f); + mSelectorView.setScaleY(scaleY); } } } diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ImageCardView.java b/v17/leanback/src/android/support/v17/leanback/widget/ImageCardView.java index 2c1c7e075f..08eb617725 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/ImageCardView.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/ImageCardView.java @@ -18,58 +18,286 @@ import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.support.annotation.ColorInt; import android.support.v17.leanback.R; -import android.text.TextUtils; import android.util.AttributeSet; +import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.ImageView.ScaleType; +import android.widget.RelativeLayout; import android.widget.TextView; /** - * A subclass of {@link BaseCardView} with an {@link ImageView} as its main region. + * A subclass of {@link BaseCardView} with an {@link ImageView} as its main + * region. The {@link ImageCardView} is highly customizable and can be used for + * various use-cases by adjusting the ImageViewCard's type to any combination of + * Title, Content, Badge or ImageOnly. + * <p> + * <h3>Styling</h3> There are three different ways to style the ImageCardView. + * <br> + * No matter what way you use, all your styles applied to an ImageCardView have + * to extend the style {@link R.style#Widget_Leanback_ImageCardViewStyle}. + * <p> + * <u>Example:</u><br> + * + * <pre> + * {@code <style name="CustomImageCardViewStyle" parent="Widget.Leanback.ImageCardViewStyle"> + <item name="cardBackground">#F0F</item> + <item name="lbImageCardViewType">Title|Content</item> + <item name="lbImageCardViewInfoAreaStyle">@style/ImageCardViewColoredInfoArea</item> + <item name="lbImageCardViewTitleStyle">@style/ImageCardViewColoredTitle</item> + </style>} + * </pre> + * <p> + * The first possibility is to set a custom Style in the Leanback Theme's + * attribute <code>imageCardViewStyle</code>. The style set here, is the default + * style for all ImageCardViews. The other two possibilities allow you to style + * a particular ImageCardView. This is usefull if you want to create multiple + * types of cards. E.g. you might want to display a card with only a title and + * another one with title and content. Thus you need to define two different + * <code>ImageCardViewStyles</code> and apply them to the ImageCardViews. You + * can do this by either using a the {@link #ImageCardView(Context, int)} + * constructor and passing a style as second argument or by setting the style in + * a layout. + * <p> + * <u>Example (using constructor):</u><br> + * + * <pre> + * {@code + * new ImageCardView(context, R.style.CustomImageCardViewStyle); + * } + * </pre> + * + * <u>Example (using style attribute in a layout):</u><br> + * + * <pre> + * {@code <android.support.v17.leanback.widget.ImageCardView + android:id="@+id/imageCardView" + style="@style/CustomImageCardViewStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + </android.support.v17.leanback.widget.ImageCardView>} + * </pre> + * <p> + * You can style all ImageCardView's components such as the title, content, + * badge, infoArea and the image itself by extending the corresponding style and + * overriding the specific attribute in your custom + * <code>ImageCardViewStyle</code>. + * + * <h3>Components</h3> The ImageCardView contains three components which can be + * combined in any combination: + * <ul> + * <li>Title: The card's title</li> + * <li>Content: A short description</li> + * <li>Badge: An icon which can be displayed on the right or left side of the + * card.</li> + * </ul> + * In order to choose the components you want to use in your ImageCardView, you + * have to specify them in the <code>lbImageCardViewType</code> attribute of + * your custom <code>ImageCardViewStyle</code>. You can combine the following + * values: <code>Title, Content, IconOnRight, IconOnLeft, ImageOnly</code>. + * <p> + * <u>Examples:</u><br> + * + * <pre> + * {@code <style name="CustomImageCardViewStyle" parent="Widget.Leanback.ImageCardViewStyle"> + ... + <item name="lbImageCardViewType">Title|Content|IconOnLeft</item> + ... + </style>} + * </pre> + * + * <pre> + * {@code <style name="CustomImageCardViewStyle" parent="Widget.Leanback.ImageCardViewStyle"> + ... + <item name="lbImageCardViewType">ImageOnly</item> + ... + </style>} + * </pre> + * + * @attr ref android.support.v17.leanback.R.styleable#LeanbackTheme_imageCardViewStyle + * @attr ref android.support.v17.leanback.R.styleable#lbImageCardView_lbImageCardViewType + * @attr ref android.support.v17.leanback.R.styleable#lbImageCardView_lbImageCardViewTitleStyle + * @attr ref android.support.v17.leanback.R.styleable#lbImageCardView_lbImageCardViewContentStyle + * @attr ref android.support.v17.leanback.R.styleable#lbImageCardView_lbImageCardViewBadgeStyle + * @attr ref android.support.v17.leanback.R.styleable#lbImageCardView_lbImageCardViewImageStyle + * @attr ref android.support.v17.leanback.R.styleable#lbImageCardView_lbImageCardViewInfoAreaStyle */ public class ImageCardView extends BaseCardView { + public static final int CARD_TYPE_FLAG_IMAGE_ONLY = 0; + public static final int CARD_TYPE_FLAG_TITLE = 1; + public static final int CARD_TYPE_FLAG_CONTENT = 2; + public static final int CARD_TYPE_FLAG_ICON_RIGHT = 4; + public static final int CARD_TYPE_FLAG_ICON_LEFT = 8; + private ImageView mImageView; - private View mInfoArea; + private ViewGroup mInfoArea; private TextView mTitleView; private TextView mContentView; private ImageView mBadgeImage; private boolean mAttachedToWindow; - public ImageCardView(Context context) { - this(context, null); + /** + * Create an ImageCardView using a given style for customization. + * + * @param context + * The Context the view is running in, through which it can + * access the current theme, resources, etc. + * @param styleResId + * The resourceId of the style you want to apply to the + * ImageCardView. The style has to extend + * {@link R.style#Widget_Leanback_ImageCardViewStyle}. + */ + public ImageCardView(Context context, int styleResId) { + super(new ContextThemeWrapper(context, styleResId), null, 0); + buildImageCardView(styleResId); } - public ImageCardView(Context context, AttributeSet attrs) { - this(context, attrs, R.attr.imageCardViewStyle); + /** + * @see #View(Context, AttributeSet, int) + */ + public ImageCardView(Context context, AttributeSet attrs, int defStyleAttr) { + super(getStyledContext(context, attrs, defStyleAttr), attrs, defStyleAttr); + buildImageCardView(getImageCardViewStyle(context, attrs, defStyleAttr)); } - public ImageCardView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); + private void buildImageCardView(int styleResId) { + // Make sure the ImageCardView is focusable. + setFocusable(true); + setFocusableInTouchMode(true); + + LayoutInflater inflater = LayoutInflater.from(getContext()); + inflater.inflate(R.layout.lb_image_card_view, this); + TypedArray cardAttrs = getContext().obtainStyledAttributes(styleResId, R.styleable.lbImageCardView); + int cardType = cardAttrs.getInt(R.styleable.lbImageCardView_lbImageCardViewType, CARD_TYPE_FLAG_IMAGE_ONLY); + boolean hasImageOnly = cardType == CARD_TYPE_FLAG_IMAGE_ONLY; + boolean hasTitle = (cardType & CARD_TYPE_FLAG_TITLE) == CARD_TYPE_FLAG_TITLE; + boolean hasContent = (cardType & CARD_TYPE_FLAG_CONTENT) == CARD_TYPE_FLAG_CONTENT; + boolean hasIconRight = (cardType & CARD_TYPE_FLAG_ICON_RIGHT) == CARD_TYPE_FLAG_ICON_RIGHT; + boolean hasIconLeft = !hasIconRight && (cardType & CARD_TYPE_FLAG_ICON_LEFT) == CARD_TYPE_FLAG_ICON_LEFT; + + mImageView = (ImageView) findViewById(R.id.main_image); + if (mImageView.getDrawable() == null) { + mImageView.setVisibility(View.INVISIBLE); + } + + mInfoArea = (ViewGroup) findViewById(R.id.info_field); + if (hasImageOnly) { + removeView(mInfoArea); + cardAttrs.recycle(); + return; + } + // Create children + if (hasTitle) { + mTitleView = (TextView) inflater.inflate(R.layout.lb_image_card_view_themed_title, mInfoArea, false); + mInfoArea.addView(mTitleView); + } + + if (hasContent) { + mContentView = (TextView) inflater.inflate(R.layout.lb_image_card_view_themed_content, mInfoArea, false); + mInfoArea.addView(mContentView); + } - LayoutInflater inflater = LayoutInflater.from(context); - View v = inflater.inflate(R.layout.lb_image_card_view, this); + if (hasIconRight || hasIconLeft) { + int layoutId = R.layout.lb_image_card_view_themed_badge_right; + if (hasIconLeft) { + layoutId = R.layout.lb_image_card_view_themed_badge_left; + } + mBadgeImage = (ImageView) inflater.inflate(layoutId, mInfoArea, false); + mInfoArea.addView(mBadgeImage); + } - mImageView = (ImageView) v.findViewById(R.id.main_image); - mImageView.setVisibility(View.INVISIBLE); - mInfoArea = v.findViewById(R.id.info_field); - mTitleView = (TextView) v.findViewById(R.id.title_text); - mContentView = (TextView) v.findViewById(R.id.content_text); - mBadgeImage = (ImageView) v.findViewById(R.id.extra_badge); + // Set up LayoutParams for children + if (hasTitle && !hasContent && mBadgeImage != null) { + RelativeLayout.LayoutParams relativeLayoutParams = (RelativeLayout.LayoutParams) mTitleView + .getLayoutParams(); + // Adjust title TextView if there is an icon but no content + if (hasIconLeft) { + relativeLayoutParams.addRule(RelativeLayout.END_OF, mBadgeImage.getId()); + } else { + relativeLayoutParams.addRule(RelativeLayout.START_OF, mBadgeImage.getId()); + } + mTitleView.setLayoutParams(relativeLayoutParams); + } - if (mInfoArea != null) { - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbImageCardView, - defStyle, 0); - try { - setInfoAreaBackground( - a.getDrawable(R.styleable.lbImageCardView_infoAreaBackground)); - } finally { - a.recycle(); + // Set up LayoutParams for children + if (hasContent) { + RelativeLayout.LayoutParams relativeLayoutParams = (RelativeLayout.LayoutParams) mContentView + .getLayoutParams(); + if (!hasTitle) { + relativeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP); + } + // Adjust content TextView if icon is on the left + if (hasIconLeft) { + relativeLayoutParams.removeRule(RelativeLayout.START_OF); + relativeLayoutParams.removeRule(RelativeLayout.ALIGN_PARENT_START); + relativeLayoutParams.addRule(RelativeLayout.END_OF, mBadgeImage.getId()); + } + mContentView.setLayoutParams(relativeLayoutParams); + } + + if (mBadgeImage != null) { + RelativeLayout.LayoutParams relativeLayoutParams = (RelativeLayout.LayoutParams) mBadgeImage + .getLayoutParams(); + if (hasContent) { + relativeLayoutParams.addRule(RelativeLayout.ALIGN_BOTTOM, mContentView.getId()); + } else if (hasTitle) { + relativeLayoutParams.addRule(RelativeLayout.ALIGN_BOTTOM, mTitleView.getId()); } + mBadgeImage.setLayoutParams(relativeLayoutParams); + } + + // Backward compatibility: Newly created ImageCardViews should change + // the InfoArea's background color in XML using the corresponding style. + // However, since older implementations might make use of the + // 'infoAreaBackground' attribute, we have to make sure to support it. + // If the user has set a specific value here, it will differ from null. + // In this case, we do want to override the value set in the style. + Drawable background = cardAttrs.getDrawable(R.styleable.lbImageCardView_infoAreaBackground); + if (null != background) { + setInfoAreaBackground(background); } + // Backward compatibility: There has to be an icon in the default + // version. If there is one, we have to set it's visibility to 'GONE'. + // Disabling 'adjustIconVisibility' allows the user to set the icon's + // visibility state in XML rather than code. + if (mBadgeImage != null && mBadgeImage.getDrawable() == null) { + mBadgeImage.setVisibility(View.GONE); + } + cardAttrs.recycle(); + } + + private static Context getStyledContext(Context context, AttributeSet attrs, int defStyleAttr) { + int style = getImageCardViewStyle(context, attrs, defStyleAttr); + return new ContextThemeWrapper(context, style); + } + + private static int getImageCardViewStyle(Context context, AttributeSet attrs, int defStyleAttr) { + // Read style attribute defined in XML layout. + int style = null == attrs ? 0 : attrs.getStyleAttribute(); + if (0 == style) { + // Not found? Read global ImageCardView style from Theme attribute. + TypedArray styledAttrs = context.obtainStyledAttributes(R.styleable.LeanbackTheme); + style = styledAttrs.getResourceId(R.styleable.LeanbackTheme_imageCardViewStyle, 0); + styledAttrs.recycle(); + } + return style; + } + + /** + * @see #View(Context) + */ + public ImageCardView(Context context) { + this(context, null); + } + + /** + * @see #View(Context, AttributeSet) + */ + public ImageCardView(Context context, AttributeSet attrs) { + this(context, attrs, R.attr.imageCardViewStyle); } /** @@ -170,7 +398,7 @@ public class ImageCardView extends BaseCardView { /** * Sets the info area background color. - */ + */ public void setInfoAreaBackgroundColor(@ColorInt int color) { if (mInfoArea != null) { mInfoArea.setBackgroundColor(color); @@ -184,7 +412,6 @@ public class ImageCardView extends BaseCardView { if (mTitleView == null) { return; } - mTitleView.setText(text); } @@ -206,7 +433,6 @@ public class ImageCardView extends BaseCardView { if (mContentView == null) { return; } - mContentView.setText(text); } @@ -229,7 +455,7 @@ public class ImageCardView extends BaseCardView { return; } mBadgeImage.setImageDrawable(drawable); - if (drawable != null && mContentView!= null && mContentView.getVisibility() != GONE) { + if (drawable != null) { mBadgeImage.setVisibility(View.VISIBLE); } else { mBadgeImage.setVisibility(View.GONE); @@ -250,8 +476,8 @@ public class ImageCardView extends BaseCardView { private void fadeIn() { mImageView.setAlpha(0f); if (mAttachedToWindow) { - mImageView.animate().alpha(1f).setDuration(mImageView.getResources().getInteger( - android.R.integer.config_shortAnimTime)); + mImageView.animate().alpha(1f) + .setDuration(mImageView.getResources().getInteger(android.R.integer.config_shortAnimTime)); } } @@ -276,4 +502,5 @@ public class ImageCardView extends BaseCardView { mImageView.setAlpha(1f); super.onDetachedFromWindow(); } + } diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ImeKeyMonitor.java b/v17/leanback/src/android/support/v17/leanback/widget/ImeKeyMonitor.java new file mode 100644 index 0000000000..4691ad2bc0 --- /dev/null +++ b/v17/leanback/src/android/support/v17/leanback/widget/ImeKeyMonitor.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 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.widget; + +import android.widget.EditText; +import android.view.KeyEvent; + +/** + * Interface for an EditText subclass that can delegate calls to onKeyPreIme up to a registered + * listener. + * <p> + * Used in editable actions within {@link android.support.v17.leanback.app.GuidedStepFragment} to + * allow for custom back key handling. Specifically, this is used to implement the behavior that + * dismissing the IME also clears edit text focus. Clients who need to supply custom layouts for + * {@link GuidedActionsStylist} with their own EditText classes should satisfy this interface in + * order to inherit this behavior. + */ +public interface ImeKeyMonitor { + + /** + * Listener interface for key events intercepted pre-IME by edit text objects. + */ + public interface ImeKeyListener { + /** + * Callback invoked from EditText's onKeyPreIme method override. Returning true tells the + * caller that the key event is handled and should not be propagated. + */ + public abstract boolean onKeyPreIme(EditText editText, int keyCode, KeyEvent event); + } + + /** + * Set the listener for this edit text object. The listener's onKeyPreIme method will be + * invoked from the host edit text's onKeyPreIme method. + */ + public void setImeKeyListener(ImeKeyListener listener); +} diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ItemBridgeAdapter.java b/v17/leanback/src/android/support/v17/leanback/widget/ItemBridgeAdapter.java index 8e3aea3fe7..57e8bfe356 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/ItemBridgeAdapter.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/ItemBridgeAdapter.java @@ -177,11 +177,15 @@ public class ItemBridgeAdapter extends RecyclerView.Adapter implements FacetProv * Sets the {@link ObjectAdapter}. */ public void setAdapter(ObjectAdapter adapter) { + if (adapter == mAdapter) { + return; + } if (mAdapter != null) { mAdapter.unregisterObserver(mDataObserver); } mAdapter = adapter; if (mAdapter == null) { + notifyDataSetChanged(); return; } @@ -189,6 +193,7 @@ public class ItemBridgeAdapter extends RecyclerView.Adapter implements FacetProv if (hasStableIds() != mAdapter.hasStableIds()) { setHasStableIds(mAdapter.hasStableIds()); } + notifyDataSetChanged(); } /** @@ -233,7 +238,7 @@ public class ItemBridgeAdapter extends RecyclerView.Adapter implements FacetProv @Override public int getItemCount() { - return mAdapter.size(); + return mAdapter != null ? mAdapter.size() : 0; } @Override diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ItemBridgeAdapterShadowOverlayWrapper.java b/v17/leanback/src/android/support/v17/leanback/widget/ItemBridgeAdapterShadowOverlayWrapper.java new file mode 100644 index 0000000000..19e6e9a6fa --- /dev/null +++ b/v17/leanback/src/android/support/v17/leanback/widget/ItemBridgeAdapterShadowOverlayWrapper.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2015 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.widget; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup.LayoutParams; + +/** + * A wrapper class working with {@link ItemBridgeAdapter} to wrap item view in a + * {@link ShadowOverlayContainer}. The ShadowOverlayContainer is created from conditions + * of {@link ShadowOverlayHelper}. + */ +public class ItemBridgeAdapterShadowOverlayWrapper extends ItemBridgeAdapter.Wrapper { + + private final ShadowOverlayHelper mHelper; + + public ItemBridgeAdapterShadowOverlayWrapper(ShadowOverlayHelper helper) { + mHelper = helper; + } + + @Override + public View createWrapper(View root) { + Context context = root.getContext(); + ShadowOverlayContainer wrapper = mHelper.createShadowOverlayContainer(context); + return wrapper; + } + @Override + public void wrap(View wrapper, View wrapped) { + ((ShadowOverlayContainer) wrapper).wrap(wrapped); + } + +} 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 9e588ebf0a..5540f788ed 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java @@ -15,8 +15,10 @@ package android.support.v17.leanback.widget; import android.content.Context; import android.content.res.TypedArray; +import android.os.Build; import android.support.v17.leanback.R; import android.support.v17.leanback.system.Settings; +import android.support.v17.leanback.transition.TransitionHelper; import android.util.Log; import android.view.KeyEvent; import android.view.View; @@ -96,6 +98,16 @@ public class ListRowPresenter extends RowPresenter { } @Override + protected void onCreate(ItemBridgeAdapter.ViewHolder viewHolder) { + if (viewHolder.itemView instanceof ViewGroup) { + TransitionHelper.setTransitionGroup((ViewGroup) viewHolder.itemView, true); + } + if (mShadowOverlayHelper != null) { + mShadowOverlayHelper.onViewCreated(viewHolder.itemView); + } + } + + @Override public void onBind(final ItemBridgeAdapter.ViewHolder viewHolder) { // Only when having an OnItemClickListner, we will attach the OnClickListener. if (mRowViewHolder.getOnItemViewClickedListener() != null) { @@ -122,9 +134,9 @@ public class ListRowPresenter extends RowPresenter { @Override public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) { - if (needsDefaultListSelectEffect()) { + if (mShadowOverlayHelper != null && mShadowOverlayHelper.needsOverlay()) { int dimmedColor = mRowViewHolder.mColorDimmer.getPaint().getColor(); - ((ShadowOverlayContainer) viewHolder.itemView).setOverlayColor(dimmedColor); + mShadowOverlayHelper.setOverlayColor(viewHolder.itemView, dimmedColor); } mRowViewHolder.syncActivatedStatus(viewHolder.itemView); } @@ -144,7 +156,10 @@ public class ListRowPresenter extends RowPresenter { private boolean mShadowEnabled = true; private int mBrowseRowsFadingEdgeLength = -1; private boolean mRoundedCornersEnabled = true; + private boolean mKeepChildForeground = true; private HashMap<Presenter, Integer> mRecycledPoolSize = new HashMap<Presenter, Integer>(); + private ShadowOverlayHelper mShadowOverlayHelper; + private ItemBridgeAdapter.Wrapper mShadowOverlayWrapper; private static int sSelectedRowTopPadding; private static int sExpandedSelectedRowTopPadding; @@ -253,44 +268,34 @@ public class ListRowPresenter extends RowPresenter { return mUseFocusDimmer; } - private ItemBridgeAdapter.Wrapper mCardWrapper = new ItemBridgeAdapter.Wrapper() { - @Override - public View createWrapper(View root) { - ShadowOverlayContainer wrapper = new ShadowOverlayContainer(root.getContext()); - wrapper.setLayoutParams( - new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); - if (isUsingZOrder(root.getContext())) { - wrapper.useDynamicShadow(); - } else { - wrapper.useStaticShadow(); - } - wrapper.initialize(needsDefaultShadow(), - needsDefaultListSelectEffect(), - areChildRoundedCornersEnabled()); - return wrapper; - } - @Override - public void wrap(View wrapper, View wrapped) { - ((ShadowOverlayContainer) wrapper).wrap(wrapped); - } - }; - @Override protected void initializeRowViewHolder(RowPresenter.ViewHolder holder) { super.initializeRowViewHolder(holder); final ViewHolder rowViewHolder = (ViewHolder) holder; - rowViewHolder.mItemBridgeAdapter = new ListRowPresenterItemBridgeAdapter(rowViewHolder); - if (needsDefaultListSelectEffect() || needsDefaultShadow() - || areChildRoundedCornersEnabled()) { - rowViewHolder.mItemBridgeAdapter.setWrapper(mCardWrapper); - } - if (needsDefaultShadow()) { - ShadowOverlayContainer.prepareParentForShadow(rowViewHolder.mGridView); + Context context = holder.view.getContext(); + if (mShadowOverlayHelper == null) { + mShadowOverlayHelper = new ShadowOverlayHelper.Builder() + .needsOverlay(needsDefaultListSelectEffect()) + .needsShadow(needsDefaultShadow()) + .needsRoundedCorner(areChildRoundedCornersEnabled()) + .preferZOrder(isUsingZOrder(context)) + .keepForegroundDrawable(mKeepChildForeground) + .options(createShadowOverlayOptions()) + .build(context); + if (mShadowOverlayHelper.needsWrapper()) { + mShadowOverlayWrapper = new ItemBridgeAdapterShadowOverlayWrapper( + mShadowOverlayHelper); + } } + rowViewHolder.mItemBridgeAdapter = new ListRowPresenterItemBridgeAdapter(rowViewHolder); + // set wrapper if needed + rowViewHolder.mItemBridgeAdapter.setWrapper(mShadowOverlayWrapper); + mShadowOverlayHelper.prepareParentForShadow(rowViewHolder.mGridView); + FocusHighlightHelper.setupBrowseItemFocusHighlight(rowViewHolder.mItemBridgeAdapter, mFocusZoomFactor, mUseFocusDimmer); - rowViewHolder.mGridView.setFocusDrawingOrderEnabled( - !isUsingZOrder(rowViewHolder.getGridView().getContext())); + rowViewHolder.mGridView.setFocusDrawingOrderEnabled(mShadowOverlayHelper.getShadowType() + == ShadowOverlayHelper.SHADOW_STATIC); rowViewHolder.mGridView.setOnChildSelectedListener( new OnChildSelectedListener() { @Override @@ -545,7 +550,7 @@ public class ListRowPresenter extends RowPresenter { * Subclass may return false to disable. */ public boolean isUsingDefaultShadow() { - return ShadowOverlayContainer.supportsShadow(); + return ShadowOverlayHelper.supportsShadow(); } /** @@ -554,8 +559,7 @@ public class ListRowPresenter extends RowPresenter { * and does not use Z-shadow on SDK >= L, it should override isUsingZOrder() return false. */ public boolean isUsingZOrder(Context context) { - return ShadowOverlayContainer.supportsDynamicShadow() && - !Settings.getInstance(context).preferStaticShadows(); + return !Settings.getInstance(context).preferStaticShadows(); } /** @@ -595,9 +599,41 @@ public class ListRowPresenter extends RowPresenter { return isUsingDefaultShadow() && getShadowEnabled(); } - @Override - public boolean canDrawOutOfBounds() { - return needsDefaultShadow(); + /** + * When ListRowPresenter applies overlay color on the child, it may change child's foreground + * Drawable. If application uses child's foreground for other purposes such as ripple effect, + * it needs tell ListRowPresenter to keep the child's foreground. The default value is true. + * + * @param keep true if keep foreground of child of this row, false ListRowPresenter might change + * the foreground of the child. + */ + public final void setKeepChildForeground(boolean keep) { + mKeepChildForeground = keep; + } + + /** + * Returns true if keeps foreground of child of this row, false otherwise. When + * ListRowPresenter applies overlay color on the child, it may change child's foreground + * Drawable. If application uses child's foreground for other purposes such as ripple effect, + * it needs tell ListRowPresenter to keep the child's foreground. The default value is true. + * + * @return true if keeps foreground of child of this row, false otherwise. + */ + public final boolean isKeepChildForeground() { + return mKeepChildForeground; + } + + /** + * Create ShadowOverlayHelper Options. Subclass may override. + * e.g. + * <code> + * return new ShadowOverlayHelper.Options().roundedCornerRadius(10); + * </code> + * + * @return The options to be used for shadow, overlay and rouded corner. + */ + protected ShadowOverlayHelper.Options createShadowOverlayOptions() { + return ShadowOverlayHelper.Options.DEFAULT; } /** @@ -615,12 +651,11 @@ public class ListRowPresenter extends RowPresenter { @Override protected void onSelectLevelChanged(RowPresenter.ViewHolder holder) { super.onSelectLevelChanged(holder); - if (needsDefaultListSelectEffect()) { + if (mShadowOverlayHelper != null && mShadowOverlayHelper.needsOverlay()) { ViewHolder vh = (ViewHolder) holder; int dimmedColor = vh.mColorDimmer.getPaint().getColor(); for (int i = 0, count = vh.mGridView.getChildCount(); i < count; i++) { - ShadowOverlayContainer wrapper = (ShadowOverlayContainer) vh.mGridView.getChildAt(i); - wrapper.setOverlayColor(dimmedColor); + mShadowOverlayHelper.setOverlayColor(vh.mGridView.getChildAt(i), dimmedColor); } if (vh.mGridView.getFadingLeftEdge()) { vh.mGridView.invalidate(); diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsPresenter.java index f1db00bbe1..17225f8863 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsPresenter.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsPresenter.java @@ -58,7 +58,7 @@ class PlaybackControlsPresenter extends ControlBarPresenter { final TextView mCurrentTime; final TextView mTotalTime; final ProgressBar mProgressBar; - int mCurrentTimeInSeconds; + int mCurrentTimeInSeconds = -1; StringBuilder mTotalTimeStringBuilder = new StringBuilder(); StringBuilder mCurrentTimeStringBuilder = new StringBuilder(); int mCurrentTimeMarginStart; diff --git a/v17/leanback/src/android/support/v17/leanback/widget/RoundedRectHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/RoundedRectHelper.java index e1c59797ea..35a5b6703a 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/RoundedRectHelper.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/RoundedRectHelper.java @@ -13,8 +13,7 @@ */ package android.support.v17.leanback.widget; -import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; +import android.support.v17.leanback.R; import android.os.Build; import android.view.View; @@ -24,7 +23,7 @@ import android.view.View; final class RoundedRectHelper { private final static RoundedRectHelper sInstance = new RoundedRectHelper(); - private Impl mImpl; + private final Impl mImpl; /** * Returns an instance of the helper. @@ -33,15 +32,27 @@ final class RoundedRectHelper { return sInstance; } + public static boolean supportsRoundedCorner() { + return Build.VERSION.SDK_INT >= 21; + } + + /** + * Sets or removes a rounded rectangle outline on the given view. + */ + public void setClipToRoundedOutline(View view, boolean clip, int radius) { + mImpl.setClipToRoundedOutline(view, clip, radius); + } + /** * Sets or removes a rounded rectangle outline on the given view. */ public void setClipToRoundedOutline(View view, boolean clip) { - mImpl.setClipToRoundedOutline(view, clip); + mImpl.setClipToRoundedOutline(view, clip, view.getResources().getDimensionPixelSize( + R.dimen.lb_rounded_rect_corner_radius)); } static interface Impl { - public void setClipToRoundedOutline(View view, boolean clip); + public void setClipToRoundedOutline(View view, boolean clip, int radius); } /** @@ -49,7 +60,7 @@ final class RoundedRectHelper { */ private static final class StubImpl implements Impl { @Override - public void setClipToRoundedOutline(View view, boolean clip) { + public void setClipToRoundedOutline(View view, boolean clip, int radius) { // Not supported } } @@ -59,13 +70,13 @@ final class RoundedRectHelper { */ private static final class Api21Impl implements Impl { @Override - public void setClipToRoundedOutline(View view, boolean clip) { - RoundedRectHelperApi21.setClipToRoundedOutline(view, clip); + public void setClipToRoundedOutline(View view, boolean clip, int radius) { + RoundedRectHelperApi21.setClipToRoundedOutline(view, clip, radius); } } private RoundedRectHelper() { - if (Build.VERSION.SDK_INT >= 21) { + if (supportsRoundedCorner()) { mImpl = new Api21Impl(); } else { mImpl = new StubImpl(); 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 4f078f14d5..cb1f2ac7cc 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java @@ -565,13 +565,6 @@ public abstract class RowPresenter extends Presenter { return mHeaderPresenter != null || needsDefaultSelectEffect(); } - /** - * Returns true if the Row view can draw outside its bounds. - */ - public boolean canDrawOutOfBounds() { - return false; - } - @Override public final void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) { onBindRowViewHolder(getRowViewHolder(viewHolder), item); @@ -642,12 +635,16 @@ public abstract class RowPresenter extends Presenter { * Changes the visibility of views. The entrance transition will be run against the views that * change visibilities. A subclass may override and begin with calling * super.setEntranceTransitionState(). This method is called by the fragment, - * it should not call it directly by the application. + * it should not be called directly by the application. + * + * @param holder The ViewHolder of the row. + * @param afterEntrance true if children of row participating in entrance transition + * should be set to visible, false otherwise. */ - public void setEntranceTransitionState(ViewHolder holder, boolean afterTransition) { + public void setEntranceTransitionState(ViewHolder holder, boolean afterEntrance) { if (holder.mHeaderViewHolder != null && holder.mHeaderViewHolder.view.getVisibility() != View.GONE) { - holder.mHeaderViewHolder.view.setVisibility(afterTransition ? + holder.mHeaderViewHolder.view.setVisibility(afterEntrance ? View.VISIBLE : View.INVISIBLE); } } diff --git a/v17/leanback/src/android/support/v17/leanback/widget/SearchBar.java b/v17/leanback/src/android/support/v17/leanback/widget/SearchBar.java index acdebcb0ab..1c3835fa18 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/SearchBar.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/SearchBar.java @@ -406,15 +406,23 @@ public class SearchBar extends RelativeLayout { * @param completions list of completions shown in the IME, can be null or empty to clear them */ public void displayCompletions(List<String> completions) { - List<CompletionInfo> infos = new ArrayList<CompletionInfo>(); + List<CompletionInfo> infos = new ArrayList<>(); if (null != completions) { for (String completion : completions) { infos.add(new CompletionInfo(infos.size(), infos.size(), completion)); } } + CompletionInfo[] array = new CompletionInfo[infos.size()]; + displayCompletions(infos.toArray(array)); + } - mInputMethodManager.displayCompletions(mSearchTextEditor, - infos.toArray(new CompletionInfo[] {})); + /** + * Updates the completion list shown by the IME + * + * @param completions list of completions shown in the IME, can be null or empty to clear them + */ + public void displayCompletions(CompletionInfo[] completions) { + mInputMethodManager.displayCompletions(mSearchTextEditor, completions); } /** diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ShadowHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/ShadowHelper.java index be70575a92..aec9673eef 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/ShadowHelper.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/ShadowHelper.java @@ -14,7 +14,6 @@ package android.support.v17.leanback.widget; import android.os.Build; -import android.view.ViewGroup; import android.view.View; @@ -32,7 +31,7 @@ final class ShadowHelper { */ static interface ShadowHelperVersionImpl { public Object addDynamicShadow( - ViewGroup shadowContainer, float unfocusedZ, float focusedZ, boolean roundedCorners); + View shadowContainer, float unfocusedZ, float focusedZ, int roundedCornerRadius); public void setZ(View view, float z); public void setShadowFocusLevel(Object impl, float level); } @@ -43,7 +42,7 @@ final class ShadowHelper { private static final class ShadowHelperStubImpl implements ShadowHelperVersionImpl { @Override public Object addDynamicShadow( - ViewGroup shadowContainer, float focusedZ, float unfocusedZ, boolean roundedCorners) { + View shadowContainer, float focusedZ, float unfocusedZ, int roundedCornerRadius) { // do nothing return null; } @@ -65,9 +64,9 @@ final class ShadowHelper { private static final class ShadowHelperApi21Impl implements ShadowHelperVersionImpl { @Override public Object addDynamicShadow( - ViewGroup shadowContainer, float unfocusedZ, float focusedZ, boolean roundedCorners) { + View shadowContainer, float unfocusedZ, float focusedZ, int roundedCornerRadius) { return ShadowHelperApi21.addDynamicShadow( - shadowContainer, unfocusedZ, focusedZ, roundedCorners); + shadowContainer, unfocusedZ, focusedZ, roundedCornerRadius); } @Override @@ -102,8 +101,8 @@ final class ShadowHelper { } public Object addDynamicShadow( - ViewGroup shadowContainer, float unfocusedZ, float focusedZ, boolean roundedCorners) { - return mImpl.addDynamicShadow(shadowContainer, unfocusedZ, focusedZ, roundedCorners); + View shadowContainer, float unfocusedZ, float focusedZ, int roundedCornerRadius) { + return mImpl.addDynamicShadow(shadowContainer, unfocusedZ, focusedZ, roundedCornerRadius); } public void setShadowFocusLevel(Object impl, float level) { diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayContainer.java b/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayContainer.java index e367494dc7..fdb7c7122d 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayContainer.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayContainer.java @@ -20,11 +20,16 @@ import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; import android.graphics.Rect; /** * Provides an SDK version-independent wrapper to support shadows, color overlays, and rounded - * corners. + * corners. It's not always preferred to create a ShadowOverlayContainer, use + * {@link ShadowOverlayHelper} instead. * <p> * {@link #prepareParentForShadow(ViewGroup)} must be called on parent of container * before using shadow. Depending on sdk version, optical bounds might be applied @@ -46,41 +51,52 @@ import android.graphics.Rect; * Call {@link #setOverlayColor(int)} to control overlay color. * </p> */ -public class ShadowOverlayContainer extends ViewGroup { +public class ShadowOverlayContainer extends FrameLayout { /** * No shadow. */ - public static final int SHADOW_NONE = 1; + public static final int SHADOW_NONE = ShadowOverlayHelper.SHADOW_NONE; /** * Shadows are fixed. */ - public static final int SHADOW_STATIC = 2; + public static final int SHADOW_STATIC = ShadowOverlayHelper.SHADOW_STATIC; /** * Shadows depend on the size, shape, and position of the view. */ - public static final int SHADOW_DYNAMIC = 3; + public static final int SHADOW_DYNAMIC = ShadowOverlayHelper.SHADOW_DYNAMIC; private boolean mInitialized; - private View mColorDimOverlay; private Object mShadowImpl; private View mWrappedView; private boolean mRoundedCorners; private int mShadowType = SHADOW_NONE; private float mUnfocusedZ; private float mFocusedZ; + private int mRoundedCornerRadius; private static final Rect sTempRect = new Rect(); + private Paint mOverlayPaint; + private int mOverlayColor; + /** + * Create ShadowOverlayContainer and auto select shadow type. + */ public ShadowOverlayContainer(Context context) { this(context, null, 0); } + /** + * Create ShadowOverlayContainer and auto select shadow type. + */ public ShadowOverlayContainer(Context context, AttributeSet attrs) { this(context, attrs, 0); } + /** + * Create ShadowOverlayContainer and auto select shadow type. + */ public ShadowOverlayContainer(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); useStaticShadow(); @@ -88,6 +104,18 @@ public class ShadowOverlayContainer extends ViewGroup { } /** + * Create ShadowOverlayContainer with specific shadowType. + */ + ShadowOverlayContainer(Context context, + int shadowType, boolean hasColorDimOverlay, + float unfocusedZ, float focusedZ, int roundedCornerRadius) { + super(context); + mUnfocusedZ = unfocusedZ; + mFocusedZ = focusedZ; + initialize(shadowType, hasColorDimOverlay, roundedCornerRadius); + } + + /** * Return true if the platform sdk supports shadow. */ public static boolean supportsShadow() { @@ -155,7 +183,7 @@ public class ShadowOverlayContainer extends ViewGroup { /** * Initialize shadows, color overlay. - * @deprecated use {@link #initialize(boolean, boolean, boolean)} instead. + * @deprecated use {@link ShadowOverlayHelper#createShadowOverlayContainer(Context)} instead. */ @Deprecated public void initialize(boolean hasShadow, boolean hasColorDimOverlay) { @@ -164,29 +192,62 @@ public class ShadowOverlayContainer extends ViewGroup { /** * Initialize shadows, color overlay, and rounded corners. All are optional. + * Shadow type are auto-selected based on {@link #useStaticShadow()} and + * {@link #useDynamicShadow()} call. + * @deprecated use {@link ShadowOverlayHelper#createShadowOverlayContainer(Context)} instead. */ + @Deprecated public void initialize(boolean hasShadow, boolean hasColorDimOverlay, boolean roundedCorners) { + int shadowType; + if (!hasShadow) { + shadowType = SHADOW_NONE; + } else { + shadowType = mShadowType; + } + int roundedCornerRadius = roundedCorners ? getContext().getResources().getDimensionPixelSize( + R.dimen.lb_rounded_rect_corner_radius) : 0; + initialize(shadowType, hasColorDimOverlay, roundedCornerRadius); + } + + /** + * Initialize shadows, color overlay, and rounded corners. All are optional. + */ + void initialize(int shadowType, boolean hasColorDimOverlay, int roundedCornerRadius) { if (mInitialized) { throw new IllegalStateException(); } mInitialized = true; - if (hasShadow) { - switch (mShadowType) { - case SHADOW_DYNAMIC: - mShadowImpl = ShadowHelper.getInstance().addDynamicShadow( - this, mUnfocusedZ, mFocusedZ, roundedCorners); - break; - case SHADOW_STATIC: - mShadowImpl = StaticShadowHelper.getInstance().addStaticShadow( - this, roundedCorners); - break; - } + mRoundedCornerRadius = roundedCornerRadius; + mRoundedCorners = roundedCornerRadius > 0; + mShadowType = shadowType; + switch (mShadowType) { + case SHADOW_DYNAMIC: + mShadowImpl = ShadowHelper.getInstance().addDynamicShadow( + this, mUnfocusedZ, mFocusedZ, mRoundedCornerRadius); + break; + case SHADOW_STATIC: + mShadowImpl = StaticShadowHelper.getInstance().addStaticShadow(this); + break; } - mRoundedCorners = roundedCorners; if (hasColorDimOverlay) { - mColorDimOverlay = LayoutInflater.from(getContext()) - .inflate(R.layout.lb_card_color_overlay, this, false); - addView(mColorDimOverlay); + setWillNotDraw(false); + mOverlayColor = Color.TRANSPARENT; + mOverlayPaint = new Paint(); + mOverlayPaint.setColor(mOverlayColor); + mOverlayPaint.setStyle(Paint.Style.FILL); + } else { + setWillNotDraw(true); + mOverlayPaint = null; + } + } + + @Override + public void draw(Canvas canvas) { + super.draw(canvas); + if (mOverlayPaint != null && mOverlayColor != Color.TRANSPARENT) { + canvas.drawRect(mWrappedView.getLeft(), mWrappedView.getTop(), + mWrappedView.getRight(), mWrappedView.getBottom(), + mOverlayPaint); } } @@ -195,19 +256,7 @@ public class ShadowOverlayContainer extends ViewGroup { */ public void setShadowFocusLevel(float level) { if (mShadowImpl != null) { - if (level < 0f) { - level = 0f; - } else if (level > 1f) { - level = 1f; - } - switch (mShadowType) { - case SHADOW_DYNAMIC: - ShadowHelper.getInstance().setShadowFocusLevel(mShadowImpl, level); - break; - case SHADOW_STATIC: - StaticShadowHelper.getInstance().setShadowFocusLevel(mShadowImpl, level); - break; - } + ShadowOverlayHelper.setShadowFocusLevel(mShadowImpl, mShadowType, level); } } @@ -215,8 +264,12 @@ public class ShadowOverlayContainer extends ViewGroup { * Set color (with alpha) of the overlay. */ public void setOverlayColor(@ColorInt int overlayColor) { - if (mColorDimOverlay != null) { - mColorDimOverlay.setBackgroundColor(overlayColor); + if (mOverlayPaint != null) { + if (overlayColor != mOverlayColor) { + mOverlayColor = overlayColor; + mOverlayPaint.setColor(overlayColor); + invalidate(); + } } } @@ -227,15 +280,27 @@ public class ShadowOverlayContainer extends ViewGroup { if (!mInitialized || mWrappedView != null) { throw new IllegalStateException(); } - if (mColorDimOverlay != null) { - addView(view, indexOfChild(mColorDimOverlay)); + ViewGroup.LayoutParams lp = view.getLayoutParams(); + if (lp != null) { + // if wrapped view has layout params, inherit everything but width/height. + // Wrapped view is assigned a FrameLayout.LayoutParams with width and height only. + // Margins, etc are assigned to the wrapper and take effect in parent container. + ViewGroup.LayoutParams wrapped_lp = new FrameLayout.LayoutParams(lp.width, lp.height); + // Uses MATCH_PARENT for MATCH_PARENT, WRAP_CONTENT for WRAP_CONTENT and fixed size, + // App can still change wrapped view fixed width/height afterwards. + lp.width = lp.width == LayoutParams.MATCH_PARENT ? + LayoutParams.MATCH_PARENT : LayoutParams.WRAP_CONTENT; + lp.height = lp.height == LayoutParams.MATCH_PARENT ? + LayoutParams.MATCH_PARENT : LayoutParams.WRAP_CONTENT; + this.setLayoutParams(lp); + addView(view, wrapped_lp); } else { addView(view); } - mWrappedView = view; - if (mRoundedCorners) { - RoundedRectHelper.getInstance().setClipToRoundedOutline(mWrappedView, true); + if (mRoundedCorners && mShadowType == SHADOW_STATIC) { + RoundedRectHelper.getInstance().setClipToRoundedOutline(view, true); } + mWrappedView = view; } /** @@ -246,67 +311,9 @@ public class ShadowOverlayContainer extends ViewGroup { } @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (mWrappedView == null) { - throw new IllegalStateException(); - } - // padding and child margin are not supported. - // first measure the wrapped view, then measure the shadow view and/or overlay view. - int childWidthMeasureSpec, childHeightMeasureSpec; - LayoutParams lp = mWrappedView.getLayoutParams(); - if (lp.width == LayoutParams.MATCH_PARENT) { - childWidthMeasureSpec = MeasureSpec.makeMeasureSpec - (MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY); - } else { - childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, lp.width); - } - if (lp.height == LayoutParams.MATCH_PARENT) { - childHeightMeasureSpec = MeasureSpec.makeMeasureSpec - (MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY); - } else { - childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 0, lp.height); - } - mWrappedView.measure(childWidthMeasureSpec, childHeightMeasureSpec); - - int measuredWidth = mWrappedView.getMeasuredWidth(); - int measuredHeight = mWrappedView.getMeasuredHeight(); - - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - if (child == mWrappedView) { - continue; - } - lp = child.getLayoutParams(); - if (lp.width == LayoutParams.MATCH_PARENT) { - childWidthMeasureSpec = MeasureSpec.makeMeasureSpec - (measuredWidth, MeasureSpec.EXACTLY); - } else { - childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, lp.width); - } - - if (lp.height == LayoutParams.MATCH_PARENT) { - childHeightMeasureSpec = MeasureSpec.makeMeasureSpec - (measuredHeight, MeasureSpec.EXACTLY); - } else { - childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 0, lp.height); - } - child.measure(childWidthMeasureSpec, childHeightMeasureSpec); - } - setMeasuredDimension(measuredWidth, measuredHeight); - } - - @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { - final int count = getChildCount(); - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - if (child.getVisibility() != GONE) { - final int width = child.getMeasuredWidth(); - final int height = child.getMeasuredHeight(); - child.layout(0, 0, width, height); - } - } - if (mWrappedView != null) { + super.onLayout(changed, l, t, r, b); + if (changed && mWrappedView != null) { sTempRect.left = (int) mWrappedView.getPivotX(); sTempRect.top = (int) mWrappedView.getPivotY(); offsetDescendantRectToMyCoords(mWrappedView, sTempRect); @@ -315,4 +322,8 @@ public class ShadowOverlayContainer extends ViewGroup { } } + @Override + public boolean hasOverlappingRendering() { + return false; + } } diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayHelper.java new file mode 100644 index 0000000000..5f942c6930 --- /dev/null +++ b/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayHelper.java @@ -0,0 +1,467 @@ +/* + * Copyright (C) 2015 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.widget; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.support.v17.leanback.R; +import android.support.v17.leanback.system.Settings; +import android.util.AttributeSet; +import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; +import android.view.View; + + +/** + * ShadowOverlayHelper is a helper class for shadow, overlay color and rounded corner. + * There are many choices to implement Shadow, overlay color. + * Initialize it with ShadowOverlayHelper.Builder and it decides the best strategy based + * on options user choose and current platform version. + * + * <li> For shadow: it may use 9-patch with opticalBounds or Z-value based shadow for + * API >= 21. When 9-patch is used, it requires a ShadowOverlayContainer + * to include 9-patch views. + * <li> For overlay: it may use ShadowOverlayContainer which overrides draw() or it may + * use setForeground(new ColorDrawable()) for API>=23. The foreground support + * might be disabled if rounded corner is applied due to performance reason. + * <li> For rounded-corner: it uses a ViewOutlineProvider for API>=21. + * + * There are two different strategies: use Wrapper with a ShadowOverlayContainer; + * or apply rounded corner, overlay and rounded-corner to the view itself. Below is an example + * of how helper is used. + * + * <code> + * ShadowOverlayHelper mHelper = new ShadowOverlayHelper.Builder(). + * .needsOverlay(true).needsRoundedCorner(true).needsShadow(true) + * .build(); + * mHelper.prepareParentForShadow(parentView); // apply optical-bounds for 9-patch shadow. + * mHelper.setOverlayColor(view, Color.argb(0x80, 0x80, 0x80, 0x80)); + * mHelper.setShadowFocusLevel(view, 1.0f); + * ... + * View initializeView(View view) { + * if (mHelper.needsWrapper()) { + * ShadowOverlayContainer wrapper = mHelper.createShadowOverlayContainer(context); + * wrapper.wrap(view); + * return wrapper; + * } else { + * mHelper.onViewCreated(view); + * return view; + * } + * } + * ... + * + * </code> + */ +public final class ShadowOverlayHelper { + + /** + * Builder for creating ShadowOverlayHelper. + */ + public static final class Builder { + + private boolean needsOverlay; + private boolean needsRoundedCorner; + private boolean needsShadow; + private boolean preferZOrder = true; + private boolean keepForegroundDrawable; + private Options options = Options.DEFAULT; + + /** + * Set if needs overlay color. + * @param needsOverlay True if needs overlay. + * @return The Builder object itself. + */ + public Builder needsOverlay(boolean needsOverlay) { + this.needsOverlay = needsOverlay; + return this; + } + + /** + * Set if needs shadow. + * @param needsShadow True if needs shadow. + * @return The Builder object itself. + */ + public Builder needsShadow(boolean needsShadow) { + this.needsShadow = needsShadow; + return this; + } + + /** + * Set if needs rounded corner. + * @param needsRoundedCorner True if needs rounded corner. + * @return The Builder object itself. + */ + public Builder needsRoundedCorner(boolean needsRoundedCorner) { + this.needsRoundedCorner = needsRoundedCorner; + return this; + } + + /** + * Set if prefer z-order shadow. On old devices, z-order shadow might be slow, + * set to false to fall back to static 9-patch shadow. Recommend to read + * from system wide Setting value: see {@link Settings}. + * + * @param preferZOrder True if prefer Z shadow. Default is true. + * @return The Builder object itself. + */ + public Builder preferZOrder(boolean preferZOrder) { + this.preferZOrder = preferZOrder; + return this; + } + + /** + * Set if not using foreground drawable for overlay color. For example if + * the view has already assigned a foreground drawable for other purposes. + * When it's true, helper will use a ShadowOverlayContainer for overlay color. + * + * @param keepForegroundDrawable True to keep the original foreground drawable. + * @return The Builder object itself. + */ + public Builder keepForegroundDrawable(boolean keepForegroundDrawable) { + this.keepForegroundDrawable = keepForegroundDrawable; + return this; + } + + /** + * Set option values e.g. Shadow Z value, rounded corner radius. + * + * @param options The Options object to create ShadowOverlayHelper. + */ + public Builder options(Options options) { + this.options = options; + return this; + } + + /** + * Create ShadowOverlayHelper object + * @param context The context uses to read Resources settings. + * @return The ShadowOverlayHelper object. + */ + public ShadowOverlayHelper build(Context context) { + final ShadowOverlayHelper helper = new ShadowOverlayHelper(); + helper.mNeedsOverlay = needsOverlay; + helper.mNeedsRoundedCorner = needsRoundedCorner && supportsRoundedCorner(); + helper.mNeedsShadow = needsShadow && supportsShadow(); + + if (helper.mNeedsRoundedCorner) { + helper.setupRoundedCornerRadius(options, context); + } + + // figure out shadow type and if we need use wrapper: + if (helper.mNeedsShadow) { + // if static shadow is prefered or dynamic shadow is not supported, + // use static shadow, otherwise use dynamic shadow. + if (!preferZOrder || !supportsDynamicShadow()) { + helper.mShadowType = SHADOW_STATIC; + // static shadow requires ShadowOverlayContainer to support crossfading + // of two shadow views. + helper.mNeedsWrapper = true; + } else { + helper.mShadowType = SHADOW_DYNAMIC; + helper.setupDynamicShadowZ(options, context); + helper.mNeedsWrapper = ((!supportsForeground() || keepForegroundDrawable) + && helper.mNeedsOverlay); + } + } else { + helper.mShadowType = SHADOW_NONE; + helper.mNeedsWrapper = ((!supportsForeground() || keepForegroundDrawable) + && helper.mNeedsOverlay); + } + + return helper; + } + + } + + /** + * Option values for ShadowOverlayContainer. + */ + public static final class Options { + + /** + * Default Options for values. + */ + public static final Options DEFAULT = new Options(); + + private int roundedCornerRadius = 0; // 0 for default value + private float dynamicShadowUnfocusedZ = -1; // < 0 for default value + private float dynamicShadowFocusedZ = -1; // < 0 for default value + /** + * Set value of rounded corner radius. + * + * @param roundedCornerRadius Number of pixels of rounded corner radius. + * Set to 0 to use default settings. + * @return The Options object itself. + */ + public Options roundedCornerRadius(int roundedCornerRadius){ + this.roundedCornerRadius = roundedCornerRadius; + return this; + } + + /** + * Set value of focused and unfocused Z value for shadow. + * + * @param unfocusedZ Number of pixels for unfocused Z value. + * @param focusedZ Number of pixels for foucsed Z value. + * @return The Options object itself. + */ + public Options dynamicShadowZ(float unfocusedZ, float focusedZ){ + this.dynamicShadowUnfocusedZ = unfocusedZ; + this.dynamicShadowFocusedZ = focusedZ; + return this; + } + + /** + * Get radius of rounded corner in pixels. + * + * @return Radius of rounded corner in pixels. + */ + public final int getRoundedCornerRadius() { + return roundedCornerRadius; + } + + /** + * Get z value of shadow when a view is not focused. + * + * @return Z value of shadow when a view is not focused. + */ + public final float getDynamicShadowUnfocusedZ() { + return dynamicShadowUnfocusedZ; + } + + /** + * Get z value of shadow when a view is focused. + * + * @return Z value of shadow when a view is focused. + */ + public final float getDynamicShadowFocusedZ() { + return dynamicShadowFocusedZ; + } + } + + /** + * No shadow. + */ + public static final int SHADOW_NONE = 1; + + /** + * Shadows are fixed. + */ + public static final int SHADOW_STATIC = 2; + + /** + * Shadows depend on the size, shape, and position of the view. + */ + public static final int SHADOW_DYNAMIC = 3; + + int mShadowType = SHADOW_NONE; + boolean mNeedsOverlay; + boolean mNeedsRoundedCorner; + boolean mNeedsShadow; + boolean mNeedsWrapper; + + int mRoundedCornerRadius; + float mUnfocusedZ; + float mFocusedZ; + + /** + * Return true if the platform sdk supports shadow. + */ + public static boolean supportsShadow() { + return StaticShadowHelper.getInstance().supportsShadow(); + } + + /** + * Returns true if the platform sdk supports dynamic shadows. + */ + public static boolean supportsDynamicShadow() { + return ShadowHelper.getInstance().supportsDynamicShadow(); + } + + /** + * Returns true if the platform sdk supports rounded corner through outline. + */ + public static boolean supportsRoundedCorner() { + return RoundedRectHelper.supportsRoundedCorner(); + } + + /** + * Returns true if view.setForeground() is supported. + */ + public static boolean supportsForeground() { + return ForegroundHelper.supportsForeground(); + } + + /* + * hide from external, should be only created by ShadowOverlayHelper.Options. + */ + ShadowOverlayHelper() { + } + + /** + * {@link #prepareParentForShadow(ViewGroup)} must be called on parent of container + * before using shadow. Depending on Shadow type, optical bounds might be applied. + */ + public void prepareParentForShadow(ViewGroup parent) { + if (mShadowType == SHADOW_STATIC) { + StaticShadowHelper.getInstance().prepareParent(parent); + } + } + + public int getShadowType() { + return mShadowType; + } + + public boolean needsOverlay() { + return mNeedsOverlay; + } + + public boolean needsRoundedCorner() { + return mNeedsRoundedCorner; + } + + /** + * Returns true if a "wrapper" ShadowOverlayContainer is needed. + * When needsWrapper() is true, call {@link #createShadowOverlayContainer(Context)} + * to create the wrapper. + */ + public boolean needsWrapper() { + return mNeedsWrapper; + } + + /** + * Create ShadowOverlayContainer for this helper. + * @param context Context to create view. + * @return ShadowOverlayContainer. + */ + public ShadowOverlayContainer createShadowOverlayContainer(Context context) { + if (!needsWrapper()) { + throw new IllegalArgumentException(); + } + return new ShadowOverlayContainer(context, mShadowType, mNeedsOverlay, + mUnfocusedZ, mFocusedZ, mRoundedCornerRadius); + } + + /** + * Set overlay color for view other than ShadowOverlayContainer. + * See also {@link ShadowOverlayContainer#setOverlayColor(int)}. + */ + public static void setNoneWrapperOverlayColor(View view, int color) { + Drawable d = ForegroundHelper.getInstance().getForeground(view); + if (d instanceof ColorDrawable) { + ((ColorDrawable) d).setColor(color); + } else { + ForegroundHelper.getInstance().setForeground(view, new ColorDrawable(color)); + } + } + + /** + * Set overlay color for view, it can be a ShadowOverlayContainer if needsWrapper() is true, + * or other view type. + */ + public void setOverlayColor(View view, int color) { + if (needsWrapper()) { + ((ShadowOverlayContainer) view).setOverlayColor(color); + } else { + setNoneWrapperOverlayColor(view, color); + } + } + + /** + * Must be called when view is created for cases {@link #needsWrapper()} is false. + * @param view + */ + public void onViewCreated(View view) { + if (!needsWrapper()) { + if (!mNeedsShadow) { + if (mNeedsRoundedCorner) { + RoundedRectHelper.getInstance().setClipToRoundedOutline(view, + true, mRoundedCornerRadius); + } + } else { + if (mShadowType == SHADOW_DYNAMIC) { + Object tag = ShadowHelper.getInstance().addDynamicShadow( + view, mUnfocusedZ, mFocusedZ, mRoundedCornerRadius); + view.setTag(R.id.lb_shadow_impl, tag); + } + } + } + } + + /** + * Set shadow focus level (0 to 1). 0 for unfocused, 1 for fully focused. + * This is for view other than ShadowOverlayContainer. + * See also {@link ShadowOverlayContainer#setShadowFocusLevel(float)}. + */ + public static void setNoneWrapperShadowFocusLevel(View view, float level) { + setShadowFocusLevel(getNoneWrapperDyamicShadowImpl(view), SHADOW_DYNAMIC, level); + } + + /** + * Set shadow focus level (0 to 1). 0 for unfocused, 1 for fully focused. + */ + public void setShadowFocusLevel(View view, float level) { + if (needsWrapper()) { + ((ShadowOverlayContainer) view).setShadowFocusLevel(level); + } else { + setShadowFocusLevel(getNoneWrapperDyamicShadowImpl(view), SHADOW_DYNAMIC, level); + } + } + + void setupDynamicShadowZ(Options options, Context context) { + if (options.getDynamicShadowUnfocusedZ() < 0f) { + Resources res = context.getResources(); + mFocusedZ = res.getDimension(R.dimen.lb_material_shadow_focused_z); + mUnfocusedZ = res.getDimension(R.dimen.lb_material_shadow_normal_z); + } else { + mFocusedZ = options.getDynamicShadowFocusedZ(); + mUnfocusedZ = options.getDynamicShadowUnfocusedZ(); + } + } + + void setupRoundedCornerRadius(Options options, Context context) { + if (options.getRoundedCornerRadius() == 0) { + Resources res = context.getResources(); + mRoundedCornerRadius = res.getDimensionPixelSize( + R.dimen.lb_rounded_rect_corner_radius); + } else { + mRoundedCornerRadius = options.getRoundedCornerRadius(); + } + } + + static Object getNoneWrapperDyamicShadowImpl(View view) { + return view.getTag(R.id.lb_shadow_impl); + } + + static void setShadowFocusLevel(Object impl, int shadowType, float level) { + if (impl != null) { + if (level < 0f) { + level = 0f; + } else if (level > 1f) { + level = 1f; + } + switch (shadowType) { + case SHADOW_DYNAMIC: + ShadowHelper.getInstance().setShadowFocusLevel(impl, level); + break; + case SHADOW_STATIC: + StaticShadowHelper.getInstance().setShadowFocusLevel(impl, level); + break; + } + } + } +} diff --git a/v17/leanback/src/android/support/v17/leanback/widget/StaticShadowHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/StaticShadowHelper.java index 4d8411b3aa..022ff1de30 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/StaticShadowHelper.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/StaticShadowHelper.java @@ -34,7 +34,7 @@ final class StaticShadowHelper { */ static interface ShadowHelperVersionImpl { public void prepareParent(ViewGroup parent); - public Object addStaticShadow(ViewGroup shadowContainer, boolean roundedCorners); + public Object addStaticShadow(ViewGroup shadowContainer); public void setShadowFocusLevel(Object impl, float level); } @@ -48,7 +48,7 @@ final class StaticShadowHelper { } @Override - public Object addStaticShadow(ViewGroup shadowContainer, boolean roundedCorners) { + public Object addStaticShadow(ViewGroup shadowContainer) { // do nothing return null; } @@ -69,8 +69,7 @@ final class StaticShadowHelper { } @Override - public Object addStaticShadow(ViewGroup shadowContainer, boolean roundedCorners) { - // Static shadows are always rounded + public Object addStaticShadow(ViewGroup shadowContainer) { return ShadowHelperJbmr2.addShadow(shadowContainer); } @@ -105,8 +104,8 @@ final class StaticShadowHelper { mImpl.prepareParent(parent); } - public Object addStaticShadow(ViewGroup shadowContainer, boolean roundedCorners) { - return mImpl.addStaticShadow(shadowContainer, roundedCorners); + public Object addStaticShadow(ViewGroup shadowContainer) { + return mImpl.addStaticShadow(shadowContainer); } public void setShadowFocusLevel(Object impl, float level) { diff --git a/v17/leanback/src/android/support/v17/leanback/widget/TitleHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/TitleHelper.java index c61087b0d3..9282bb1f87 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/TitleHelper.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/TitleHelper.java @@ -36,8 +36,6 @@ public class TitleHelper { private Object mSceneWithTitle; private Object mSceneWithoutTitle; - static TransitionHelper sTransitionHelper = TransitionHelper.getInstance(); - // When moving focus off the TitleView, this focus search listener assumes that the view that // should take focus comes before the TitleView in a focus search starting at the scene root. private final BrowseFrameLayout.OnFocusSearchListener mOnFocusSearchListener = @@ -68,16 +66,16 @@ public class TitleHelper { private void createTransitions() { mTitleUpTransition = LeanbackTransitionHelper.loadTitleOutTransition( - mSceneRoot.getContext(), sTransitionHelper); + mSceneRoot.getContext()); mTitleDownTransition = LeanbackTransitionHelper.loadTitleInTransition( - mSceneRoot.getContext(), sTransitionHelper); - mSceneWithTitle = sTransitionHelper.createScene(mSceneRoot, new Runnable() { + mSceneRoot.getContext()); + mSceneWithTitle = TransitionHelper.createScene(mSceneRoot, new Runnable() { @Override public void run() { mTitleView.setVisibility(View.VISIBLE); } }); - mSceneWithoutTitle = sTransitionHelper.createScene(mSceneRoot, new Runnable() { + mSceneWithoutTitle = TransitionHelper.createScene(mSceneRoot, new Runnable() { @Override public void run() { mTitleView.setVisibility(View.INVISIBLE); @@ -90,9 +88,9 @@ public class TitleHelper { */ public void showTitle(boolean show) { if (show) { - sTransitionHelper.runTransition(mSceneWithTitle, mTitleDownTransition); + TransitionHelper.runTransition(mSceneWithTitle, mTitleDownTransition); } else { - sTransitionHelper.runTransition(mSceneWithoutTitle, mTitleUpTransition); + TransitionHelper.runTransition(mSceneWithoutTitle, mTitleUpTransition); } } diff --git a/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridPresenter.java index 16b66cde35..7af5ea0cf7 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridPresenter.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridPresenter.java @@ -16,6 +16,7 @@ package android.support.v17.leanback.widget; import android.content.Context; import android.support.v17.leanback.R; import android.support.v17.leanback.system.Settings; +import android.support.v17.leanback.transition.TransitionHelper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -31,6 +32,17 @@ public class VerticalGridPresenter extends Presenter { class VerticalGridItemBridgeAdapter extends ItemBridgeAdapter { @Override + protected void onCreate(ItemBridgeAdapter.ViewHolder viewHolder) { + if (viewHolder.itemView instanceof ViewGroup) { + TransitionHelper.setTransitionGroup((ViewGroup) viewHolder.itemView, + true); + } + if (mShadowOverlayHelper != null) { + mShadowOverlayHelper.onViewCreated(viewHolder.itemView); + } + } + + @Override public void onBind(final ItemBridgeAdapter.ViewHolder itemViewHolder) { // Only when having an OnItemClickListner, we attach the OnClickListener. if (getOnItemViewClickedListener() != null) { @@ -83,9 +95,12 @@ public class VerticalGridPresenter extends Presenter { private int mFocusZoomFactor; private boolean mUseFocusDimmer; private boolean mShadowEnabled = true; + private boolean mKeepChildForeground = true; private OnItemViewSelectedListener mOnItemViewSelectedListener; private OnItemViewClickedListener mOnItemViewClickedListener; private boolean mRoundedCornersEnabled = true; + private ShadowOverlayHelper mShadowOverlayHelper; + private ItemBridgeAdapter.Wrapper mShadowOverlayWrapper; /** * Constructs a VerticalGridPresenter with defaults. @@ -170,7 +185,7 @@ public class VerticalGridPresenter extends Presenter { * Subclass may return false to disable. */ public boolean isUsingDefaultShadow() { - return ShadowOverlayContainer.supportsShadow(); + return ShadowOverlayHelper.supportsShadow(); } /** @@ -194,8 +209,7 @@ public class VerticalGridPresenter extends Presenter { * and does not use Z-shadow on SDK >= L, it should override isUsingZOrder() return false. */ public boolean isUsingZOrder(Context context) { - return ShadowOverlayContainer.supportsDynamicShadow() && - !Settings.getInstance(context).preferStaticShadows(); + return !Settings.getInstance(context).preferStaticShadows(); } final boolean needsDefaultShadow() { @@ -216,7 +230,6 @@ public class VerticalGridPresenter extends Presenter { return mUseFocusDimmer; } - @Override public final ViewHolder onCreateViewHolder(ViewGroup parent) { ViewHolder vh = createGridViewHolder(parent); @@ -238,21 +251,6 @@ public class VerticalGridPresenter extends Presenter { return new ViewHolder((VerticalGridView) root.findViewById(R.id.browse_grid)); } - private ItemBridgeAdapter.Wrapper mWrapper = new ItemBridgeAdapter.Wrapper() { - @Override - public View createWrapper(View root) { - ShadowOverlayContainer wrapper = new ShadowOverlayContainer(root.getContext()); - wrapper.setLayoutParams( - new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); - wrapper.initialize(needsDefaultShadow(), true, areChildRoundedCornersEnabled()); - return wrapper; - } - @Override - public void wrap(View wrapper, View wrapped) { - ((ShadowOverlayContainer) wrapper).wrap(wrapped); - } - }; - /** * Called after a {@link VerticalGridPresenter.ViewHolder} is created. * Subclasses may override this method and start by calling @@ -268,12 +266,25 @@ public class VerticalGridPresenter extends Presenter { vh.getGridView().setNumColumns(mNumColumns); vh.mInitialized = true; - vh.mItemBridgeAdapter.setWrapper(mWrapper); - if (needsDefaultShadow() || areChildRoundedCornersEnabled()) { - ShadowOverlayContainer.prepareParentForShadow(vh.getGridView()); - ((ViewGroup) vh.view).setClipChildren(false); + Context context = vh.mGridView.getContext(); + if (mShadowOverlayHelper == null) { + mShadowOverlayHelper = new ShadowOverlayHelper.Builder() + .needsOverlay(mUseFocusDimmer) + .needsShadow(needsDefaultShadow()) + .needsRoundedCorner(areChildRoundedCornersEnabled()) + .preferZOrder(isUsingZOrder(context)) + .keepForegroundDrawable(mKeepChildForeground) + .options(createShadowOverlayOptions()) + .build(context); + if (mShadowOverlayHelper.needsWrapper()) { + mShadowOverlayWrapper = new ItemBridgeAdapterShadowOverlayWrapper( + mShadowOverlayHelper); + } } - vh.getGridView().setFocusDrawingOrderEnabled(!isUsingZOrder(vh.getGridView().getContext())); + vh.mItemBridgeAdapter.setWrapper(mShadowOverlayWrapper); + mShadowOverlayHelper.prepareParentForShadow(vh.mGridView); + vh.getGridView().setFocusDrawingOrderEnabled(mShadowOverlayHelper.getShadowType() + == ShadowOverlayHelper.SHADOW_STATIC); FocusHighlightHelper.setupBrowseItemFocusHighlight(vh.mItemBridgeAdapter, mFocusZoomFactor, mUseFocusDimmer); @@ -286,6 +297,39 @@ public class VerticalGridPresenter extends Presenter { }); } + /** + * Set if keeps foreground of child of this grid, the foreground will not + * be used for overlay color. Default value is true. + * + * @param keep True if keep foreground of child of this grid. + */ + public final void setKeepChildForeground(boolean keep) { + mKeepChildForeground = keep; + } + + /** + * Returns true if keeps foreground of child of this grid, the foreground will not + * be used for overlay color. Default value is true. + * + * @return True if keeps foreground of child of this grid. + */ + public final boolean getKeepChildForeground() { + return mKeepChildForeground; + } + + /** + * Create ShadowOverlayHelper Options. Subclass may override. + * e.g. + * <code> + * return new ShadowOverlayHelper.Options().roundedCornerRadius(10); + * </code> + * + * @return The options to be used for shadow, overlay and rouded corner. + */ + protected ShadowOverlayHelper.Options createShadowOverlayOptions() { + return ShadowOverlayHelper.Options.DEFAULT; + } + @Override public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) { if (DEBUG) Log.v(TAG, "onBindViewHolder " + item); @@ -345,4 +389,18 @@ public class VerticalGridPresenter extends Presenter { } } } + + /** + * Changes the visibility of views. The entrance transition will be run against the views that + * change visibilities. This method is called by the fragment, it should not be called + * directly by the application. + * + * @param holder The ViewHolder for the vertical grid. + * @param afterEntrance true if children of vertical grid participating in entrance transition + * should be set to visible, false otherwise. + */ + public void setEntranceTransitionState(VerticalGridPresenter.ViewHolder holder, + boolean afterEntrance) { + holder.mGridView.setChildrenVisibility(afterEntrance? View.VISIBLE : View.INVISIBLE); + } } diff --git a/v17/preference-leanback/Android.mk b/v17/preference-leanback/Android.mk index c3bc8037f5..14be7eb93b 100644 --- a/v17/preference-leanback/Android.mk +++ b/v17/preference-leanback/Android.mk @@ -34,6 +34,8 @@ LOCAL_AAPT_FLAGS := \ LOCAL_JAR_EXCLUDE_FILES := none include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files := $(LOCAL_SRC_FILES) + # ----------------------------------------------------------------------- # A helper sub-library that makes direct use of API 21. @@ -41,9 +43,12 @@ include $(CLEAR_VARS) LOCAL_MODULE := android-support-v17-preference-leanback-api21 LOCAL_SDK_VERSION := 21 LOCAL_SRC_FILES := $(call all-java-files-under, api21) -LOCAL_JAVA_LIBRARIES := android-support-v17-preference-leanback-res +LOCAL_JAVA_LIBRARIES := android-support-v17-preference-leanback-res \ + android-support-v17-leanback include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) + # Here is the final static library that apps can link against. # The R class is automatically excluded from the generated library. # Applications that use this library must specify LOCAL_RESOURCE_DIR @@ -65,11 +70,12 @@ LOCAL_JAVA_LIBRARIES := \ android-support-v17-preference-leanback-res include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) + # API Check # --------------------------------------------- support_module := $(LOCAL_MODULE) support_module_api_dir := $(LOCAL_PATH)/api -support_module_src_files := $(LOCAL_SRC_FILES) support_module_java_libraries := $(LOCAL_JAVA_LIBRARIES) support_module_java_packages := android.support.v17.preference include $(SUPPORT_API_CHECK) diff --git a/v17/preference-leanback/api/23.txt b/v17/preference-leanback/api/23.0.0.txt index 06316abcff..06316abcff 100644 --- a/v17/preference-leanback/api/23.txt +++ b/v17/preference-leanback/api/23.0.0.txt diff --git a/v17/preference-leanback/api/23.1.0.txt b/v17/preference-leanback/api/23.1.0.txt new file mode 100644 index 0000000000..06316abcff --- /dev/null +++ b/v17/preference-leanback/api/23.1.0.txt @@ -0,0 +1,61 @@ +package android.support.v17.preference { + + public abstract class BaseLeanbackPreferenceFragment extends android.support.v14.preference.PreferenceFragment { + ctor public BaseLeanbackPreferenceFragment(); + } + + public class LeanbackListPreferenceDialogFragment extends android.support.v17.preference.LeanbackPreferenceDialogFragment { + ctor public LeanbackListPreferenceDialogFragment(); + method public static android.support.v17.preference.LeanbackListPreferenceDialogFragment newInstanceMulti(java.lang.String); + method public static android.support.v17.preference.LeanbackListPreferenceDialogFragment newInstanceSingle(java.lang.String); + method public android.support.v7.widget.RecyclerView.Adapter onCreateAdapter(); + } + + public class LeanbackListPreferenceDialogFragment.AdapterMulti extends android.support.v7.widget.RecyclerView.Adapter implements android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener { + ctor public LeanbackListPreferenceDialogFragment.AdapterMulti(java.lang.CharSequence[], java.lang.CharSequence[], java.util.Set<java.lang.String>); + method public int getItemCount(); + method public void onBindViewHolder(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder, int); + method public android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder onCreateViewHolder(android.view.ViewGroup, int); + method public void onItemClick(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder); + } + + public class LeanbackListPreferenceDialogFragment.AdapterSingle extends android.support.v7.widget.RecyclerView.Adapter implements android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener { + ctor public LeanbackListPreferenceDialogFragment.AdapterSingle(java.lang.CharSequence[], java.lang.CharSequence[], java.lang.CharSequence); + method public int getItemCount(); + method public void onBindViewHolder(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder, int); + method public android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder onCreateViewHolder(android.view.ViewGroup, int); + method public void onItemClick(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder); + } + + public static class LeanbackListPreferenceDialogFragment.ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder implements android.view.View.OnClickListener { + ctor public LeanbackListPreferenceDialogFragment.ViewHolder(android.view.View, android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener); + method public android.view.ViewGroup getContainer(); + method public android.widget.TextView getTitleView(); + method public android.widget.Checkable getWidgetView(); + method public void onClick(android.view.View); + } + + public static abstract interface LeanbackListPreferenceDialogFragment.ViewHolder.OnItemClickListener { + method public abstract void onItemClick(android.support.v17.preference.LeanbackListPreferenceDialogFragment.ViewHolder); + } + + public class LeanbackPreferenceDialogFragment extends android.app.Fragment { + ctor public LeanbackPreferenceDialogFragment(); + method public android.support.v7.preference.DialogPreference getPreference(); + field public static final java.lang.String ARG_KEY = "key"; + } + + public abstract class LeanbackPreferenceFragment extends android.support.v17.preference.BaseLeanbackPreferenceFragment { + ctor public LeanbackPreferenceFragment(); + } + + public abstract class LeanbackSettingsFragment extends android.app.Fragment { + ctor public LeanbackSettingsFragment(); + method public boolean onPreferenceDisplayDialog(android.support.v14.preference.PreferenceFragment, android.support.v7.preference.Preference); + method public abstract void onPreferenceStartInitialScreen(); + method public void startImmersiveFragment(android.app.Fragment); + method public void startPreferenceFragment(android.app.Fragment); + } + +} + diff --git a/v17/preference-leanback/api21/android/support/v17/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java b/v17/preference-leanback/api21/android/support/v17/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java index 2002a54b9a..37e7a7976e 100644 --- a/v17/preference-leanback/api21/android/support/v17/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java +++ b/v17/preference-leanback/api21/android/support/v17/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java @@ -16,21 +16,10 @@ package android.support.v17.preference; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.animation.TimeInterpolator; +import android.support.v17.leanback.transition.FadeAndShortSlide; import android.app.Fragment; -import android.graphics.Path; -import android.transition.Fade; import android.transition.Transition; -import android.transition.TransitionValues; -import android.transition.Visibility; import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.DecelerateInterpolator; /** * @hide @@ -38,8 +27,8 @@ import android.view.animation.DecelerateInterpolator; public class LeanbackPreferenceFragmentTransitionHelperApi21 { public static void addTransitions(Fragment f) { - final Transition transitionStartEdge = new FadeAndShortSlideTransition(Gravity.START); - final Transition transitionEndEdge = new FadeAndShortSlideTransition(Gravity.END); + final Transition transitionStartEdge = new FadeAndShortSlide(Gravity.START); + final Transition transitionEndEdge = new FadeAndShortSlide(Gravity.END); f.setEnterTransition(transitionEndEdge); f.setExitTransition(transitionStartEdge); @@ -47,273 +36,5 @@ public class LeanbackPreferenceFragmentTransitionHelperApi21 { f.setReturnTransition(transitionEndEdge); } - private static class FadeAndShortSlideTransition extends Visibility { - - private static final TimeInterpolator sDecelerate = new DecelerateInterpolator(); -// private static final TimeInterpolator sAccelerate = new AccelerateInterpolator(); - private static final String PROPNAME_SCREEN_POSITION = - "android:fadeAndShortSlideTransition:screenPosition"; - - private CalculateSlide mSlideCalculator = sCalculateEnd; - private Visibility mFade = new Fade(); - - private interface CalculateSlide { - - /** Returns the translation value for view when it goes out of the scene */ - float getGoneX(ViewGroup sceneRoot, View view); - } - - private static final CalculateSlide sCalculateStart = new CalculateSlide() { - @Override - public float getGoneX(ViewGroup sceneRoot, View view) { - final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; - final float x; - if (isRtl) { - x = view.getTranslationX() + sceneRoot.getWidth() / 4; - } else { - x = view.getTranslationX() - sceneRoot.getWidth() / 4; - } - return x; - } - }; - - private static final CalculateSlide sCalculateEnd = new CalculateSlide() { - @Override - public float getGoneX(ViewGroup sceneRoot, View view) { - final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; - final float x; - if (isRtl) { - x = view.getTranslationX() - sceneRoot.getWidth() / 4; - } else { - x = view.getTranslationX() + sceneRoot.getWidth() / 4; - } - return x; - } - }; - - public FadeAndShortSlideTransition(int slideEdge) { - setSlideEdge(slideEdge); - } - - @Override - public void setEpicenterCallback(EpicenterCallback epicenterCallback) { - super.setEpicenterCallback(epicenterCallback); - mFade.setEpicenterCallback(epicenterCallback); - } - - private void captureValues(TransitionValues transitionValues) { - View view = transitionValues.view; - int[] position = new int[2]; - view.getLocationOnScreen(position); - transitionValues.values.put(PROPNAME_SCREEN_POSITION, position[0]); - } - - @Override - public void captureStartValues(TransitionValues transitionValues) { - super.captureStartValues(transitionValues); - mFade.captureStartValues(transitionValues); - captureValues(transitionValues); - } - - @Override - public void captureEndValues(TransitionValues transitionValues) { - super.captureEndValues(transitionValues); - mFade.captureEndValues(transitionValues); - captureValues(transitionValues); - } - - public void setSlideEdge(int slideEdge) { - switch (slideEdge) { - case Gravity.START: - mSlideCalculator = sCalculateStart; - break; - case Gravity.END: - mSlideCalculator = sCalculateEnd; - break; - default: - throw new IllegalArgumentException("Invalid slide direction"); - } -// SidePropagation propagation = new SidePropagation(); -// propagation.setSide(slideEdge); -// setPropagation(propagation); - } - - @Override - public Animator onAppear(ViewGroup sceneRoot, View view, - TransitionValues startValues, TransitionValues endValues) { - if (endValues == null) { - return null; - } - Integer position = (Integer) endValues.values.get(PROPNAME_SCREEN_POSITION); - float endX = view.getTranslationX(); - float startX = mSlideCalculator.getGoneX(sceneRoot, view); - final Animator slideAnimator = TranslationAnimationCreator - .createAnimation(view, endValues, position, - startX, endX, sDecelerate, this); - final AnimatorSet set = new AnimatorSet(); - set.play(slideAnimator) - .with(mFade.onAppear(sceneRoot, view, startValues, endValues)); - - return set; - } - - @Override - public Animator onDisappear(ViewGroup sceneRoot, View view, - TransitionValues startValues, TransitionValues endValues) { - if (startValues == null) { - return null; - } - Integer position = (Integer) startValues.values.get(PROPNAME_SCREEN_POSITION); - float startX = view.getTranslationX(); - float endX = mSlideCalculator.getGoneX(sceneRoot, view); - final Animator slideAnimator = TranslationAnimationCreator - .createAnimation(view, startValues, position, - startX, endX, sDecelerate /*sAccelerate*/, this); - final AnimatorSet set = new AnimatorSet(); - set.play(slideAnimator) - .with(mFade.onDisappear(sceneRoot, view, startValues, endValues)); - - return set; - } - - @Override - public Transition addListener(TransitionListener listener) { - mFade.addListener(listener); - return super.addListener(listener); - } - - @Override - public Transition removeListener(TransitionListener listener) { - mFade.removeListener(listener); - return super.removeListener(listener); - } - - @Override - public Transition clone() { - FadeAndShortSlideTransition clone = null; - clone = (FadeAndShortSlideTransition) super.clone(); - clone.mFade = (Visibility) mFade.clone(); - return clone; - } - } - - /** - * This class is used by Slide and Explode to create an animator that goes from the start - * position to the end position. It takes into account the canceled position so that it - * will not blink out or shift suddenly when the transition is interrupted. - */ - private static class TranslationAnimationCreator { - - /** - * Creates an animator that can be used for x and/or y translations. When interrupted, - * it sets a tag to keep track of the position so that it may be continued from position. - * - * @param view The view being moved. This may be in the overlay for onDisappear. - * @param values The values containing the view in the view hierarchy. - * @param viewPosX The x screen coordinate of view - * @param startX The start translation x of view - * @param endX The end translation x of view - * @param interpolator The interpolator to use with this animator. - * @return An animator that moves from (startX, startY) to (endX, endY) unless there was - * a previous interruption, in which case it moves from the current position to - * (endX, endY). - */ - static Animator createAnimation(View view, TransitionValues values, int viewPosX, - float startX, float endX, TimeInterpolator interpolator, - Transition transition) { - float terminalX = view.getTranslationX(); - Integer startPosition = (Integer) values.view.getTag(R.id.transitionPosition); - if (startPosition != null) { - startX = startPosition - viewPosX + terminalX; - } - // Initial position is at translation startX, startY, so position is offset by that - // amount - int startPosX = viewPosX + Math.round(startX - terminalX); - - view.setTranslationX(startX); - if (startX == endX) { - return null; - } - Path path = new Path(); - path.moveTo(startX, 0); - path.lineTo(endX, 0); - ObjectAnimator anim = - ObjectAnimator.ofFloat(view, View.TRANSLATION_X, View.TRANSLATION_Y, path); - - TransitionPositionListener listener = new TransitionPositionListener(view, values.view, - startPosX, terminalX); - transition.addListener(listener); - anim.addListener(listener); - anim.addPauseListener(listener); - anim.setInterpolator(interpolator); - return anim; - } - - private static class TransitionPositionListener extends AnimatorListenerAdapter implements - Transition.TransitionListener { - - private final View mViewInHierarchy; - private final View mMovingView; - private final int mStartX; - private Integer mTransitionPosition; - private float mPausedX; - private final float mTerminalX; - - private TransitionPositionListener(View movingView, View viewInHierarchy, - int startX, float terminalX) { - mMovingView = movingView; - mViewInHierarchy = viewInHierarchy; - mStartX = startX - Math.round(mMovingView.getTranslationX()); - mTerminalX = terminalX; - mTransitionPosition = (Integer) mViewInHierarchy.getTag(R.id.transitionPosition); - if (mTransitionPosition != null) { - mViewInHierarchy.setTag(R.id.transitionPosition, null); - } - } - - @Override - public void onAnimationCancel(Animator animation) { - mTransitionPosition = Math.round(mStartX + mMovingView.getTranslationX()); - mViewInHierarchy.setTag(R.id.transitionPosition, mTransitionPosition); - } - - @Override - public void onAnimationEnd(Animator animator) { - } - - @Override - public void onAnimationPause(Animator animator) { - mPausedX = mMovingView.getTranslationX(); - mMovingView.setTranslationX(mTerminalX); - } - - @Override - public void onAnimationResume(Animator animator) { - mMovingView.setTranslationX(mPausedX); - } - - @Override - public void onTransitionStart(Transition transition) { - } - - @Override - public void onTransitionEnd(Transition transition) { - mMovingView.setTranslationX(mTerminalX); - } - - @Override - public void onTransitionCancel(Transition transition) { - } - - @Override - public void onTransitionPause(Transition transition) { - } - - @Override - public void onTransitionResume(Transition transition) { - } - } - - } } diff --git a/v17/preference-leanback/build.gradle b/v17/preference-leanback/build.gradle index 4063122edd..0ddcdd2dc8 100644 --- a/v17/preference-leanback/build.gradle +++ b/v17/preference-leanback/build.gradle @@ -56,3 +56,37 @@ android { abortOnError false } } + +uploadArchives { + repositories { + mavenDeployer { + repository(url: uri(rootProject.ext.supportRepoOut)) { + } + + pom.project { + name 'Android Support Leanback Preference v17' + description "Android Support Leanback Preference v17" + url 'http://developer.android.com/tools/extras/support-library.html' + inceptionYear '2015' + + licenses { + license { + name 'The Apache Software License, Version 2.0' + url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + distribution 'repo' + } + } + + scm { + url "http://source.android.com" + connection "scm:git:https://android.googlesource.com/platform/frameworks/support" + } + developers { + developer { + name 'The Android Open Source Project' + } + } + } + } + } +} diff --git a/v17/preference-leanback/res/values/ids.xml b/v17/preference-leanback/res/color/lb_preference_item_primary_text_color.xml index 20c1edaa00..efdf1c0976 100644 --- a/v17/preference-leanback/res/values/ids.xml +++ b/v17/preference-leanback/res/color/lb_preference_item_primary_text_color.xml @@ -15,6 +15,7 @@ ~ limitations under the License --> -<resources> - <item name="transitionPosition" type="id" /> -</resources> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" android:color="@color/lb_preference_item_primary_text_color_disabled" /> + <item android:color="@color/lb_preference_item_primary_text_color_default"/> +</selector> diff --git a/v17/preference-leanback/res/color/lb_preference_item_secondary_text_color.xml b/v17/preference-leanback/res/color/lb_preference_item_secondary_text_color.xml new file mode 100644 index 0000000000..68bb81aef5 --- /dev/null +++ b/v17/preference-leanback/res/color/lb_preference_item_secondary_text_color.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 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 + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" android:color="@color/lb_preference_item_secondary_text_color_disabled" /> + <item android:color="@color/lb_preference_item_secondary_text_color_default"/> +</selector> diff --git a/v17/preference-leanback/res/layout/leanback_list_preference_fragment.xml b/v17/preference-leanback/res/layout/leanback_list_preference_fragment.xml index 9fae0f888c..f073f3e463 100644 --- a/v17/preference-leanback/res/layout/leanback_list_preference_fragment.xml +++ b/v17/preference-leanback/res/layout/leanback_list_preference_fragment.xml @@ -20,6 +20,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/lb_preference_decor_list_background" + android:elevation="@dimen/lb_preference_decor_elevation" android:orientation="vertical" android:transitionGroup="false" > @@ -29,17 +30,18 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/defaultBrandColor" + android:elevation="@dimen/lb_preference_decor_title_container_elevation" android:transitionGroup="false" > <TextView android:id="@+id/decor_title" android:layout_width="match_parent" android:layout_height="@dimen/lb_preference_decor_title_text_height" + android:layout_marginTop="@dimen/lb_preference_decor_title_margin_top" + android:layout_marginStart="@dimen/lb_preference_decor_title_margin_start" + android:layout_marginEnd="@dimen/lb_preference_decor_title_margin_end" android:fontFamily="sans-serif-condensed" android:gravity="center_vertical" - android:paddingTop="@dimen/lb_preference_decor_title_padding_top" - android:paddingStart="@dimen/lb_preference_decor_title_padding_start" - android:paddingEnd="@dimen/lb_preference_decor_title_padding_end" android:singleLine="true" android:textSize="@dimen/lb_preference_decor_title_text_size" android:textColor="?android:attr/textColorPrimary" @@ -50,6 +52,7 @@ android:id="@android:id/message" android:layout_width="match_parent" android:layout_height="wrap_content" + android:fontFamily="sans-serif-condensed" android:paddingTop="14dp" android:paddingBottom="14dp" android:paddingStart="24dp" diff --git a/v17/preference-leanback/res/layout/leanback_list_preference_item_multi.xml b/v17/preference-leanback/res/layout/leanback_list_preference_item_multi.xml index 3b1345cbd5..728ecff6d7 100644 --- a/v17/preference-leanback/res/layout/leanback_list_preference_item_multi.xml +++ b/v17/preference-leanback/res/layout/leanback_list_preference_item_multi.xml @@ -18,20 +18,35 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/container" android:layout_width="match_parent" - android:layout_height="?android:attr/listPreferredItemHeight" + android:layout_height="wrap_content" android:background="?android:attr/selectableItemBackground" android:clickable="true" android:focusable="true" android:descendantFocusability="blocksDescendants" - android:orientation="horizontal"> + android:orientation="horizontal" + android:paddingStart="@dimen/lb_preference_item_padding_start" + android:paddingEnd="@dimen/lb_preference_item_padding_end" > + <CheckBox android:id="@+id/button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" /> - <TextView - android:id="@android:id/title" - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="1" - android:gravity="center_vertical" /> + android:layout_width="@dimen/lb_preference_item_icon_size" + android:layout_height="@dimen/lb_preference_item_icon_size" + android:layout_marginEnd="@dimen/lb_preference_item_icon_margin_end" + android:layout_gravity="center_vertical" /> + + <LinearLayout android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical"> + <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_top" /> + <TextView + android:id="@android:id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/lb_preference_item_primary_text_margin_bottom" + android:fontFamily="sans-serif-condensed" + android:textColor="@color/lb_preference_item_primary_text_color" + android:textSize="@dimen/lb_preference_item_primary_text_size"/> + <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_bottom" /> + </LinearLayout> + </LinearLayout> diff --git a/v17/preference-leanback/res/layout/leanback_list_preference_item_single.xml b/v17/preference-leanback/res/layout/leanback_list_preference_item_single.xml index eaf42a4fa8..354ca41abd 100644 --- a/v17/preference-leanback/res/layout/leanback_list_preference_item_single.xml +++ b/v17/preference-leanback/res/layout/leanback_list_preference_item_single.xml @@ -18,20 +18,35 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/container" android:layout_width="match_parent" - android:layout_height="?android:attr/listPreferredItemHeight" + android:layout_height="wrap_content" android:background="?android:attr/selectableItemBackground" android:clickable="true" android:focusable="true" android:descendantFocusability="blocksDescendants" - android:orientation="horizontal"> + android:orientation="horizontal" + android:paddingStart="@dimen/lb_preference_item_padding_start" + android:paddingEnd="@dimen/lb_preference_item_padding_end" > + <RadioButton android:id="@+id/button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" /> - <TextView - android:id="@android:id/title" - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="1" - android:gravity="center_vertical" /> + android:layout_width="@dimen/lb_preference_item_icon_size" + android:layout_height="@dimen/lb_preference_item_icon_size" + android:layout_marginEnd="@dimen/lb_preference_item_icon_margin_end" + android:layout_gravity="center_vertical" /> + + <LinearLayout android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical"> + <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_top" /> + <TextView + android:id="@android:id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/lb_preference_item_primary_text_margin_bottom" + android:fontFamily="sans-serif-condensed" + android:textColor="@color/lb_preference_item_primary_text_color" + android:textSize="@dimen/lb_preference_item_primary_text_size"/> + <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_bottom" /> + </LinearLayout> + </LinearLayout> diff --git a/v17/preference-leanback/res/layout/leanback_preference.xml b/v17/preference-leanback/res/layout/leanback_preference.xml new file mode 100644 index 0000000000..05ca21c14e --- /dev/null +++ b/v17/preference-leanback/res/layout/leanback_preference.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 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 + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?android:attr/selectableItemBackground" + android:clickable="true" + android:focusable="true" + android:descendantFocusability="blocksDescendants" + android:orientation="horizontal" + android:paddingStart="@dimen/lb_preference_item_padding_start" + android:paddingEnd="@dimen/lb_preference_item_padding_end" > + + <FrameLayout + android:id="@+id/icon_frame" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" > + <ImageView + android:id="@android:id/icon" + android:layout_width="@dimen/lb_preference_item_icon_size" + android:layout_height="@dimen/lb_preference_item_icon_size" + android:layout_marginEnd="@dimen/lb_preference_item_icon_margin_end" + /> + </FrameLayout> + + <LinearLayout android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:orientation="vertical"> + <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_top" /> + <TextView + android:id="@android:id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/lb_preference_item_primary_text_margin_bottom" + android:fontFamily="sans-serif-condensed" + android:textColor="@color/lb_preference_item_primary_text_color" + android:textSize="@dimen/lb_preference_item_primary_text_size"/> + <TextView + android:id="@android:id/summary" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:fontFamily="sans-serif-condensed" + android:textColor="@color/lb_preference_item_secondary_text_color" + android:textSize="@dimen/lb_preference_item_secondary_text_size" + android:maxLines="4" /> + <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_bottom" /> + </LinearLayout> + + <!-- Preference should place its actual preference widget here. --> + <LinearLayout android:id="@android:id/widget_frame" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:gravity="center_vertical" + android:orientation="vertical" /> + +</LinearLayout> diff --git a/v17/preference-leanback/res/layout/leanback_preference_category.xml b/v17/preference-leanback/res/layout/leanback_preference_category.xml new file mode 100644 index 0000000000..9b978f3616 --- /dev/null +++ b/v17/preference-leanback/res/layout/leanback_preference_category.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 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 + --> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="@dimen/lb_preference_category_height" + android:clipToPadding="false" + android:paddingStart="@dimen/lb_preference_item_padding_start" + android:paddingEnd="@dimen/lb_preference_item_padding_end"> + + <TextView + android:id="@android:id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:fontFamily="sans-serif-condensed" + android:textColor="?android:attr/colorAccent" + android:textSize="@dimen/lb_preference_category_text_size"/> +</FrameLayout> diff --git a/v17/preference-leanback/res/layout/leanback_preference_fragment.xml b/v17/preference-leanback/res/layout/leanback_preference_fragment.xml index d119c2dfa0..199e0f71de 100644 --- a/v17/preference-leanback/res/layout/leanback_preference_fragment.xml +++ b/v17/preference-leanback/res/layout/leanback_preference_fragment.xml @@ -20,6 +20,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/lb_preference_decor_list_background" + android:elevation="@dimen/lb_preference_decor_elevation" android:orientation="vertical" android:transitionGroup="false" > @@ -29,17 +30,18 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/defaultBrandColor" + android:elevation="@dimen/lb_preference_decor_title_container_elevation" android:transitionGroup="false" > <TextView android:id="@+id/decor_title" android:layout_width="match_parent" android:layout_height="@dimen/lb_preference_decor_title_text_height" + android:layout_marginTop="@dimen/lb_preference_decor_title_margin_top" + android:layout_marginStart="@dimen/lb_preference_decor_title_margin_start" + android:layout_marginEnd="@dimen/lb_preference_decor_title_margin_end" android:fontFamily="sans-serif-condensed" android:gravity="center_vertical" - android:paddingTop="@dimen/lb_preference_decor_title_padding_top" - android:paddingStart="@dimen/lb_preference_decor_title_padding_start" - android:paddingEnd="@dimen/lb_preference_decor_title_padding_end" android:singleLine="true" android:textSize="@dimen/lb_preference_decor_title_text_size" android:textColor="?android:attr/textColorPrimary" diff --git a/v17/preference-leanback/res/layout/leanback_preference_information.xml b/v17/preference-leanback/res/layout/leanback_preference_information.xml new file mode 100644 index 0000000000..18da8d9bfa --- /dev/null +++ b/v17/preference-leanback/res/layout/leanback_preference_information.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 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 + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?android:attr/selectableItemBackground" + android:clickable="false" + android:focusable="false" + android:descendantFocusability="blocksDescendants" + android:orientation="horizontal" + android:paddingStart="@dimen/lb_preference_item_padding_start" + android:paddingEnd="@dimen/lb_preference_item_padding_end" > + + <LinearLayout android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:orientation="vertical"> + <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_top" /> + <TextView + android:id="@android:id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/lb_preference_item_primary_text_margin_bottom" + android:fontFamily="sans-serif-condensed" + android:textColor="@color/lb_preference_item_primary_text_color" + android:textSize="@dimen/lb_preference_item_primary_text_size"/> + <TextView + android:id="@android:id/summary" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:fontFamily="sans-serif-condensed" + android:textColor="@color/lb_preference_item_secondary_text_color" + android:textSize="@dimen/lb_preference_item_secondary_text_size" + android:maxLines="4" /> + <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_bottom" /> + </LinearLayout> + + <!-- Preference should place its actual preference widget here. --> + <LinearLayout android:id="@android:id/widget_frame" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:gravity="center_vertical" + android:orientation="vertical" /> + +</LinearLayout> diff --git a/v17/preference-leanback/res/layout/leanback_preferences_list.xml b/v17/preference-leanback/res/layout/leanback_preferences_list.xml index 15ecebc293..21e4e4a5b2 100644 --- a/v17/preference-leanback/res/layout/leanback_preferences_list.xml +++ b/v17/preference-leanback/res/layout/leanback_preferences_list.xml @@ -19,4 +19,5 @@ android:id="@+id/list" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="match_parent" /> + android:layout_height="match_parent" + style="?attr/preferenceFragmentListStyle" /> diff --git a/v17/preference-leanback/res/values/colors.xml b/v17/preference-leanback/res/values/colors.xml index de6c888463..30a373af5d 100644 --- a/v17/preference-leanback/res/values/colors.xml +++ b/v17/preference-leanback/res/values/colors.xml @@ -16,4 +16,11 @@ --> <resources> <color name="lb_preference_decor_list_background">#263238</color> + + <color name="lb_preference_item_primary_text_color_default">#EEEEEE</color> + <color name="lb_preference_item_primary_text_color_disabled">#4DEEEEEE</color> + + <color name="lb_preference_item_secondary_text_color_default">#B3EEEEEE</color> + <color name="lb_preference_item_secondary_text_color_disabled">#4DEEEEEE</color> + </resources> diff --git a/v17/preference-leanback/res/values/dimens.xml b/v17/preference-leanback/res/values/dimens.xml index 49763fe4fa..f3d36af530 100644 --- a/v17/preference-leanback/res/values/dimens.xml +++ b/v17/preference-leanback/res/values/dimens.xml @@ -15,11 +15,27 @@ ~ limitations under the License --> <resources> - <dimen name="lb_preference_decor_title_text_height">64dp</dimen> - <dimen name="lb_preference_decor_title_padding_top">27dp</dimen> - <dimen name="lb_preference_decor_title_padding_start">24dp</dimen> - <dimen name="lb_preference_decor_title_padding_end">56dp</dimen> - <dimen name="lb_preference_decor_title_text_size">20sp</dimen> + <dimen name="lb_preference_decor_title_text_height">64dp</dimen> + <dimen name="lb_preference_decor_title_margin_top">27dp</dimen> + <dimen name="lb_preference_decor_title_margin_start">24dp</dimen> + <dimen name="lb_preference_decor_title_margin_end">56dp</dimen> + <dimen name="lb_preference_decor_title_text_size">20sp</dimen> + <dimen name="lb_preference_decor_title_container_elevation">2dp</dimen> + <dimen name="lb_preference_decor_elevation">6dp</dimen> - <dimen name="lb_settings_pane_width">360dp</dimen> + <dimen name="lb_preference_item_padding_start">24dp</dimen> + <dimen name="lb_preference_item_padding_end">56dp</dimen> + <dimen name="lb_preference_item_icon_size">32dp</dimen> + <dimen name="lb_preference_item_icon_margin_end">16dp</dimen> + + <dimen name="lb_preference_item_primary_text_size">14sp</dimen> + <dimen name="lb_preference_item_primary_text_margin_bottom">2dp</dimen> + <dimen name="lb_preference_item_secondary_text_size">12sp</dimen> + <dimen name="lb_preference_item_text_space_top">14dp</dimen> + <dimen name="lb_preference_item_text_space_bottom">13dp</dimen> + + <dimen name="lb_preference_category_text_size">12sp</dimen> + <dimen name="lb_preference_category_height">40dp</dimen> + + <dimen name="lb_settings_pane_width">360dp</dimen> </resources> diff --git a/v17/preference-leanback/res/values/styles.xml b/v17/preference-leanback/res/values/styles.xml new file mode 100644 index 0000000000..42d2b5b63a --- /dev/null +++ b/v17/preference-leanback/res/values/styles.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 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 + --> + +<resources> + + <style name="LeanbackPreference"> + <item name="android:layout">@layout/leanback_preference</item> + </style> + + <style name="LeanbackPreference.Information"> + <item name="android:layout">@layout/leanback_preference_information</item> + <item name="android:enabled">false</item> + <item name="android:shouldDisableView">false</item> + </style> + + <style name="LeanbackPreference.Category"> + <item name="android:layout">@layout/leanback_preference_category</item> + <!-- The title should not dim if the category is disabled, instead only the preference children should dim. --> + <item name="android:shouldDisableView">false</item> + <item name="android:selectable">false</item> + </style> + + <style name="LeanbackPreference.CheckBoxPreference"> + <item name="android:widgetLayout">@layout/preference_widget_checkbox</item> + </style> + + <style name="LeanbackPreference.SwitchPreferenceCompat"> + <item name="android:widgetLayout">@layout/preference_widget_switch_compat</item> + <item name="android:switchTextOn">@string/v7_preference_on</item> + <item name="android:switchTextOff">@string/v7_preference_off</item> + </style> + + <style name="LeanbackPreference.SwitchPreference"> + <item name="android:widgetLayout">@layout/preference_widget_switch</item> + <item name="android:switchTextOn">@string/v7_preference_on</item> + <item name="android:switchTextOff">@string/v7_preference_off</item> + </style> + + <style name="LeanbackPreference.PreferenceScreen"> + </style> + + <style name="LeanbackPreference.DialogPreference"> + <item name="android:positiveButtonText">@android:string/ok</item> + <item name="android:negativeButtonText">@android:string/cancel</item> + </style> + + <style name="LeanbackPreference.DialogPreference.EditTextPreference"> + <item name="android:dialogLayout">@layout/preference_dialog_edittext</item> + </style> + + <style name="PreferenceFragment.Leanback"> + <item name="android:divider">@null</item> + </style> + + <style name="PreferenceFragmentList.Leanback"> + <item name="android:paddingStart">0dp</item> + <item name="android:paddingEnd">0dp</item> + </style> + +</resources> diff --git a/v17/preference-leanback/res/values/themes.xml b/v17/preference-leanback/res/values/themes.xml new file mode 100644 index 0000000000..591fdfa1a7 --- /dev/null +++ b/v17/preference-leanback/res/values/themes.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 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 + --> + +<resources> + <style name="PreferenceThemeOverlay.v14.Leanback"> + <item name="preferenceScreenStyle">@style/LeanbackPreference.PreferenceScreen</item> + <item name="preferenceFragmentCompatStyle">@style/PreferenceFragment.Leanback</item> + <item name="preferenceFragmentStyle">@style/PreferenceFragment.Leanback</item> + <item name="preferenceCategoryStyle">@style/LeanbackPreference.Category</item> + <item name="preferenceStyle">@style/LeanbackPreference</item> + <item name="preferenceInformationStyle">@style/LeanbackPreference.Information</item> + <item name="checkBoxPreferenceStyle">@style/LeanbackPreference.CheckBoxPreference</item> + <item name="switchPreferenceCompatStyle">@style/LeanbackPreference.SwitchPreferenceCompat</item> + <item name="switchPreferenceStyle">@style/LeanbackPreference.SwitchPreference</item> + <item name="dialogPreferenceStyle">@style/LeanbackPreference.DialogPreference</item> + <item name="editTextPreferenceStyle">@style/LeanbackPreference.DialogPreference.EditTextPreference</item> + <item name="preferenceFragmentListStyle">@style/PreferenceFragmentList.Leanback</item> + </style> +</resources> diff --git a/v17/preference-leanback/src/android/support/v17/preference/LeanbackListPreferenceDialogFragment.java b/v17/preference-leanback/src/android/support/v17/preference/LeanbackListPreferenceDialogFragment.java index 92fdd62d79..a82f54334c 100644 --- a/v17/preference-leanback/src/android/support/v17/preference/LeanbackListPreferenceDialogFragment.java +++ b/v17/preference-leanback/src/android/support/v17/preference/LeanbackListPreferenceDialogFragment.java @@ -156,8 +156,15 @@ public class LeanbackListPreferenceDialogFragment extends LeanbackPreferenceDial public void onItemClick(ViewHolder viewHolder) { final int index = viewHolder.getAdapterPosition(); final CharSequence entry = mEntryValues[index]; - mSelectedValue = entry; - ((ListPreference) getPreference()).setValue(entry.toString()); + final ListPreference preference = (ListPreference) getPreference(); + if (index >= 0) { + String value = mEntryValues[index].toString(); + if (preference.callChangeListener(value)) { + preference.setValue(value); + mSelectedValue = entry; + } + } + getFragmentManager().popBackStack(); notifyDataSetChanged(); } @@ -206,7 +213,20 @@ public class LeanbackListPreferenceDialogFragment extends LeanbackPreferenceDial } else { mSelections.add(entry); } - ((MultiSelectListPreference) getPreference()).setValues(mSelections); + final MultiSelectListPreference multiSelectListPreference + = (MultiSelectListPreference) getPreference(); + // Pass copies of the set to callChangeListener and setValues to avoid mutations + if (multiSelectListPreference.callChangeListener(new HashSet<>(mSelections))) { + multiSelectListPreference.setValues(new HashSet<>(mSelections)); + } else { + // Change refused, back it out + if (mSelections.contains(entry)) { + mSelections.remove(entry); + } else { + mSelections.add(entry); + } + } + notifyDataSetChanged(); } } diff --git a/v17/tests/AndroidManifest.xml b/v17/tests/AndroidManifest.xml index f78d4356e4..f4dd2936e6 100644 --- a/v17/tests/AndroidManifest.xml +++ b/v17/tests/AndroidManifest.xml @@ -15,7 +15,7 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android.support.v17.leanback.tests"> - <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="17"/> + <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="23"/> <!-- This declares that this application uses the instrumentation test runner targeting @@ -33,6 +33,14 @@ <activity android:name="android.support.v17.leanback.widget.GridActivity" android:exported="true" /> - </application> + <activity android:name="android.support.v17.leanback.app.BrowseFragmentTestActivity" + android:theme="@style/Theme.Leanback.Browse" + android:exported="true" /> + + <activity android:name="android.support.v17.leanback.app.BrowseSupportFragmentTestActivity" + android:theme="@style/Theme.Leanback.Browse" + android:exported="true" /> + + </application> </manifest> diff --git a/v17/tests/generatev4.py b/v17/tests/generatev4.py new file mode 100755 index 0000000000..8dc4dc23aa --- /dev/null +++ b/v17/tests/generatev4.py @@ -0,0 +1,78 @@ +#!/usr/bin/python + +# Copyright (C) 2015 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. + +import os +import sys + +print "Generate v4 fragment related code for leanback" + +files = ['BrowseTest'] + +cls = ['BrowseTest', 'Background', 'Base', 'BaseRow', 'Browse', 'Details', 'Error', 'Headers', + 'PlaybackOverlay', 'Rows', 'Search', 'VerticalGrid', 'Branded'] + +for w in files: + print "copy {}Fragment to {}SupportFragment".format(w, w) + + file = open('src/android/support/v17/leanback/app/{}Fragment.java'.format(w), 'r') + outfile = open('src/android/support/v17/leanback/app/{}SupportFragment.java'.format(w), 'w') + + outfile.write("/* This file is auto-generated from {}Fragment.java. DO NOT MODIFY. */\n\n".format(w)) + + for line in file: + for w in cls: + line = line.replace('{}Fragment'.format(w), '{}SupportFragment'.format(w)) + line = line.replace('android.app.Fragment', 'android.support.v4.app.Fragment') + line = line.replace('android.app.Activity', 'android.support.v4.app.FragmentActivity') + outfile.write(line) + file.close() + outfile.close() + +testcls = ['Browse'] + +for w in testcls: + print "copy {}FrgamentTest to {}SupportFragmentTest".format(w, w) + + file = open('src/android/support/v17/leanback/app/{}FragmentTest.java'.format(w), 'r') + outfile = open('src/android/support/v17/leanback/app/{}SupportFragmentTest.java'.format(w), 'w') + + outfile.write("/* This file is auto-generated from {}FrgamentTest.java. DO NOT MODIFY. */\n\n".format(w)) + + for line in file: + for w in testcls: + line = line.replace('{}FragmentTest'.format(w), '{}SupportFragmentTest'.format(w)) + line = line.replace('{}FragmentTestActivity'.format(w), '{}SupportFragmentTestActivity'.format(w)) + line = line.replace('{}TestFragment'.format(w), '{}TestSupportFragment'.format(w)) + outfile.write(line) + file.close() + outfile.close() + + +print "copy BrowseFragmentTestActivity to BrowseSupportFragmentTestActivity" +file = open('src/android/support/v17/leanback/app/BrowseFragmentTestActivity.java', 'r') +outfile = open('src/android/support/v17/leanback/app/BrowseSupportFragmentTestActivity.java', 'w') +outfile.write("/* This file is auto-generated from BrowseFragmentTestActivity.java. DO NOT MODIFY. */\n\n") +for line in file: + line = line.replace('BrowseTestFragment', 'BrowseTestSupportFragment') + line = line.replace('BrowseFragmentTestActivity', 'BrowseSupportFragmentTestActivity') + line = line.replace('android.app.Fragment', 'android.support.v4.app.Fragment') + line = line.replace('android.app.Activity', 'android.support.v4.app.FragmentActivity') + line = line.replace('extends Activity', 'extends FragmentActivity') + line = line.replace('getFragmentManager', 'getSupportFragmentManager') + outfile.write(line) +file.close() +outfile.close() + diff --git a/v17/tests/res/layout/browse.xml b/v17/tests/res/layout/browse.xml new file mode 100644 index 0000000000..01226dadc7 --- /dev/null +++ b/v17/tests/res/layout/browse.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2015 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. +--> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/main_frame" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <!-- container for hosting GuidedStepFragment background --> + <FrameLayout android:id="@+id/lb_guidedstep_background" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + +</FrameLayout> diff --git a/v17/tests/src/android/support/v17/leanback/app/BrowseFragmentTest.java b/v17/tests/src/android/support/v17/leanback/app/BrowseFragmentTest.java new file mode 100644 index 0000000000..e9b1063b30 --- /dev/null +++ b/v17/tests/src/android/support/v17/leanback/app/BrowseFragmentTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2015 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.support.v17.leanback.tests.R; +import android.test.ActivityInstrumentationTestCase2; +import android.text.Selection; +import android.text.Spannable; +import android.util.Log; +import android.util.SparseArray; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; +import android.support.v7.widget.RecyclerViewAccessibilityDelegate; + +import android.app.Instrumentation; +import android.content.Intent; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; + +/** + * @hide from javadoc + */ +public class BrowseFragmentTest extends + ActivityInstrumentationTestCase2<BrowseFragmentTestActivity> { + + static final long TRANSITION_LENGTH = 1000; + + Instrumentation mInstrumentation; + BrowseFragmentTestActivity mActivity; + + public BrowseFragmentTest() { + super(BrowseFragmentTestActivity.class); + } + + private void initActivity(Intent intent) { + setActivityIntent(intent); + mActivity = getActivity(); + try { + Thread.sleep(intent.getLongExtra(BrowseFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, + BrowseTestFragment.DEFAULT_LOAD_DATA_DELAY) + TRANSITION_LENGTH); + } catch (InterruptedException ex) { + } + } + + public void testTwoBackKeysWithBackStack() throws Throwable { + mInstrumentation = getInstrumentation(); + Intent intent = new Intent(mInstrumentation.getContext(), BrowseFragmentTestActivity.class); + intent.putExtra(BrowseFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, (long) 1000); + intent.putExtra(BrowseFragmentTestActivity.EXTRA_ADD_TO_BACKSTACK , true); + initActivity(intent); + + sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); + Thread.sleep(TRANSITION_LENGTH); + + sendKeys(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_BACK); + } + + public void testTwoBackKeysWithoutBackStack() throws Throwable { + mInstrumentation = getInstrumentation(); + Intent intent = new Intent(mInstrumentation.getContext(), BrowseFragmentTestActivity.class); + intent.putExtra(BrowseFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, (long) 1000); + intent.putExtra(BrowseFragmentTestActivity.EXTRA_ADD_TO_BACKSTACK , false); + initActivity(intent); + + sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); + Thread.sleep(TRANSITION_LENGTH); + + sendKeys(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_BACK); + } + +} diff --git a/v17/tests/src/android/support/v17/leanback/app/BrowseFragmentTestActivity.java b/v17/tests/src/android/support/v17/leanback/app/BrowseFragmentTestActivity.java new file mode 100644 index 0000000000..ee433bdb0d --- /dev/null +++ b/v17/tests/src/android/support/v17/leanback/app/BrowseFragmentTestActivity.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 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.app.FragmentTransaction; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v17.leanback.tests.R; + +/** + * @hide from javadoc + */ +public class BrowseFragmentTestActivity extends Activity { + + public static final String EXTRA_ADD_TO_BACKSTACK = "addToBackStack"; + public static final String EXTRA_NUM_ROWS = "numRows"; + public static final String EXTRA_REPEAT_PER_ROW = "repeatPerRow"; + public static final String EXTRA_LOAD_DATA_DELAY = "loadDataDelay"; + public static final String EXTRA_TEST_ENTRANCE_TRANSITION = "testEntranceTransition"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Intent intent = getIntent(); + + BrowseTestFragment.NUM_ROWS = intent.getIntExtra(EXTRA_NUM_ROWS, + BrowseTestFragment.DEFAULT_NUM_ROWS); + BrowseTestFragment.REPEAT_PER_ROW = intent.getIntExtra(EXTRA_REPEAT_PER_ROW, + BrowseTestFragment.DEFAULT_REPEAT_PER_ROW); + BrowseTestFragment.LOAD_DATA_DELAY = intent.getLongExtra(EXTRA_LOAD_DATA_DELAY, + BrowseTestFragment.DEFAULT_LOAD_DATA_DELAY); + BrowseTestFragment.TEST_ENTRANCE_TRANSITION = intent.getBooleanExtra( + EXTRA_TEST_ENTRANCE_TRANSITION, + BrowseTestFragment.DEFAULT_TEST_ENTRANCE_TRANSITION); + setContentView(R.layout.browse); + FragmentTransaction ft = getFragmentManager().beginTransaction(); + ft.replace(R.id.main_frame, new BrowseTestFragment()); + if (intent.getBooleanExtra(EXTRA_ADD_TO_BACKSTACK, false)) { + ft.addToBackStack(null); + } + ft.commit(); + } +} diff --git a/v17/tests/src/android/support/v17/leanback/app/BrowseSupportFragmentTest.java b/v17/tests/src/android/support/v17/leanback/app/BrowseSupportFragmentTest.java new file mode 100644 index 0000000000..0c901be000 --- /dev/null +++ b/v17/tests/src/android/support/v17/leanback/app/BrowseSupportFragmentTest.java @@ -0,0 +1,95 @@ +/* This file is auto-generated from BrowseFrgamentTest.java. DO NOT MODIFY. */ + +/* + * Copyright (C) 2015 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.support.v17.leanback.tests.R; +import android.test.ActivityInstrumentationTestCase2; +import android.text.Selection; +import android.text.Spannable; +import android.util.Log; +import android.util.SparseArray; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; +import android.support.v7.widget.RecyclerViewAccessibilityDelegate; + +import android.app.Instrumentation; +import android.content.Intent; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; + +/** + * @hide from javadoc + */ +public class BrowseSupportFragmentTest extends + ActivityInstrumentationTestCase2<BrowseSupportFragmentTestActivity> { + + static final long TRANSITION_LENGTH = 1000; + + Instrumentation mInstrumentation; + BrowseSupportFragmentTestActivity mActivity; + + public BrowseSupportFragmentTest() { + super(BrowseSupportFragmentTestActivity.class); + } + + private void initActivity(Intent intent) { + setActivityIntent(intent); + mActivity = getActivity(); + try { + Thread.sleep(intent.getLongExtra(BrowseSupportFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, + BrowseTestSupportFragment.DEFAULT_LOAD_DATA_DELAY) + TRANSITION_LENGTH); + } catch (InterruptedException ex) { + } + } + + public void testTwoBackKeysWithBackStack() throws Throwable { + mInstrumentation = getInstrumentation(); + Intent intent = new Intent(mInstrumentation.getContext(), BrowseSupportFragmentTestActivity.class); + intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, (long) 1000); + intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_ADD_TO_BACKSTACK , true); + initActivity(intent); + + sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); + Thread.sleep(TRANSITION_LENGTH); + + sendKeys(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_BACK); + } + + public void testTwoBackKeysWithoutBackStack() throws Throwable { + mInstrumentation = getInstrumentation(); + Intent intent = new Intent(mInstrumentation.getContext(), BrowseSupportFragmentTestActivity.class); + intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, (long) 1000); + intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_ADD_TO_BACKSTACK , false); + initActivity(intent); + + sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); + Thread.sleep(TRANSITION_LENGTH); + + sendKeys(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_BACK); + } + +} diff --git a/v17/tests/src/android/support/v17/leanback/app/BrowseSupportFragmentTestActivity.java b/v17/tests/src/android/support/v17/leanback/app/BrowseSupportFragmentTestActivity.java new file mode 100644 index 0000000000..036d25486a --- /dev/null +++ b/v17/tests/src/android/support/v17/leanback/app/BrowseSupportFragmentTestActivity.java @@ -0,0 +1,60 @@ +/* This file is auto-generated from BrowseFragmentTestActivity.java. DO NOT MODIFY. */ + +/* + * Copyright (C) 2015 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.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentTransaction; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v17.leanback.tests.R; + +/** + * @hide from javadoc + */ +public class BrowseSupportFragmentTestActivity extends FragmentActivity { + + public static final String EXTRA_ADD_TO_BACKSTACK = "addToBackStack"; + public static final String EXTRA_NUM_ROWS = "numRows"; + public static final String EXTRA_REPEAT_PER_ROW = "repeatPerRow"; + public static final String EXTRA_LOAD_DATA_DELAY = "loadDataDelay"; + public static final String EXTRA_TEST_ENTRANCE_TRANSITION = "testEntranceTransition"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Intent intent = getIntent(); + + BrowseTestSupportFragment.NUM_ROWS = intent.getIntExtra(EXTRA_NUM_ROWS, + BrowseTestSupportFragment.DEFAULT_NUM_ROWS); + BrowseTestSupportFragment.REPEAT_PER_ROW = intent.getIntExtra(EXTRA_REPEAT_PER_ROW, + BrowseTestSupportFragment.DEFAULT_REPEAT_PER_ROW); + BrowseTestSupportFragment.LOAD_DATA_DELAY = intent.getLongExtra(EXTRA_LOAD_DATA_DELAY, + BrowseTestSupportFragment.DEFAULT_LOAD_DATA_DELAY); + BrowseTestSupportFragment.TEST_ENTRANCE_TRANSITION = intent.getBooleanExtra( + EXTRA_TEST_ENTRANCE_TRANSITION, + BrowseTestSupportFragment.DEFAULT_TEST_ENTRANCE_TRANSITION); + setContentView(R.layout.browse); + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + ft.replace(R.id.main_frame, new BrowseTestSupportFragment()); + if (intent.getBooleanExtra(EXTRA_ADD_TO_BACKSTACK, false)) { + ft.addToBackStack(null); + } + ft.commit(); + } +} diff --git a/v17/tests/src/android/support/v17/leanback/app/BrowseTestFragment.java b/v17/tests/src/android/support/v17/leanback/app/BrowseTestFragment.java new file mode 100644 index 0000000000..744a926e87 --- /dev/null +++ b/v17/tests/src/android/support/v17/leanback/app/BrowseTestFragment.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2015 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.os.Bundle; +import android.os.Handler; +import android.support.v17.leanback.widget.ArrayObjectAdapter; +import android.support.v17.leanback.widget.HeaderItem; +import android.support.v17.leanback.widget.ListRow; +import android.support.v17.leanback.widget.ListRowPresenter; +import android.support.v17.leanback.widget.OnItemViewClickedListener; +import android.support.v17.leanback.widget.OnItemViewSelectedListener; +import android.support.v17.leanback.widget.Presenter; +import android.support.v17.leanback.widget.Row; +import android.support.v17.leanback.widget.RowPresenter; +import android.util.Log; +import android.view.View; + +/** + * @hide from javadoc + */ +public class BrowseTestFragment extends BrowseFragment { + private static final String TAG = "BrowseTestFragment"; + + final static int DEFAULT_NUM_ROWS = 100; + final static int DEFAULT_REPEAT_PER_ROW = 20; + final static long DEFAULT_LOAD_DATA_DELAY = 2000; + final static boolean DEFAULT_TEST_ENTRANCE_TRANSITION = true; + + static int NUM_ROWS = DEFAULT_NUM_ROWS; + static int REPEAT_PER_ROW = DEFAULT_REPEAT_PER_ROW; + static long LOAD_DATA_DELAY = DEFAULT_LOAD_DATA_DELAY; + static boolean TEST_ENTRANCE_TRANSITION = DEFAULT_TEST_ENTRANCE_TRANSITION; + + private ArrayObjectAdapter mRowsAdapter; + + // For good performance, it's important to use a single instance of + // a card presenter for all rows using that presenter. + final static StringPresenter sCardPresenter = new StringPresenter(); + + @Override + public void onCreate(Bundle savedInstanceState) { + Log.i(TAG, "onCreate"); + super.onCreate(savedInstanceState); + + setTitle("BrowseTestFragment"); + setHeadersState(HEADERS_ENABLED); + + setOnSearchClickedListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Log.i(TAG, "onSearchClicked"); + } + }); + + setupRows(); + setOnItemViewClickedListener(new ItemViewClickedListener()); + setOnItemViewSelectedListener(new OnItemViewSelectedListener() { + @Override + public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item, + RowPresenter.ViewHolder rowViewHolder, Row row) { + Log.i(TAG, "onItemSelected: " + item + " row " + row); + } + }); + if (TEST_ENTRANCE_TRANSITION) { + // don't run entrance transition if fragment is restored. + if (savedInstanceState == null) { + prepareEntranceTransition(); + } + } + // simulates in a real world use case data being loaded two seconds later + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + loadData(); + startEntranceTransition(); + } + }, LOAD_DATA_DELAY); + } + + private void setupRows() { + ListRowPresenter lrp = new ListRowPresenter(); + + mRowsAdapter = new ArrayObjectAdapter(lrp); + + setAdapter(mRowsAdapter); + } + + private void loadData() { + for (int i = 0; i < NUM_ROWS; ++i) { + ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(sCardPresenter); + for (int j = 0; j < REPEAT_PER_ROW; ++j) { + listRowAdapter.add("Hello world"); + listRowAdapter.add("This is a test"); + listRowAdapter.add("Android TV"); + listRowAdapter.add("Leanback"); + listRowAdapter.add("Hello world"); + listRowAdapter.add("Android TV"); + listRowAdapter.add("Leanback"); + listRowAdapter.add("GuidedStepFragment"); + } + HeaderItem header = new HeaderItem(i, "Row " + i); + mRowsAdapter.add(new ListRow(header, listRowAdapter)); + } + + } + + private final class ItemViewClickedListener implements OnItemViewClickedListener { + @Override + public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, + RowPresenter.ViewHolder rowViewHolder, Row row) { + Log.i(TAG, "onItemClicked: " + item + " row " + row); + } + } +} diff --git a/v17/tests/src/android/support/v17/leanback/app/BrowseTestSupportFragment.java b/v17/tests/src/android/support/v17/leanback/app/BrowseTestSupportFragment.java new file mode 100644 index 0000000000..1a01b7b177 --- /dev/null +++ b/v17/tests/src/android/support/v17/leanback/app/BrowseTestSupportFragment.java @@ -0,0 +1,128 @@ +/* This file is auto-generated from BrowseTestFragment.java. DO NOT MODIFY. */ + +/* + * Copyright (C) 2015 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.os.Bundle; +import android.os.Handler; +import android.support.v17.leanback.widget.ArrayObjectAdapter; +import android.support.v17.leanback.widget.HeaderItem; +import android.support.v17.leanback.widget.ListRow; +import android.support.v17.leanback.widget.ListRowPresenter; +import android.support.v17.leanback.widget.OnItemViewClickedListener; +import android.support.v17.leanback.widget.OnItemViewSelectedListener; +import android.support.v17.leanback.widget.Presenter; +import android.support.v17.leanback.widget.Row; +import android.support.v17.leanback.widget.RowPresenter; +import android.util.Log; +import android.view.View; + +/** + * @hide from javadoc + */ +public class BrowseTestSupportFragment extends BrowseSupportFragment { + private static final String TAG = "BrowseTestSupportFragment"; + + final static int DEFAULT_NUM_ROWS = 100; + final static int DEFAULT_REPEAT_PER_ROW = 20; + final static long DEFAULT_LOAD_DATA_DELAY = 2000; + final static boolean DEFAULT_TEST_ENTRANCE_TRANSITION = true; + + static int NUM_ROWS = DEFAULT_NUM_ROWS; + static int REPEAT_PER_ROW = DEFAULT_REPEAT_PER_ROW; + static long LOAD_DATA_DELAY = DEFAULT_LOAD_DATA_DELAY; + static boolean TEST_ENTRANCE_TRANSITION = DEFAULT_TEST_ENTRANCE_TRANSITION; + + private ArrayObjectAdapter mRowsAdapter; + + // For good performance, it's important to use a single instance of + // a card presenter for all rows using that presenter. + final static StringPresenter sCardPresenter = new StringPresenter(); + + @Override + public void onCreate(Bundle savedInstanceState) { + Log.i(TAG, "onCreate"); + super.onCreate(savedInstanceState); + + setTitle("BrowseTestSupportFragment"); + setHeadersState(HEADERS_ENABLED); + + setOnSearchClickedListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Log.i(TAG, "onSearchClicked"); + } + }); + + setupRows(); + setOnItemViewClickedListener(new ItemViewClickedListener()); + setOnItemViewSelectedListener(new OnItemViewSelectedListener() { + @Override + public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item, + RowPresenter.ViewHolder rowViewHolder, Row row) { + Log.i(TAG, "onItemSelected: " + item + " row " + row); + } + }); + if (TEST_ENTRANCE_TRANSITION) { + // don't run entrance transition if fragment is restored. + if (savedInstanceState == null) { + prepareEntranceTransition(); + } + } + // simulates in a real world use case data being loaded two seconds later + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + loadData(); + startEntranceTransition(); + } + }, LOAD_DATA_DELAY); + } + + private void setupRows() { + ListRowPresenter lrp = new ListRowPresenter(); + + mRowsAdapter = new ArrayObjectAdapter(lrp); + + setAdapter(mRowsAdapter); + } + + private void loadData() { + for (int i = 0; i < NUM_ROWS; ++i) { + ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(sCardPresenter); + for (int j = 0; j < REPEAT_PER_ROW; ++j) { + listRowAdapter.add("Hello world"); + listRowAdapter.add("This is a test"); + listRowAdapter.add("Android TV"); + listRowAdapter.add("Leanback"); + listRowAdapter.add("Hello world"); + listRowAdapter.add("Android TV"); + listRowAdapter.add("Leanback"); + listRowAdapter.add("GuidedStepFragment"); + } + HeaderItem header = new HeaderItem(i, "Row " + i); + mRowsAdapter.add(new ListRow(header, listRowAdapter)); + } + + } + + private final class ItemViewClickedListener implements OnItemViewClickedListener { + @Override + public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, + RowPresenter.ViewHolder rowViewHolder, Row row) { + Log.i(TAG, "onItemClicked: " + item + " row " + row); + } + } +} diff --git a/v17/tests/src/android/support/v17/leanback/app/StringPresenter.java b/v17/tests/src/android/support/v17/leanback/app/StringPresenter.java new file mode 100644 index 0000000000..e6e0793f0b --- /dev/null +++ b/v17/tests/src/android/support/v17/leanback/app/StringPresenter.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015 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.graphics.Color; +import android.support.v17.leanback.widget.Presenter; +import android.util.Log; +import android.view.ViewGroup; +import android.widget.TextView; + +/** + * @hide from javadoc + */ +public class StringPresenter extends Presenter { + private static final boolean DEBUG = false; + private static final String TAG = "StringPresenter"; + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent) { + if (DEBUG) Log.d(TAG, "onCreateViewHolder"); + TextView tv = new TextView(parent.getContext()); + tv.setFocusable(true); + tv.setFocusableInTouchMode(true); + tv.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 200)); + tv.setBackgroundColor(Color.CYAN); + tv.setTextColor(Color.BLACK); + return new ViewHolder(tv); + } + + @Override + public void onBindViewHolder(ViewHolder viewHolder, Object item) { + if (DEBUG) Log.d(TAG, "onBindViewHolder for " + item.toString()); + ((TextView) viewHolder.view).setText(item.toString()); + } + + @Override + public void onUnbindViewHolder(ViewHolder viewHolder) { + if (DEBUG) Log.d(TAG, "onUnbindViewHolder"); + } +} diff --git a/v17/tests/src/android/support/v17/leanback/widget/GridActivity.java b/v17/tests/src/android/support/v17/leanback/widget/GridActivity.java index 145ae4850d..902a665a40 100644 --- a/v17/tests/src/android/support/v17/leanback/widget/GridActivity.java +++ b/v17/tests/src/android/support/v17/leanback/widget/GridActivity.java @@ -360,7 +360,8 @@ public class GridActivity extends Activity { holder.mItemAlignment = null; } if (mChildLayout == -1) { - ((TextView) holder.itemView).setText("Item "+mItemLengths[position]); + ((TextView) holder.itemView).setText("Item "+mItemLengths[position] + + " type=" + getItemViewType(position)); boolean focusable = true; if (mItemFocusables != null) { focusable = mItemFocusables[position]; @@ -370,7 +371,8 @@ public class GridActivity extends Activity { holder.itemView.setBackgroundColor(Color.LTGRAY); } else { if (holder.itemView instanceof TextView) { - ((TextView) holder.itemView).setText("Item "+mItemLengths[position]); + ((TextView) holder.itemView).setText("Item "+mItemLengths[position] + + " type=" + getItemViewType(position)); } } updateSize(holder.itemView, position); @@ -380,6 +382,7 @@ public class GridActivity extends Activity { public int getItemCount() { return mNumItems; } + } void updateSize(View view, int position) { diff --git a/v17/tests/src/android/support/v17/leanback/widget/GridWidgetTest.java b/v17/tests/src/android/support/v17/leanback/widget/GridWidgetTest.java index e9e53e4a33..335f449bd4 100644 --- a/v17/tests/src/android/support/v17/leanback/widget/GridWidgetTest.java +++ b/v17/tests/src/android/support/v17/leanback/widget/GridWidgetTest.java @@ -21,6 +21,7 @@ import android.text.Selection; import android.text.Spannable; import android.util.Log; import android.util.SparseArray; +import android.util.SparseIntArray; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; @@ -105,6 +106,23 @@ public class GridWidgetTest extends ActivityInstrumentationTestCase2<GridActivit Thread.sleep(500); } + protected void waitForScrollIdleAndItemAnimation(Runnable verify) throws Throwable { + waitForScrollIdle(); + waitForItemAnimation(); + verify.run(); + } + + protected void waitForItemAnimation() throws Throwable { + Thread.sleep(100); + while (mGridView.getItemAnimator() != null && mGridView.getItemAnimator().isRunning()) { + try { + Thread.sleep(100); + } catch (InterruptedException ex) { + break; + } + } + } + /** * Wait for grid view stop scroll and optionally verify state of grid view. */ @@ -1161,12 +1179,12 @@ public class GridWidgetTest extends ActivityInstrumentationTestCase2<GridActivit initActivity(intent); mGridView.setSelectedPositionSmooth(0); - waitForScrollIdle(mVerifyLayout); + waitForScrollIdleAndItemAnimation(mVerifyLayout); for (int i = 0; i < pressDown; i++) { sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); } - waitForScrollIdle(mVerifyLayout); + waitForScrollIdleAndItemAnimation(mVerifyLayout); assertFalse(mGridView.isFocused()); } @@ -1570,6 +1588,40 @@ public class GridWidgetTest extends ActivityInstrumentationTestCase2<GridActivit ((VerticalGridViewEx) mGridView).mSmoothScrollByCalled < 10); } + public void testSmoothscrollerCancelled() throws Throwable { + mInstrumentation = getInstrumentation(); + Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class); + intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, + R.layout.vertical_linear); + intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true); + int[] items = new int[100]; + for (int i = 0; i < items.length; i++) { + items[i] = 680; + } + intent.putExtra(GridActivity.EXTRA_ITEMS, items); + intent.putExtra(GridActivity.EXTRA_STAGGERED, false); + mOrientation = BaseGridView.VERTICAL; + mNumRows = 1; + + initActivity(intent); + + mGridView.setSelectedPositionSmooth(0); + waitForScrollIdle(mVerifyLayout); + assertTrue(mGridView.getChildAt(0).hasFocus()); + + int targetPosition = items.length - 1; + mGridView.setSelectedPositionSmooth(targetPosition); + runTestOnUiThread(new Runnable() { + public void run() { + mGridView.stopScroll(); + } + }); + Thread.sleep(100); + assertEquals(mGridView.getSelectedPosition(), targetPosition); + assertSame(mGridView.getLayoutManager().findViewByPosition(targetPosition), + mGridView.findFocus()); + } + public void testSetNumRowsAndAddItem() throws Throwable { mInstrumentation = getInstrumentation(); Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class); @@ -1656,7 +1708,7 @@ public class GridWidgetTest extends ActivityInstrumentationTestCase2<GridActivit mGridView.setSelectedPositionSmooth(0); } }); - waitForScrollIdle(mVerifyLayout); + waitForScrollIdleAndItemAnimation(mVerifyLayout); verifyMargin(); runTestOnUiThread(new Runnable() { @@ -1664,7 +1716,7 @@ public class GridWidgetTest extends ActivityInstrumentationTestCase2<GridActivit mGridView.setSelectedPositionSmooth(1); } }); - waitForScrollIdle(mVerifyLayout); + waitForScrollIdleAndItemAnimation(mVerifyLayout); verifyMargin(); } @@ -1735,7 +1787,7 @@ public class GridWidgetTest extends ActivityInstrumentationTestCase2<GridActivit mGridView.setSelectedPositionSmooth(1); } }); - waitForScrollIdle(mVerifyLayout); + waitForScrollIdleAndItemAnimation(mVerifyLayout); assertEquals(((TextView) mGridView.getChildAt(0)).getSelectionStart(), 1); assertEquals(((TextView) mGridView.getChildAt(0)).getSelectionEnd(), 2); assertEquals(((TextView) mGridView.getChildAt(1)).getSelectionStart(), 1); @@ -1753,7 +1805,7 @@ public class GridWidgetTest extends ActivityInstrumentationTestCase2<GridActivit mGridView.setSelectedPositionSmooth(0); } }); - waitForScrollIdle(mVerifyLayout); + waitForScrollIdleAndItemAnimation(mVerifyLayout); assertEquals(((TextView) mGridView.getChildAt(0)).getSelectionStart(), 1); assertEquals(((TextView) mGridView.getChildAt(0)).getSelectionEnd(), 2); assertEquals(((TextView) mGridView.getChildAt(1)).getSelectionStart(), 1); @@ -1835,6 +1887,20 @@ public class GridWidgetTest extends ActivityInstrumentationTestCase2<GridActivit } } + static class ChangeableViewTypesProvider implements ViewTypeProvider { + static SparseIntArray sViewTypes = new SparseIntArray(); + @Override + public int getViewType(int position) { + return sViewTypes.get(position); + } + public static void clear() { + sViewTypes.clear(); + } + public static void setViewType(int position, int type) { + sViewTypes.put(position, type); + } + } + static class PositionItemAlignmentFacetProviderForRelativeLayout1 implements ItemAlignmentFacetProvider { ItemAlignmentFacet mMultipleFacet; @@ -2050,6 +2116,38 @@ public class GridWidgetTest extends ActivityInstrumentationTestCase2<GridActivit assertEquals(0, mGridView.getSelectedPosition()); } + public void testNotifyItemTypeChangedSelectionEvent() throws Throwable { + mInstrumentation = getInstrumentation(); + Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class); + intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, + R.layout.vertical_linear); + intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 10); + intent.putExtra(GridActivity.EXTRA_VIEWTYPEPROVIDER_CLASS, + ChangeableViewTypesProvider.class.getName()); + ChangeableViewTypesProvider.clear(); + initActivity(intent); + mOrientation = BaseGridView.HORIZONTAL; + mNumRows = 1; + + final ArrayList<Integer> selectedLog = new ArrayList<Integer>(); + mGridView.setOnChildSelectedListener(new OnChildSelectedListener() { + public void onChildSelected(ViewGroup parent, View view, int position, long id) { + selectedLog.add(position); + } + }); + + runTestOnUiThread(new Runnable() { + public void run() { + ChangeableViewTypesProvider.setViewType(0, 1); + mGridView.getAdapter().notifyItemChanged(0, 1); + } + }); + waitForTransientStateGone(null); + assertEquals(0, mGridView.getSelectedPosition()); + assertEquals(selectedLog.size(), 1); + assertEquals((int) selectedLog.get(0), 0); + } + public void testSelectionSmoothAndAddItemInOneCycle() throws Throwable { mInstrumentation = getInstrumentation(); Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class); diff --git a/v17/tests/src/android/support/v17/leanback/widget/ShadowOverlayContainerTest.java b/v17/tests/src/android/support/v17/leanback/widget/ShadowOverlayContainerTest.java new file mode 100644 index 0000000000..e7ec4bf0ce --- /dev/null +++ b/v17/tests/src/android/support/v17/leanback/widget/ShadowOverlayContainerTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2015 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.widget; + +import android.test.AndroidTestCase; +import android.view.View.MeasureSpec; +import android.view.ViewGroup.LayoutParams; +import android.widget.FrameLayout; +import android.widget.TextView; + + +public class ShadowOverlayContainerTest extends AndroidTestCase { + + public void testWrapContent() { + FrameLayout frameLayout = new FrameLayout(getContext()); + TextView textView = new TextView(getContext()); + textView.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT)); + textView.setText("abc"); + ShadowOverlayContainer container = new ShadowOverlayContainer(getContext()); + container.initialize(true, true, true); + container.wrap(textView); + frameLayout.addView(container); + frameLayout.measure(MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY)); + frameLayout.layout(0, 0, 500, 500); + assertTrue(textView.getWidth() > 0); + assertTrue(textView.getWidth() < 500); + assertTrue(textView.getHeight() > 0); + assertTrue(textView.getHeight() < 500); + assertEquals(container.getWidth(), textView.getWidth()); + assertEquals(container.getHeight(), textView.getHeight()); + + // change layout size of textView after wrap() + textView.setLayoutParams(new FrameLayout.LayoutParams(123, 123)); + frameLayout.measure(MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY)); + frameLayout.layout(0, 0, 500, 500); + assertTrue(textView.getWidth() == 123); + assertTrue(textView.getHeight() == 123); + assertEquals(container.getWidth(), textView.getWidth()); + assertEquals(container.getHeight(), textView.getHeight()); + } + + public void testFixedSize() { + FrameLayout frameLayout = new FrameLayout(getContext()); + TextView textView = new TextView(getContext()); + textView.setLayoutParams(new FrameLayout.LayoutParams(200, LayoutParams.WRAP_CONTENT)); + textView.setText("abc"); + ShadowOverlayContainer container = new ShadowOverlayContainer(getContext()); + container.initialize(true, true, true); + container.wrap(textView); + frameLayout.addView(container); + frameLayout.measure(MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY)); + frameLayout.layout(0, 0, 500, 500); + assertTrue(textView.getWidth() == 200); + assertTrue(textView.getHeight() > 0); + assertTrue(textView.getHeight() < 500); + assertEquals(container.getWidth(), textView.getWidth()); + assertEquals(container.getHeight(), textView.getHeight()); + + // change layout size of textView after wrap() + textView.setLayoutParams(new FrameLayout.LayoutParams(123, 123)); + frameLayout.measure(MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY)); + frameLayout.layout(0, 0, 500, 500); + assertTrue(textView.getWidth() == 123); + assertTrue(textView.getHeight() == 123); + assertEquals(container.getWidth(), textView.getWidth()); + assertEquals(container.getHeight(), textView.getHeight()); + } + + public void testMatchParent() { + FrameLayout frameLayout = new FrameLayout(getContext()); + TextView textView = new TextView(getContext()); + textView.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.WRAP_CONTENT)); + textView.setText("abc"); + ShadowOverlayContainer container = new ShadowOverlayContainer(getContext()); + container.initialize(true, true, true); + container.wrap(textView); + frameLayout.addView(container); + frameLayout.measure(MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY)); + frameLayout.layout(0, 0, 500, 500); + assertTrue(textView.getWidth() == 500); + assertTrue(textView.getHeight() > 0); + assertTrue(textView.getHeight() < 500); + assertEquals(container.getWidth(), textView.getWidth()); + assertEquals(container.getHeight(), textView.getHeight()); + } +} |
