diff options
Diffstat (limited to 'v4')
74 files changed, 8739 insertions, 451 deletions
diff --git a/v4/Android.mk b/v4/Android.mk index 2050725c2a..4336e40928 100644 --- a/v4/Android.mk +++ b/v4/Android.mk @@ -22,6 +22,9 @@ LOCAL_SRC_FILES := $(call all-java-files-under, donut) LOCAL_STATIC_JAVA_LIBRARIES := android-support-annotations include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files := $(LOCAL_SRC_FILES) +support_module_java_libraries := android-support-annotations + # ----------------------------------------------------------------------- # A helper sub-library that makes direct use of Eclair APIs. @@ -32,6 +35,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, eclair) LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-donut include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) + # ----------------------------------------------------------------------- # A helper sub-library that makes direct use of Eclair MR1 APIs. @@ -42,6 +47,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, eclair-mr1) LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-eclair include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) + # ----------------------------------------------------------------------- # A helper sub-library that makes direct use of Froyo APIs. @@ -52,6 +59,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, froyo) LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-eclair-mr1 include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) + # ----------------------------------------------------------------------- # A helper sub-library that makes direct use of Gingerbread APIs. @@ -62,6 +71,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, gingerbread) LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-froyo include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) + # ----------------------------------------------------------------------- # A helper sub-library that makes direct use of Honeycomb APIs. @@ -72,6 +83,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, honeycomb) LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-gingerbread include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) + # ----------------------------------------------------------------------- # A helper sub-library that makes direct use of Honeycomb MR1 APIs. @@ -82,6 +95,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, honeycomb_mr1) LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-honeycomb include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) + # ----------------------------------------------------------------------- # A helper sub-library that makes direct use of Honeycomb MR2 APIs. @@ -92,6 +107,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, honeycomb_mr2) LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-honeycomb-mr1 include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) + # ----------------------------------------------------------------------- # A helper sub-library that makes direct use of Ice Cream Sandwich APIs. @@ -102,6 +119,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, ics) LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-honeycomb-mr2 include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) + # ----------------------------------------------------------------------- # A helper sub-library that makes direct use of Ice Cream Sandwich MR1 APIs. @@ -112,6 +131,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, ics-mr1) LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-ics include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) + # ----------------------------------------------------------------------- # A helper sub-library that makes direct use of JellyBean APIs. @@ -122,6 +143,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, jellybean) LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-ics-mr1 include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) + # ----------------------------------------------------------------------- # A helper sub-library that makes direct use of JellyBean MR1 APIs. @@ -132,6 +155,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, jellybean-mr1) LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-jellybean include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) + # ----------------------------------------------------------------------- # A helper sub-library that makes direct use of JellyBean MR2 APIs. @@ -142,6 +167,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, jellybean-mr2) LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-jellybean-mr1 include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) + # ----------------------------------------------------------------------- # A helper sub-library that makes direct use of KitKat APIs. @@ -152,6 +179,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, kitkat) LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-jellybean-mr2 include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) + # ----------------------------------------------------------------------- # A helper sub-library that makes direct use of V20 APIs. @@ -162,16 +191,33 @@ LOCAL_SRC_FILES := $(call all-java-files-under, api20) LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-kitkat include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) + +# ----------------------------------------------------------------------- + +# A helper sub-library that allows to use Lollipop internal APIs. +include $(CLEAR_VARS) +LOCAL_MODULE := android-support-v4-api21-internal +LOCAL_SDK_VERSION := 21 +LOCAL_SRC_FILES := \ + $(call all-java-files-under, api21/android/content/pm) \ + $(call all-java-files-under, api21/android/service/media) +LOCAL_MODULE_TAGS := optional +include $(BUILD_JAVA_LIBRARY) + # ----------------------------------------------------------------------- # A helper sub-library that makes direct use of Lollipop APIs. include $(CLEAR_VARS) LOCAL_MODULE := android-support-v4-api21 LOCAL_SDK_VERSION := 21 -LOCAL_SRC_FILES := $(call all-java-files-under, api21) +LOCAL_SRC_FILES := $(call all-java-files-under, api21/android/support) +LOCAL_JAVA_LIBRARIES := android-support-v4-api21-internal LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-api20 include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) + # ----------------------------------------------------------------------- # A helper sub-library that makes direct use of V22 APIs. @@ -182,6 +228,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, api22) LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-api21 include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) + # ----------------------------------------------------------------------- # A helper sub-library that makes direct use of V23 APIs. @@ -189,9 +237,12 @@ include $(CLEAR_VARS) LOCAL_MODULE := android-support-v4-api23 LOCAL_SDK_VERSION := current LOCAL_SRC_FILES := $(call all-java-files-under, api23) +LOCAL_JAVA_LIBRARIES := android-support-v4-api21-internal LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-api22 include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) + # ----------------------------------------------------------------------- # Here is the final static library that apps can link against. @@ -204,11 +255,12 @@ LOCAL_SRC_FILES := $(call all-java-files-under, java) \ LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4-api23 include $(BUILD_STATIC_JAVA_LIBRARY) +support_module_src_files += $(LOCAL_SRC_FILES) +support_module_aidl_includes := $(LOCAL_AIDL_INCLUDES) + # API Check # --------------------------------------------- support_module := $(LOCAL_MODULE) support_module_api_dir := $(LOCAL_PATH)/api -support_module_src_files := $(LOCAL_SRC_FILES) -support_module_java_libraries := android-support-v4 support_module_java_packages := android.support.v4.* include $(SUPPORT_API_CHECK) diff --git a/v4/api/23.txt b/v4/api/23.0.0.txt index 27d5993e8b..27d5993e8b 100644 --- a/v4/api/23.txt +++ b/v4/api/23.0.0.txt diff --git a/v4/api/23.1.0.txt b/v4/api/23.1.0.txt new file mode 100644 index 0000000000..ccefe22f49 --- /dev/null +++ b/v4/api/23.1.0.txt @@ -0,0 +1,3520 @@ +package android.support.v4.accessibilityservice { + + public class AccessibilityServiceInfoCompat { + method public static java.lang.String capabilityToString(int); + method public static java.lang.String feedbackTypeToString(int); + method public static java.lang.String flagToString(int); + method public static boolean getCanRetrieveWindowContent(android.accessibilityservice.AccessibilityServiceInfo); + method public static int getCapabilities(android.accessibilityservice.AccessibilityServiceInfo); + method public static java.lang.String getDescription(android.accessibilityservice.AccessibilityServiceInfo); + method public static java.lang.String getId(android.accessibilityservice.AccessibilityServiceInfo); + method public static android.content.pm.ResolveInfo getResolveInfo(android.accessibilityservice.AccessibilityServiceInfo); + method public static java.lang.String getSettingsActivityName(android.accessibilityservice.AccessibilityServiceInfo); + field public static final int CAPABILITY_CAN_FILTER_KEY_EVENTS = 8; // 0x8 + field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4 + field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2 + field public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 1; // 0x1 + field public static final int DEFAULT = 1; // 0x1 + field public static final int FEEDBACK_ALL_MASK = -1; // 0xffffffff + field public static final int FEEDBACK_BRAILLE = 32; // 0x20 + field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2 + field public static final int FLAG_REPORT_VIEW_IDS = 16; // 0x10 + field public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8 + field public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 32; // 0x20 + field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4 + } + +} + +package android.support.v4.animation { + + public abstract class AnimatorCompatHelper { + method public static void clearInterpolator(android.view.View); + method public static android.support.v4.animation.ValueAnimatorCompat emptyValueAnimator(); + } + + public abstract interface AnimatorListenerCompat { + method public abstract void onAnimationCancel(android.support.v4.animation.ValueAnimatorCompat); + method public abstract void onAnimationEnd(android.support.v4.animation.ValueAnimatorCompat); + method public abstract void onAnimationRepeat(android.support.v4.animation.ValueAnimatorCompat); + method public abstract void onAnimationStart(android.support.v4.animation.ValueAnimatorCompat); + } + + public abstract interface AnimatorUpdateListenerCompat { + method public abstract void onAnimationUpdate(android.support.v4.animation.ValueAnimatorCompat); + } + + public abstract interface ValueAnimatorCompat { + method public abstract void addListener(android.support.v4.animation.AnimatorListenerCompat); + method public abstract void addUpdateListener(android.support.v4.animation.AnimatorUpdateListenerCompat); + method public abstract void cancel(); + method public abstract float getAnimatedFraction(); + method public abstract void setDuration(long); + method public abstract void setTarget(android.view.View); + method public abstract void start(); + } + +} + +package android.support.v4.app { + + public deprecated class ActionBarDrawerToggle implements android.support.v4.widget.DrawerLayout.DrawerListener { + ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, int, int, int); + ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, boolean, int, int, int); + method public boolean isDrawerIndicatorEnabled(); + method public void onConfigurationChanged(android.content.res.Configuration); + method public void onDrawerClosed(android.view.View); + method public void onDrawerOpened(android.view.View); + method public void onDrawerSlide(android.view.View, float); + method public void onDrawerStateChanged(int); + method public boolean onOptionsItemSelected(android.view.MenuItem); + method public void setDrawerIndicatorEnabled(boolean); + method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable); + method public void setHomeAsUpIndicator(int); + method public void syncState(); + } + + public static abstract interface ActionBarDrawerToggle.Delegate { + method public abstract android.graphics.drawable.Drawable getThemeUpIndicator(); + method public abstract void setActionBarDescription(int); + method public abstract void setActionBarUpIndicator(android.graphics.drawable.Drawable, int); + } + + public static abstract interface ActionBarDrawerToggle.DelegateProvider { + method public abstract android.support.v4.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate(); + } + + public class ActivityCompat extends android.support.v4.content.ContextCompat { + ctor public ActivityCompat(); + method public static void finishAffinity(android.app.Activity); + method public static void finishAfterTransition(android.app.Activity); + method public android.net.Uri getReferrer(android.app.Activity); + method public static boolean invalidateOptionsMenu(android.app.Activity); + method public static void postponeEnterTransition(android.app.Activity); + method public static void requestPermissions(android.app.Activity, java.lang.String[], int); + method public static void setEnterSharedElementCallback(android.app.Activity, android.support.v4.app.SharedElementCallback); + method public static void setExitSharedElementCallback(android.app.Activity, android.support.v4.app.SharedElementCallback); + method public static boolean shouldShowRequestPermissionRationale(android.app.Activity, java.lang.String); + method public static void startActivity(android.app.Activity, android.content.Intent, android.os.Bundle); + method public static void startActivityForResult(android.app.Activity, android.content.Intent, int, android.os.Bundle); + method public static void startPostponedEnterTransition(android.app.Activity); + } + + public static abstract interface ActivityCompat.OnRequestPermissionsResultCallback { + method public abstract void onRequestPermissionsResult(int, java.lang.String[], int[]); + } + + public final class ActivityManagerCompat { + method public static boolean isLowRamDevice(android.app.ActivityManager); + } + + public class ActivityOptionsCompat { + ctor protected ActivityOptionsCompat(); + method public static android.support.v4.app.ActivityOptionsCompat makeCustomAnimation(android.content.Context, int, int); + method public static android.support.v4.app.ActivityOptionsCompat makeScaleUpAnimation(android.view.View, int, int, int, int); + method public static android.support.v4.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, android.view.View, java.lang.String); + method public static android.support.v4.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, android.support.v4.util.Pair<android.view.View, java.lang.String>...); + method public static android.support.v4.app.ActivityOptionsCompat makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int); + method public android.os.Bundle toBundle(); + method public void update(android.support.v4.app.ActivityOptionsCompat); + } + + public class AppOpsManagerCompat { + ctor public AppOpsManagerCompat(); + method public static int noteOp(android.content.Context, java.lang.String, int, java.lang.String); + method public static int noteProxyOp(android.content.Context, java.lang.String, java.lang.String); + method public static java.lang.String permissionToOp(java.lang.String); + field public static final int MODE_ALLOWED = 0; // 0x0 + field public static final int MODE_DEFAULT = 3; // 0x3 + field public static final int MODE_IGNORED = 1; // 0x1 + } + + abstract class BaseFragmentActivityDonut extends android.app.Activity { + } + + abstract class BaseFragmentActivityHoneycomb extends android.support.v4.app.BaseFragmentActivityDonut { + } + + public class BundleCompat { + ctor public BundleCompat(); + method public static android.os.IBinder getBinder(android.os.Bundle, java.lang.String); + method public static void putBinder(android.os.Bundle, java.lang.String, android.os.IBinder); + } + + public class DialogFragment extends android.support.v4.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener { + ctor public DialogFragment(); + method public void dismiss(); + method public void dismissAllowingStateLoss(); + method public android.app.Dialog getDialog(); + method public boolean getShowsDialog(); + method public int getTheme(); + method public boolean isCancelable(); + method public void onCancel(android.content.DialogInterface); + method public android.app.Dialog onCreateDialog(android.os.Bundle); + method public void onDismiss(android.content.DialogInterface); + method public void setCancelable(boolean); + method public void setShowsDialog(boolean); + method public void setStyle(int, int); + method public void show(android.support.v4.app.FragmentManager, java.lang.String); + method public int show(android.support.v4.app.FragmentTransaction, java.lang.String); + field public static final int STYLE_NORMAL = 0; // 0x0 + field public static final int STYLE_NO_FRAME = 2; // 0x2 + field public static final int STYLE_NO_INPUT = 3; // 0x3 + field public static final int STYLE_NO_TITLE = 1; // 0x1 + } + + public class Fragment implements android.content.ComponentCallbacks android.view.View.OnCreateContextMenuListener { + ctor public Fragment(); + method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]); + method public final boolean equals(java.lang.Object); + method public final android.support.v4.app.FragmentActivity getActivity(); + method public boolean getAllowEnterTransitionOverlap(); + method public boolean getAllowReturnTransitionOverlap(); + method public final android.os.Bundle getArguments(); + method public final android.support.v4.app.FragmentManager getChildFragmentManager(); + method public android.content.Context getContext(); + method public java.lang.Object getEnterTransition(); + method public java.lang.Object getExitTransition(); + method public final android.support.v4.app.FragmentManager getFragmentManager(); + method public final java.lang.Object getHost(); + method public final int getId(); + method public android.support.v4.app.LoaderManager getLoaderManager(); + method public final android.support.v4.app.Fragment getParentFragment(); + method public java.lang.Object getReenterTransition(); + method public final android.content.res.Resources getResources(); + method public final boolean getRetainInstance(); + method public java.lang.Object getReturnTransition(); + method public java.lang.Object getSharedElementEnterTransition(); + method public java.lang.Object getSharedElementReturnTransition(); + method public final java.lang.String getString(int); + method public final java.lang.String getString(int, java.lang.Object...); + method public final java.lang.String getTag(); + method public final android.support.v4.app.Fragment getTargetFragment(); + method public final int getTargetRequestCode(); + method public final java.lang.CharSequence getText(int); + method public boolean getUserVisibleHint(); + method public android.view.View getView(); + method public final int hashCode(); + method public static android.support.v4.app.Fragment instantiate(android.content.Context, java.lang.String); + method public static android.support.v4.app.Fragment instantiate(android.content.Context, java.lang.String, android.os.Bundle); + method public final boolean isAdded(); + method public final boolean isDetached(); + method public final boolean isHidden(); + method public final boolean isInLayout(); + method public final boolean isRemoving(); + method public final boolean isResumed(); + method public final boolean isVisible(); + method public void onActivityCreated(android.os.Bundle); + method public void onActivityResult(int, int, android.content.Intent); + method public void onAttach(android.content.Context); + method public deprecated void onAttach(android.app.Activity); + method public void onConfigurationChanged(android.content.res.Configuration); + method public boolean onContextItemSelected(android.view.MenuItem); + method public void onCreate(android.os.Bundle); + method public android.view.animation.Animation onCreateAnimation(int, boolean, int); + method public void onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo); + method public void onCreateOptionsMenu(android.view.Menu, android.view.MenuInflater); + method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle); + method public void onDestroy(); + method public void onDestroyOptionsMenu(); + method public void onDestroyView(); + method public void onDetach(); + method public void onHiddenChanged(boolean); + method public void onInflate(android.content.Context, android.util.AttributeSet, android.os.Bundle); + method public deprecated void onInflate(android.app.Activity, android.util.AttributeSet, android.os.Bundle); + method public void onLowMemory(); + method public boolean onOptionsItemSelected(android.view.MenuItem); + method public void onOptionsMenuClosed(android.view.Menu); + method public void onPause(); + method public void onPrepareOptionsMenu(android.view.Menu); + method public void onRequestPermissionsResult(int, java.lang.String[], int[]); + method public void onResume(); + method public void onSaveInstanceState(android.os.Bundle); + method public void onStart(); + method public void onStop(); + method public void onViewCreated(android.view.View, android.os.Bundle); + method public void onViewStateRestored(android.os.Bundle); + method public void registerForContextMenu(android.view.View); + method public final void requestPermissions(java.lang.String[], int); + method public void setAllowEnterTransitionOverlap(boolean); + method public void setAllowReturnTransitionOverlap(boolean); + method public void setArguments(android.os.Bundle); + method public void setEnterSharedElementCallback(android.support.v4.app.SharedElementCallback); + method public void setEnterTransition(java.lang.Object); + method public void setExitSharedElementCallback(android.support.v4.app.SharedElementCallback); + method public void setExitTransition(java.lang.Object); + method public void setHasOptionsMenu(boolean); + method public void setInitialSavedState(android.support.v4.app.Fragment.SavedState); + method public void setMenuVisibility(boolean); + method public void setReenterTransition(java.lang.Object); + method public void setRetainInstance(boolean); + method public void setReturnTransition(java.lang.Object); + method public void setSharedElementEnterTransition(java.lang.Object); + method public void setSharedElementReturnTransition(java.lang.Object); + method public void setTargetFragment(android.support.v4.app.Fragment, int); + method public void setUserVisibleHint(boolean); + method public boolean shouldShowRequestPermissionRationale(java.lang.String); + method public void startActivity(android.content.Intent); + method public void startActivityForResult(android.content.Intent, int); + method public void unregisterForContextMenu(android.view.View); + } + + public static class Fragment.InstantiationException extends java.lang.RuntimeException { + ctor public Fragment.InstantiationException(java.lang.String, java.lang.Exception); + } + + public static class Fragment.SavedState implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.support.v4.app.Fragment.SavedState> CREATOR; + } + + public class FragmentActivity extends android.support.v4.app.BaseFragmentActivityHoneycomb implements android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback { + ctor public FragmentActivity(); + method public java.lang.Object getLastCustomNonConfigurationInstance(); + method public android.support.v4.app.FragmentManager getSupportFragmentManager(); + method public android.support.v4.app.LoaderManager getSupportLoaderManager(); + method public final android.support.v4.media.session.MediaControllerCompat getSupportMediaController(); + method public void onAttachFragment(android.support.v4.app.Fragment); + method protected void onResumeFragments(); + method public java.lang.Object onRetainCustomNonConfigurationInstance(); + method public final java.lang.Object onRetainNonConfigurationInstance(); + method public void setEnterSharedElementCallback(android.support.v4.app.SharedElementCallback); + method public void setExitSharedElementCallback(android.support.v4.app.SharedElementCallback); + method public final void setSupportMediaController(android.support.v4.media.session.MediaControllerCompat); + method public void startActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int); + method public void supportFinishAfterTransition(); + method public void supportInvalidateOptionsMenu(); + method public void supportPostponeEnterTransition(); + method public void supportStartPostponedEnterTransition(); + method public final void validateRequestPermissionsRequestCode(int); + } + + public abstract class FragmentContainer { + ctor public FragmentContainer(); + method public abstract android.view.View onFindViewById(int); + method public abstract boolean onHasView(); + } + + public class FragmentController { + method public void attachHost(android.support.v4.app.Fragment); + method public static final android.support.v4.app.FragmentController createController(android.support.v4.app.FragmentHostCallback<?>); + method public void dispatchActivityCreated(); + method public void dispatchConfigurationChanged(android.content.res.Configuration); + method public boolean dispatchContextItemSelected(android.view.MenuItem); + method public void dispatchCreate(); + method public boolean dispatchCreateOptionsMenu(android.view.Menu, android.view.MenuInflater); + method public void dispatchDestroy(); + method public void dispatchDestroyView(); + method public void dispatchLowMemory(); + method public boolean dispatchOptionsItemSelected(android.view.MenuItem); + method public void dispatchOptionsMenuClosed(android.view.Menu); + method public void dispatchPause(); + method public boolean dispatchPrepareOptionsMenu(android.view.Menu); + method public void dispatchReallyStop(); + method public void dispatchResume(); + method public void dispatchStart(); + method public void dispatchStop(); + method public void doLoaderDestroy(); + method public void doLoaderRetain(); + method public void doLoaderStart(); + method public void doLoaderStop(boolean); + method public void dumpLoaders(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]); + method public boolean execPendingActions(); + method public java.util.List<android.support.v4.app.Fragment> getActiveFragments(java.util.List<android.support.v4.app.Fragment>); + method public int getActiveFragmentsCount(); + method public android.support.v4.app.FragmentManager getSupportFragmentManager(); + method public android.support.v4.app.LoaderManager getSupportLoaderManager(); + method public void noteStateNotSaved(); + method public android.view.View onCreateView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet); + method public void reportLoaderStart(); + method public void restoreAllState(android.os.Parcelable, java.util.List<android.support.v4.app.Fragment>); + method public void restoreLoaderNonConfig(android.support.v4.util.SimpleArrayMap<java.lang.String, android.support.v4.app.LoaderManager>); + method public android.support.v4.util.SimpleArrayMap<java.lang.String, android.support.v4.app.LoaderManager> retainLoaderNonConfig(); + method public java.util.List<android.support.v4.app.Fragment> retainNonConfig(); + method public android.os.Parcelable saveAllState(); + } + + public abstract class FragmentHostCallback extends android.support.v4.app.FragmentContainer { + ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int); + method public void onDump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]); + method public android.view.View onFindViewById(int); + method public abstract E onGetHost(); + method public android.view.LayoutInflater onGetLayoutInflater(); + method public int onGetWindowAnimations(); + method public boolean onHasView(); + method public boolean onHasWindowAnimations(); + method public void onRequestPermissionsFromFragment(android.support.v4.app.Fragment, java.lang.String[], int); + method public boolean onShouldSaveFragmentState(android.support.v4.app.Fragment); + method public boolean onShouldShowRequestPermissionRationale(java.lang.String); + method public void onStartActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int); + method public void onSupportInvalidateOptionsMenu(); + } + + public abstract class FragmentManager { + ctor public FragmentManager(); + method public abstract void addOnBackStackChangedListener(android.support.v4.app.FragmentManager.OnBackStackChangedListener); + method public abstract android.support.v4.app.FragmentTransaction beginTransaction(); + method public abstract void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]); + method public static void enableDebugLogging(boolean); + method public abstract boolean executePendingTransactions(); + method public abstract android.support.v4.app.Fragment findFragmentById(int); + method public abstract android.support.v4.app.Fragment findFragmentByTag(java.lang.String); + method public abstract android.support.v4.app.FragmentManager.BackStackEntry getBackStackEntryAt(int); + method public abstract int getBackStackEntryCount(); + method public abstract android.support.v4.app.Fragment getFragment(android.os.Bundle, java.lang.String); + method public abstract boolean isDestroyed(); + method public abstract void popBackStack(); + method public abstract void popBackStack(java.lang.String, int); + method public abstract void popBackStack(int, int); + method public abstract boolean popBackStackImmediate(); + method public abstract boolean popBackStackImmediate(java.lang.String, int); + method public abstract boolean popBackStackImmediate(int, int); + method public abstract void putFragment(android.os.Bundle, java.lang.String, android.support.v4.app.Fragment); + method public abstract void removeOnBackStackChangedListener(android.support.v4.app.FragmentManager.OnBackStackChangedListener); + method public abstract android.support.v4.app.Fragment.SavedState saveFragmentInstanceState(android.support.v4.app.Fragment); + field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1 + } + + public static abstract interface FragmentManager.BackStackEntry { + method public abstract java.lang.CharSequence getBreadCrumbShortTitle(); + method public abstract int getBreadCrumbShortTitleRes(); + method public abstract java.lang.CharSequence getBreadCrumbTitle(); + method public abstract int getBreadCrumbTitleRes(); + method public abstract int getId(); + method public abstract java.lang.String getName(); + } + + public static abstract interface FragmentManager.OnBackStackChangedListener { + method public abstract void onBackStackChanged(); + } + + public abstract class FragmentPagerAdapter extends android.support.v4.view.PagerAdapter { + ctor public FragmentPagerAdapter(android.support.v4.app.FragmentManager); + method public abstract android.support.v4.app.Fragment getItem(int); + method public long getItemId(int); + method public boolean isViewFromObject(android.view.View, java.lang.Object); + } + + public abstract class FragmentStatePagerAdapter extends android.support.v4.view.PagerAdapter { + ctor public FragmentStatePagerAdapter(android.support.v4.app.FragmentManager); + method public abstract android.support.v4.app.Fragment getItem(int); + method public boolean isViewFromObject(android.view.View, java.lang.Object); + } + + public class FragmentTabHost extends android.widget.TabHost implements android.widget.TabHost.OnTabChangeListener { + ctor public FragmentTabHost(android.content.Context); + ctor public FragmentTabHost(android.content.Context, android.util.AttributeSet); + method public void addTab(android.widget.TabHost.TabSpec, java.lang.Class<?>, android.os.Bundle); + method public void onTabChanged(java.lang.String); + method public void setup(android.content.Context, android.support.v4.app.FragmentManager); + method public void setup(android.content.Context, android.support.v4.app.FragmentManager, int); + } + + public abstract class FragmentTransaction { + ctor public FragmentTransaction(); + method public abstract android.support.v4.app.FragmentTransaction add(android.support.v4.app.Fragment, java.lang.String); + method public abstract android.support.v4.app.FragmentTransaction add(int, android.support.v4.app.Fragment); + method public abstract android.support.v4.app.FragmentTransaction add(int, android.support.v4.app.Fragment, java.lang.String); + method public abstract android.support.v4.app.FragmentTransaction addSharedElement(android.view.View, java.lang.String); + method public abstract android.support.v4.app.FragmentTransaction addToBackStack(java.lang.String); + method public abstract android.support.v4.app.FragmentTransaction attach(android.support.v4.app.Fragment); + method public abstract int commit(); + method public abstract int commitAllowingStateLoss(); + method public abstract android.support.v4.app.FragmentTransaction detach(android.support.v4.app.Fragment); + method public abstract android.support.v4.app.FragmentTransaction disallowAddToBackStack(); + method public abstract android.support.v4.app.FragmentTransaction hide(android.support.v4.app.Fragment); + method public abstract boolean isAddToBackStackAllowed(); + method public abstract boolean isEmpty(); + method public abstract android.support.v4.app.FragmentTransaction remove(android.support.v4.app.Fragment); + method public abstract android.support.v4.app.FragmentTransaction replace(int, android.support.v4.app.Fragment); + method public abstract android.support.v4.app.FragmentTransaction replace(int, android.support.v4.app.Fragment, java.lang.String); + method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbShortTitle(int); + method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbShortTitle(java.lang.CharSequence); + method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbTitle(int); + method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbTitle(java.lang.CharSequence); + method public abstract android.support.v4.app.FragmentTransaction setCustomAnimations(int, int); + method public abstract android.support.v4.app.FragmentTransaction setCustomAnimations(int, int, int, int); + method public abstract android.support.v4.app.FragmentTransaction setTransition(int); + method public abstract android.support.v4.app.FragmentTransaction setTransitionStyle(int); + method public abstract android.support.v4.app.FragmentTransaction show(android.support.v4.app.Fragment); + field public static final int TRANSIT_ENTER_MASK = 4096; // 0x1000 + field public static final int TRANSIT_EXIT_MASK = 8192; // 0x2000 + field public static final int TRANSIT_FRAGMENT_CLOSE = 8194; // 0x2002 + field public static final int TRANSIT_FRAGMENT_FADE = 4099; // 0x1003 + field public static final int TRANSIT_FRAGMENT_OPEN = 4097; // 0x1001 + field public static final int TRANSIT_NONE = 0; // 0x0 + field public static final int TRANSIT_UNSET = -1; // 0xffffffff + } + + public class ListFragment extends android.support.v4.app.Fragment { + ctor public ListFragment(); + method public android.widget.ListAdapter getListAdapter(); + method public android.widget.ListView getListView(); + method public long getSelectedItemId(); + method public int getSelectedItemPosition(); + method public void onListItemClick(android.widget.ListView, android.view.View, int, long); + method public void setEmptyText(java.lang.CharSequence); + method public void setListAdapter(android.widget.ListAdapter); + method public void setListShown(boolean); + method public void setListShownNoAnimation(boolean); + method public void setSelection(int); + } + + public abstract class LoaderManager { + ctor public LoaderManager(); + method public abstract void destroyLoader(int); + method public abstract void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]); + method public static void enableDebugLogging(boolean); + method public abstract android.support.v4.content.Loader<D> getLoader(int); + method public boolean hasRunningLoaders(); + method public abstract android.support.v4.content.Loader<D> initLoader(int, android.os.Bundle, android.support.v4.app.LoaderManager.LoaderCallbacks<D>); + method public abstract android.support.v4.content.Loader<D> restartLoader(int, android.os.Bundle, android.support.v4.app.LoaderManager.LoaderCallbacks<D>); + } + + public static abstract interface LoaderManager.LoaderCallbacks { + method public abstract android.support.v4.content.Loader<D> onCreateLoader(int, android.os.Bundle); + method public abstract void onLoadFinished(android.support.v4.content.Loader<D>, D); + method public abstract void onLoaderReset(android.support.v4.content.Loader<D>); + } + + public class NavUtils { + method public static android.content.Intent getParentActivityIntent(android.app.Activity); + method public static android.content.Intent getParentActivityIntent(android.content.Context, java.lang.Class<?>) throws android.content.pm.PackageManager.NameNotFoundException; + method public static android.content.Intent getParentActivityIntent(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException; + method public static java.lang.String getParentActivityName(android.app.Activity); + method public static java.lang.String getParentActivityName(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException; + method public static void navigateUpFromSameTask(android.app.Activity); + method public static void navigateUpTo(android.app.Activity, android.content.Intent); + method public static boolean shouldUpRecreateTask(android.app.Activity, android.content.Intent); + field public static final java.lang.String PARENT_ACTIVITY = "android.support.PARENT_ACTIVITY"; + } + + public class NotificationCompat { + ctor public NotificationCompat(); + method public static android.support.v4.app.NotificationCompat.Action getAction(android.app.Notification, int); + method public static int getActionCount(android.app.Notification); + method public static java.lang.String getCategory(android.app.Notification); + method public static android.os.Bundle getExtras(android.app.Notification); + method public static java.lang.String getGroup(android.app.Notification); + method public static boolean getLocalOnly(android.app.Notification); + method public static java.lang.String getSortKey(android.app.Notification); + method public static boolean isGroupSummary(android.app.Notification); + field public static final java.lang.String CATEGORY_ALARM = "alarm"; + field public static final java.lang.String CATEGORY_CALL = "call"; + field public static final java.lang.String CATEGORY_EMAIL = "email"; + field public static final java.lang.String CATEGORY_ERROR = "err"; + field public static final java.lang.String CATEGORY_EVENT = "event"; + field public static final java.lang.String CATEGORY_MESSAGE = "msg"; + field public static final java.lang.String CATEGORY_PROGRESS = "progress"; + field public static final java.lang.String CATEGORY_PROMO = "promo"; + field public static final java.lang.String CATEGORY_RECOMMENDATION = "recommendation"; + field public static final java.lang.String CATEGORY_SERVICE = "service"; + field public static final java.lang.String CATEGORY_SOCIAL = "social"; + field public static final java.lang.String CATEGORY_STATUS = "status"; + field public static final java.lang.String CATEGORY_SYSTEM = "sys"; + field public static final java.lang.String CATEGORY_TRANSPORT = "transport"; + field public static final int COLOR_DEFAULT = 0; // 0x0 + field public static final int DEFAULT_ALL = -1; // 0xffffffff + field public static final int DEFAULT_LIGHTS = 4; // 0x4 + field public static final int DEFAULT_SOUND = 1; // 0x1 + field public static final int DEFAULT_VIBRATE = 2; // 0x2 + field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri"; + field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText"; + field public static final java.lang.String EXTRA_COMPACT_ACTIONS = "android.compactActions"; + field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText"; + field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon"; + field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big"; + field public static final java.lang.String EXTRA_MEDIA_SESSION = "android.mediaSession"; + field public static final java.lang.String EXTRA_PEOPLE = "android.people"; + field public static final java.lang.String EXTRA_PICTURE = "android.picture"; + field public static final java.lang.String EXTRA_PROGRESS = "android.progress"; + field public static final java.lang.String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate"; + field public static final java.lang.String EXTRA_PROGRESS_MAX = "android.progressMax"; + field public static final java.lang.String EXTRA_SHOW_CHRONOMETER = "android.showChronometer"; + field public static final java.lang.String EXTRA_SHOW_WHEN = "android.showWhen"; + field public static final java.lang.String EXTRA_SMALL_ICON = "android.icon"; + field public static final java.lang.String EXTRA_SUB_TEXT = "android.subText"; + field public static final java.lang.String EXTRA_SUMMARY_TEXT = "android.summaryText"; + field public static final java.lang.String EXTRA_TEMPLATE = "android.template"; + field public static final java.lang.String EXTRA_TEXT = "android.text"; + field public static final java.lang.String EXTRA_TEXT_LINES = "android.textLines"; + field public static final java.lang.String EXTRA_TITLE = "android.title"; + field public static final java.lang.String EXTRA_TITLE_BIG = "android.title.big"; + field public static final int FLAG_AUTO_CANCEL = 16; // 0x10 + field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40 + field public static final int FLAG_GROUP_SUMMARY = 512; // 0x200 + field public static final deprecated int FLAG_HIGH_PRIORITY = 128; // 0x80 + field public static final int FLAG_INSISTENT = 4; // 0x4 + field public static final int FLAG_LOCAL_ONLY = 256; // 0x100 + field public static final int FLAG_NO_CLEAR = 32; // 0x20 + field public static final int FLAG_ONGOING_EVENT = 2; // 0x2 + field public static final int FLAG_ONLY_ALERT_ONCE = 8; // 0x8 + field public static final int FLAG_SHOW_LIGHTS = 1; // 0x1 + field public static final int PRIORITY_DEFAULT = 0; // 0x0 + field public static final int PRIORITY_HIGH = 1; // 0x1 + field public static final int PRIORITY_LOW = -1; // 0xffffffff + field public static final int PRIORITY_MAX = 2; // 0x2 + field public static final int PRIORITY_MIN = -2; // 0xfffffffe + field public static final int STREAM_DEFAULT = -1; // 0xffffffff + field public static final int VISIBILITY_PRIVATE = 0; // 0x0 + field public static final int VISIBILITY_PUBLIC = 1; // 0x1 + field public static final int VISIBILITY_SECRET = -1; // 0xffffffff + } + + public static class NotificationCompat.Action extends android.support.v4.app.NotificationCompatBase.Action { + ctor public NotificationCompat.Action(int, java.lang.CharSequence, android.app.PendingIntent); + method public android.app.PendingIntent getActionIntent(); + method public android.os.Bundle getExtras(); + method public int getIcon(); + method public android.support.v4.app.RemoteInput[] getRemoteInputs(); + method public java.lang.CharSequence getTitle(); + field public android.app.PendingIntent actionIntent; + field public int icon; + field public java.lang.CharSequence title; + } + + public static final class NotificationCompat.Action.Builder { + ctor public NotificationCompat.Action.Builder(int, java.lang.CharSequence, android.app.PendingIntent); + ctor public NotificationCompat.Action.Builder(android.support.v4.app.NotificationCompat.Action); + method public android.support.v4.app.NotificationCompat.Action.Builder addExtras(android.os.Bundle); + method public android.support.v4.app.NotificationCompat.Action.Builder addRemoteInput(android.support.v4.app.RemoteInput); + method public android.support.v4.app.NotificationCompat.Action build(); + method public android.support.v4.app.NotificationCompat.Action.Builder extend(android.support.v4.app.NotificationCompat.Action.Extender); + method public android.os.Bundle getExtras(); + } + + public static abstract interface NotificationCompat.Action.Extender { + method public abstract android.support.v4.app.NotificationCompat.Action.Builder extend(android.support.v4.app.NotificationCompat.Action.Builder); + } + + public static final class NotificationCompat.Action.WearableExtender implements android.support.v4.app.NotificationCompat.Action.Extender { + ctor public NotificationCompat.Action.WearableExtender(); + ctor public NotificationCompat.Action.WearableExtender(android.support.v4.app.NotificationCompat.Action); + method public android.support.v4.app.NotificationCompat.Action.WearableExtender clone(); + method public android.support.v4.app.NotificationCompat.Action.Builder extend(android.support.v4.app.NotificationCompat.Action.Builder); + method public java.lang.CharSequence getCancelLabel(); + method public java.lang.CharSequence getConfirmLabel(); + method public java.lang.CharSequence getInProgressLabel(); + method public boolean isAvailableOffline(); + method public android.support.v4.app.NotificationCompat.Action.WearableExtender setAvailableOffline(boolean); + method public android.support.v4.app.NotificationCompat.Action.WearableExtender setCancelLabel(java.lang.CharSequence); + method public android.support.v4.app.NotificationCompat.Action.WearableExtender setConfirmLabel(java.lang.CharSequence); + method public android.support.v4.app.NotificationCompat.Action.WearableExtender setInProgressLabel(java.lang.CharSequence); + } + + public static class NotificationCompat.BigPictureStyle extends android.support.v4.app.NotificationCompat.Style { + ctor public NotificationCompat.BigPictureStyle(); + ctor public NotificationCompat.BigPictureStyle(android.support.v4.app.NotificationCompat.Builder); + method public android.support.v4.app.NotificationCompat.BigPictureStyle bigLargeIcon(android.graphics.Bitmap); + method public android.support.v4.app.NotificationCompat.BigPictureStyle bigPicture(android.graphics.Bitmap); + method public android.support.v4.app.NotificationCompat.BigPictureStyle setBigContentTitle(java.lang.CharSequence); + method public android.support.v4.app.NotificationCompat.BigPictureStyle setSummaryText(java.lang.CharSequence); + } + + public static class NotificationCompat.BigTextStyle extends android.support.v4.app.NotificationCompat.Style { + ctor public NotificationCompat.BigTextStyle(); + ctor public NotificationCompat.BigTextStyle(android.support.v4.app.NotificationCompat.Builder); + method public android.support.v4.app.NotificationCompat.BigTextStyle bigText(java.lang.CharSequence); + method public android.support.v4.app.NotificationCompat.BigTextStyle setBigContentTitle(java.lang.CharSequence); + method public android.support.v4.app.NotificationCompat.BigTextStyle setSummaryText(java.lang.CharSequence); + } + + public static class NotificationCompat.Builder { + ctor public NotificationCompat.Builder(android.content.Context); + method public android.support.v4.app.NotificationCompat.Builder addAction(int, java.lang.CharSequence, android.app.PendingIntent); + method public android.support.v4.app.NotificationCompat.Builder addAction(android.support.v4.app.NotificationCompat.Action); + method public android.support.v4.app.NotificationCompat.Builder addExtras(android.os.Bundle); + method public android.support.v4.app.NotificationCompat.Builder addPerson(java.lang.String); + method public android.app.Notification build(); + method public android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Extender); + method public android.os.Bundle getExtras(); + method public deprecated android.app.Notification getNotification(); + method protected static java.lang.CharSequence limitCharSequenceLength(java.lang.CharSequence); + method public android.support.v4.app.NotificationCompat.Builder setAutoCancel(boolean); + method public android.support.v4.app.NotificationCompat.Builder setCategory(java.lang.String); + method public android.support.v4.app.NotificationCompat.Builder setColor(int); + method public android.support.v4.app.NotificationCompat.Builder setContent(android.widget.RemoteViews); + method public android.support.v4.app.NotificationCompat.Builder setContentInfo(java.lang.CharSequence); + method public android.support.v4.app.NotificationCompat.Builder setContentIntent(android.app.PendingIntent); + method public android.support.v4.app.NotificationCompat.Builder setContentText(java.lang.CharSequence); + method public android.support.v4.app.NotificationCompat.Builder setContentTitle(java.lang.CharSequence); + method public android.support.v4.app.NotificationCompat.Builder setDefaults(int); + method public android.support.v4.app.NotificationCompat.Builder setDeleteIntent(android.app.PendingIntent); + method public android.support.v4.app.NotificationCompat.Builder setExtras(android.os.Bundle); + method public android.support.v4.app.NotificationCompat.Builder setFullScreenIntent(android.app.PendingIntent, boolean); + method public android.support.v4.app.NotificationCompat.Builder setGroup(java.lang.String); + method public android.support.v4.app.NotificationCompat.Builder setGroupSummary(boolean); + method public android.support.v4.app.NotificationCompat.Builder setLargeIcon(android.graphics.Bitmap); + method public android.support.v4.app.NotificationCompat.Builder setLights(int, int, int); + method public android.support.v4.app.NotificationCompat.Builder setLocalOnly(boolean); + method public android.support.v4.app.NotificationCompat.Builder setNumber(int); + method public android.support.v4.app.NotificationCompat.Builder setOngoing(boolean); + method public android.support.v4.app.NotificationCompat.Builder setOnlyAlertOnce(boolean); + method public android.support.v4.app.NotificationCompat.Builder setPriority(int); + method public android.support.v4.app.NotificationCompat.Builder setProgress(int, int, boolean); + method public android.support.v4.app.NotificationCompat.Builder setPublicVersion(android.app.Notification); + method public android.support.v4.app.NotificationCompat.Builder setShowWhen(boolean); + method public android.support.v4.app.NotificationCompat.Builder setSmallIcon(int); + method public android.support.v4.app.NotificationCompat.Builder setSmallIcon(int, int); + method public android.support.v4.app.NotificationCompat.Builder setSortKey(java.lang.String); + method public android.support.v4.app.NotificationCompat.Builder setSound(android.net.Uri); + method public android.support.v4.app.NotificationCompat.Builder setSound(android.net.Uri, int); + method public android.support.v4.app.NotificationCompat.Builder setStyle(android.support.v4.app.NotificationCompat.Style); + method public android.support.v4.app.NotificationCompat.Builder setSubText(java.lang.CharSequence); + method public android.support.v4.app.NotificationCompat.Builder setTicker(java.lang.CharSequence); + method public android.support.v4.app.NotificationCompat.Builder setTicker(java.lang.CharSequence, android.widget.RemoteViews); + method public android.support.v4.app.NotificationCompat.Builder setUsesChronometer(boolean); + method public android.support.v4.app.NotificationCompat.Builder setVibrate(long[]); + method public android.support.v4.app.NotificationCompat.Builder setVisibility(int); + method public android.support.v4.app.NotificationCompat.Builder setWhen(long); + field public java.util.ArrayList<java.lang.String> mPeople; + } + + public static final class NotificationCompat.CarExtender implements android.support.v4.app.NotificationCompat.Extender { + ctor public NotificationCompat.CarExtender(); + ctor public NotificationCompat.CarExtender(android.app.Notification); + method public android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Builder); + method public int getColor(); + method public android.graphics.Bitmap getLargeIcon(); + method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation getUnreadConversation(); + method public android.support.v4.app.NotificationCompat.CarExtender setColor(int); + method public android.support.v4.app.NotificationCompat.CarExtender setLargeIcon(android.graphics.Bitmap); + method public android.support.v4.app.NotificationCompat.CarExtender setUnreadConversation(android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation); + } + + public static class NotificationCompat.CarExtender.UnreadConversation extends android.support.v4.app.NotificationCompatBase.UnreadConversation { + method public long getLatestTimestamp(); + method public java.lang.String[] getMessages(); + method public java.lang.String getParticipant(); + method public java.lang.String[] getParticipants(); + method public android.app.PendingIntent getReadPendingIntent(); + method public android.support.v4.app.RemoteInput getRemoteInput(); + method public android.app.PendingIntent getReplyPendingIntent(); + } + + public static class NotificationCompat.CarExtender.UnreadConversation.Builder { + ctor public NotificationCompat.CarExtender.UnreadConversation.Builder(java.lang.String); + method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder addMessage(java.lang.String); + method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation build(); + method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder setLatestTimestamp(long); + method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReadPendingIntent(android.app.PendingIntent); + method public android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReplyAction(android.app.PendingIntent, android.support.v4.app.RemoteInput); + } + + public static abstract interface NotificationCompat.Extender { + method public abstract android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Builder); + } + + public static class NotificationCompat.InboxStyle extends android.support.v4.app.NotificationCompat.Style { + ctor public NotificationCompat.InboxStyle(); + ctor public NotificationCompat.InboxStyle(android.support.v4.app.NotificationCompat.Builder); + method public android.support.v4.app.NotificationCompat.InboxStyle addLine(java.lang.CharSequence); + method public android.support.v4.app.NotificationCompat.InboxStyle setBigContentTitle(java.lang.CharSequence); + method public android.support.v4.app.NotificationCompat.InboxStyle setSummaryText(java.lang.CharSequence); + } + + public static abstract class NotificationCompat.Style { + ctor public NotificationCompat.Style(); + method public android.app.Notification build(); + method public void setBuilder(android.support.v4.app.NotificationCompat.Builder); + } + + public static final class NotificationCompat.WearableExtender implements android.support.v4.app.NotificationCompat.Extender { + ctor public NotificationCompat.WearableExtender(); + ctor public NotificationCompat.WearableExtender(android.app.Notification); + method public android.support.v4.app.NotificationCompat.WearableExtender addAction(android.support.v4.app.NotificationCompat.Action); + method public android.support.v4.app.NotificationCompat.WearableExtender addActions(java.util.List<android.support.v4.app.NotificationCompat.Action>); + method public android.support.v4.app.NotificationCompat.WearableExtender addPage(android.app.Notification); + method public android.support.v4.app.NotificationCompat.WearableExtender addPages(java.util.List<android.app.Notification>); + method public android.support.v4.app.NotificationCompat.WearableExtender clearActions(); + method public android.support.v4.app.NotificationCompat.WearableExtender clearPages(); + method public android.support.v4.app.NotificationCompat.WearableExtender clone(); + method public android.support.v4.app.NotificationCompat.Builder extend(android.support.v4.app.NotificationCompat.Builder); + method public java.util.List<android.support.v4.app.NotificationCompat.Action> getActions(); + method public android.graphics.Bitmap getBackground(); + method public int getContentAction(); + method public int getContentIcon(); + method public int getContentIconGravity(); + method public boolean getContentIntentAvailableOffline(); + method public int getCustomContentHeight(); + method public int getCustomSizePreset(); + method public android.app.PendingIntent getDisplayIntent(); + method public int getGravity(); + method public boolean getHintAvoidBackgroundClipping(); + method public boolean getHintHideIcon(); + method public int getHintScreenTimeout(); + method public boolean getHintShowBackgroundOnly(); + method public java.util.List<android.app.Notification> getPages(); + method public boolean getStartScrollBottom(); + method public android.support.v4.app.NotificationCompat.WearableExtender setBackground(android.graphics.Bitmap); + method public android.support.v4.app.NotificationCompat.WearableExtender setContentAction(int); + method public android.support.v4.app.NotificationCompat.WearableExtender setContentIcon(int); + method public android.support.v4.app.NotificationCompat.WearableExtender setContentIconGravity(int); + method public android.support.v4.app.NotificationCompat.WearableExtender setContentIntentAvailableOffline(boolean); + method public android.support.v4.app.NotificationCompat.WearableExtender setCustomContentHeight(int); + method public android.support.v4.app.NotificationCompat.WearableExtender setCustomSizePreset(int); + method public android.support.v4.app.NotificationCompat.WearableExtender setDisplayIntent(android.app.PendingIntent); + method public android.support.v4.app.NotificationCompat.WearableExtender setGravity(int); + method public android.support.v4.app.NotificationCompat.WearableExtender setHintAvoidBackgroundClipping(boolean); + method public android.support.v4.app.NotificationCompat.WearableExtender setHintHideIcon(boolean); + method public android.support.v4.app.NotificationCompat.WearableExtender setHintScreenTimeout(int); + method public android.support.v4.app.NotificationCompat.WearableExtender setHintShowBackgroundOnly(boolean); + method public android.support.v4.app.NotificationCompat.WearableExtender setStartScrollBottom(boolean); + field public static final int SCREEN_TIMEOUT_LONG = -1; // 0xffffffff + field public static final int SCREEN_TIMEOUT_SHORT = 0; // 0x0 + field public static final int SIZE_DEFAULT = 0; // 0x0 + field public static final int SIZE_FULL_SCREEN = 5; // 0x5 + field public static final int SIZE_LARGE = 4; // 0x4 + field public static final int SIZE_MEDIUM = 3; // 0x3 + field public static final int SIZE_SMALL = 2; // 0x2 + field public static final int SIZE_XSMALL = 1; // 0x1 + field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff + } + + public class NotificationCompatBase { + ctor public NotificationCompatBase(); + } + + public static abstract class NotificationCompatBase.Action { + ctor public NotificationCompatBase.Action(); + method public abstract android.app.PendingIntent getActionIntent(); + method public abstract android.os.Bundle getExtras(); + method public abstract int getIcon(); + method public abstract android.support.v4.app.RemoteInputCompatBase.RemoteInput[] getRemoteInputs(); + method public abstract java.lang.CharSequence getTitle(); + } + + public static abstract class NotificationCompatBase.UnreadConversation { + ctor public NotificationCompatBase.UnreadConversation(); + } + + public final class NotificationCompatExtras { + field public static final java.lang.String EXTRA_ACTION_EXTRAS = "android.support.actionExtras"; + field public static final java.lang.String EXTRA_GROUP_KEY = "android.support.groupKey"; + field public static final java.lang.String EXTRA_GROUP_SUMMARY = "android.support.isGroupSummary"; + field public static final java.lang.String EXTRA_LOCAL_ONLY = "android.support.localOnly"; + field public static final java.lang.String EXTRA_REMOTE_INPUTS = "android.support.remoteInputs"; + field public static final java.lang.String EXTRA_SORT_KEY = "android.support.sortKey"; + } + + public abstract class NotificationCompatSideChannelService extends android.app.Service { + ctor public NotificationCompatSideChannelService(); + method public abstract void cancel(java.lang.String, int, java.lang.String); + method public abstract void cancelAll(java.lang.String); + method public abstract void notify(java.lang.String, int, java.lang.String, android.app.Notification); + method public android.os.IBinder onBind(android.content.Intent); + } + + public class NotificationManagerCompat { + method public void cancel(int); + method public void cancel(java.lang.String, int); + method public void cancelAll(); + method public static android.support.v4.app.NotificationManagerCompat from(android.content.Context); + method public static java.util.Set<java.lang.String> getEnabledListenerPackages(android.content.Context); + method public void notify(int, android.app.Notification); + method public void notify(java.lang.String, int, android.app.Notification); + field public static final java.lang.String ACTION_BIND_SIDE_CHANNEL = "android.support.BIND_NOTIFICATION_SIDE_CHANNEL"; + field public static final java.lang.String EXTRA_USE_SIDE_CHANNEL = "android.support.useSideChannel"; + } + + public class RemoteInput extends android.support.v4.app.RemoteInputCompatBase.RemoteInput { + method public static void addResultsToIntent(android.support.v4.app.RemoteInput[], android.content.Intent, android.os.Bundle); + method public boolean getAllowFreeFormInput(); + method public java.lang.CharSequence[] getChoices(); + method public android.os.Bundle getExtras(); + method public java.lang.CharSequence getLabel(); + method public java.lang.String getResultKey(); + method public static android.os.Bundle getResultsFromIntent(android.content.Intent); + field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData"; + field public static final java.lang.String RESULTS_CLIP_LABEL = "android.remoteinput.results"; + } + + public static final class RemoteInput.Builder { + ctor public RemoteInput.Builder(java.lang.String); + method public android.support.v4.app.RemoteInput.Builder addExtras(android.os.Bundle); + method public android.support.v4.app.RemoteInput build(); + method public android.os.Bundle getExtras(); + method public android.support.v4.app.RemoteInput.Builder setAllowFreeFormInput(boolean); + method public android.support.v4.app.RemoteInput.Builder setChoices(java.lang.CharSequence[]); + method public android.support.v4.app.RemoteInput.Builder setLabel(java.lang.CharSequence); + } + + class RemoteInputCompatBase { + } + + public static abstract class RemoteInputCompatBase.RemoteInput { + ctor public RemoteInputCompatBase.RemoteInput(); + method protected abstract boolean getAllowFreeFormInput(); + method protected abstract java.lang.CharSequence[] getChoices(); + method protected abstract android.os.Bundle getExtras(); + method protected abstract java.lang.CharSequence getLabel(); + method protected abstract java.lang.String getResultKey(); + } + + public class ServiceCompat { + field public static final int START_STICKY = 1; // 0x1 + } + + public class ShareCompat { + ctor public ShareCompat(); + method public static void configureMenuItem(android.view.MenuItem, android.support.v4.app.ShareCompat.IntentBuilder); + method public static void configureMenuItem(android.view.Menu, int, android.support.v4.app.ShareCompat.IntentBuilder); + method public static android.content.ComponentName getCallingActivity(android.app.Activity); + method public static java.lang.String getCallingPackage(android.app.Activity); + field public static final java.lang.String EXTRA_CALLING_ACTIVITY = "android.support.v4.app.EXTRA_CALLING_ACTIVITY"; + field public static final java.lang.String EXTRA_CALLING_PACKAGE = "android.support.v4.app.EXTRA_CALLING_PACKAGE"; + } + + public static class ShareCompat.IntentBuilder { + method public android.support.v4.app.ShareCompat.IntentBuilder addEmailBcc(java.lang.String); + method public android.support.v4.app.ShareCompat.IntentBuilder addEmailBcc(java.lang.String[]); + method public android.support.v4.app.ShareCompat.IntentBuilder addEmailCc(java.lang.String); + method public android.support.v4.app.ShareCompat.IntentBuilder addEmailCc(java.lang.String[]); + method public android.support.v4.app.ShareCompat.IntentBuilder addEmailTo(java.lang.String); + method public android.support.v4.app.ShareCompat.IntentBuilder addEmailTo(java.lang.String[]); + method public android.support.v4.app.ShareCompat.IntentBuilder addStream(android.net.Uri); + method public android.content.Intent createChooserIntent(); + method public static android.support.v4.app.ShareCompat.IntentBuilder from(android.app.Activity); + method public android.content.Intent getIntent(); + method public android.support.v4.app.ShareCompat.IntentBuilder setChooserTitle(java.lang.CharSequence); + method public android.support.v4.app.ShareCompat.IntentBuilder setChooserTitle(int); + method public android.support.v4.app.ShareCompat.IntentBuilder setEmailBcc(java.lang.String[]); + method public android.support.v4.app.ShareCompat.IntentBuilder setEmailCc(java.lang.String[]); + method public android.support.v4.app.ShareCompat.IntentBuilder setEmailTo(java.lang.String[]); + method public android.support.v4.app.ShareCompat.IntentBuilder setHtmlText(java.lang.String); + method public android.support.v4.app.ShareCompat.IntentBuilder setStream(android.net.Uri); + method public android.support.v4.app.ShareCompat.IntentBuilder setSubject(java.lang.String); + method public android.support.v4.app.ShareCompat.IntentBuilder setText(java.lang.CharSequence); + method public android.support.v4.app.ShareCompat.IntentBuilder setType(java.lang.String); + method public void startChooser(); + } + + public static class ShareCompat.IntentReader { + method public static android.support.v4.app.ShareCompat.IntentReader from(android.app.Activity); + method public android.content.ComponentName getCallingActivity(); + method public android.graphics.drawable.Drawable getCallingActivityIcon(); + method public android.graphics.drawable.Drawable getCallingApplicationIcon(); + method public java.lang.CharSequence getCallingApplicationLabel(); + method public java.lang.String getCallingPackage(); + method public java.lang.String[] getEmailBcc(); + method public java.lang.String[] getEmailCc(); + method public java.lang.String[] getEmailTo(); + method public java.lang.String getHtmlText(); + method public android.net.Uri getStream(); + method public android.net.Uri getStream(int); + method public int getStreamCount(); + method public java.lang.String getSubject(); + method public java.lang.CharSequence getText(); + method public java.lang.String getType(); + method public boolean isMultipleShare(); + method public boolean isShareIntent(); + method public boolean isSingleShare(); + } + + public abstract class SharedElementCallback { + ctor public SharedElementCallback(); + method public android.os.Parcelable onCaptureSharedElementSnapshot(android.view.View, android.graphics.Matrix, android.graphics.RectF); + method public android.view.View onCreateSnapshotView(android.content.Context, android.os.Parcelable); + method public void onMapSharedElements(java.util.List<java.lang.String>, java.util.Map<java.lang.String, android.view.View>); + method public void onRejectSharedElements(java.util.List<android.view.View>); + method public void onSharedElementEnd(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>); + method public void onSharedElementStart(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>); + } + + public class TaskStackBuilder implements java.lang.Iterable { + method public android.support.v4.app.TaskStackBuilder addNextIntent(android.content.Intent); + method public android.support.v4.app.TaskStackBuilder addNextIntentWithParentStack(android.content.Intent); + method public android.support.v4.app.TaskStackBuilder addParentStack(android.app.Activity); + method public android.support.v4.app.TaskStackBuilder addParentStack(java.lang.Class<?>); + method public android.support.v4.app.TaskStackBuilder addParentStack(android.content.ComponentName); + method public static android.support.v4.app.TaskStackBuilder create(android.content.Context); + method public android.content.Intent editIntentAt(int); + method public static deprecated android.support.v4.app.TaskStackBuilder from(android.content.Context); + method public deprecated android.content.Intent getIntent(int); + method public int getIntentCount(); + method public android.content.Intent[] getIntents(); + method public android.app.PendingIntent getPendingIntent(int, int); + method public android.app.PendingIntent getPendingIntent(int, int, android.os.Bundle); + method public deprecated java.util.Iterator<android.content.Intent> iterator(); + method public void startActivities(); + method public void startActivities(android.os.Bundle); + } + + public static abstract interface TaskStackBuilder.SupportParentable { + method public abstract android.content.Intent getSupportParentActivityIntent(); + } + +} + +package android.support.v4.content { + + public abstract class AsyncTaskLoader extends android.support.v4.content.Loader { + ctor public AsyncTaskLoader(android.content.Context); + method public void cancelLoadInBackground(); + method public boolean isLoadInBackgroundCanceled(); + method public abstract D loadInBackground(); + method public void onCanceled(D); + method protected D onLoadInBackground(); + method public void setUpdateThrottle(long); + } + + public class ContentResolverCompat { + method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.support.v4.os.CancellationSignal); + } + + public class ContextCompat { + ctor public ContextCompat(); + method public static int checkSelfPermission(android.content.Context, java.lang.String); + method public final java.io.File getCodeCacheDir(android.content.Context); + method public static final int getColor(android.content.Context, int); + method public static final android.content.res.ColorStateList getColorStateList(android.content.Context, int); + method public static final android.graphics.drawable.Drawable getDrawable(android.content.Context, int); + method public static java.io.File[] getExternalCacheDirs(android.content.Context); + method public static java.io.File[] getExternalFilesDirs(android.content.Context, java.lang.String); + method public final java.io.File getNoBackupFilesDir(android.content.Context); + method public static java.io.File[] getObbDirs(android.content.Context); + method public static boolean startActivities(android.content.Context, android.content.Intent[]); + method public static boolean startActivities(android.content.Context, android.content.Intent[], android.os.Bundle); + } + + public class CursorLoader extends android.support.v4.content.AsyncTaskLoader { + ctor public CursorLoader(android.content.Context); + ctor public CursorLoader(android.content.Context, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String); + method public void deliverResult(android.database.Cursor); + method public java.lang.String[] getProjection(); + method public java.lang.String getSelection(); + method public java.lang.String[] getSelectionArgs(); + method public java.lang.String getSortOrder(); + method public android.net.Uri getUri(); + method public android.database.Cursor loadInBackground(); + method public void onCanceled(android.database.Cursor); + method public void setProjection(java.lang.String[]); + method public void setSelection(java.lang.String); + method public void setSelectionArgs(java.lang.String[]); + method public void setSortOrder(java.lang.String); + method public void setUri(android.net.Uri); + } + + public class FileProvider extends android.content.ContentProvider { + ctor public FileProvider(); + method public int delete(android.net.Uri, java.lang.String, java.lang.String[]); + method public java.lang.String getType(android.net.Uri); + method public static android.net.Uri getUriForFile(android.content.Context, java.lang.String, java.io.File); + method public android.net.Uri insert(android.net.Uri, android.content.ContentValues); + method public boolean onCreate(); + method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String); + method public int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]); + } + + public class IntentCompat { + method public static android.content.Intent makeMainActivity(android.content.ComponentName); + method public static android.content.Intent makeMainSelectorActivity(java.lang.String, java.lang.String); + method public static android.content.Intent makeRestartActivityTask(android.content.ComponentName); + field public static final java.lang.String ACTION_EXTERNAL_APPLICATIONS_AVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE"; + field public static final java.lang.String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE"; + field public static final java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list"; + field public static final java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list"; + field public static final java.lang.String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT"; + field public static final int FLAG_ACTIVITY_CLEAR_TASK = 32768; // 0x8000 + field public static final int FLAG_ACTIVITY_TASK_ON_HOME = 16384; // 0x4000 + } + + public class Loader { + ctor public Loader(android.content.Context); + method public void abandon(); + method public boolean cancelLoad(); + method public void commitContentChanged(); + method public java.lang.String dataToString(D); + method public void deliverCancellation(); + method public void deliverResult(D); + method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]); + method public void forceLoad(); + method public android.content.Context getContext(); + method public int getId(); + method public boolean isAbandoned(); + method public boolean isReset(); + method public boolean isStarted(); + method protected void onAbandon(); + method protected boolean onCancelLoad(); + method public void onContentChanged(); + method protected void onForceLoad(); + method protected void onReset(); + method protected void onStartLoading(); + method protected void onStopLoading(); + method public void registerListener(int, android.support.v4.content.Loader.OnLoadCompleteListener<D>); + method public void registerOnLoadCanceledListener(android.support.v4.content.Loader.OnLoadCanceledListener<D>); + method public void reset(); + method public void rollbackContentChanged(); + method public final void startLoading(); + method public void stopLoading(); + method public boolean takeContentChanged(); + method public void unregisterListener(android.support.v4.content.Loader.OnLoadCompleteListener<D>); + method public void unregisterOnLoadCanceledListener(android.support.v4.content.Loader.OnLoadCanceledListener<D>); + } + + public final class Loader.ForceLoadContentObserver extends android.database.ContentObserver { + ctor public Loader.ForceLoadContentObserver(); + } + + public static abstract interface Loader.OnLoadCanceledListener { + method public abstract void onLoadCanceled(android.support.v4.content.Loader<D>); + } + + public static abstract interface Loader.OnLoadCompleteListener { + method public abstract void onLoadComplete(android.support.v4.content.Loader<D>, D); + } + + public class LocalBroadcastManager { + method public static android.support.v4.content.LocalBroadcastManager getInstance(android.content.Context); + method public void registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter); + method public boolean sendBroadcast(android.content.Intent); + method public void sendBroadcastSync(android.content.Intent); + method public void unregisterReceiver(android.content.BroadcastReceiver); + } + + public class ParallelExecutorCompat { + ctor public ParallelExecutorCompat(); + method public static java.util.concurrent.Executor getParallelExecutor(); + } + + public final class PermissionChecker { + method public static int checkCallingOrSelfPermission(android.content.Context, java.lang.String); + method public static int checkCallingPermission(android.content.Context, java.lang.String, java.lang.String); + method public static int checkPermission(android.content.Context, java.lang.String, int, int, java.lang.String); + method public static int checkSelfPermission(android.content.Context, java.lang.String); + field public static final int PERMISSION_DENIED = -1; // 0xffffffff + field public static final int PERMISSION_DENIED_APP_OP = -2; // 0xfffffffe + field public static final int PERMISSION_GRANTED = 0; // 0x0 + } + + public static abstract class PermissionChecker.PermissionResult implements java.lang.annotation.Annotation { + } + + public class SharedPreferencesCompat { + ctor public SharedPreferencesCompat(); + } + + public static class SharedPreferencesCompat.EditorCompat { + method public void apply(android.content.SharedPreferences.Editor); + method public static android.support.v4.content.SharedPreferencesCompat.EditorCompat getInstance(); + } + + public abstract class WakefulBroadcastReceiver extends android.content.BroadcastReceiver { + ctor public WakefulBroadcastReceiver(); + method public static boolean completeWakefulIntent(android.content.Intent); + method public static android.content.ComponentName startWakefulService(android.content.Context, android.content.Intent); + } + +} + +package android.support.v4.content.pm { + + public class ActivityInfoCompat { + field public static final int CONFIG_UI_MODE = 512; // 0x200 + } + +} + +package android.support.v4.content.res { + + public class ResourcesCompat { + ctor public ResourcesCompat(); + method public int getColor(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException; + method public android.content.res.ColorStateList getColorStateList(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException; + method public static android.graphics.drawable.Drawable getDrawable(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException; + method public static android.graphics.drawable.Drawable getDrawableForDensity(android.content.res.Resources, int, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException; + } + +} + +package android.support.v4.database { + + public class DatabaseUtilsCompat { + method public static java.lang.String[] appendSelectionArgs(java.lang.String[], java.lang.String[]); + method public static java.lang.String concatenateWhere(java.lang.String, java.lang.String); + } + +} + +package android.support.v4.graphics { + + public class BitmapCompat { + ctor public BitmapCompat(); + method public static int getAllocationByteCount(android.graphics.Bitmap); + method public static boolean hasMipMap(android.graphics.Bitmap); + method public static void setHasMipMap(android.graphics.Bitmap, boolean); + } + + public class ColorUtils { + method public static int HSLToColor(float[]); + method public static void RGBToHSL(int, int, int, float[]); + method public static double calculateContrast(int, int); + method public static double calculateLuminance(int); + method public static int calculateMinimumAlpha(int, int, float); + method public static void colorToHSL(int, float[]); + method public static int compositeColors(int, int); + method public static int setAlphaComponent(int, int); + } + +} + +package android.support.v4.graphics.drawable { + + public class DrawableCompat { + ctor public DrawableCompat(); + method public static int getLayoutDirection(android.graphics.drawable.Drawable); + method public static boolean isAutoMirrored(android.graphics.drawable.Drawable); + method public static void jumpToCurrentState(android.graphics.drawable.Drawable); + method public static void setAutoMirrored(android.graphics.drawable.Drawable, boolean); + method public static void setHotspot(android.graphics.drawable.Drawable, float, float); + method public static void setHotspotBounds(android.graphics.drawable.Drawable, int, int, int, int); + method public static void setLayoutDirection(android.graphics.drawable.Drawable, int); + method public static void setTint(android.graphics.drawable.Drawable, int); + method public static void setTintList(android.graphics.drawable.Drawable, android.content.res.ColorStateList); + method public static void setTintMode(android.graphics.drawable.Drawable, android.graphics.PorterDuff.Mode); + method public static T unwrap(android.graphics.drawable.Drawable); + method public static android.graphics.drawable.Drawable wrap(android.graphics.drawable.Drawable); + } + + public abstract class RoundedBitmapDrawable extends android.graphics.drawable.Drawable { + method public void draw(android.graphics.Canvas); + method public final android.graphics.Bitmap getBitmap(); + method public float getCornerRadius(); + method public int getGravity(); + method public int getOpacity(); + method public final android.graphics.Paint getPaint(); + method public boolean hasAntiAlias(); + method public boolean hasMipMap(); + method public boolean isCircular(); + method public void setAlpha(int); + method public void setAntiAlias(boolean); + method public void setCircular(boolean); + method public void setColorFilter(android.graphics.ColorFilter); + method public void setCornerRadius(float); + method public void setGravity(int); + method public void setMipMap(boolean); + method public void setTargetDensity(android.graphics.Canvas); + method public void setTargetDensity(android.util.DisplayMetrics); + method public void setTargetDensity(int); + } + + public class RoundedBitmapDrawableFactory { + ctor public RoundedBitmapDrawableFactory(); + method public static android.support.v4.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, android.graphics.Bitmap); + method public static android.support.v4.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, java.lang.String); + method public static android.support.v4.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, java.io.InputStream); + } + +} + +package android.support.v4.hardware.display { + + public abstract class DisplayManagerCompat { + method public abstract android.view.Display getDisplay(int); + method public abstract android.view.Display[] getDisplays(); + method public abstract android.view.Display[] getDisplays(java.lang.String); + method public static android.support.v4.hardware.display.DisplayManagerCompat getInstance(android.content.Context); + field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION"; + } + +} + +package android.support.v4.hardware.fingerprint { + + public class FingerprintManagerCompat { + method public void authenticate(android.support.v4.hardware.fingerprint.FingerprintManagerCompat.CryptoObject, int, android.support.v4.os.CancellationSignal, android.support.v4.hardware.fingerprint.FingerprintManagerCompat.AuthenticationCallback, android.os.Handler); + method public static android.support.v4.hardware.fingerprint.FingerprintManagerCompat from(android.content.Context); + method public boolean hasEnrolledFingerprints(); + method public boolean isHardwareDetected(); + } + + public static abstract class FingerprintManagerCompat.AuthenticationCallback { + ctor public FingerprintManagerCompat.AuthenticationCallback(); + method public void onAuthenticationError(int, java.lang.CharSequence); + method public void onAuthenticationFailed(); + method public void onAuthenticationHelp(int, java.lang.CharSequence); + method public void onAuthenticationSucceeded(android.support.v4.hardware.fingerprint.FingerprintManagerCompat.AuthenticationResult); + } + + public static final class FingerprintManagerCompat.AuthenticationResult { + ctor public FingerprintManagerCompat.AuthenticationResult(android.support.v4.hardware.fingerprint.FingerprintManagerCompat.CryptoObject); + method public android.support.v4.hardware.fingerprint.FingerprintManagerCompat.CryptoObject getCryptoObject(); + } + + public static class FingerprintManagerCompat.CryptoObject { + ctor public FingerprintManagerCompat.CryptoObject(java.security.Signature); + ctor public FingerprintManagerCompat.CryptoObject(javax.crypto.Cipher); + ctor public FingerprintManagerCompat.CryptoObject(javax.crypto.Mac); + method public javax.crypto.Cipher getCipher(); + method public javax.crypto.Mac getMac(); + method public java.security.Signature getSignature(); + } + +} + +package android.support.v4.media { + + public final class MediaDescriptionCompat implements android.os.Parcelable { + method public int describeContents(); + method public static android.support.v4.media.MediaDescriptionCompat fromMediaDescription(java.lang.Object); + method public java.lang.CharSequence getDescription(); + method public android.os.Bundle getExtras(); + method public android.graphics.Bitmap getIconBitmap(); + method public android.net.Uri getIconUri(); + method public java.lang.Object getMediaDescription(); + method public java.lang.String getMediaId(); + method public android.net.Uri getMediaUri(); + method public java.lang.CharSequence getSubtitle(); + method public java.lang.CharSequence getTitle(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaDescriptionCompat> CREATOR; + } + + public static final class MediaDescriptionCompat.Builder { + ctor public MediaDescriptionCompat.Builder(); + method public android.support.v4.media.MediaDescriptionCompat build(); + method public android.support.v4.media.MediaDescriptionCompat.Builder setDescription(java.lang.CharSequence); + method public android.support.v4.media.MediaDescriptionCompat.Builder setExtras(android.os.Bundle); + method public android.support.v4.media.MediaDescriptionCompat.Builder setIconBitmap(android.graphics.Bitmap); + method public android.support.v4.media.MediaDescriptionCompat.Builder setIconUri(android.net.Uri); + method public android.support.v4.media.MediaDescriptionCompat.Builder setMediaId(java.lang.String); + method public android.support.v4.media.MediaDescriptionCompat.Builder setMediaUri(android.net.Uri); + method public android.support.v4.media.MediaDescriptionCompat.Builder setSubtitle(java.lang.CharSequence); + method public android.support.v4.media.MediaDescriptionCompat.Builder setTitle(java.lang.CharSequence); + } + + public final class MediaMetadataCompat implements android.os.Parcelable { + method public boolean containsKey(java.lang.String); + method public int describeContents(); + method public static android.support.v4.media.MediaMetadataCompat fromMediaMetadata(java.lang.Object); + method public android.graphics.Bitmap getBitmap(java.lang.String); + method public android.os.Bundle getBundle(); + method public android.support.v4.media.MediaDescriptionCompat getDescription(); + method public long getLong(java.lang.String); + method public java.lang.Object getMediaMetadata(); + method public android.support.v4.media.RatingCompat getRating(java.lang.String); + method public java.lang.String getString(java.lang.String); + method public java.lang.CharSequence getText(java.lang.String); + method public java.util.Set<java.lang.String> keySet(); + method public int size(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaMetadataCompat> CREATOR; + field public static final java.lang.String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM"; + field public static final java.lang.String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART"; + field public static final java.lang.String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST"; + field public static final java.lang.String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI"; + field public static final java.lang.String METADATA_KEY_ART = "android.media.metadata.ART"; + field public static final java.lang.String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST"; + field public static final java.lang.String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI"; + field public static final java.lang.String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR"; + field public static final java.lang.String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION"; + field public static final java.lang.String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER"; + field public static final java.lang.String METADATA_KEY_DATE = "android.media.metadata.DATE"; + field public static final java.lang.String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER"; + field public static final java.lang.String METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION"; + field public static final java.lang.String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON"; + field public static final java.lang.String METADATA_KEY_DISPLAY_ICON_URI = "android.media.metadata.DISPLAY_ICON_URI"; + field public static final java.lang.String METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE"; + field public static final java.lang.String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE"; + field public static final java.lang.String METADATA_KEY_DURATION = "android.media.metadata.DURATION"; + field public static final java.lang.String METADATA_KEY_GENRE = "android.media.metadata.GENRE"; + field public static final java.lang.String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID"; + field public static final java.lang.String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS"; + field public static final java.lang.String METADATA_KEY_RATING = "android.media.metadata.RATING"; + field public static final java.lang.String METADATA_KEY_TITLE = "android.media.metadata.TITLE"; + field public static final java.lang.String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER"; + field public static final java.lang.String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING"; + field public static final java.lang.String METADATA_KEY_WRITER = "android.media.metadata.WRITER"; + field public static final java.lang.String METADATA_KEY_YEAR = "android.media.metadata.YEAR"; + } + + public static final class MediaMetadataCompat.Builder { + ctor public MediaMetadataCompat.Builder(); + ctor public MediaMetadataCompat.Builder(android.support.v4.media.MediaMetadataCompat); + method public android.support.v4.media.MediaMetadataCompat build(); + method public android.support.v4.media.MediaMetadataCompat.Builder putBitmap(java.lang.String, android.graphics.Bitmap); + method public android.support.v4.media.MediaMetadataCompat.Builder putLong(java.lang.String, long); + method public android.support.v4.media.MediaMetadataCompat.Builder putRating(java.lang.String, android.support.v4.media.RatingCompat); + method public android.support.v4.media.MediaMetadataCompat.Builder putString(java.lang.String, java.lang.String); + method public android.support.v4.media.MediaMetadataCompat.Builder putText(java.lang.String, java.lang.CharSequence); + } + + public final class RatingCompat implements android.os.Parcelable { + method public int describeContents(); + method public static android.support.v4.media.RatingCompat fromRating(java.lang.Object); + method public float getPercentRating(); + method public java.lang.Object getRating(); + method public int getRatingStyle(); + method public float getStarRating(); + method public boolean hasHeart(); + method public boolean isRated(); + method public boolean isThumbUp(); + method public static android.support.v4.media.RatingCompat newHeartRating(boolean); + method public static android.support.v4.media.RatingCompat newPercentageRating(float); + method public static android.support.v4.media.RatingCompat newStarRating(int, float); + method public static android.support.v4.media.RatingCompat newThumbRating(boolean); + method public static android.support.v4.media.RatingCompat newUnratedRating(int); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.support.v4.media.RatingCompat> CREATOR; + field public static final int RATING_3_STARS = 3; // 0x3 + field public static final int RATING_4_STARS = 4; // 0x4 + field public static final int RATING_5_STARS = 5; // 0x5 + field public static final int RATING_HEART = 1; // 0x1 + field public static final int RATING_NONE = 0; // 0x0 + field public static final int RATING_PERCENTAGE = 6; // 0x6 + field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2 + } + + public abstract class TransportController { + ctor public TransportController(); + method public abstract int getBufferPercentage(); + method public abstract long getCurrentPosition(); + method public abstract long getDuration(); + method public abstract int getTransportControlFlags(); + method public abstract boolean isPlaying(); + method public abstract void pausePlaying(); + method public abstract void registerStateListener(android.support.v4.media.TransportStateListener); + method public abstract void seekTo(long); + method public abstract void startPlaying(); + method public abstract void stopPlaying(); + method public abstract void unregisterStateListener(android.support.v4.media.TransportStateListener); + } + + public class TransportMediator extends android.support.v4.media.TransportController { + ctor public TransportMediator(android.app.Activity, android.support.v4.media.TransportPerformer); + ctor public TransportMediator(android.view.View, android.support.v4.media.TransportPerformer); + method public void destroy(); + method public boolean dispatchKeyEvent(android.view.KeyEvent); + method public int getBufferPercentage(); + method public long getCurrentPosition(); + method public long getDuration(); + method public java.lang.Object getRemoteControlClient(); + method public int getTransportControlFlags(); + method public boolean isPlaying(); + method public void pausePlaying(); + method public void refreshState(); + method public void registerStateListener(android.support.v4.media.TransportStateListener); + method public void seekTo(long); + method public void startPlaying(); + method public void stopPlaying(); + method public void unregisterStateListener(android.support.v4.media.TransportStateListener); + field public static final int FLAG_KEY_MEDIA_FAST_FORWARD = 64; // 0x40 + field public static final int FLAG_KEY_MEDIA_NEXT = 128; // 0x80 + field public static final int FLAG_KEY_MEDIA_PAUSE = 16; // 0x10 + field public static final int FLAG_KEY_MEDIA_PLAY = 4; // 0x4 + field public static final int FLAG_KEY_MEDIA_PLAY_PAUSE = 8; // 0x8 + field public static final int FLAG_KEY_MEDIA_PREVIOUS = 1; // 0x1 + field public static final int FLAG_KEY_MEDIA_REWIND = 2; // 0x2 + field public static final int FLAG_KEY_MEDIA_STOP = 32; // 0x20 + field public static final int KEYCODE_MEDIA_PAUSE = 127; // 0x7f + field public static final int KEYCODE_MEDIA_PLAY = 126; // 0x7e + field public static final int KEYCODE_MEDIA_RECORD = 130; // 0x82 + } + + public abstract class TransportPerformer { + ctor public TransportPerformer(); + method public void onAudioFocusChange(int); + method public int onGetBufferPercentage(); + method public abstract long onGetCurrentPosition(); + method public abstract long onGetDuration(); + method public int onGetTransportControlFlags(); + method public abstract boolean onIsPlaying(); + method public boolean onMediaButtonDown(int, android.view.KeyEvent); + method public boolean onMediaButtonUp(int, android.view.KeyEvent); + method public abstract void onPause(); + method public abstract void onSeekTo(long); + method public abstract void onStart(); + method public abstract void onStop(); + } + + public class TransportStateListener { + ctor public TransportStateListener(); + method public void onPlayingChanged(android.support.v4.media.TransportController); + method public void onTransportControlsChanged(android.support.v4.media.TransportController); + } + + public abstract class VolumeProviderCompat { + ctor public VolumeProviderCompat(int, int, int); + method public final int getCurrentVolume(); + method public final int getMaxVolume(); + method public final int getVolumeControl(); + method public java.lang.Object getVolumeProvider(); + method public void onAdjustVolume(int); + method public void onSetVolumeTo(int); + method public void setCallback(android.support.v4.media.VolumeProviderCompat.Callback); + method public final void setCurrentVolume(int); + field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2 + field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0 + field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1 + } + + public static abstract class VolumeProviderCompat.Callback { + ctor public VolumeProviderCompat.Callback(); + method public abstract void onVolumeChanged(android.support.v4.media.VolumeProviderCompat); + } + +} + +package android.support.v4.media.session { + + public class MediaButtonReceiver extends android.content.BroadcastReceiver { + ctor public MediaButtonReceiver(); + method public static android.view.KeyEvent handleIntent(android.support.v4.media.session.MediaSessionCompat, android.content.Intent); + method public void onReceive(android.content.Context, android.content.Intent); + } + + public final class MediaControllerCompat { + ctor public MediaControllerCompat(android.content.Context, android.support.v4.media.session.MediaSessionCompat); + ctor public MediaControllerCompat(android.content.Context, android.support.v4.media.session.MediaSessionCompat.Token) throws android.os.RemoteException; + method public void adjustVolume(int, int); + method public boolean dispatchMediaButtonEvent(android.view.KeyEvent); + method public android.os.Bundle getExtras(); + method public long getFlags(); + method public java.lang.Object getMediaController(); + method public android.support.v4.media.MediaMetadataCompat getMetadata(); + method public java.lang.String getPackageName(); + method public android.support.v4.media.session.MediaControllerCompat.PlaybackInfo getPlaybackInfo(); + method public android.support.v4.media.session.PlaybackStateCompat getPlaybackState(); + method public java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem> getQueue(); + method public java.lang.CharSequence getQueueTitle(); + method public int getRatingType(); + method public android.app.PendingIntent getSessionActivity(); + method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken(); + method public android.support.v4.media.session.MediaControllerCompat.TransportControls getTransportControls(); + method public void registerCallback(android.support.v4.media.session.MediaControllerCompat.Callback); + method public void registerCallback(android.support.v4.media.session.MediaControllerCompat.Callback, android.os.Handler); + method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver); + method public void setVolumeTo(int, int); + method public void unregisterCallback(android.support.v4.media.session.MediaControllerCompat.Callback); + } + + public static abstract class MediaControllerCompat.Callback implements android.os.IBinder.DeathRecipient { + ctor public MediaControllerCompat.Callback(); + method public void binderDied(); + method public void onAudioInfoChanged(android.support.v4.media.session.MediaControllerCompat.PlaybackInfo); + method public void onExtrasChanged(android.os.Bundle); + method public void onMetadataChanged(android.support.v4.media.MediaMetadataCompat); + method public void onPlaybackStateChanged(android.support.v4.media.session.PlaybackStateCompat); + method public void onQueueChanged(java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem>); + method public void onQueueTitleChanged(java.lang.CharSequence); + method public void onSessionDestroyed(); + method public void onSessionEvent(java.lang.String, android.os.Bundle); + } + + public static final class MediaControllerCompat.PlaybackInfo { + method public int getAudioStream(); + method public int getCurrentVolume(); + method public int getMaxVolume(); + method public int getPlaybackType(); + method public int getVolumeControl(); + field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1 + field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2 + } + + public static abstract class MediaControllerCompat.TransportControls { + method public abstract void fastForward(); + method public abstract void pause(); + method public abstract void play(); + method public abstract void playFromMediaId(java.lang.String, android.os.Bundle); + method public abstract void playFromSearch(java.lang.String, android.os.Bundle); + method public abstract void playFromUri(android.net.Uri, android.os.Bundle); + method public abstract void rewind(); + method public abstract void seekTo(long); + method public abstract void sendCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction, android.os.Bundle); + method public abstract void sendCustomAction(java.lang.String, android.os.Bundle); + method public abstract void setRating(android.support.v4.media.RatingCompat); + method public abstract void skipToNext(); + method public abstract void skipToPrevious(); + method public abstract void skipToQueueItem(long); + method public abstract void stop(); + } + + public class MediaSessionCompat { + ctor public MediaSessionCompat(android.content.Context, java.lang.String); + ctor public MediaSessionCompat(android.content.Context, java.lang.String, android.content.ComponentName, android.app.PendingIntent); + method public void addOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener); + method public android.support.v4.media.session.MediaControllerCompat getController(); + method public java.lang.Object getMediaSession(); + method public java.lang.Object getRemoteControlClient(); + method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken(); + method public boolean isActive(); + method public static android.support.v4.media.session.MediaSessionCompat obtain(android.content.Context, java.lang.Object); + method public void release(); + method public void removeOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener); + method public void sendSessionEvent(java.lang.String, android.os.Bundle); + method public void setActive(boolean); + method public void setCallback(android.support.v4.media.session.MediaSessionCompat.Callback); + method public void setCallback(android.support.v4.media.session.MediaSessionCompat.Callback, android.os.Handler); + method public void setExtras(android.os.Bundle); + method public void setFlags(int); + method public void setMediaButtonReceiver(android.app.PendingIntent); + method public void setMetadata(android.support.v4.media.MediaMetadataCompat); + method public void setPlaybackState(android.support.v4.media.session.PlaybackStateCompat); + method public void setPlaybackToLocal(int); + method public void setPlaybackToRemote(android.support.v4.media.VolumeProviderCompat); + method public void setQueue(java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem>); + method public void setQueueTitle(java.lang.CharSequence); + method public void setRatingType(int); + method public void setSessionActivity(android.app.PendingIntent); + field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1 + field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2 + } + + public static abstract class MediaSessionCompat.Callback { + ctor public MediaSessionCompat.Callback(); + method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver); + method public void onCustomAction(java.lang.String, android.os.Bundle); + method public void onFastForward(); + method public boolean onMediaButtonEvent(android.content.Intent); + method public void onPause(); + method public void onPlay(); + method public void onPlayFromMediaId(java.lang.String, android.os.Bundle); + method public void onPlayFromSearch(java.lang.String, android.os.Bundle); + method public void onPlayFromUri(android.net.Uri, android.os.Bundle); + method public void onRewind(); + method public void onSeekTo(long); + method public void onSetRating(android.support.v4.media.RatingCompat); + method public void onSkipToNext(); + method public void onSkipToPrevious(); + method public void onSkipToQueueItem(long); + method public void onStop(); + } + + public static abstract interface MediaSessionCompat.OnActiveChangeListener { + method public abstract void onActiveChanged(); + } + + public static final class MediaSessionCompat.QueueItem implements android.os.Parcelable { + ctor public MediaSessionCompat.QueueItem(android.support.v4.media.MediaDescriptionCompat, long); + method public int describeContents(); + method public android.support.v4.media.MediaDescriptionCompat getDescription(); + method public long getQueueId(); + method public java.lang.Object getQueueItem(); + method public static android.support.v4.media.session.MediaSessionCompat.QueueItem obtain(java.lang.Object); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.support.v4.media.session.MediaSessionCompat.QueueItem> CREATOR; + field public static final int UNKNOWN_ID = -1; // 0xffffffff + } + + public static final class MediaSessionCompat.Token implements android.os.Parcelable { + method public int describeContents(); + method public static android.support.v4.media.session.MediaSessionCompat.Token fromToken(java.lang.Object); + method public java.lang.Object getToken(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.support.v4.media.session.MediaSessionCompat.Token> CREATOR; + } + + public class ParcelableVolumeInfo implements android.os.Parcelable { + ctor public ParcelableVolumeInfo(int, int, int, int, int); + ctor public ParcelableVolumeInfo(android.os.Parcel); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.support.v4.media.session.ParcelableVolumeInfo> CREATOR; + field public int audioStream; + field public int controlType; + field public int currentVolume; + field public int maxVolume; + field public int volumeType; + } + + public final class PlaybackStateCompat implements android.os.Parcelable { + method public int describeContents(); + method public static android.support.v4.media.session.PlaybackStateCompat fromPlaybackState(java.lang.Object); + method public long getActions(); + method public long getActiveQueueItemId(); + method public long getBufferedPosition(); + method public java.util.List<android.support.v4.media.session.PlaybackStateCompat.CustomAction> getCustomActions(); + method public java.lang.CharSequence getErrorMessage(); + method public android.os.Bundle getExtras(); + method public long getLastPositionUpdateTime(); + method public float getPlaybackSpeed(); + method public java.lang.Object getPlaybackState(); + method public long getPosition(); + method public int getState(); + method public void writeToParcel(android.os.Parcel, int); + field public static final long ACTION_FAST_FORWARD = 64L; // 0x40L + field public static final long ACTION_PAUSE = 2L; // 0x2L + field public static final long ACTION_PLAY = 4L; // 0x4L + field public static final long ACTION_PLAY_FROM_MEDIA_ID = 1024L; // 0x400L + field public static final long ACTION_PLAY_FROM_SEARCH = 2048L; // 0x800L + field public static final long ACTION_PLAY_FROM_URI = 8192L; // 0x2000L + field public static final long ACTION_PLAY_PAUSE = 512L; // 0x200L + field public static final long ACTION_REWIND = 8L; // 0x8L + field public static final long ACTION_SEEK_TO = 256L; // 0x100L + field public static final long ACTION_SET_RATING = 128L; // 0x80L + field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L + field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L + field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L + field public static final long ACTION_STOP = 1L; // 0x1L + field public static final android.os.Parcelable.Creator<android.support.v4.media.session.PlaybackStateCompat> CREATOR; + field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL + field public static final int STATE_BUFFERING = 6; // 0x6 + field public static final int STATE_CONNECTING = 8; // 0x8 + field public static final int STATE_ERROR = 7; // 0x7 + field public static final int STATE_FAST_FORWARDING = 4; // 0x4 + field public static final int STATE_NONE = 0; // 0x0 + field public static final int STATE_PAUSED = 2; // 0x2 + field public static final int STATE_PLAYING = 3; // 0x3 + field public static final int STATE_REWINDING = 5; // 0x5 + field public static final int STATE_SKIPPING_TO_NEXT = 10; // 0xa + field public static final int STATE_SKIPPING_TO_PREVIOUS = 9; // 0x9 + field public static final int STATE_SKIPPING_TO_QUEUE_ITEM = 11; // 0xb + field public static final int STATE_STOPPED = 1; // 0x1 + } + + public static final class PlaybackStateCompat.Builder { + ctor public PlaybackStateCompat.Builder(); + ctor public PlaybackStateCompat.Builder(android.support.v4.media.session.PlaybackStateCompat); + method public android.support.v4.media.session.PlaybackStateCompat.Builder addCustomAction(java.lang.String, java.lang.String, int); + method public android.support.v4.media.session.PlaybackStateCompat.Builder addCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction); + method public android.support.v4.media.session.PlaybackStateCompat build(); + method public android.support.v4.media.session.PlaybackStateCompat.Builder setActions(long); + method public android.support.v4.media.session.PlaybackStateCompat.Builder setActiveQueueItemId(long); + method public android.support.v4.media.session.PlaybackStateCompat.Builder setBufferedPosition(long); + method public android.support.v4.media.session.PlaybackStateCompat.Builder setErrorMessage(java.lang.CharSequence); + method public android.support.v4.media.session.PlaybackStateCompat.Builder setExtras(android.os.Bundle); + method public android.support.v4.media.session.PlaybackStateCompat.Builder setState(int, long, float); + method public android.support.v4.media.session.PlaybackStateCompat.Builder setState(int, long, float, long); + } + + public static final class PlaybackStateCompat.CustomAction implements android.os.Parcelable { + method public int describeContents(); + method public static android.support.v4.media.session.PlaybackStateCompat.CustomAction fromCustomAction(java.lang.Object); + method public java.lang.String getAction(); + method public java.lang.Object getCustomAction(); + method public android.os.Bundle getExtras(); + method public int getIcon(); + method public java.lang.CharSequence getName(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.support.v4.media.session.PlaybackStateCompat.CustomAction> CREATOR; + } + + public static final class PlaybackStateCompat.CustomAction.Builder { + ctor public PlaybackStateCompat.CustomAction.Builder(java.lang.String, java.lang.CharSequence, int); + method public android.support.v4.media.session.PlaybackStateCompat.CustomAction build(); + method public android.support.v4.media.session.PlaybackStateCompat.CustomAction.Builder setExtras(android.os.Bundle); + } + +} + +package android.support.v4.net { + + public class ConnectivityManagerCompat { + ctor public ConnectivityManagerCompat(); + method public static android.net.NetworkInfo getNetworkInfoFromBroadcast(android.net.ConnectivityManager, android.content.Intent); + method public static boolean isActiveNetworkMetered(android.net.ConnectivityManager); + } + + public class TrafficStatsCompat { + ctor public TrafficStatsCompat(); + method public static void clearThreadStatsTag(); + method public static int getThreadStatsTag(); + method public static void incrementOperationCount(int); + method public static void incrementOperationCount(int, int); + method public static void setThreadStatsTag(int); + method public static void tagSocket(java.net.Socket) throws java.net.SocketException; + method public static void untagSocket(java.net.Socket) throws java.net.SocketException; + } + +} + +package android.support.v4.os { + + public class AsyncTaskCompat { + ctor public AsyncTaskCompat(); + method public static android.os.AsyncTask<Params, Progress, Result> executeParallel(android.os.AsyncTask<Params, Progress, Result>, Params...); + } + + public final class CancellationSignal { + ctor public CancellationSignal(); + method public void cancel(); + method public java.lang.Object getCancellationSignalObject(); + method public boolean isCanceled(); + method public void setOnCancelListener(android.support.v4.os.CancellationSignal.OnCancelListener); + method public void throwIfCanceled(); + } + + public static abstract interface CancellationSignal.OnCancelListener { + method public abstract void onCancel(); + } + + public class EnvironmentCompat { + ctor public EnvironmentCompat(); + method public static java.lang.String getStorageState(java.io.File); + field public static final java.lang.String MEDIA_UNKNOWN = "unknown"; + } + + public class OperationCanceledException extends java.lang.RuntimeException { + ctor public OperationCanceledException(); + ctor public OperationCanceledException(java.lang.String); + } + + public class ParcelableCompat { + ctor public ParcelableCompat(); + method public static android.os.Parcelable.Creator<T> newCreator(android.support.v4.os.ParcelableCompatCreatorCallbacks<T>); + } + + public abstract interface ParcelableCompatCreatorCallbacks { + method public abstract T createFromParcel(android.os.Parcel, java.lang.ClassLoader); + method public abstract T[] newArray(int); + } + + public class TraceCompat { + ctor public TraceCompat(); + method public static void beginSection(java.lang.String); + method public static void endSection(); + } + +} + +package android.support.v4.print { + + public final class PrintHelper { + ctor public PrintHelper(android.content.Context); + method public int getColorMode(); + method public int getOrientation(); + method public int getScaleMode(); + method public void printBitmap(java.lang.String, android.graphics.Bitmap); + method public void printBitmap(java.lang.String, android.graphics.Bitmap, android.support.v4.print.PrintHelper.OnPrintFinishCallback); + method public void printBitmap(java.lang.String, android.net.Uri) throws java.io.FileNotFoundException; + method public void printBitmap(java.lang.String, android.net.Uri, android.support.v4.print.PrintHelper.OnPrintFinishCallback) throws java.io.FileNotFoundException; + method public void setColorMode(int); + method public void setOrientation(int); + method public void setScaleMode(int); + method public static boolean systemSupportsPrint(); + field public static final int COLOR_MODE_COLOR = 2; // 0x2 + field public static final int COLOR_MODE_MONOCHROME = 1; // 0x1 + field public static final int ORIENTATION_LANDSCAPE = 1; // 0x1 + field public static final int ORIENTATION_PORTRAIT = 2; // 0x2 + field public static final int SCALE_MODE_FILL = 2; // 0x2 + field public static final int SCALE_MODE_FIT = 1; // 0x1 + } + + public static abstract interface PrintHelper.OnPrintFinishCallback { + method public abstract void onFinish(); + } + +} + +package android.support.v4.provider { + + public abstract class DocumentFile { + method public abstract boolean canRead(); + method public abstract boolean canWrite(); + method public abstract android.support.v4.provider.DocumentFile createDirectory(java.lang.String); + method public abstract android.support.v4.provider.DocumentFile createFile(java.lang.String, java.lang.String); + method public abstract boolean delete(); + method public abstract boolean exists(); + method public android.support.v4.provider.DocumentFile findFile(java.lang.String); + method public static android.support.v4.provider.DocumentFile fromFile(java.io.File); + method public static android.support.v4.provider.DocumentFile fromSingleUri(android.content.Context, android.net.Uri); + method public static android.support.v4.provider.DocumentFile fromTreeUri(android.content.Context, android.net.Uri); + method public abstract java.lang.String getName(); + method public android.support.v4.provider.DocumentFile getParentFile(); + method public abstract java.lang.String getType(); + method public abstract android.net.Uri getUri(); + method public abstract boolean isDirectory(); + method public static boolean isDocumentUri(android.content.Context, android.net.Uri); + method public abstract boolean isFile(); + method public abstract long lastModified(); + method public abstract long length(); + method public abstract android.support.v4.provider.DocumentFile[] listFiles(); + method public abstract boolean renameTo(java.lang.String); + } + +} + +package android.support.v4.text { + + public final class BidiFormatter { + method public static android.support.v4.text.BidiFormatter getInstance(); + method public static android.support.v4.text.BidiFormatter getInstance(boolean); + method public static android.support.v4.text.BidiFormatter getInstance(java.util.Locale); + method public boolean getStereoReset(); + method public boolean isRtl(java.lang.String); + method public boolean isRtlContext(); + method public java.lang.String unicodeWrap(java.lang.String, android.support.v4.text.TextDirectionHeuristicCompat, boolean); + method public java.lang.String unicodeWrap(java.lang.String, android.support.v4.text.TextDirectionHeuristicCompat); + method public java.lang.String unicodeWrap(java.lang.String, boolean); + method public java.lang.String unicodeWrap(java.lang.String); + } + + public static final class BidiFormatter.Builder { + ctor public BidiFormatter.Builder(); + ctor public BidiFormatter.Builder(boolean); + ctor public BidiFormatter.Builder(java.util.Locale); + method public android.support.v4.text.BidiFormatter build(); + method public android.support.v4.text.BidiFormatter.Builder setTextDirectionHeuristic(android.support.v4.text.TextDirectionHeuristicCompat); + method public android.support.v4.text.BidiFormatter.Builder stereoReset(boolean); + } + + public class ICUCompat { + ctor public ICUCompat(); + method public static java.lang.String maximizeAndGetScript(java.util.Locale); + } + + public abstract interface TextDirectionHeuristicCompat { + method public abstract boolean isRtl(char[], int, int); + method public abstract boolean isRtl(java.lang.CharSequence, int, int); + } + + public class TextDirectionHeuristicsCompat { + ctor public TextDirectionHeuristicsCompat(); + field public static final android.support.v4.text.TextDirectionHeuristicCompat ANYRTL_LTR; + field public static final android.support.v4.text.TextDirectionHeuristicCompat FIRSTSTRONG_LTR; + field public static final android.support.v4.text.TextDirectionHeuristicCompat FIRSTSTRONG_RTL; + field public static final android.support.v4.text.TextDirectionHeuristicCompat LOCALE; + field public static final android.support.v4.text.TextDirectionHeuristicCompat LTR; + field public static final android.support.v4.text.TextDirectionHeuristicCompat RTL; + } + + public class TextUtilsCompat { + ctor public TextUtilsCompat(); + method public static int getLayoutDirectionFromLocale(java.util.Locale); + method public static java.lang.String htmlEncode(java.lang.String); + field public static final java.util.Locale ROOT; + } + +} + +package android.support.v4.util { + + public class ArrayMap extends android.support.v4.util.SimpleArrayMap implements java.util.Map { + ctor public ArrayMap(); + ctor public ArrayMap(int); + ctor public ArrayMap(android.support.v4.util.SimpleArrayMap); + method public boolean containsAll(java.util.Collection<?>); + method public java.util.Set<java.util.Map.Entry<K, V>> entrySet(); + method public java.util.Set<K> keySet(); + method public void putAll(java.util.Map<? extends K, ? extends V>); + method public boolean removeAll(java.util.Collection<?>); + method public boolean retainAll(java.util.Collection<?>); + method public java.util.Collection<V> values(); + } + + public class AtomicFile { + ctor public AtomicFile(java.io.File); + method public void delete(); + method public void failWrite(java.io.FileOutputStream); + method public void finishWrite(java.io.FileOutputStream); + method public java.io.File getBaseFile(); + method public java.io.FileInputStream openRead() throws java.io.FileNotFoundException; + method public byte[] readFully() throws java.io.IOException; + method public java.io.FileOutputStream startWrite() throws java.io.IOException; + } + + public final class CircularArray { + ctor public CircularArray(); + ctor public CircularArray(int); + method public void addFirst(E); + method public void addLast(E); + method public void clear(); + method public E get(int); + method public E getFirst(); + method public E getLast(); + method public boolean isEmpty(); + method public E popFirst(); + method public E popLast(); + method public void removeFromEnd(int); + method public void removeFromStart(int); + method public int size(); + } + + public final class CircularIntArray { + ctor public CircularIntArray(); + ctor public CircularIntArray(int); + method public void addFirst(int); + method public void addLast(int); + method public void clear(); + method public int get(int); + method public int getFirst(); + method public int getLast(); + method public boolean isEmpty(); + method public int popFirst(); + method public int popLast(); + method public void removeFromEnd(int); + method public void removeFromStart(int); + method public int size(); + } + + public class LongSparseArray { + ctor public LongSparseArray(); + ctor public LongSparseArray(int); + method public void append(long, E); + method public void clear(); + method public android.support.v4.util.LongSparseArray<E> clone(); + method public void delete(long); + method public E get(long); + method public E get(long, E); + method public int indexOfKey(long); + method public int indexOfValue(E); + method public long keyAt(int); + method public void put(long, E); + method public void remove(long); + method public void removeAt(int); + method public void setValueAt(int, E); + method public int size(); + method public E valueAt(int); + } + + public class LruCache { + ctor public LruCache(int); + method protected V create(K); + method public final synchronized int createCount(); + method protected void entryRemoved(boolean, K, V, V); + method public final void evictAll(); + method public final synchronized int evictionCount(); + method public final V get(K); + method public final synchronized int hitCount(); + method public final synchronized int maxSize(); + method public final synchronized int missCount(); + method public final V put(K, V); + method public final synchronized int putCount(); + method public final V remove(K); + method public void resize(int); + method public final synchronized int size(); + method protected int sizeOf(K, V); + method public final synchronized java.util.Map<K, V> snapshot(); + method public final synchronized java.lang.String toString(); + method public void trimToSize(int); + } + + public class Pair { + ctor public Pair(F, S); + method public static android.support.v4.util.Pair<A, B> create(A, B); + field public final F first; + field public final S second; + } + + public final class Pools { + } + + public static abstract interface Pools.Pool { + method public abstract T acquire(); + method public abstract boolean release(T); + } + + public static class Pools.SimplePool implements android.support.v4.util.Pools.Pool { + ctor public Pools.SimplePool(int); + method public T acquire(); + method public boolean release(T); + } + + public static class Pools.SynchronizedPool extends android.support.v4.util.Pools.SimplePool { + ctor public Pools.SynchronizedPool(int); + } + + public class SimpleArrayMap { + ctor public SimpleArrayMap(); + ctor public SimpleArrayMap(int); + ctor public SimpleArrayMap(android.support.v4.util.SimpleArrayMap); + method public void clear(); + method public boolean containsKey(java.lang.Object); + method public boolean containsValue(java.lang.Object); + method public void ensureCapacity(int); + method public V get(java.lang.Object); + method public int indexOfKey(java.lang.Object); + method public boolean isEmpty(); + method public K keyAt(int); + method public V put(K, V); + method public void putAll(android.support.v4.util.SimpleArrayMap<? extends K, ? extends V>); + method public V remove(java.lang.Object); + method public V removeAt(int); + method public V setValueAt(int, V); + method public int size(); + method public V valueAt(int); + } + + public class SparseArrayCompat { + ctor public SparseArrayCompat(); + ctor public SparseArrayCompat(int); + method public void append(int, E); + method public void clear(); + method public android.support.v4.util.SparseArrayCompat<E> clone(); + method public void delete(int); + method public E get(int); + method public E get(int, E); + method public int indexOfKey(int); + method public int indexOfValue(E); + method public int keyAt(int); + method public void put(int, E); + method public void remove(int); + method public void removeAt(int); + method public void removeAtRange(int, int); + method public void setValueAt(int, E); + method public int size(); + method public E valueAt(int); + } + +} + +package android.support.v4.view { + + public class AccessibilityDelegateCompat { + ctor public AccessibilityDelegateCompat(); + method public boolean dispatchPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent); + method public android.support.v4.view.accessibility.AccessibilityNodeProviderCompat getAccessibilityNodeProvider(android.view.View); + method public void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent); + method public void onInitializeAccessibilityNodeInfo(android.view.View, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat); + method public void onPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent); + method public boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent); + method public boolean performAccessibilityAction(android.view.View, int, android.os.Bundle); + method public void sendAccessibilityEvent(android.view.View, int); + method public void sendAccessibilityEventUnchecked(android.view.View, android.view.accessibility.AccessibilityEvent); + } + + public abstract class ActionProvider { + ctor public ActionProvider(android.content.Context); + method public android.content.Context getContext(); + method public boolean hasSubMenu(); + method public boolean isVisible(); + method public abstract android.view.View onCreateActionView(); + method public android.view.View onCreateActionView(android.view.MenuItem); + method public boolean onPerformDefaultAction(); + method public void onPrepareSubMenu(android.view.SubMenu); + method public boolean overridesItemVisibility(); + method public void refreshVisibility(); + method public void setVisibilityListener(android.support.v4.view.ActionProvider.VisibilityListener); + } + + public static abstract interface ActionProvider.VisibilityListener { + method public abstract void onActionProviderVisibilityChanged(boolean); + } + + public class GestureDetectorCompat { + ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener); + ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler); + method public boolean isLongpressEnabled(); + method public boolean onTouchEvent(android.view.MotionEvent); + method public void setIsLongpressEnabled(boolean); + method public void setOnDoubleTapListener(android.view.GestureDetector.OnDoubleTapListener); + } + + public class GravityCompat { + ctor public GravityCompat(); + method public static void apply(int, int, int, android.graphics.Rect, android.graphics.Rect, int); + method public static void apply(int, int, int, android.graphics.Rect, int, int, android.graphics.Rect, int); + method public static void applyDisplay(int, android.graphics.Rect, android.graphics.Rect, int); + method public static int getAbsoluteGravity(int, int); + field public static final int END = 8388613; // 0x800005 + field public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = 8388615; // 0x800007 + field public static final int RELATIVE_LAYOUT_DIRECTION = 8388608; // 0x800000 + field public static final int START = 8388611; // 0x800003 + } + + public class InputDeviceCompat { + ctor public InputDeviceCompat(); + field public static final int SOURCE_ANY = -256; // 0xffffff00 + field public static final int SOURCE_CLASS_BUTTON = 1; // 0x1 + field public static final int SOURCE_CLASS_JOYSTICK = 16; // 0x10 + field public static final int SOURCE_CLASS_MASK = 255; // 0xff + field public static final int SOURCE_CLASS_NONE = 0; // 0x0 + field public static final int SOURCE_CLASS_POINTER = 2; // 0x2 + field public static final int SOURCE_CLASS_POSITION = 8; // 0x8 + field public static final int SOURCE_CLASS_TRACKBALL = 4; // 0x4 + field public static final int SOURCE_DPAD = 513; // 0x201 + field public static final int SOURCE_GAMEPAD = 1025; // 0x401 + field public static final int SOURCE_HDMI = 33554433; // 0x2000001 + field public static final int SOURCE_JOYSTICK = 16777232; // 0x1000010 + field public static final int SOURCE_KEYBOARD = 257; // 0x101 + field public static final int SOURCE_MOUSE = 8194; // 0x2002 + field public static final int SOURCE_STYLUS = 16386; // 0x4002 + field public static final int SOURCE_TOUCHPAD = 1048584; // 0x100008 + field public static final int SOURCE_TOUCHSCREEN = 4098; // 0x1002 + field public static final int SOURCE_TOUCH_NAVIGATION = 2097152; // 0x200000 + field public static final int SOURCE_TRACKBALL = 65540; // 0x10004 + field public static final int SOURCE_UNKNOWN = 0; // 0x0 + } + + public class KeyEventCompat { + ctor public KeyEventCompat(); + method public static boolean dispatch(android.view.KeyEvent, android.view.KeyEvent.Callback, java.lang.Object, java.lang.Object); + method public static java.lang.Object getKeyDispatcherState(android.view.View); + method public static boolean hasModifiers(android.view.KeyEvent, int); + method public static boolean hasNoModifiers(android.view.KeyEvent); + method public static boolean isTracking(android.view.KeyEvent); + method public static boolean metaStateHasModifiers(int, int); + method public static boolean metaStateHasNoModifiers(int); + method public static int normalizeMetaState(int); + method public static void startTracking(android.view.KeyEvent); + } + + public class LayoutInflaterCompat { + method public static void setFactory(android.view.LayoutInflater, android.support.v4.view.LayoutInflaterFactory); + } + + public abstract interface LayoutInflaterFactory { + method public abstract android.view.View onCreateView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet); + } + + public class MarginLayoutParamsCompat { + ctor public MarginLayoutParamsCompat(); + method public static int getLayoutDirection(android.view.ViewGroup.MarginLayoutParams); + method public static int getMarginEnd(android.view.ViewGroup.MarginLayoutParams); + method public static int getMarginStart(android.view.ViewGroup.MarginLayoutParams); + method public static boolean isMarginRelative(android.view.ViewGroup.MarginLayoutParams); + method public static void resolveLayoutDirection(android.view.ViewGroup.MarginLayoutParams, int); + method public static void setLayoutDirection(android.view.ViewGroup.MarginLayoutParams, int); + method public static void setMarginEnd(android.view.ViewGroup.MarginLayoutParams, int); + method public static void setMarginStart(android.view.ViewGroup.MarginLayoutParams, int); + } + + public class MenuCompat { + ctor public MenuCompat(); + method public static deprecated void setShowAsAction(android.view.MenuItem, int); + } + + public class MenuItemCompat { + ctor public MenuItemCompat(); + method public static boolean collapseActionView(android.view.MenuItem); + method public static boolean expandActionView(android.view.MenuItem); + method public static android.support.v4.view.ActionProvider getActionProvider(android.view.MenuItem); + method public static android.view.View getActionView(android.view.MenuItem); + method public static boolean isActionViewExpanded(android.view.MenuItem); + method public static android.view.MenuItem setActionProvider(android.view.MenuItem, android.support.v4.view.ActionProvider); + method public static android.view.MenuItem setActionView(android.view.MenuItem, android.view.View); + method public static android.view.MenuItem setActionView(android.view.MenuItem, int); + method public static android.view.MenuItem setOnActionExpandListener(android.view.MenuItem, android.support.v4.view.MenuItemCompat.OnActionExpandListener); + method public static void setShowAsAction(android.view.MenuItem, int); + field public static final int SHOW_AS_ACTION_ALWAYS = 2; // 0x2 + field public static final int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8; // 0x8 + field public static final int SHOW_AS_ACTION_IF_ROOM = 1; // 0x1 + field public static final int SHOW_AS_ACTION_NEVER = 0; // 0x0 + field public static final int SHOW_AS_ACTION_WITH_TEXT = 4; // 0x4 + } + + public static abstract interface MenuItemCompat.OnActionExpandListener { + method public abstract boolean onMenuItemActionCollapse(android.view.MenuItem); + method public abstract boolean onMenuItemActionExpand(android.view.MenuItem); + } + + public class MotionEventCompat { + ctor public MotionEventCompat(); + method public static int findPointerIndex(android.view.MotionEvent, int); + method public static int getActionIndex(android.view.MotionEvent); + method public static int getActionMasked(android.view.MotionEvent); + method public static float getAxisValue(android.view.MotionEvent, int); + method public static float getAxisValue(android.view.MotionEvent, int, int); + method public static int getPointerCount(android.view.MotionEvent); + method public static int getPointerId(android.view.MotionEvent, int); + method public static int getSource(android.view.MotionEvent); + method public static float getX(android.view.MotionEvent, int); + method public static float getY(android.view.MotionEvent, int); + field public static final int ACTION_HOVER_ENTER = 9; // 0x9 + field public static final int ACTION_HOVER_EXIT = 10; // 0xa + field public static final int ACTION_HOVER_MOVE = 7; // 0x7 + field public static final int ACTION_MASK = 255; // 0xff + field public static final int ACTION_POINTER_DOWN = 5; // 0x5 + field public static final int ACTION_POINTER_INDEX_MASK = 65280; // 0xff00 + field public static final int ACTION_POINTER_INDEX_SHIFT = 8; // 0x8 + field public static final int ACTION_POINTER_UP = 6; // 0x6 + field public static final int ACTION_SCROLL = 8; // 0x8 + field public static final int AXIS_BRAKE = 23; // 0x17 + field public static final int AXIS_DISTANCE = 24; // 0x18 + field public static final int AXIS_GAS = 22; // 0x16 + field public static final int AXIS_GENERIC_1 = 32; // 0x20 + field public static final int AXIS_GENERIC_10 = 41; // 0x29 + field public static final int AXIS_GENERIC_11 = 42; // 0x2a + field public static final int AXIS_GENERIC_12 = 43; // 0x2b + field public static final int AXIS_GENERIC_13 = 44; // 0x2c + field public static final int AXIS_GENERIC_14 = 45; // 0x2d + field public static final int AXIS_GENERIC_15 = 46; // 0x2e + field public static final int AXIS_GENERIC_16 = 47; // 0x2f + field public static final int AXIS_GENERIC_2 = 33; // 0x21 + field public static final int AXIS_GENERIC_3 = 34; // 0x22 + field public static final int AXIS_GENERIC_4 = 35; // 0x23 + field public static final int AXIS_GENERIC_5 = 36; // 0x24 + field public static final int AXIS_GENERIC_6 = 37; // 0x25 + field public static final int AXIS_GENERIC_7 = 38; // 0x26 + field public static final int AXIS_GENERIC_8 = 39; // 0x27 + field public static final int AXIS_GENERIC_9 = 40; // 0x28 + field public static final int AXIS_HAT_X = 15; // 0xf + field public static final int AXIS_HAT_Y = 16; // 0x10 + field public static final int AXIS_HSCROLL = 10; // 0xa + field public static final int AXIS_LTRIGGER = 17; // 0x11 + field public static final int AXIS_ORIENTATION = 8; // 0x8 + field public static final int AXIS_PRESSURE = 2; // 0x2 + field public static final int AXIS_RTRIGGER = 18; // 0x12 + field public static final int AXIS_RUDDER = 20; // 0x14 + field public static final int AXIS_RX = 12; // 0xc + field public static final int AXIS_RY = 13; // 0xd + field public static final int AXIS_RZ = 14; // 0xe + field public static final int AXIS_SIZE = 3; // 0x3 + field public static final int AXIS_THROTTLE = 19; // 0x13 + field public static final int AXIS_TILT = 25; // 0x19 + field public static final int AXIS_TOOL_MAJOR = 6; // 0x6 + field public static final int AXIS_TOOL_MINOR = 7; // 0x7 + field public static final int AXIS_TOUCH_MAJOR = 4; // 0x4 + field public static final int AXIS_TOUCH_MINOR = 5; // 0x5 + field public static final int AXIS_VSCROLL = 9; // 0x9 + field public static final int AXIS_WHEEL = 21; // 0x15 + field public static final int AXIS_X = 0; // 0x0 + field public static final int AXIS_Y = 1; // 0x1 + field public static final int AXIS_Z = 11; // 0xb + } + + public abstract interface NestedScrollingChild { + method public abstract boolean dispatchNestedFling(float, float, boolean); + method public abstract boolean dispatchNestedPreFling(float, float); + method public abstract boolean dispatchNestedPreScroll(int, int, int[], int[]); + method public abstract boolean dispatchNestedScroll(int, int, int, int, int[]); + method public abstract boolean hasNestedScrollingParent(); + method public abstract boolean isNestedScrollingEnabled(); + method public abstract void setNestedScrollingEnabled(boolean); + method public abstract boolean startNestedScroll(int); + method public abstract void stopNestedScroll(); + } + + public class NestedScrollingChildHelper { + ctor public NestedScrollingChildHelper(android.view.View); + method public boolean dispatchNestedFling(float, float, boolean); + method public boolean dispatchNestedPreFling(float, float); + method public boolean dispatchNestedPreScroll(int, int, int[], int[]); + method public boolean dispatchNestedScroll(int, int, int, int, int[]); + method public boolean hasNestedScrollingParent(); + method public boolean isNestedScrollingEnabled(); + method public void onDetachedFromWindow(); + method public void onStopNestedScroll(android.view.View); + method public void setNestedScrollingEnabled(boolean); + method public boolean startNestedScroll(int); + method public void stopNestedScroll(); + } + + public abstract interface NestedScrollingParent { + method public abstract int getNestedScrollAxes(); + method public abstract boolean onNestedFling(android.view.View, float, float, boolean); + method public abstract boolean onNestedPreFling(android.view.View, float, float); + method public abstract void onNestedPreScroll(android.view.View, int, int, int[]); + method public abstract void onNestedScroll(android.view.View, int, int, int, int); + method public abstract void onNestedScrollAccepted(android.view.View, android.view.View, int); + method public abstract boolean onStartNestedScroll(android.view.View, android.view.View, int); + method public abstract void onStopNestedScroll(android.view.View); + } + + public class NestedScrollingParentHelper { + ctor public NestedScrollingParentHelper(android.view.ViewGroup); + method public int getNestedScrollAxes(); + method public void onNestedScrollAccepted(android.view.View, android.view.View, int); + method public void onStopNestedScroll(android.view.View); + } + + public abstract interface OnApplyWindowInsetsListener { + method public abstract android.support.v4.view.WindowInsetsCompat onApplyWindowInsets(android.view.View, android.support.v4.view.WindowInsetsCompat); + } + + public abstract class PagerAdapter { + ctor public PagerAdapter(); + method public void destroyItem(android.view.ViewGroup, int, java.lang.Object); + method public deprecated void destroyItem(android.view.View, int, java.lang.Object); + method public void finishUpdate(android.view.ViewGroup); + method public deprecated void finishUpdate(android.view.View); + method public abstract int getCount(); + method public int getItemPosition(java.lang.Object); + method public java.lang.CharSequence getPageTitle(int); + method public float getPageWidth(int); + method public java.lang.Object instantiateItem(android.view.ViewGroup, int); + method public deprecated java.lang.Object instantiateItem(android.view.View, int); + method public abstract boolean isViewFromObject(android.view.View, java.lang.Object); + method public void notifyDataSetChanged(); + method public void registerDataSetObserver(android.database.DataSetObserver); + method public void restoreState(android.os.Parcelable, java.lang.ClassLoader); + method public android.os.Parcelable saveState(); + method public void setPrimaryItem(android.view.ViewGroup, int, java.lang.Object); + method public deprecated void setPrimaryItem(android.view.View, int, java.lang.Object); + method public void startUpdate(android.view.ViewGroup); + method public deprecated void startUpdate(android.view.View); + method public void unregisterDataSetObserver(android.database.DataSetObserver); + field public static final int POSITION_NONE = -2; // 0xfffffffe + field public static final int POSITION_UNCHANGED = -1; // 0xffffffff + } + + public class PagerTabStrip extends android.support.v4.view.PagerTitleStrip { + ctor public PagerTabStrip(android.content.Context); + ctor public PagerTabStrip(android.content.Context, android.util.AttributeSet); + method public boolean getDrawFullUnderline(); + method public int getTabIndicatorColor(); + method public void setDrawFullUnderline(boolean); + method public void setTabIndicatorColor(int); + method public void setTabIndicatorColorResource(int); + } + + public class PagerTitleStrip extends android.view.ViewGroup { + ctor public PagerTitleStrip(android.content.Context); + ctor public PagerTitleStrip(android.content.Context, android.util.AttributeSet); + method public int getTextSpacing(); + method protected void onLayout(boolean, int, int, int, int); + method public void setGravity(int); + method public void setNonPrimaryAlpha(float); + method public void setTextColor(int); + method public void setTextSize(int, float); + method public void setTextSpacing(int); + } + + public class ScaleGestureDetectorCompat { + method public static boolean isQuickScaleEnabled(java.lang.Object); + method public static void setQuickScaleEnabled(java.lang.Object, boolean); + } + + public abstract interface ScrollingView { + method public abstract int computeHorizontalScrollExtent(); + method public abstract int computeHorizontalScrollOffset(); + method public abstract int computeHorizontalScrollRange(); + method public abstract int computeVerticalScrollExtent(); + method public abstract int computeVerticalScrollOffset(); + method public abstract int computeVerticalScrollRange(); + } + + public class VelocityTrackerCompat { + ctor public VelocityTrackerCompat(); + method public static float getXVelocity(android.view.VelocityTracker, int); + method public static float getYVelocity(android.view.VelocityTracker, int); + } + + public class ViewCompat { + ctor public ViewCompat(); + method public static android.support.v4.view.ViewPropertyAnimatorCompat animate(android.view.View); + method public static boolean canScrollHorizontally(android.view.View, int); + method public static boolean canScrollVertically(android.view.View, int); + method public static int combineMeasuredStates(int, int); + method public static android.support.v4.view.WindowInsetsCompat dispatchApplyWindowInsets(android.view.View, android.support.v4.view.WindowInsetsCompat); + method public static void dispatchFinishTemporaryDetach(android.view.View); + method public static boolean dispatchNestedFling(android.view.View, float, float, boolean); + method public static boolean dispatchNestedPreFling(android.view.View, float, float); + method public static boolean dispatchNestedPreScroll(android.view.View, int, int, int[], int[]); + method public static boolean dispatchNestedScroll(android.view.View, int, int, int, int, int[]); + method public static void dispatchStartTemporaryDetach(android.view.View); + method public static int getAccessibilityLiveRegion(android.view.View); + method public static android.support.v4.view.accessibility.AccessibilityNodeProviderCompat getAccessibilityNodeProvider(android.view.View); + method public static float getAlpha(android.view.View); + method public static android.content.res.ColorStateList getBackgroundTintList(android.view.View); + method public static android.graphics.PorterDuff.Mode getBackgroundTintMode(android.view.View); + method public static android.graphics.Rect getClipBounds(android.view.View); + method public static float getElevation(android.view.View); + method public static boolean getFitsSystemWindows(android.view.View); + method public static int getImportantForAccessibility(android.view.View); + method public static int getLabelFor(android.view.View); + method public static int getLayerType(android.view.View); + method public static int getLayoutDirection(android.view.View); + method public static int getMeasuredHeightAndState(android.view.View); + method public static int getMeasuredState(android.view.View); + method public static int getMeasuredWidthAndState(android.view.View); + method public static int getMinimumHeight(android.view.View); + method public static int getMinimumWidth(android.view.View); + method public static int getOverScrollMode(android.view.View); + method public static int getPaddingEnd(android.view.View); + method public static int getPaddingStart(android.view.View); + method public static android.view.ViewParent getParentForAccessibility(android.view.View); + method public static float getPivotX(android.view.View); + method public static float getPivotY(android.view.View); + method public static float getRotation(android.view.View); + method public static float getRotationX(android.view.View); + method public static float getRotationY(android.view.View); + method public static float getScaleX(android.view.View); + method public static float getScaleY(android.view.View); + method public static int getScrollIndicators(android.view.View); + method public static java.lang.String getTransitionName(android.view.View); + method public static float getTranslationX(android.view.View); + method public static float getTranslationY(android.view.View); + method public static float getTranslationZ(android.view.View); + method public static int getWindowSystemUiVisibility(android.view.View); + method public static float getX(android.view.View); + method public static float getY(android.view.View); + method public static float getZ(android.view.View); + method public static boolean hasAccessibilityDelegate(android.view.View); + method public static boolean hasNestedScrollingParent(android.view.View); + method public static boolean hasOnClickListeners(android.view.View); + method public static boolean hasOverlappingRendering(android.view.View); + method public static boolean hasTransientState(android.view.View); + method public static boolean isAttachedToWindow(android.view.View); + method public static boolean isLaidOut(android.view.View); + method public static boolean isNestedScrollingEnabled(android.view.View); + method public static boolean isOpaque(android.view.View); + method public static boolean isPaddingRelative(android.view.View); + method public static void jumpDrawablesToCurrentState(android.view.View); + method public static void offsetLeftAndRight(android.view.View, int); + method public static void offsetTopAndBottom(android.view.View, int); + method public static android.support.v4.view.WindowInsetsCompat onApplyWindowInsets(android.view.View, android.support.v4.view.WindowInsetsCompat); + method public static void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent); + method public static void onInitializeAccessibilityNodeInfo(android.view.View, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat); + method public static void onPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent); + method public static boolean performAccessibilityAction(android.view.View, int, android.os.Bundle); + method public static void postInvalidateOnAnimation(android.view.View); + method public static void postInvalidateOnAnimation(android.view.View, int, int, int, int); + method public static void postOnAnimation(android.view.View, java.lang.Runnable); + method public static void postOnAnimationDelayed(android.view.View, java.lang.Runnable, long); + method public static void requestApplyInsets(android.view.View); + method public static int resolveSizeAndState(int, int, int); + method public static void setAccessibilityDelegate(android.view.View, android.support.v4.view.AccessibilityDelegateCompat); + method public static void setAccessibilityLiveRegion(android.view.View, int); + method public static void setActivated(android.view.View, boolean); + method public static void setAlpha(android.view.View, float); + method public static void setBackgroundTintList(android.view.View, android.content.res.ColorStateList); + method public static void setBackgroundTintMode(android.view.View, android.graphics.PorterDuff.Mode); + method public static void setChildrenDrawingOrderEnabled(android.view.ViewGroup, boolean); + method public static void setClipBounds(android.view.View, android.graphics.Rect); + method public static void setElevation(android.view.View, float); + method public static void setFitsSystemWindows(android.view.View, boolean); + method public static void setHasTransientState(android.view.View, boolean); + method public static void setImportantForAccessibility(android.view.View, int); + method public static void setLabelFor(android.view.View, int); + method public static void setLayerPaint(android.view.View, android.graphics.Paint); + method public static void setLayerType(android.view.View, int, android.graphics.Paint); + method public static void setLayoutDirection(android.view.View, int); + method public static void setNestedScrollingEnabled(android.view.View, boolean); + method public static void setOnApplyWindowInsetsListener(android.view.View, android.support.v4.view.OnApplyWindowInsetsListener); + method public static void setOverScrollMode(android.view.View, int); + method public static void setPaddingRelative(android.view.View, int, int, int, int); + method public static void setPivotX(android.view.View, float); + method public static void setPivotY(android.view.View, float); + method public static void setRotation(android.view.View, float); + method public static void setRotationX(android.view.View, float); + method public static void setRotationY(android.view.View, float); + method public static void setSaveFromParentEnabled(android.view.View, boolean); + method public static void setScaleX(android.view.View, float); + method public static void setScaleY(android.view.View, float); + method public static void setScrollIndicators(android.view.View, int); + method public static void setScrollIndicators(android.view.View, int, int); + method public static void setTransitionName(android.view.View, java.lang.String); + method public static void setTranslationX(android.view.View, float); + method public static void setTranslationY(android.view.View, float); + method public static void setTranslationZ(android.view.View, float); + method public static void setX(android.view.View, float); + method public static void setY(android.view.View, float); + method public static boolean startNestedScroll(android.view.View, int); + method public static void stopNestedScroll(android.view.View); + field public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 2; // 0x2 + field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0 + field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1 + field public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0; // 0x0 + field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2 + field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 4; // 0x4 + field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1 + field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2 + field public static final int LAYER_TYPE_NONE = 0; // 0x0 + field public static final int LAYER_TYPE_SOFTWARE = 1; // 0x1 + field public static final int LAYOUT_DIRECTION_INHERIT = 2; // 0x2 + field public static final int LAYOUT_DIRECTION_LOCALE = 3; // 0x3 + field public static final int LAYOUT_DIRECTION_LTR = 0; // 0x0 + field public static final int LAYOUT_DIRECTION_RTL = 1; // 0x1 + field public static final int MEASURED_HEIGHT_STATE_SHIFT = 16; // 0x10 + field public static final int MEASURED_SIZE_MASK = 16777215; // 0xffffff + field public static final int MEASURED_STATE_MASK = -16777216; // 0xff000000 + field public static final int MEASURED_STATE_TOO_SMALL = 16777216; // 0x1000000 + field public static final int OVER_SCROLL_ALWAYS = 0; // 0x0 + field public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; // 0x1 + field public static final int OVER_SCROLL_NEVER = 2; // 0x2 + field public static final int SCROLL_AXIS_HORIZONTAL = 1; // 0x1 + field public static final int SCROLL_AXIS_NONE = 0; // 0x0 + field public static final int SCROLL_AXIS_VERTICAL = 2; // 0x2 + field public static final int SCROLL_INDICATOR_BOTTOM = 2; // 0x2 + field public static final int SCROLL_INDICATOR_END = 32; // 0x20 + field public static final int SCROLL_INDICATOR_LEFT = 4; // 0x4 + field public static final int SCROLL_INDICATOR_RIGHT = 8; // 0x8 + field public static final int SCROLL_INDICATOR_START = 16; // 0x10 + field public static final int SCROLL_INDICATOR_TOP = 1; // 0x1 + } + + public class ViewConfigurationCompat { + ctor public ViewConfigurationCompat(); + method public static int getScaledPagingTouchSlop(android.view.ViewConfiguration); + method public static boolean hasPermanentMenuKey(android.view.ViewConfiguration); + } + + public class ViewGroupCompat { + method public static int getLayoutMode(android.view.ViewGroup); + method public static int getNestedScrollAxes(android.view.ViewGroup); + method public static boolean isTransitionGroup(android.view.ViewGroup); + method public static boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent); + method public static void setLayoutMode(android.view.ViewGroup, int); + method public static void setMotionEventSplittingEnabled(android.view.ViewGroup, boolean); + method public static void setTransitionGroup(android.view.ViewGroup, boolean); + field public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; // 0x0 + field public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; // 0x1 + } + + public class ViewPager extends android.view.ViewGroup { + ctor public ViewPager(android.content.Context); + ctor public ViewPager(android.content.Context, android.util.AttributeSet); + method public void addOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener); + method public boolean arrowScroll(int); + method public boolean beginFakeDrag(); + method protected boolean canScroll(android.view.View, boolean, int, int, int); + method public void clearOnPageChangeListeners(); + method public void endFakeDrag(); + method public boolean executeKeyEvent(android.view.KeyEvent); + method public void fakeDragBy(float); + method public android.support.v4.view.PagerAdapter getAdapter(); + method public int getCurrentItem(); + method public int getOffscreenPageLimit(); + method public int getPageMargin(); + method public boolean isFakeDragging(); + method protected void onLayout(boolean, int, int, int, int); + method protected void onPageScrolled(int, float, int); + method public void onRestoreInstanceState(android.os.Parcelable); + method public android.os.Parcelable onSaveInstanceState(); + method public void removeOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener); + method public void setAdapter(android.support.v4.view.PagerAdapter); + method public void setCurrentItem(int); + method public void setCurrentItem(int, boolean); + method public void setOffscreenPageLimit(int); + method public deprecated void setOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener); + method public void setPageMargin(int); + method public void setPageMarginDrawable(android.graphics.drawable.Drawable); + method public void setPageMarginDrawable(int); + method public void setPageTransformer(boolean, android.support.v4.view.ViewPager.PageTransformer); + field public static final int SCROLL_STATE_DRAGGING = 1; // 0x1 + field public static final int SCROLL_STATE_IDLE = 0; // 0x0 + field public static final int SCROLL_STATE_SETTLING = 2; // 0x2 + } + + public static class ViewPager.LayoutParams extends android.view.ViewGroup.LayoutParams { + ctor public ViewPager.LayoutParams(); + ctor public ViewPager.LayoutParams(android.content.Context, android.util.AttributeSet); + field public int gravity; + field public boolean isDecor; + } + + public static abstract interface ViewPager.OnPageChangeListener { + method public abstract void onPageScrollStateChanged(int); + method public abstract void onPageScrolled(int, float, int); + method public abstract void onPageSelected(int); + } + + public static abstract interface ViewPager.PageTransformer { + method public abstract void transformPage(android.view.View, float); + } + + public static class ViewPager.SavedState extends android.view.View.BaseSavedState { + ctor public ViewPager.SavedState(android.os.Parcelable); + field public static final android.os.Parcelable.Creator<android.support.v4.view.ViewPager.SavedState> CREATOR; + } + + public static class ViewPager.SimpleOnPageChangeListener implements android.support.v4.view.ViewPager.OnPageChangeListener { + ctor public ViewPager.SimpleOnPageChangeListener(); + method public void onPageScrollStateChanged(int); + method public void onPageScrolled(int, float, int); + method public void onPageSelected(int); + } + + public class ViewParentCompat { + method public static void notifySubtreeAccessibilityStateChanged(android.view.ViewParent, android.view.View, android.view.View, int); + method public static boolean onNestedFling(android.view.ViewParent, android.view.View, float, float, boolean); + method public static boolean onNestedPreFling(android.view.ViewParent, android.view.View, float, float); + method public static void onNestedPreScroll(android.view.ViewParent, android.view.View, int, int, int[]); + method public static void onNestedScroll(android.view.ViewParent, android.view.View, int, int, int, int); + method public static void onNestedScrollAccepted(android.view.ViewParent, android.view.View, android.view.View, int); + method public static boolean onStartNestedScroll(android.view.ViewParent, android.view.View, android.view.View, int); + method public static void onStopNestedScroll(android.view.ViewParent, android.view.View); + method public static boolean requestSendAccessibilityEvent(android.view.ViewParent, android.view.View, android.view.accessibility.AccessibilityEvent); + } + + public class ViewPropertyAnimatorCompat { + method public android.support.v4.view.ViewPropertyAnimatorCompat alpha(float); + method public android.support.v4.view.ViewPropertyAnimatorCompat alphaBy(float); + method public void cancel(); + method public long getDuration(); + method public android.view.animation.Interpolator getInterpolator(); + method public long getStartDelay(); + method public android.support.v4.view.ViewPropertyAnimatorCompat rotation(float); + method public android.support.v4.view.ViewPropertyAnimatorCompat rotationBy(float); + method public android.support.v4.view.ViewPropertyAnimatorCompat rotationX(float); + method public android.support.v4.view.ViewPropertyAnimatorCompat rotationXBy(float); + method public android.support.v4.view.ViewPropertyAnimatorCompat rotationY(float); + method public android.support.v4.view.ViewPropertyAnimatorCompat rotationYBy(float); + method public android.support.v4.view.ViewPropertyAnimatorCompat scaleX(float); + method public android.support.v4.view.ViewPropertyAnimatorCompat scaleXBy(float); + method public android.support.v4.view.ViewPropertyAnimatorCompat scaleY(float); + method public android.support.v4.view.ViewPropertyAnimatorCompat scaleYBy(float); + method public android.support.v4.view.ViewPropertyAnimatorCompat setDuration(long); + method public android.support.v4.view.ViewPropertyAnimatorCompat setInterpolator(android.view.animation.Interpolator); + method public android.support.v4.view.ViewPropertyAnimatorCompat setListener(android.support.v4.view.ViewPropertyAnimatorListener); + method public android.support.v4.view.ViewPropertyAnimatorCompat setStartDelay(long); + method public android.support.v4.view.ViewPropertyAnimatorCompat setUpdateListener(android.support.v4.view.ViewPropertyAnimatorUpdateListener); + method public void start(); + method public android.support.v4.view.ViewPropertyAnimatorCompat translationX(float); + method public android.support.v4.view.ViewPropertyAnimatorCompat translationXBy(float); + method public android.support.v4.view.ViewPropertyAnimatorCompat translationY(float); + method public android.support.v4.view.ViewPropertyAnimatorCompat translationYBy(float); + method public android.support.v4.view.ViewPropertyAnimatorCompat translationZ(float); + method public android.support.v4.view.ViewPropertyAnimatorCompat translationZBy(float); + method public android.support.v4.view.ViewPropertyAnimatorCompat withEndAction(java.lang.Runnable); + method public android.support.v4.view.ViewPropertyAnimatorCompat withLayer(); + method public android.support.v4.view.ViewPropertyAnimatorCompat withStartAction(java.lang.Runnable); + method public android.support.v4.view.ViewPropertyAnimatorCompat x(float); + method public android.support.v4.view.ViewPropertyAnimatorCompat xBy(float); + method public android.support.v4.view.ViewPropertyAnimatorCompat y(float); + method public android.support.v4.view.ViewPropertyAnimatorCompat yBy(float); + method public android.support.v4.view.ViewPropertyAnimatorCompat z(float); + method public android.support.v4.view.ViewPropertyAnimatorCompat zBy(float); + } + + public abstract interface ViewPropertyAnimatorListener { + method public abstract void onAnimationCancel(android.view.View); + method public abstract void onAnimationEnd(android.view.View); + method public abstract void onAnimationStart(android.view.View); + } + + public class ViewPropertyAnimatorListenerAdapter implements android.support.v4.view.ViewPropertyAnimatorListener { + ctor public ViewPropertyAnimatorListenerAdapter(); + method public void onAnimationCancel(android.view.View); + method public void onAnimationEnd(android.view.View); + method public void onAnimationStart(android.view.View); + } + + public abstract interface ViewPropertyAnimatorUpdateListener { + method public abstract void onAnimationUpdate(android.view.View); + } + + public class WindowCompat { + ctor public WindowCompat(); + field public static final int FEATURE_ACTION_BAR = 8; // 0x8 + field public static final int FEATURE_ACTION_BAR_OVERLAY = 9; // 0x9 + field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa + } + + public class WindowInsetsCompat { + method public android.support.v4.view.WindowInsetsCompat consumeStableInsets(); + method public android.support.v4.view.WindowInsetsCompat consumeSystemWindowInsets(); + method public int getStableInsetBottom(); + method public int getStableInsetLeft(); + method public int getStableInsetRight(); + method public int getStableInsetTop(); + method public int getSystemWindowInsetBottom(); + method public int getSystemWindowInsetLeft(); + method public int getSystemWindowInsetRight(); + method public int getSystemWindowInsetTop(); + method public boolean hasInsets(); + method public boolean hasStableInsets(); + method public boolean hasSystemWindowInsets(); + method public boolean isConsumed(); + method public boolean isRound(); + method public android.support.v4.view.WindowInsetsCompat replaceSystemWindowInsets(int, int, int, int); + method public android.support.v4.view.WindowInsetsCompat replaceSystemWindowInsets(android.graphics.Rect); + } + +} + +package android.support.v4.view.accessibility { + + public class AccessibilityEventCompat { + method public static void appendRecord(android.view.accessibility.AccessibilityEvent, android.support.v4.view.accessibility.AccessibilityRecordCompat); + method public static android.support.v4.view.accessibility.AccessibilityRecordCompat asRecord(android.view.accessibility.AccessibilityEvent); + method public static int getContentChangeTypes(android.view.accessibility.AccessibilityEvent); + method public static android.support.v4.view.accessibility.AccessibilityRecordCompat getRecord(android.view.accessibility.AccessibilityEvent, int); + method public static int getRecordCount(android.view.accessibility.AccessibilityEvent); + method public static void setContentChangeTypes(android.view.accessibility.AccessibilityEvent, int); + field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4 + field public static final int CONTENT_CHANGE_TYPE_SUBTREE = 1; // 0x1 + field public static final int CONTENT_CHANGE_TYPE_TEXT = 2; // 0x2 + field public static final int CONTENT_CHANGE_TYPE_UNDEFINED = 0; // 0x0 + field public static final int TYPES_ALL_MASK = -1; // 0xffffffff + field public static final int TYPE_ANNOUNCEMENT = 16384; // 0x4000 + field public static final int TYPE_GESTURE_DETECTION_END = 524288; // 0x80000 + field public static final int TYPE_GESTURE_DETECTION_START = 262144; // 0x40000 + field public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 1024; // 0x400 + field public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 512; // 0x200 + field public static final int TYPE_TOUCH_INTERACTION_END = 2097152; // 0x200000 + field public static final int TYPE_TOUCH_INTERACTION_START = 1048576; // 0x100000 + field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUSED = 32768; // 0x8000 + field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 65536; // 0x10000 + field public static final int TYPE_VIEW_HOVER_ENTER = 128; // 0x80 + field public static final int TYPE_VIEW_HOVER_EXIT = 256; // 0x100 + field public static final int TYPE_VIEW_SCROLLED = 4096; // 0x1000 + field public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192; // 0x2000 + field public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 131072; // 0x20000 + field public static final int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800 + } + + public class AccessibilityManagerCompat { + ctor public AccessibilityManagerCompat(); + method public static boolean addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat); + method public static java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getEnabledAccessibilityServiceList(android.view.accessibility.AccessibilityManager, int); + method public static java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getInstalledAccessibilityServiceList(android.view.accessibility.AccessibilityManager); + method public static boolean isTouchExplorationEnabled(android.view.accessibility.AccessibilityManager); + method public static boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager, android.support.v4.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat); + } + + public static abstract class AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat { + ctor public AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat(); + method public abstract void onAccessibilityStateChanged(boolean); + } + + public class AccessibilityNodeInfoCompat { + ctor public AccessibilityNodeInfoCompat(java.lang.Object); + method public void addAction(int); + method public void addAction(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat); + method public void addChild(android.view.View); + method public void addChild(android.view.View, int); + method public boolean canOpenPopup(); + method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(java.lang.String); + method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByViewId(java.lang.String); + method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat findFocus(int); + method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat focusSearch(int); + method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat> getActionList(); + method public int getActions(); + method public void getBoundsInParent(android.graphics.Rect); + method public void getBoundsInScreen(android.graphics.Rect); + method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getChild(int); + method public int getChildCount(); + method public java.lang.CharSequence getClassName(); + method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat getCollectionInfo(); + method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat getCollectionItemInfo(); + method public java.lang.CharSequence getContentDescription(); + method public java.lang.CharSequence getError(); + method public android.os.Bundle getExtras(); + method public java.lang.Object getInfo(); + method public int getInputType(); + method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getLabelFor(); + method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getLabeledBy(); + method public int getLiveRegion(); + method public int getMaxTextLength(); + method public int getMovementGranularities(); + method public java.lang.CharSequence getPackageName(); + method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getParent(); + method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat getRangeInfo(); + method public java.lang.CharSequence getText(); + method public int getTextSelectionEnd(); + method public int getTextSelectionStart(); + method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getTraversalAfter(); + method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getTraversalBefore(); + method public java.lang.String getViewIdResourceName(); + method public android.support.v4.view.accessibility.AccessibilityWindowInfoCompat getWindow(); + method public int getWindowId(); + method public boolean isAccessibilityFocused(); + method public boolean isCheckable(); + method public boolean isChecked(); + method public boolean isClickable(); + method public boolean isContentInvalid(); + method public boolean isDismissable(); + method public boolean isEditable(); + method public boolean isEnabled(); + method public boolean isFocusable(); + method public boolean isFocused(); + method public boolean isLongClickable(); + method public boolean isMultiLine(); + method public boolean isPassword(); + method public boolean isScrollable(); + method public boolean isSelected(); + method public boolean isVisibleToUser(); + method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain(android.view.View); + method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain(android.view.View, int); + method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain(); + method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat obtain(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat); + method public boolean performAction(int); + method public boolean performAction(int, android.os.Bundle); + method public void recycle(); + method public boolean refresh(); + method public boolean removeAction(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat); + method public boolean removeChild(android.view.View); + method public boolean removeChild(android.view.View, int); + method public void setAccessibilityFocused(boolean); + method public void setBoundsInParent(android.graphics.Rect); + method public void setBoundsInScreen(android.graphics.Rect); + method public void setCanOpenPopup(boolean); + method public void setCheckable(boolean); + method public void setChecked(boolean); + method public void setClassName(java.lang.CharSequence); + method public void setClickable(boolean); + method public void setCollectionInfo(java.lang.Object); + method public void setCollectionItemInfo(java.lang.Object); + method public void setContentDescription(java.lang.CharSequence); + method public void setContentInvalid(boolean); + method public void setDismissable(boolean); + method public void setEditable(boolean); + method public void setEnabled(boolean); + method public void setError(java.lang.CharSequence); + method public void setFocusable(boolean); + method public void setFocused(boolean); + method public void setInputType(int); + method public void setLabelFor(android.view.View); + method public void setLabelFor(android.view.View, int); + method public void setLabeledBy(android.view.View); + method public void setLabeledBy(android.view.View, int); + method public void setLiveRegion(int); + method public void setLongClickable(boolean); + method public void setMaxTextLength(int); + method public void setMovementGranularities(int); + method public void setMultiLine(boolean); + method public void setPackageName(java.lang.CharSequence); + method public void setParent(android.view.View); + method public void setParent(android.view.View, int); + method public void setPassword(boolean); + method public void setRangeInfo(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat); + method public void setScrollable(boolean); + method public void setSelected(boolean); + method public void setSource(android.view.View); + method public void setSource(android.view.View, int); + method public void setText(java.lang.CharSequence); + method public void setTextSelection(int, int); + method public void setTraversalAfter(android.view.View); + method public void setTraversalAfter(android.view.View, int); + method public void setTraversalBefore(android.view.View); + method public void setTraversalBefore(android.view.View, int); + method public void setViewIdResourceName(java.lang.String); + method public void setVisibleToUser(boolean); + field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40 + field public static final java.lang.String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN"; + field public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING"; + field public static final java.lang.String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT"; + field public static final java.lang.String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT"; + field public static final java.lang.String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT"; + field public static final java.lang.String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE"; + field public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 128; // 0x80 + field public static final int ACTION_CLEAR_FOCUS = 2; // 0x2 + field public static final int ACTION_CLEAR_SELECTION = 8; // 0x8 + field public static final int ACTION_CLICK = 16; // 0x10 + field public static final int ACTION_COLLAPSE = 524288; // 0x80000 + field public static final int ACTION_COPY = 16384; // 0x4000 + field public static final int ACTION_CUT = 65536; // 0x10000 + field public static final int ACTION_DISMISS = 1048576; // 0x100000 + field public static final int ACTION_EXPAND = 262144; // 0x40000 + field public static final int ACTION_FOCUS = 1; // 0x1 + field public static final int ACTION_LONG_CLICK = 32; // 0x20 + field public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 256; // 0x100 + field public static final int ACTION_NEXT_HTML_ELEMENT = 1024; // 0x400 + field public static final int ACTION_PASTE = 32768; // 0x8000 + field public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 512; // 0x200 + field public static final int ACTION_PREVIOUS_HTML_ELEMENT = 2048; // 0x800 + field public static final int ACTION_SCROLL_BACKWARD = 8192; // 0x2000 + field public static final int ACTION_SCROLL_FORWARD = 4096; // 0x1000 + field public static final int ACTION_SELECT = 4; // 0x4 + field public static final int ACTION_SET_SELECTION = 131072; // 0x20000 + field public static final int ACTION_SET_TEXT = 2097152; // 0x200000 + field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2 + field public static final int FOCUS_INPUT = 1; // 0x1 + field public static final int MOVEMENT_GRANULARITY_CHARACTER = 1; // 0x1 + field public static final int MOVEMENT_GRANULARITY_LINE = 4; // 0x4 + field public static final int MOVEMENT_GRANULARITY_PAGE = 16; // 0x10 + field public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 8; // 0x8 + field public static final int MOVEMENT_GRANULARITY_WORD = 2; // 0x2 + } + + public static class AccessibilityNodeInfoCompat.AccessibilityActionCompat { + ctor public AccessibilityNodeInfoCompat.AccessibilityActionCompat(int, java.lang.CharSequence); + method public int getId(); + method public java.lang.CharSequence getLabel(); + field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_ACCESSIBILITY_FOCUS; + field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLEAR_ACCESSIBILITY_FOCUS; + field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLEAR_FOCUS; + field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLEAR_SELECTION; + field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CLICK; + field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_COLLAPSE; + field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_COPY; + field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_CUT; + field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_DISMISS; + field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_EXPAND; + field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_FOCUS; + field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_LONG_CLICK; + field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_NEXT_AT_MOVEMENT_GRANULARITY; + field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_NEXT_HTML_ELEMENT; + field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PASTE; + field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY; + field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PREVIOUS_HTML_ELEMENT; + field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_BACKWARD; + field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_FORWARD; + field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SELECT; + field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SET_SELECTION; + field public static final android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SET_TEXT; + } + + public static class AccessibilityNodeInfoCompat.CollectionInfoCompat { + method public int getColumnCount(); + method public int getRowCount(); + method public boolean isHierarchical(); + method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat obtain(int, int, boolean, int); + field public static final int SELECTION_MODE_MULTIPLE = 2; // 0x2 + field public static final int SELECTION_MODE_NONE = 0; // 0x0 + field public static final int SELECTION_MODE_SINGLE = 1; // 0x1 + } + + public static class AccessibilityNodeInfoCompat.CollectionItemInfoCompat { + method public int getColumnIndex(); + method public int getColumnSpan(); + method public int getRowIndex(); + method public int getRowSpan(); + method public boolean isHeading(); + method public boolean isSelected(); + method public static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat obtain(int, int, int, int, boolean, boolean); + } + + public static class AccessibilityNodeInfoCompat.RangeInfoCompat { + method public float getCurrent(); + method public float getMax(); + method public float getMin(); + method public int getType(); + field public static final int RANGE_TYPE_FLOAT = 1; // 0x1 + field public static final int RANGE_TYPE_INT = 0; // 0x0 + field public static final int RANGE_TYPE_PERCENT = 2; // 0x2 + } + + public class AccessibilityNodeProviderCompat { + ctor public AccessibilityNodeProviderCompat(); + ctor public AccessibilityNodeProviderCompat(java.lang.Object); + method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int); + method public java.util.List<android.support.v4.view.accessibility.AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(java.lang.String, int); + method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat findFocus(int); + method public java.lang.Object getProvider(); + method public boolean performAction(int, int, android.os.Bundle); + } + + public class AccessibilityRecordCompat { + ctor public deprecated AccessibilityRecordCompat(java.lang.Object); + method public int getAddedCount(); + method public java.lang.CharSequence getBeforeText(); + method public java.lang.CharSequence getClassName(); + method public java.lang.CharSequence getContentDescription(); + method public int getCurrentItemIndex(); + method public int getFromIndex(); + method public deprecated java.lang.Object getImpl(); + method public int getItemCount(); + method public int getMaxScrollX(); + method public int getMaxScrollY(); + method public android.os.Parcelable getParcelableData(); + method public int getRemovedCount(); + method public int getScrollX(); + method public int getScrollY(); + method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getSource(); + method public java.util.List<java.lang.CharSequence> getText(); + method public int getToIndex(); + method public int getWindowId(); + method public boolean isChecked(); + method public boolean isEnabled(); + method public boolean isFullScreen(); + method public boolean isPassword(); + method public boolean isScrollable(); + method public static android.support.v4.view.accessibility.AccessibilityRecordCompat obtain(android.support.v4.view.accessibility.AccessibilityRecordCompat); + method public static android.support.v4.view.accessibility.AccessibilityRecordCompat obtain(); + method public void recycle(); + method public void setAddedCount(int); + method public void setBeforeText(java.lang.CharSequence); + method public void setChecked(boolean); + method public void setClassName(java.lang.CharSequence); + method public void setContentDescription(java.lang.CharSequence); + method public void setCurrentItemIndex(int); + method public void setEnabled(boolean); + method public void setFromIndex(int); + method public void setFullScreen(boolean); + method public void setItemCount(int); + method public void setMaxScrollX(int); + method public void setMaxScrollY(int); + method public void setParcelableData(android.os.Parcelable); + method public void setPassword(boolean); + method public void setRemovedCount(int); + method public void setScrollX(int); + method public void setScrollY(int); + method public void setScrollable(boolean); + method public void setSource(android.view.View); + method public void setSource(android.view.View, int); + method public void setToIndex(int); + } + + public class AccessibilityWindowInfoCompat { + method public void getBoundsInScreen(android.graphics.Rect); + method public android.support.v4.view.accessibility.AccessibilityWindowInfoCompat getChild(int); + method public int getChildCount(); + method public int getId(); + method public int getLayer(); + method public android.support.v4.view.accessibility.AccessibilityWindowInfoCompat getParent(); + method public android.support.v4.view.accessibility.AccessibilityNodeInfoCompat getRoot(); + method public int getType(); + method public boolean isAccessibilityFocused(); + method public boolean isActive(); + method public boolean isFocused(); + method public static android.support.v4.view.accessibility.AccessibilityWindowInfoCompat obtain(); + method public static android.support.v4.view.accessibility.AccessibilityWindowInfoCompat obtain(android.support.v4.view.accessibility.AccessibilityWindowInfoCompat); + method public void recycle(); + field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4 + field public static final int TYPE_APPLICATION = 1; // 0x1 + field public static final int TYPE_INPUT_METHOD = 2; // 0x2 + field public static final int TYPE_SYSTEM = 3; // 0x3 + } + +} + +package android.support.v4.view.animation { + + public class FastOutLinearInInterpolator extends android.support.v4.view.animation.LookupTableInterpolator { + ctor public FastOutLinearInInterpolator(); + } + + public class FastOutSlowInInterpolator extends android.support.v4.view.animation.LookupTableInterpolator { + ctor public FastOutSlowInInterpolator(); + } + + public class LinearOutSlowInInterpolator extends android.support.v4.view.animation.LookupTableInterpolator { + ctor public LinearOutSlowInInterpolator(); + } + + abstract class LookupTableInterpolator implements android.view.animation.Interpolator { + ctor public LookupTableInterpolator(float[]); + method public float getInterpolation(float); + } + + public class PathInterpolatorCompat { + method public static android.view.animation.Interpolator create(android.graphics.Path); + method public static android.view.animation.Interpolator create(float, float); + method public static android.view.animation.Interpolator create(float, float, float, float); + } + +} + +package android.support.v4.widget { + + public abstract class AutoScrollHelper implements android.view.View.OnTouchListener { + ctor public AutoScrollHelper(android.view.View); + method public abstract boolean canTargetScrollHorizontally(int); + method public abstract boolean canTargetScrollVertically(int); + method public boolean isEnabled(); + method public boolean isExclusive(); + method public boolean onTouch(android.view.View, android.view.MotionEvent); + method public abstract void scrollTargetBy(int, int); + method public android.support.v4.widget.AutoScrollHelper setActivationDelay(int); + method public android.support.v4.widget.AutoScrollHelper setEdgeType(int); + method public android.support.v4.widget.AutoScrollHelper setEnabled(boolean); + method public android.support.v4.widget.AutoScrollHelper setExclusive(boolean); + method public android.support.v4.widget.AutoScrollHelper setMaximumEdges(float, float); + method public android.support.v4.widget.AutoScrollHelper setMaximumVelocity(float, float); + method public android.support.v4.widget.AutoScrollHelper setMinimumVelocity(float, float); + method public android.support.v4.widget.AutoScrollHelper setRampDownDuration(int); + method public android.support.v4.widget.AutoScrollHelper setRampUpDuration(int); + method public android.support.v4.widget.AutoScrollHelper setRelativeEdges(float, float); + method public android.support.v4.widget.AutoScrollHelper setRelativeVelocity(float, float); + field public static final int EDGE_TYPE_INSIDE = 0; // 0x0 + field public static final int EDGE_TYPE_INSIDE_EXTEND = 1; // 0x1 + field public static final int EDGE_TYPE_OUTSIDE = 2; // 0x2 + field public static final float NO_MAX = 3.4028235E38f; + field public static final float NO_MIN = 0.0f; + field public static final float RELATIVE_UNSPECIFIED = 0.0f; + } + + public final class CompoundButtonCompat { + method public static android.graphics.drawable.Drawable getButtonDrawable(android.widget.CompoundButton); + method public static android.content.res.ColorStateList getButtonTintList(android.widget.CompoundButton); + method public static android.graphics.PorterDuff.Mode getButtonTintMode(android.widget.CompoundButton); + method public static void setButtonTintList(android.widget.CompoundButton, android.content.res.ColorStateList); + method public static void setButtonTintMode(android.widget.CompoundButton, android.graphics.PorterDuff.Mode); + } + + public class ContentLoadingProgressBar extends android.widget.ProgressBar { + ctor public ContentLoadingProgressBar(android.content.Context); + ctor public ContentLoadingProgressBar(android.content.Context, android.util.AttributeSet); + method public void hide(); + method public void onAttachedToWindow(); + method public void onDetachedFromWindow(); + method public void show(); + } + + public abstract class CursorAdapter extends android.widget.BaseAdapter { + ctor public deprecated CursorAdapter(android.content.Context, android.database.Cursor); + ctor public CursorAdapter(android.content.Context, android.database.Cursor, boolean); + ctor public CursorAdapter(android.content.Context, android.database.Cursor, int); + method public abstract void bindView(android.view.View, android.content.Context, android.database.Cursor); + method public void changeCursor(android.database.Cursor); + method public java.lang.CharSequence convertToString(android.database.Cursor); + method public int getCount(); + method public android.database.Cursor getCursor(); + method public android.widget.Filter getFilter(); + method public android.widget.FilterQueryProvider getFilterQueryProvider(); + method public java.lang.Object getItem(int); + method public long getItemId(int); + method public android.view.View getView(int, android.view.View, android.view.ViewGroup); + method protected deprecated void init(android.content.Context, android.database.Cursor, boolean); + method public android.view.View newDropDownView(android.content.Context, android.database.Cursor, android.view.ViewGroup); + method public abstract android.view.View newView(android.content.Context, android.database.Cursor, android.view.ViewGroup); + method protected void onContentChanged(); + method public android.database.Cursor runQueryOnBackgroundThread(java.lang.CharSequence); + method public void setFilterQueryProvider(android.widget.FilterQueryProvider); + method public android.database.Cursor swapCursor(android.database.Cursor); + field public static final deprecated int FLAG_AUTO_REQUERY = 1; // 0x1 + field public static final int FLAG_REGISTER_CONTENT_OBSERVER = 2; // 0x2 + } + + public class DrawerLayout extends android.view.ViewGroup { + ctor public DrawerLayout(android.content.Context); + ctor public DrawerLayout(android.content.Context, android.util.AttributeSet); + ctor public DrawerLayout(android.content.Context, android.util.AttributeSet, int); + method public void closeDrawer(android.view.View); + method public void closeDrawer(int); + method public void closeDrawers(); + method public float getDrawerElevation(); + method public int getDrawerLockMode(int); + method public int getDrawerLockMode(android.view.View); + method public java.lang.CharSequence getDrawerTitle(int); + method public android.graphics.drawable.Drawable getStatusBarBackgroundDrawable(); + method public boolean isDrawerOpen(android.view.View); + method public boolean isDrawerOpen(int); + method public boolean isDrawerVisible(android.view.View); + method public boolean isDrawerVisible(int); + method public void onDraw(android.graphics.Canvas); + method protected void onLayout(boolean, int, int, int, int); + method public void openDrawer(android.view.View); + method public void openDrawer(int); + method public void setDrawerElevation(float); + method public void setDrawerListener(android.support.v4.widget.DrawerLayout.DrawerListener); + method public void setDrawerLockMode(int); + method public void setDrawerLockMode(int, int); + method public void setDrawerLockMode(int, android.view.View); + method public void setDrawerShadow(android.graphics.drawable.Drawable, int); + method public void setDrawerShadow(int, int); + method public void setDrawerTitle(int, java.lang.CharSequence); + method public void setScrimColor(int); + method public void setStatusBarBackground(android.graphics.drawable.Drawable); + method public void setStatusBarBackground(int); + method public void setStatusBarBackgroundColor(int); + field public static final int LOCK_MODE_LOCKED_CLOSED = 1; // 0x1 + field public static final int LOCK_MODE_LOCKED_OPEN = 2; // 0x2 + field public static final int LOCK_MODE_UNLOCKED = 0; // 0x0 + field public static final int STATE_DRAGGING = 1; // 0x1 + field public static final int STATE_IDLE = 0; // 0x0 + field public static final int STATE_SETTLING = 2; // 0x2 + } + + public static abstract interface DrawerLayout.DrawerListener { + method public abstract void onDrawerClosed(android.view.View); + method public abstract void onDrawerOpened(android.view.View); + method public abstract void onDrawerSlide(android.view.View, float); + method public abstract void onDrawerStateChanged(int); + } + + public static class DrawerLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams { + ctor public DrawerLayout.LayoutParams(android.content.Context, android.util.AttributeSet); + ctor public DrawerLayout.LayoutParams(int, int); + ctor public DrawerLayout.LayoutParams(int, int, int); + ctor public DrawerLayout.LayoutParams(android.support.v4.widget.DrawerLayout.LayoutParams); + ctor public DrawerLayout.LayoutParams(android.view.ViewGroup.LayoutParams); + ctor public DrawerLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams); + field public int gravity; + } + + protected static class DrawerLayout.SavedState extends android.view.View.BaseSavedState { + ctor public DrawerLayout.SavedState(android.os.Parcel); + ctor public DrawerLayout.SavedState(android.os.Parcelable); + field public static final android.os.Parcelable.Creator<android.support.v4.widget.DrawerLayout.SavedState> CREATOR; + } + + public static abstract class DrawerLayout.SimpleDrawerListener implements android.support.v4.widget.DrawerLayout.DrawerListener { + ctor public DrawerLayout.SimpleDrawerListener(); + method public void onDrawerClosed(android.view.View); + method public void onDrawerOpened(android.view.View); + method public void onDrawerSlide(android.view.View, float); + method public void onDrawerStateChanged(int); + } + + public class EdgeEffectCompat { + ctor public EdgeEffectCompat(android.content.Context); + method public boolean draw(android.graphics.Canvas); + method public void finish(); + method public boolean isFinished(); + method public boolean onAbsorb(int); + method public deprecated boolean onPull(float); + method public boolean onPull(float, float); + method public boolean onRelease(); + method public void setSize(int, int); + } + + public abstract class ExploreByTouchHelper extends android.support.v4.view.AccessibilityDelegateCompat { + ctor public ExploreByTouchHelper(android.view.View); + method public boolean dispatchHoverEvent(android.view.MotionEvent); + method public int getFocusedVirtualView(); + method protected abstract int getVirtualViewAt(float, float); + method protected abstract void getVisibleVirtualViews(java.util.List<java.lang.Integer>); + method public void invalidateRoot(); + method public void invalidateVirtualView(int); + method protected abstract boolean onPerformActionForVirtualView(int, int, android.os.Bundle); + method protected abstract void onPopulateEventForVirtualView(int, android.view.accessibility.AccessibilityEvent); + method public void onPopulateNodeForHost(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat); + method protected abstract void onPopulateNodeForVirtualView(int, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat); + method public boolean sendEventForVirtualView(int, int); + field public static final int HOST_ID = -1; // 0xffffffff + field public static final int INVALID_ID = -2147483648; // 0x80000000 + } + + public class ListPopupWindowCompat { + method public static android.view.View.OnTouchListener createDragToOpenListener(java.lang.Object, android.view.View); + } + + public class ListViewAutoScrollHelper extends android.support.v4.widget.AutoScrollHelper { + ctor public ListViewAutoScrollHelper(android.widget.ListView); + method public boolean canTargetScrollHorizontally(int); + method public boolean canTargetScrollVertically(int); + method public void scrollTargetBy(int, int); + } + + public class NestedScrollView extends android.widget.FrameLayout implements android.support.v4.view.NestedScrollingChild android.support.v4.view.NestedScrollingParent android.support.v4.view.ScrollingView { + ctor public NestedScrollView(android.content.Context); + ctor public NestedScrollView(android.content.Context, android.util.AttributeSet); + ctor public NestedScrollView(android.content.Context, android.util.AttributeSet, int); + method public boolean arrowScroll(int); + method protected int computeScrollDeltaToGetChildRectOnScreen(android.graphics.Rect); + method public boolean executeKeyEvent(android.view.KeyEvent); + method public void fling(int); + method public boolean fullScroll(int); + method public int getMaxScrollAmount(); + method public boolean isFillViewport(); + method public boolean isSmoothScrollingEnabled(); + method public void onAttachedToWindow(); + method public boolean pageScroll(int); + method public void setFillViewport(boolean); + method public void setOnScrollChangeListener(android.support.v4.widget.NestedScrollView.OnScrollChangeListener); + method public void setSmoothScrollingEnabled(boolean); + method public final void smoothScrollBy(int, int); + method public final void smoothScrollTo(int, int); + } + + public static abstract interface NestedScrollView.OnScrollChangeListener { + method public abstract void onScrollChange(android.support.v4.widget.NestedScrollView, int, int, int, int); + } + + public class PopupMenuCompat { + method public static android.view.View.OnTouchListener getDragToOpenListener(java.lang.Object); + } + + public class PopupWindowCompat { + method public static boolean getOverlapAnchor(android.widget.PopupWindow); + method public static int getWindowLayoutType(android.widget.PopupWindow); + method public static void setOverlapAnchor(android.widget.PopupWindow, boolean); + method public static void setWindowLayoutType(android.widget.PopupWindow, int); + method public static void showAsDropDown(android.widget.PopupWindow, android.view.View, int, int, int); + } + + public abstract class ResourceCursorAdapter extends android.support.v4.widget.CursorAdapter { + ctor public deprecated ResourceCursorAdapter(android.content.Context, int, android.database.Cursor); + ctor public ResourceCursorAdapter(android.content.Context, int, android.database.Cursor, boolean); + ctor public ResourceCursorAdapter(android.content.Context, int, android.database.Cursor, int); + method public android.view.View newView(android.content.Context, android.database.Cursor, android.view.ViewGroup); + method public void setDropDownViewResource(int); + method public void setViewResource(int); + } + + public class ScrollerCompat { + method public void abortAnimation(); + method public boolean computeScrollOffset(); + method public static android.support.v4.widget.ScrollerCompat create(android.content.Context); + method public static android.support.v4.widget.ScrollerCompat create(android.content.Context, android.view.animation.Interpolator); + method public void fling(int, int, int, int, int, int, int, int); + method public void fling(int, int, int, int, int, int, int, int, int, int); + method public float getCurrVelocity(); + method public int getCurrX(); + method public int getCurrY(); + method public int getFinalX(); + method public int getFinalY(); + method public boolean isFinished(); + method public boolean isOverScrolled(); + method public void notifyHorizontalEdgeReached(int, int, int); + method public void notifyVerticalEdgeReached(int, int, int); + method public boolean springBack(int, int, int, int, int, int); + method public void startScroll(int, int, int, int); + method public void startScroll(int, int, int, int, int); + } + + public class SearchViewCompat { + method public static java.lang.CharSequence getQuery(android.view.View); + method public static boolean isIconified(android.view.View); + method public static boolean isQueryRefinementEnabled(android.view.View); + method public static boolean isSubmitButtonEnabled(android.view.View); + method public static android.view.View newSearchView(android.content.Context); + method public static void setIconified(android.view.View, boolean); + method public static void setImeOptions(android.view.View, int); + method public static void setInputType(android.view.View, int); + method public static void setMaxWidth(android.view.View, int); + method public static void setOnCloseListener(android.view.View, android.support.v4.widget.SearchViewCompat.OnCloseListenerCompat); + method public static void setOnQueryTextListener(android.view.View, android.support.v4.widget.SearchViewCompat.OnQueryTextListenerCompat); + method public static void setQuery(android.view.View, java.lang.CharSequence, boolean); + method public static void setQueryHint(android.view.View, java.lang.CharSequence); + method public static void setQueryRefinementEnabled(android.view.View, boolean); + method public static void setSearchableInfo(android.view.View, android.content.ComponentName); + method public static void setSubmitButtonEnabled(android.view.View, boolean); + } + + public static abstract class SearchViewCompat.OnCloseListenerCompat { + ctor public SearchViewCompat.OnCloseListenerCompat(); + method public boolean onClose(); + } + + public static abstract class SearchViewCompat.OnQueryTextListenerCompat { + ctor public SearchViewCompat.OnQueryTextListenerCompat(); + method public boolean onQueryTextChange(java.lang.String); + method public boolean onQueryTextSubmit(java.lang.String); + } + + public class SimpleCursorAdapter extends android.support.v4.widget.ResourceCursorAdapter { + ctor public deprecated SimpleCursorAdapter(android.content.Context, int, android.database.Cursor, java.lang.String[], int[]); + ctor public SimpleCursorAdapter(android.content.Context, int, android.database.Cursor, java.lang.String[], int[], int); + method public void bindView(android.view.View, android.content.Context, android.database.Cursor); + method public void changeCursorAndColumns(android.database.Cursor, java.lang.String[], int[]); + method public android.support.v4.widget.SimpleCursorAdapter.CursorToStringConverter getCursorToStringConverter(); + method public int getStringConversionColumn(); + method public android.support.v4.widget.SimpleCursorAdapter.ViewBinder getViewBinder(); + method public void setCursorToStringConverter(android.support.v4.widget.SimpleCursorAdapter.CursorToStringConverter); + method public void setStringConversionColumn(int); + method public void setViewBinder(android.support.v4.widget.SimpleCursorAdapter.ViewBinder); + method public void setViewImage(android.widget.ImageView, java.lang.String); + method public void setViewText(android.widget.TextView, java.lang.String); + } + + public static abstract interface SimpleCursorAdapter.CursorToStringConverter { + method public abstract java.lang.CharSequence convertToString(android.database.Cursor); + } + + public static abstract interface SimpleCursorAdapter.ViewBinder { + method public abstract boolean setViewValue(android.view.View, android.database.Cursor, int); + } + + public class SlidingPaneLayout extends android.view.ViewGroup { + ctor public SlidingPaneLayout(android.content.Context); + ctor public SlidingPaneLayout(android.content.Context, android.util.AttributeSet); + ctor public SlidingPaneLayout(android.content.Context, android.util.AttributeSet, int); + method protected boolean canScroll(android.view.View, boolean, int, int, int); + method public deprecated boolean canSlide(); + method public boolean closePane(); + method public int getCoveredFadeColor(); + method public int getParallaxDistance(); + method public int getSliderFadeColor(); + method public boolean isOpen(); + method public boolean isSlideable(); + method protected void onLayout(boolean, int, int, int, int); + method public boolean openPane(); + method public void setCoveredFadeColor(int); + method public void setPanelSlideListener(android.support.v4.widget.SlidingPaneLayout.PanelSlideListener); + method public void setParallaxDistance(int); + method public deprecated void setShadowDrawable(android.graphics.drawable.Drawable); + method public void setShadowDrawableLeft(android.graphics.drawable.Drawable); + method public void setShadowDrawableRight(android.graphics.drawable.Drawable); + method public deprecated void setShadowResource(int); + method public void setShadowResourceLeft(int); + method public void setShadowResourceRight(int); + method public void setSliderFadeColor(int); + method public deprecated void smoothSlideClosed(); + method public deprecated void smoothSlideOpen(); + } + + public static class SlidingPaneLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams { + ctor public SlidingPaneLayout.LayoutParams(); + ctor public SlidingPaneLayout.LayoutParams(int, int); + ctor public SlidingPaneLayout.LayoutParams(android.view.ViewGroup.LayoutParams); + ctor public SlidingPaneLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams); + ctor public SlidingPaneLayout.LayoutParams(android.support.v4.widget.SlidingPaneLayout.LayoutParams); + ctor public SlidingPaneLayout.LayoutParams(android.content.Context, android.util.AttributeSet); + field public float weight; + } + + public static abstract interface SlidingPaneLayout.PanelSlideListener { + method public abstract void onPanelClosed(android.view.View); + method public abstract void onPanelOpened(android.view.View); + method public abstract void onPanelSlide(android.view.View, float); + } + + public static class SlidingPaneLayout.SimplePanelSlideListener implements android.support.v4.widget.SlidingPaneLayout.PanelSlideListener { + ctor public SlidingPaneLayout.SimplePanelSlideListener(); + method public void onPanelClosed(android.view.View); + method public void onPanelOpened(android.view.View); + method public void onPanelSlide(android.view.View, float); + } + + public class Space extends android.view.View { + ctor public Space(android.content.Context, android.util.AttributeSet, int); + ctor public Space(android.content.Context, android.util.AttributeSet); + ctor public Space(android.content.Context); + } + + public class SwipeRefreshLayout extends android.view.ViewGroup implements android.support.v4.view.NestedScrollingChild android.support.v4.view.NestedScrollingParent { + ctor public SwipeRefreshLayout(android.content.Context); + ctor public SwipeRefreshLayout(android.content.Context, android.util.AttributeSet); + method public boolean canChildScrollUp(); + method public int getProgressCircleDiameter(); + method public boolean isRefreshing(); + method protected void onLayout(boolean, int, int, int, int); + method public void onMeasure(int, int); + method public deprecated void setColorScheme(int...); + method public void setColorSchemeColors(int...); + method public void setColorSchemeResources(int...); + method public void setDistanceToTriggerSync(int); + method public void setOnRefreshListener(android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener); + method public deprecated void setProgressBackgroundColor(int); + method public void setProgressBackgroundColorSchemeColor(int); + method public void setProgressBackgroundColorSchemeResource(int); + method public void setProgressViewEndTarget(boolean, int); + method public void setProgressViewOffset(boolean, int, int); + method public void setRefreshing(boolean); + method public void setSize(int); + field public static final int DEFAULT = 1; // 0x1 + field public static final int LARGE = 0; // 0x0 + field protected int mFrom; + field protected int mOriginalOffsetTop; + } + + public static abstract interface SwipeRefreshLayout.OnRefreshListener { + method public abstract void onRefresh(); + } + + public class TextViewCompat { + method public static int getMaxLines(android.widget.TextView); + method public static int getMinLines(android.widget.TextView); + method public static void setCompoundDrawablesRelative(android.widget.TextView, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable); + method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable); + method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, int, int, int, int); + } + + public class ViewDragHelper { + method public void abort(); + method protected boolean canScroll(android.view.View, boolean, int, int, int, int); + method public void cancel(); + method public void captureChildView(android.view.View, int); + method public boolean checkTouchSlop(int); + method public boolean checkTouchSlop(int, int); + method public boolean continueSettling(boolean); + method public static android.support.v4.widget.ViewDragHelper create(android.view.ViewGroup, android.support.v4.widget.ViewDragHelper.Callback); + method public static android.support.v4.widget.ViewDragHelper create(android.view.ViewGroup, float, android.support.v4.widget.ViewDragHelper.Callback); + method public android.view.View findTopChildUnder(int, int); + method public void flingCapturedView(int, int, int, int); + method public int getActivePointerId(); + method public android.view.View getCapturedView(); + method public int getEdgeSize(); + method public float getMinVelocity(); + method public int getTouchSlop(); + method public int getViewDragState(); + method public boolean isCapturedViewUnder(int, int); + method public boolean isEdgeTouched(int); + method public boolean isEdgeTouched(int, int); + method public boolean isPointerDown(int); + method public boolean isViewUnder(android.view.View, int, int); + method public void processTouchEvent(android.view.MotionEvent); + method public void setEdgeTrackingEnabled(int); + method public void setMinVelocity(float); + method public boolean settleCapturedViewAt(int, int); + method public boolean shouldInterceptTouchEvent(android.view.MotionEvent); + method public boolean smoothSlideViewTo(android.view.View, int, int); + field public static final int DIRECTION_ALL = 3; // 0x3 + field public static final int DIRECTION_HORIZONTAL = 1; // 0x1 + field public static final int DIRECTION_VERTICAL = 2; // 0x2 + field public static final int EDGE_ALL = 15; // 0xf + field public static final int EDGE_BOTTOM = 8; // 0x8 + field public static final int EDGE_LEFT = 1; // 0x1 + field public static final int EDGE_RIGHT = 2; // 0x2 + field public static final int EDGE_TOP = 4; // 0x4 + field public static final int INVALID_POINTER = -1; // 0xffffffff + field public static final int STATE_DRAGGING = 1; // 0x1 + field public static final int STATE_IDLE = 0; // 0x0 + field public static final int STATE_SETTLING = 2; // 0x2 + } + + public static abstract class ViewDragHelper.Callback { + ctor public ViewDragHelper.Callback(); + method public int clampViewPositionHorizontal(android.view.View, int, int); + method public int clampViewPositionVertical(android.view.View, int, int); + method public int getOrderedChildIndex(int); + method public int getViewHorizontalDragRange(android.view.View); + method public int getViewVerticalDragRange(android.view.View); + method public void onEdgeDragStarted(int, int); + method public boolean onEdgeLock(int); + method public void onEdgeTouched(int, int); + method public void onViewCaptured(android.view.View, int); + method public void onViewDragStateChanged(int); + method public void onViewPositionChanged(android.view.View, int, int, int, int); + method public void onViewReleased(android.view.View, float, float); + method public abstract boolean tryCaptureView(android.view.View, int); + } + +} + diff --git a/v4/api/current.txt b/v4/api/current.txt index 3c4290e6a5..9f50a24886 100644 --- a/v4/api/current.txt +++ b/v4/api/current.txt @@ -26,36 +26,6 @@ package android.support.v4.accessibilityservice { } -package android.support.v4.animation { - - public abstract class AnimatorCompatHelper { - method public static void clearInterpolator(android.view.View); - method public static android.support.v4.animation.ValueAnimatorCompat emptyValueAnimator(); - } - - public abstract interface AnimatorListenerCompat { - method public abstract void onAnimationCancel(android.support.v4.animation.ValueAnimatorCompat); - method public abstract void onAnimationEnd(android.support.v4.animation.ValueAnimatorCompat); - method public abstract void onAnimationRepeat(android.support.v4.animation.ValueAnimatorCompat); - method public abstract void onAnimationStart(android.support.v4.animation.ValueAnimatorCompat); - } - - public abstract interface AnimatorUpdateListenerCompat { - method public abstract void onAnimationUpdate(android.support.v4.animation.ValueAnimatorCompat); - } - - public abstract interface ValueAnimatorCompat { - method public abstract void addListener(android.support.v4.animation.AnimatorListenerCompat); - method public abstract void addUpdateListener(android.support.v4.animation.AnimatorUpdateListenerCompat); - method public abstract void cancel(); - method public abstract float getAnimatedFraction(); - method public abstract void setDuration(long); - method public abstract void setTarget(android.view.View); - method public abstract void start(); - } - -} - package android.support.v4.app { public deprecated class ActionBarDrawerToggle implements android.support.v4.widget.DrawerLayout.DrawerListener { @@ -274,12 +244,14 @@ package android.support.v4.app { method public java.lang.Object getLastCustomNonConfigurationInstance(); method public android.support.v4.app.FragmentManager getSupportFragmentManager(); method public android.support.v4.app.LoaderManager getSupportLoaderManager(); + method public final android.support.v4.media.session.MediaControllerCompat getSupportMediaController(); method public void onAttachFragment(android.support.v4.app.Fragment); method protected void onResumeFragments(); method public java.lang.Object onRetainCustomNonConfigurationInstance(); method public final java.lang.Object onRetainNonConfigurationInstance(); method public void setEnterSharedElementCallback(android.support.v4.app.SharedElementCallback); method public void setExitSharedElementCallback(android.support.v4.app.SharedElementCallback); + method public final void setSupportMediaController(android.support.v4.media.session.MediaControllerCompat); method public void startActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int); method public void supportFinishAfterTransition(); method public void supportInvalidateOptionsMenu(); @@ -560,7 +532,7 @@ package android.support.v4.app { field public static final int VISIBILITY_SECRET = -1; // 0xffffffff } - public static class NotificationCompat.Action extends android.support.v4.app.NotificationCompatBase.Action { + public static class NotificationCompat.Action { ctor public NotificationCompat.Action(int, java.lang.CharSequence, android.app.PendingIntent); method public android.app.PendingIntent getActionIntent(); method public android.os.Bundle getExtras(); @@ -681,7 +653,7 @@ package android.support.v4.app { method public android.support.v4.app.NotificationCompat.CarExtender setUnreadConversation(android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation); } - public static class NotificationCompat.CarExtender.UnreadConversation extends android.support.v4.app.NotificationCompatBase.UnreadConversation { + public static class NotificationCompat.CarExtender.UnreadConversation { method public long getLatestTimestamp(); method public java.lang.String[] getMessages(); method public java.lang.String getParticipant(); @@ -770,23 +742,6 @@ package android.support.v4.app { field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff } - public class NotificationCompatBase { - ctor public NotificationCompatBase(); - } - - public static abstract class NotificationCompatBase.Action { - ctor public NotificationCompatBase.Action(); - method public abstract android.app.PendingIntent getActionIntent(); - method public abstract android.os.Bundle getExtras(); - method public abstract int getIcon(); - method public abstract android.support.v4.app.RemoteInputCompatBase.RemoteInput[] getRemoteInputs(); - method public abstract java.lang.CharSequence getTitle(); - } - - public static abstract class NotificationCompatBase.UnreadConversation { - ctor public NotificationCompatBase.UnreadConversation(); - } - public final class NotificationCompatExtras { field public static final java.lang.String EXTRA_ACTION_EXTRAS = "android.support.actionExtras"; field public static final java.lang.String EXTRA_GROUP_KEY = "android.support.groupKey"; @@ -964,13 +919,13 @@ package android.support.v4.content { public class ContextCompat { ctor public ContextCompat(); method public static int checkSelfPermission(android.content.Context, java.lang.String); - method public final java.io.File getCodeCacheDir(android.content.Context); + method public static java.io.File getCodeCacheDir(android.content.Context); method public static final int getColor(android.content.Context, int); method public static final android.content.res.ColorStateList getColorStateList(android.content.Context, int); method public static final android.graphics.drawable.Drawable getDrawable(android.content.Context, int); method public static java.io.File[] getExternalCacheDirs(android.content.Context); method public static java.io.File[] getExternalFilesDirs(android.content.Context, java.lang.String); - method public final java.io.File getNoBackupFilesDir(android.content.Context); + method public static java.io.File getNoBackupFilesDir(android.content.Context); method public static java.io.File[] getObbDirs(android.content.Context); method public static boolean startActivities(android.content.Context, android.content.Intent[]); method public static boolean startActivities(android.content.Context, android.content.Intent[], android.os.Bundle); @@ -1071,6 +1026,11 @@ package android.support.v4.content { method public void unregisterReceiver(android.content.BroadcastReceiver); } + public class ParallelExecutorCompat { + ctor public ParallelExecutorCompat(); + method public static java.util.concurrent.Executor getParallelExecutor(); + } + public final class PermissionChecker { method public static int checkCallingOrSelfPermission(android.content.Context, java.lang.String); method public static int checkCallingPermission(android.content.Context, java.lang.String, java.lang.String); @@ -1113,6 +1073,8 @@ package android.support.v4.content.res { public class ResourcesCompat { ctor public ResourcesCompat(); + method public static int getColor(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException; + method public static android.content.res.ColorStateList getColorStateList(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException; method public static android.graphics.drawable.Drawable getDrawable(android.content.res.Resources, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException; method public static android.graphics.drawable.Drawable getDrawableForDensity(android.content.res.Resources, int, int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException; } @@ -1246,6 +1208,77 @@ package android.support.v4.hardware.fingerprint { package android.support.v4.media { + public final class MediaBrowserCompat { + ctor public MediaBrowserCompat(android.content.Context, android.content.ComponentName, android.support.v4.media.MediaBrowserCompat.ConnectionCallback, android.os.Bundle); + method public void connect(); + method public void disconnect(); + method public android.os.Bundle getExtras(); + method public void getItem(java.lang.String, android.support.v4.media.MediaBrowserCompat.ItemCallback); + method public java.lang.String getRoot(); + method public android.content.ComponentName getServiceComponent(); + method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken(); + method public boolean isConnected(); + method public void subscribe(java.lang.String, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback); + method public void unsubscribe(java.lang.String); + } + + public static class MediaBrowserCompat.ConnectionCallback { + ctor public MediaBrowserCompat.ConnectionCallback(); + method public void onConnected(); + method public void onConnectionFailed(); + method public void onConnectionSuspended(); + } + + public static abstract class MediaBrowserCompat.ItemCallback { + ctor public MediaBrowserCompat.ItemCallback(); + method public void onError(java.lang.String); + method public void onItemLoaded(android.support.v4.media.MediaBrowserCompat.MediaItem); + } + + public static class MediaBrowserCompat.MediaItem implements android.os.Parcelable { + ctor public MediaBrowserCompat.MediaItem(android.support.v4.media.MediaDescriptionCompat, int); + method public int describeContents(); + method public android.support.v4.media.MediaDescriptionCompat getDescription(); + method public int getFlags(); + method public java.lang.String getMediaId(); + method public boolean isBrowsable(); + method public boolean isPlayable(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaBrowserCompat.MediaItem> CREATOR; + field public static final int FLAG_BROWSABLE = 1; // 0x1 + field public static final int FLAG_PLAYABLE = 2; // 0x2 + } + + public static abstract class MediaBrowserCompat.SubscriptionCallback { + ctor public MediaBrowserCompat.SubscriptionCallback(); + method public void onChildrenLoaded(java.lang.String, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>); + method public void onError(java.lang.String); + } + + public abstract class MediaBrowserServiceCompat extends android.app.Service { + ctor public MediaBrowserServiceCompat(); + method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]); + method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken(); + method public void notifyChildrenChanged(java.lang.String); + method public android.os.IBinder onBind(android.content.Intent); + method public abstract android.support.v4.media.MediaBrowserServiceCompat.BrowserRoot onGetRoot(java.lang.String, int, android.os.Bundle); + method public abstract void onLoadChildren(java.lang.String, android.support.v4.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem>>); + method public void onLoadItem(java.lang.String, android.support.v4.media.MediaBrowserServiceCompat.Result<android.support.v4.media.MediaBrowserCompat.MediaItem>); + method public void setSessionToken(android.support.v4.media.session.MediaSessionCompat.Token); + field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService"; + } + + public static final class MediaBrowserServiceCompat.BrowserRoot { + ctor public MediaBrowserServiceCompat.BrowserRoot(java.lang.String, android.os.Bundle); + method public android.os.Bundle getExtras(); + method public java.lang.String getRootId(); + } + + public static class MediaBrowserServiceCompat.Result { + method public void detach(); + method public void sendResult(T); + } + public final class MediaDescriptionCompat implements android.os.Parcelable { method public int describeContents(); method public static android.support.v4.media.MediaDescriptionCompat fromMediaDescription(java.lang.Object); @@ -1449,6 +1482,12 @@ package android.support.v4.media { package android.support.v4.media.session { + public class MediaButtonReceiver extends android.content.BroadcastReceiver { + ctor public MediaButtonReceiver(); + method public static android.view.KeyEvent handleIntent(android.support.v4.media.session.MediaSessionCompat, android.content.Intent); + method public void onReceive(android.content.Context, android.content.Intent); + } + public final class MediaControllerCompat { ctor public MediaControllerCompat(android.content.Context, android.support.v4.media.session.MediaSessionCompat); ctor public MediaControllerCompat(android.content.Context, android.support.v4.media.session.MediaSessionCompat.Token) throws android.os.RemoteException; @@ -1516,6 +1555,7 @@ package android.support.v4.media.session { } public class MediaSessionCompat { + ctor public MediaSessionCompat(android.content.Context, java.lang.String); ctor public MediaSessionCompat(android.content.Context, java.lang.String, android.content.ComponentName, android.app.PendingIntent); method public void addOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener); method public android.support.v4.media.session.MediaControllerCompat getController(); @@ -2349,6 +2389,13 @@ package android.support.v4.view { method public abstract int computeVerticalScrollRange(); } + public abstract interface TintableBackgroundView { + method public abstract android.content.res.ColorStateList getSupportBackgroundTintList(); + method public abstract android.graphics.PorterDuff.Mode getSupportBackgroundTintMode(); + method public abstract void setSupportBackgroundTintList(android.content.res.ColorStateList); + method public abstract void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode); + } + public class VelocityTrackerCompat { ctor public VelocityTrackerCompat(); method public static float getXVelocity(android.view.VelocityTracker, int); @@ -2396,6 +2443,7 @@ package android.support.v4.view { method public static float getRotationY(android.view.View); method public static float getScaleX(android.view.View); method public static float getScaleY(android.view.View); + method public static int getScrollIndicators(android.view.View); method public static java.lang.String getTransitionName(android.view.View); method public static float getTranslationX(android.view.View); method public static float getTranslationY(android.view.View); @@ -2406,6 +2454,7 @@ package android.support.v4.view { method public static float getZ(android.view.View); method public static boolean hasAccessibilityDelegate(android.view.View); method public static boolean hasNestedScrollingParent(android.view.View); + method public static boolean hasOnClickListeners(android.view.View); method public static boolean hasOverlappingRendering(android.view.View); method public static boolean hasTransientState(android.view.View); method public static boolean isAttachedToWindow(android.view.View); @@ -2455,6 +2504,8 @@ package android.support.v4.view { method public static void setSaveFromParentEnabled(android.view.View, boolean); method public static void setScaleX(android.view.View, float); method public static void setScaleY(android.view.View, float); + method public static void setScrollIndicators(android.view.View, int); + method public static void setScrollIndicators(android.view.View, int, int); method public static void setTransitionName(android.view.View, java.lang.String); method public static void setTranslationX(android.view.View, float); method public static void setTranslationY(android.view.View, float); @@ -2487,6 +2538,12 @@ package android.support.v4.view { field public static final int SCROLL_AXIS_HORIZONTAL = 1; // 0x1 field public static final int SCROLL_AXIS_NONE = 0; // 0x0 field public static final int SCROLL_AXIS_VERTICAL = 2; // 0x2 + field public static final int SCROLL_INDICATOR_BOTTOM = 2; // 0x2 + field public static final int SCROLL_INDICATOR_END = 32; // 0x20 + field public static final int SCROLL_INDICATOR_LEFT = 4; // 0x4 + field public static final int SCROLL_INDICATOR_RIGHT = 8; // 0x8 + field public static final int SCROLL_INDICATOR_START = 16; // 0x10 + field public static final int SCROLL_INDICATOR_TOP = 1; // 0x1 } public class ViewConfigurationCompat { @@ -3138,6 +3195,7 @@ package android.support.v4.widget { method public void setStatusBarBackgroundColor(int); field public static final int LOCK_MODE_LOCKED_CLOSED = 1; // 0x1 field public static final int LOCK_MODE_LOCKED_OPEN = 2; // 0x2 + field public static final int LOCK_MODE_UNDEFINED = 3; // 0x3 field public static final int LOCK_MODE_UNLOCKED = 0; // 0x0 field public static final int STATE_DRAGGING = 1; // 0x1 field public static final int STATE_IDLE = 0; // 0x0 @@ -3215,7 +3273,7 @@ package android.support.v4.widget { method public void scrollTargetBy(int, int); } - public class NestedScrollView extends android.widget.FrameLayout implements android.support.v4.view.NestedScrollingChild android.support.v4.view.NestedScrollingParent { + public class NestedScrollView extends android.widget.FrameLayout implements android.support.v4.view.NestedScrollingChild android.support.v4.view.NestedScrollingParent android.support.v4.view.ScrollingView { ctor public NestedScrollView(android.content.Context); ctor public NestedScrollView(android.content.Context, android.util.AttributeSet); ctor public NestedScrollView(android.content.Context, android.util.AttributeSet, int); @@ -3230,11 +3288,16 @@ package android.support.v4.widget { method public void onAttachedToWindow(); method public boolean pageScroll(int); method public void setFillViewport(boolean); + method public void setOnScrollChangeListener(android.support.v4.widget.NestedScrollView.OnScrollChangeListener); method public void setSmoothScrollingEnabled(boolean); method public final void smoothScrollBy(int, int); method public final void smoothScrollTo(int, int); } + public static abstract interface NestedScrollView.OnScrollChangeListener { + method public abstract void onScrollChange(android.support.v4.widget.NestedScrollView, int, int, int, int); + } + public class PopupMenuCompat { method public static android.view.View.OnTouchListener getDragToOpenListener(java.lang.Object); } @@ -3272,6 +3335,7 @@ package android.support.v4.widget { method public boolean isOverScrolled(); method public void notifyHorizontalEdgeReached(int, int, int); method public void notifyVerticalEdgeReached(int, int, int); + method public boolean springBack(int, int, int, int, int, int); method public void startScroll(int, int, int, int); method public void startScroll(int, int, int, int, int); } @@ -3417,9 +3481,19 @@ package android.support.v4.widget { } public class TextViewCompat { + method public static int getMaxLines(android.widget.TextView); + method public static int getMinLines(android.widget.TextView); method public static void setCompoundDrawablesRelative(android.widget.TextView, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable); method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable); method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, int, int, int, int); + method public static void setTextAppearance(android.widget.TextView, int); + } + + public abstract interface TintableCompoundButton { + method public abstract android.content.res.ColorStateList getSupportButtonTintList(); + method public abstract android.graphics.PorterDuff.Mode getSupportButtonTintMode(); + method public abstract void setSupportButtonTintList(android.content.res.ColorStateList); + method public abstract void setSupportButtonTintMode(android.graphics.PorterDuff.Mode); } public class ViewDragHelper { diff --git a/v4/api21/android/content/pm/ParceledListSlice.java b/v4/api21/android/content/pm/ParceledListSlice.java new file mode 100644 index 0000000000..b5183c0b94 --- /dev/null +++ b/v4/api21/android/content/pm/ParceledListSlice.java @@ -0,0 +1,70 @@ +/* + * 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.content.pm; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.List; + +/** + * A dummy implementation for overriding a hidden framework class, ParceledListSlice. + * When there are duplicated signatures between app and framework code, the framework code will be + * run. + * @hide + */ +public class ParceledListSlice<T extends Parcelable> implements Parcelable { + public ParceledListSlice(List<T> list) { + } + + @SuppressWarnings("unchecked") + private ParceledListSlice(Parcel p, ClassLoader loader) { + } + + private static void verifySameType(final Class<?> expected, final Class<?> actual) { + } + + public List<T> getList() { + return null; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + } + + @SuppressWarnings("unchecked") + public static final Parcelable.ClassLoaderCreator<ParceledListSlice> CREATOR = + new Parcelable.ClassLoaderCreator<ParceledListSlice>() { + public ParceledListSlice createFromParcel(Parcel in) { + return null; + } + + @Override + public ParceledListSlice createFromParcel(Parcel in, ClassLoader loader) { + return null; + } + + public ParceledListSlice[] newArray(int size) { + return null; + } + }; +} diff --git a/v4/api21/android/service/media/IMediaBrowserService.java b/v4/api21/android/service/media/IMediaBrowserService.java new file mode 100644 index 0000000000..086dc9cc0d --- /dev/null +++ b/v4/api21/android/service/media/IMediaBrowserService.java @@ -0,0 +1,65 @@ +/* + * 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.service.media; + +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; +import android.os.IInterface; +import android.os.Parcel; +import android.os.RemoteException; +import android.os.ResultReceiver; + +/** + * A dummy implementation for overriding a hidden framework class, IMediaBrowserService. + * When there are duplicated signatures between app and framework code, the framework code will be + * run. + * TODO: Consider using aidl instead of this. + * @hide + */ +public interface IMediaBrowserService extends IInterface { + + public static abstract class Stub extends Binder + implements IMediaBrowserService { + public Stub() { + } + + public static IMediaBrowserService asInterface(IBinder obj) { + return null; + } + + @Override + public IBinder asBinder() { + return null; + } + + @Override + public boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws android.os.RemoteException { + return false; + } + } + + public void connect(String pkg, Bundle rootHints, IMediaBrowserServiceCallbacks callbacks) + throws android.os.RemoteException; + public void disconnect(IMediaBrowserServiceCallbacks callbacks) throws RemoteException; + public void addSubscription(String uri, IMediaBrowserServiceCallbacks callbacks) + throws RemoteException; + public void removeSubscription(String uri, IMediaBrowserServiceCallbacks callbacks) + throws RemoteException; + public void getMediaItem(String uri, ResultReceiver cb) throws android.os.RemoteException; +} diff --git a/v4/api21/android/service/media/IMediaBrowserServiceCallbacks.java b/v4/api21/android/service/media/IMediaBrowserServiceCallbacks.java new file mode 100644 index 0000000000..fe988fee08 --- /dev/null +++ b/v4/api21/android/service/media/IMediaBrowserServiceCallbacks.java @@ -0,0 +1,91 @@ +/* + * 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.service.media; + +import android.content.pm.ParceledListSlice; +import android.media.session.MediaSession; +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; +import android.os.IInterface; +import android.os.Parcel; +import android.os.RemoteException; + +/** + * A dummy implementation for overriding a hidden framework class, IMediaBrowserServiceCallbacks. + * When there are duplicated signatures between app and framework code, the framework code will be + * run. + * TODO: Consider using aidl instead of this. + * @hide + */ +public interface IMediaBrowserServiceCallbacks extends IInterface { + public static abstract class Stub extends Binder implements IMediaBrowserServiceCallbacks + { + public Stub() { + } + + public static IMediaBrowserServiceCallbacks asInterface(IBinder obj) { + return null; + } + + @Override + public IBinder asBinder() { + return null; + } + + @Override + public boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + return false; + } + + private static class Proxy implements IMediaBrowserServiceCallbacks + { + Proxy(IBinder remote) { + } + + @Override + public IBinder asBinder() { + return null; + } + + public String getInterfaceDescriptor() { + return null; + } + + @Override + public void onConnect(String root, MediaSession.Token session, Bundle extras) + throws RemoteException { + } + + @Override + public void onConnectFailed() throws RemoteException { + } + + @Override + public void onLoadChildren(String mediaId, ParceledListSlice list) + throws RemoteException { + } + } + } + + public void onConnect(String root, MediaSession.Token session, Bundle extras) + throws RemoteException; + public void onConnectFailed() throws RemoteException; + public void onLoadChildren(String mediaId, ParceledListSlice list) throws RemoteException; +} + diff --git a/v4/api21/android/support/v4/app/ActivityCompat21.java b/v4/api21/android/support/v4/app/ActivityCompat21.java index f690fff0ed..2e6de1e462 100644 --- a/v4/api21/android/support/v4/app/ActivityCompat21.java +++ b/v4/api21/android/support/v4/app/ActivityCompat21.java @@ -21,6 +21,7 @@ import android.app.SharedElementCallback; import android.content.Context; import android.graphics.Matrix; import android.graphics.RectF; +import android.media.session.MediaController; import android.os.Parcelable; import android.view.View; @@ -31,6 +32,10 @@ import java.util.Map; class ActivityCompat21 { + public static void setMediaController(Activity activity, Object mediaControllerObj) { + activity.setMediaController((MediaController) mediaControllerObj); + } + public static void finishAfterTransition(Activity activity) { activity.finishAfterTransition(); } diff --git a/v4/api21/android/support/v4/graphics/drawable/DrawableCompatLollipop.java b/v4/api21/android/support/v4/graphics/drawable/DrawableCompatLollipop.java index 93990b47fb..2f3b45a0d4 100644 --- a/v4/api21/android/support/v4/graphics/drawable/DrawableCompatLollipop.java +++ b/v4/api21/android/support/v4/graphics/drawable/DrawableCompatLollipop.java @@ -37,46 +37,28 @@ class DrawableCompatLollipop { } public static void setTint(Drawable drawable, int tint) { - if (drawable instanceof DrawableWrapperLollipop) { - // GradientDrawable on Lollipop does not support tinting, so we'll use our compatible - // functionality instead - DrawableCompatBase.setTint(drawable, tint); - } else { - // Else, we'll use the framework API - drawable.setTint(tint); - } + drawable.setTint(tint); } public static void setTintList(Drawable drawable, ColorStateList tint) { - if (drawable instanceof DrawableWrapperLollipop) { - // GradientDrawable on Lollipop does not support tinting, so we'll use our compatible - // functionality instead - DrawableCompatBase.setTintList(drawable, tint); - } else { - // Else, we'll use the framework API - drawable.setTintList(tint); - } + drawable.setTintList(tint); } public static void setTintMode(Drawable drawable, PorterDuff.Mode tintMode) { - if (drawable instanceof DrawableWrapperLollipop) { - // GradientDrawable on Lollipop does not support tinting, so we'll use our compatible - // functionality instead - DrawableCompatBase.setTintMode(drawable, tintMode); - } else { - // Else, we'll use the framework API - drawable.setTintMode(tintMode); - } + drawable.setTintMode(tintMode); } - public static Drawable wrapForTinting(Drawable drawable) { - if (drawable instanceof GradientDrawable || drawable instanceof DrawableContainer) { - // GradientDrawable on Lollipop does not support tinting, so we'll use our compatible - // functionality instead. We also do the same for DrawableContainers since they may - // contain GradientDrawable instances. - return new DrawableWrapperLollipop(drawable); + public static Drawable wrapForTinting(final Drawable drawable) { + if (!(drawable instanceof DrawableWrapperLollipop)) { + return new DrawableWrapperLollipop(drawable, shouldForceCompatTinting(drawable)); } return drawable; } + private static boolean shouldForceCompatTinting(Drawable drawable) { + // GradientDrawable on Lollipop does not support tinting, so we'll use our compatible + // functionality instead. We also do the same for DrawableContainers since they may + // contain GradientDrawable instances. + return drawable instanceof GradientDrawable || drawable instanceof DrawableContainer; + } } diff --git a/v4/api21/android/support/v4/graphics/drawable/DrawableWrapperLollipop.java b/v4/api21/android/support/v4/graphics/drawable/DrawableWrapperLollipop.java index 1f15040056..9533afd80f 100644 --- a/v4/api21/android/support/v4/graphics/drawable/DrawableWrapperLollipop.java +++ b/v4/api21/android/support/v4/graphics/drawable/DrawableWrapperLollipop.java @@ -16,15 +16,24 @@ package android.support.v4.graphics.drawable; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.graphics.Outline; +import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.drawable.Drawable; class DrawableWrapperLollipop extends DrawableWrapperKitKat { + private final boolean mUseCompatTinting; + DrawableWrapperLollipop(Drawable drawable) { + this(drawable, false); + } + + DrawableWrapperLollipop(Drawable drawable, boolean useCompatTinting) { super(drawable); + mUseCompatTinting = useCompatTinting; } @Override @@ -56,4 +65,47 @@ class DrawableWrapperLollipop extends DrawableWrapperKitKat { public Rect getDirtyBounds() { return mDrawable.getDirtyBounds(); } + + @Override + public void setTintList(ColorStateList tint) { + if (mUseCompatTinting) { + setCompatTintList(tint); + } else { + mDrawable.setTintList(tint); + } + } + + @Override + public void setTint(int tintColor) { + if (mUseCompatTinting) { + setCompatTint(tintColor); + } else { + mDrawable.setTint(tintColor); + } + } + + @Override + public void setTintMode(PorterDuff.Mode tintMode) { + if (mUseCompatTinting) { + setCompatTintMode(tintMode); + } else { + mDrawable.setTintMode(tintMode); + } + } + + @Override + public boolean setState(int[] stateSet) { + if (super.setState(stateSet)) { + // Manually invalidate because the framework doesn't currently force an invalidation + // on a state change + invalidateSelf(); + return true; + } + return false; + } + + @Override + protected boolean isCompatTintEnabled() { + return mUseCompatTinting; + } } diff --git a/v4/api21/android/support/v4/media/MediaBrowserCompatApi21.java b/v4/api21/android/support/v4/media/MediaBrowserCompatApi21.java new file mode 100644 index 0000000000..c6ccb14f2d --- /dev/null +++ b/v4/api21/android/support/v4/media/MediaBrowserCompatApi21.java @@ -0,0 +1,153 @@ +/* + * 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.v4.media; + +import android.content.ComponentName; +import android.content.Context; +import android.media.browse.MediaBrowser; +import android.os.Bundle; +import android.os.Parcel; +import android.support.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; + +class MediaBrowserCompatApi21 { + static final String NULL_MEDIA_ITEM_ID = + "android.support.v4.media.MediaBrowserCompat.NULL_MEDIA_ITEM"; + + public static Object createConnectionCallback(ConnectionCallback callback) { + return new ConnectionCallbackProxy<>(callback); + } + + public static Object createBrowser(Context context, ComponentName serviceComponent, + Object callback, Bundle rootHints) { + return new MediaBrowser(context, serviceComponent, + (MediaBrowser.ConnectionCallback) callback, rootHints); + } + + public static void connect(Object browserObj) { + ((MediaBrowser)browserObj).connect(); + } + + public static void disconnect(Object browserObj) { + ((MediaBrowser)browserObj).disconnect(); + + } + + public static boolean isConnected(Object browserObj) { + return ((MediaBrowser)browserObj).isConnected(); + } + + public static ComponentName getServiceComponent(Object browserObj) { + return ((MediaBrowser)browserObj).getServiceComponent(); + } + + public static String getRoot(Object browserObj) { + return ((MediaBrowser)browserObj).getRoot(); + } + + public static Bundle getExtras(Object browserObj) { + return ((MediaBrowser)browserObj).getExtras(); + } + + public static Object getSessionToken(Object browserObj) { + return ((MediaBrowser)browserObj).getSessionToken(); + } + + public static Object createSubscriptionCallback(SubscriptionCallback callback) { + return new SubscriptionCallbackProxy<>(callback); + } + + public static void subscribe( + Object browserObj, String parentId, Object subscriptionCallbackObj) { + ((MediaBrowser)browserObj).subscribe(parentId, + (MediaBrowser.SubscriptionCallback) subscriptionCallbackObj); + } + + public static void unsubscribe(Object browserObj, String parentId) { + ((MediaBrowser)browserObj).unsubscribe(parentId); + } + + interface ConnectionCallback { + void onConnected(); + void onConnectionSuspended(); + void onConnectionFailed(); + } + + static class ConnectionCallbackProxy<T extends ConnectionCallback> + extends MediaBrowser.ConnectionCallback { + protected final T mConnectionCallback; + + public ConnectionCallbackProxy(T connectionCallback) { + mConnectionCallback = connectionCallback; + } + + @Override + public void onConnected() { + mConnectionCallback.onConnected(); + } + + @Override + public void onConnectionSuspended() { + mConnectionCallback.onConnectionSuspended(); + } + + @Override + public void onConnectionFailed() { + mConnectionCallback.onConnectionFailed(); + } + } + + interface SubscriptionCallback { + void onChildrenLoaded(@NonNull String parentId, List<Parcel> children); + void onError(@NonNull String parentId); + } + + static class SubscriptionCallbackProxy<T extends SubscriptionCallback> + extends MediaBrowser.SubscriptionCallback { + protected final T mSubscriptionCallback; + + public SubscriptionCallbackProxy(T callback) { + mSubscriptionCallback = callback; + } + + @Override + public void onChildrenLoaded(@NonNull String parentId, + List<MediaBrowser.MediaItem> children) { + List<Parcel> parcelList = null; + if (children != null && children.size() == 1 + && children.get(0).getMediaId().equals(NULL_MEDIA_ITEM_ID)) { + children = null; + } + if (children != null) { + parcelList = new ArrayList<>(); + for (MediaBrowser.MediaItem item : children) { + Parcel parcel = Parcel.obtain(); + item.writeToParcel(parcel, 0); + parcelList.add(parcel); + } + } + mSubscriptionCallback.onChildrenLoaded(parentId, parcelList); + } + + @Override + public void onError(@NonNull String parentId) { + mSubscriptionCallback.onError(parentId); + } + } +} diff --git a/v4/api21/android/support/v4/media/MediaBrowserServiceCompatApi21.java b/v4/api21/android/support/v4/media/MediaBrowserServiceCompatApi21.java new file mode 100644 index 0000000000..a1506d3f22 --- /dev/null +++ b/v4/api21/android/support/v4/media/MediaBrowserServiceCompatApi21.java @@ -0,0 +1,165 @@ +/* + * 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.v4.media; + +import android.content.Intent; +import android.content.pm.ParceledListSlice; +import android.media.MediaDescription; +import android.media.browse.MediaBrowser; +import android.media.session.MediaSession; +import android.os.Build; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Parcel; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.service.media.IMediaBrowserService; +import android.service.media.IMediaBrowserServiceCallbacks; +import android.service.media.MediaBrowserService; + +import java.util.ArrayList; +import java.util.List; + +class MediaBrowserServiceCompatApi21 { + + public static Object createService() { + return new MediaBrowserServiceAdaptorApi21(); + } + + public static void onCreate(Object serviceObj, ServiceImplApi21 serviceImpl) { + ((MediaBrowserServiceAdaptorApi21) serviceObj).onCreate(serviceImpl); + } + + public static IBinder onBind(Object serviceObj, Intent intent) { + return ((MediaBrowserServiceAdaptorApi21) serviceObj).onBind(intent); + } + + public interface ServiceImplApi21 { + void connect(final String pkg, final Bundle rootHints, final ServiceCallbacks callbacks); + void disconnect(final ServiceCallbacks callbacks); + void addSubscription(final String id, final ServiceCallbacks callbacks); + void removeSubscription(final String id, final ServiceCallbacks callbacks); + } + + public interface ServiceCallbacks { + IBinder asBinder(); + void onConnect(String root, Object session, Bundle extras) throws RemoteException; + void onConnectFailed() throws RemoteException; + void onLoadChildren(String mediaId, List<Parcel> list) throws RemoteException; + } + + public static class ServiceCallbacksApi21 implements ServiceCallbacks { + private static final ParceledListSlice sNullParceledListSlice; + static { + MediaDescription nullDescription = new MediaDescription.Builder().setMediaId( + MediaBrowserCompatApi21.NULL_MEDIA_ITEM_ID).build(); + MediaBrowser.MediaItem nullMediaItem = new MediaBrowser.MediaItem(nullDescription, 0); + List<MediaBrowser.MediaItem> nullMediaItemList = new ArrayList<>(); + nullMediaItemList.add(nullMediaItem); + sNullParceledListSlice = new ParceledListSlice(nullMediaItemList); + } + + private final IMediaBrowserServiceCallbacks mCallbacks; + + ServiceCallbacksApi21(IMediaBrowserServiceCallbacks callbacks) { + mCallbacks = callbacks; + } + + public IBinder asBinder() { + return mCallbacks.asBinder(); + } + + public void onConnect(String root, Object session, Bundle extras) throws RemoteException { + mCallbacks.onConnect(root, (MediaSession.Token) session, extras); + } + + public void onConnectFailed() throws RemoteException { + mCallbacks.onConnectFailed(); + } + + public void onLoadChildren(String mediaId, List<Parcel> list) throws RemoteException { + List<MediaBrowser.MediaItem> itemList = null; + if (list != null) { + itemList = new ArrayList<>(); + for (Parcel parcel : list) { + parcel.setDataPosition(0); + itemList.add(MediaBrowser.MediaItem.CREATOR.createFromParcel(parcel)); + parcel.recycle(); + } + } + ParceledListSlice<MediaBrowser.MediaItem> pls; + if (Build.VERSION.SDK_INT > 23) { + pls = itemList == null ? null : new ParceledListSlice(itemList); + } else { + pls = itemList == null ? sNullParceledListSlice : new ParceledListSlice(itemList); + } + mCallbacks.onLoadChildren(mediaId, pls); + } + } + + static class MediaBrowserServiceAdaptorApi21 { + ServiceBinderProxyApi21 mBinder; + + public void onCreate(ServiceImplApi21 serviceImpl) { + mBinder = new ServiceBinderProxyApi21(serviceImpl); + } + + public IBinder onBind(Intent intent) { + if (MediaBrowserService.SERVICE_INTERFACE.equals(intent.getAction())) { + return mBinder; + } + return null; + } + + static class ServiceBinderProxyApi21 extends IMediaBrowserService.Stub { + final ServiceImplApi21 mServiceImpl; + + ServiceBinderProxyApi21(ServiceImplApi21 serviceImpl) { + mServiceImpl = serviceImpl; + } + + @Override + public void connect(final String pkg, final Bundle rootHints, + final IMediaBrowserServiceCallbacks callbacks) { + mServiceImpl.connect(pkg, rootHints, new ServiceCallbacksApi21(callbacks)); + } + + @Override + public void disconnect(final IMediaBrowserServiceCallbacks callbacks) { + mServiceImpl.disconnect(new ServiceCallbacksApi21(callbacks)); + } + + + @Override + public void addSubscription(final String id, + final IMediaBrowserServiceCallbacks callbacks) { + mServiceImpl.addSubscription(id, new ServiceCallbacksApi21(callbacks)); + } + + @Override + public void removeSubscription(final String id, + final IMediaBrowserServiceCallbacks callbacks) { + mServiceImpl.removeSubscription(id, new ServiceCallbacksApi21(callbacks)); + } + + @Override + public void getMediaItem(final String mediaId, final ResultReceiver receiver) { + // No operation since this method is added in API 23. + } + } + } +} diff --git a/v4/api21/android/support/v4/media/MediaDescriptionCompatApi21.java b/v4/api21/android/support/v4/media/MediaDescriptionCompatApi21.java index 991515accc..234a77a6c8 100644 --- a/v4/api21/android/support/v4/media/MediaDescriptionCompatApi21.java +++ b/v4/api21/android/support/v4/media/MediaDescriptionCompatApi21.java @@ -21,7 +21,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.Parcel; -public class MediaDescriptionCompatApi21 { +class MediaDescriptionCompatApi21 { public static String getMediaId(Object descriptionObj) { return ((MediaDescription) descriptionObj).getMediaId(); @@ -59,7 +59,7 @@ public class MediaDescriptionCompatApi21 { return MediaDescription.CREATOR.createFromParcel(in); } - public static class Builder { + static class Builder { public static Object newInstance() { return new MediaDescription.Builder(); } diff --git a/v4/api21/android/support/v4/media/MediaMetadataCompatApi21.java b/v4/api21/android/support/v4/media/MediaMetadataCompatApi21.java index eddcf76944..fd51f783ce 100644 --- a/v4/api21/android/support/v4/media/MediaMetadataCompatApi21.java +++ b/v4/api21/android/support/v4/media/MediaMetadataCompatApi21.java @@ -19,6 +19,7 @@ package android.support.v4.media; import android.graphics.Bitmap; import android.media.MediaMetadata; import android.media.Rating; +import android.os.Parcel; import java.util.Set; @@ -43,6 +44,14 @@ class MediaMetadataCompatApi21 { return ((MediaMetadata) metadataObj).getText(key); } + public static void writeToParcel(Object metadataObj, Parcel dest, int flags) { + ((MediaMetadata) metadataObj).writeToParcel(dest, flags); + } + + public static Object createFromParcel(Parcel in) { + return MediaMetadata.CREATOR.createFromParcel(in); + } + public static class Builder { public static Object newInstance() { return new MediaMetadata.Builder(); diff --git a/v4/api21/android/support/v4/media/session/MediaSessionCompatApi21.java b/v4/api21/android/support/v4/media/session/MediaSessionCompatApi21.java index 809439137a..b3e7fd11f1 100644 --- a/v4/api21/android/support/v4/media/session/MediaSessionCompatApi21.java +++ b/v4/api21/android/support/v4/media/session/MediaSessionCompatApi21.java @@ -132,7 +132,7 @@ class MediaSessionCompatApi21 { ((MediaSession) sessionObj).setExtras(extras); } - public static interface Callback { + static interface Callback { public void onCommand(String command, Bundle extras, ResultReceiver cb); public boolean onMediaButtonEvent(Intent mediaButtonIntent); public void onPlay(); diff --git a/v4/api22/android/support/v4/graphics/drawable/DrawableCompatApi22.java b/v4/api22/android/support/v4/graphics/drawable/DrawableCompatApi22.java index bfd2bea122..50f265704a 100644 --- a/v4/api22/android/support/v4/graphics/drawable/DrawableCompatApi22.java +++ b/v4/api22/android/support/v4/graphics/drawable/DrawableCompatApi22.java @@ -24,8 +24,8 @@ import android.graphics.drawable.Drawable; class DrawableCompatApi22 { public static Drawable wrapForTinting(Drawable drawable) { - // We don't need to wrap anything in Lollipop-MR1 - return drawable; + // We need to wrap to force an invalidation on any state change + return new DrawableWrapperLollipop(drawable); } } diff --git a/v4/api23/android/support/v4/app/AppOpsManagerCompat23.java b/v4/api23/android/support/v4/app/AppOpsManagerCompat23.java index 64a1206949..72e07bf0ed 100644 --- a/v4/api23/android/support/v4/app/AppOpsManagerCompat23.java +++ b/v4/api23/android/support/v4/app/AppOpsManagerCompat23.java @@ -22,7 +22,7 @@ import android.content.Context; /** * AppOpsManager implementations for API 23. */ -public class AppOpsManagerCompat23 { +class AppOpsManagerCompat23 { public static String permissionToOp(String permission) { return AppOpsManager.permissionToOp(permission); } diff --git a/v4/api23/android/support/v4/content/ResourcesCompatApi23.java b/v4/api23/android/support/v4/content/ResourcesCompatApi23.java new file mode 100644 index 0000000000..c44f9cef1f --- /dev/null +++ b/v4/api23/android/support/v4/content/ResourcesCompatApi23.java @@ -0,0 +1,33 @@ +/* + * 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.v4.content.res; + +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.content.res.Resources.NotFoundException; +import android.content.res.Resources.Theme; + +class ResourcesCompatApi23 { + public static int getColor(Resources res, int id, Theme theme) throws NotFoundException { + return res.getColor(id, theme); + } + + public static ColorStateList getColorStateList(Resources res, int id, Theme theme) + throws NotFoundException { + return res.getColorStateList(id, theme); + } +} diff --git a/v4/api23/android/support/v4/media/MediaBrowserCompatApi23.java b/v4/api23/android/support/v4/media/MediaBrowserCompatApi23.java new file mode 100644 index 0000000000..1e9df1a439 --- /dev/null +++ b/v4/api23/android/support/v4/media/MediaBrowserCompatApi23.java @@ -0,0 +1,57 @@ +/* + * 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.v4.media; + +import android.media.browse.MediaBrowser; +import android.os.Parcel; +import android.support.annotation.NonNull; + +class MediaBrowserCompatApi23 { + + public static Object createItemCallback(ItemCallback callback) { + return new ItemCallbackProxy<>(callback); + } + + public static void getItem(Object browserObj, String mediaId, Object itemCallbackObj) { + ((MediaBrowser) browserObj).getItem(mediaId, ((MediaBrowser.ItemCallback) itemCallbackObj)); + } + + interface ItemCallback { + void onItemLoaded(Parcel itemParcel); + void onError(@NonNull String itemId); + } + + static class ItemCallbackProxy<T extends ItemCallback> extends MediaBrowser.ItemCallback { + protected final T mItemCallback; + + public ItemCallbackProxy(T callback) { + mItemCallback = callback; + } + + @Override + public void onItemLoaded(MediaBrowser.MediaItem item) { + Parcel parcel = Parcel.obtain(); + item.writeToParcel(parcel, 0); + mItemCallback.onItemLoaded(parcel); + } + + @Override + public void onError(@NonNull String itemId) { + mItemCallback.onError(itemId); + } + } +} diff --git a/v4/api23/android/support/v4/media/MediaBrowserServiceCompatApi23.java b/v4/api23/android/support/v4/media/MediaBrowserServiceCompatApi23.java new file mode 100644 index 0000000000..fcaea408a2 --- /dev/null +++ b/v4/api23/android/support/v4/media/MediaBrowserServiceCompatApi23.java @@ -0,0 +1,86 @@ +/* + * 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.v4.media; + +import android.media.browse.MediaBrowser; +import android.os.Bundle; +import android.os.Parcel; +import android.os.ResultReceiver; +import android.service.media.MediaBrowserService; +import android.util.Log; + +class MediaBrowserServiceCompatApi23 extends MediaBrowserServiceCompatApi21 { + private static final String TAG = "MediaBrowserServiceCompatApi21"; + + public static Object createService() { + return new MediaBrowserServiceAdaptorApi23(); + } + + public static void onCreate(Object serviceObj, ServiceImplApi23 serviceImpl) { + ((MediaBrowserServiceAdaptorApi23) serviceObj).onCreate(serviceImpl); + } + + public interface ServiceImplApi23 extends ServiceImplApi21 { + void getMediaItem(final String mediaId, final ItemCallback cb); + } + + public interface ItemCallback { + void onItemLoaded(int resultCode, Bundle resultData, Parcel itemParcel); + } + + static class MediaBrowserServiceAdaptorApi23 extends MediaBrowserServiceAdaptorApi21 { + + public void onCreate(ServiceImplApi23 serviceImpl) { + mBinder = new ServiceBinderProxyApi23(serviceImpl); + } + + private static class ServiceBinderProxyApi23 extends ServiceBinderProxyApi21 { + ServiceImplApi23 mServiceImpl; + + ServiceBinderProxyApi23(ServiceImplApi23 serviceImpl) { + super(serviceImpl); + mServiceImpl = serviceImpl; + } + + @Override + public void getMediaItem(final String mediaId, final ResultReceiver receiver) { + final String KEY_MEDIA_ITEM; + try { + KEY_MEDIA_ITEM = (String) MediaBrowserService.class.getDeclaredField( + "KEY_MEDIA_ITEM").get(null); + } catch (IllegalAccessException | NoSuchFieldException e) { + Log.i(TAG, "Failed to get KEY_MEDIA_ITEM via reflection", e); + return; + } + + mServiceImpl.getMediaItem(mediaId, new ItemCallback() { + @Override + public void onItemLoaded(int resultCode, Bundle resultData, Parcel itemParcel) { + if (itemParcel != null) { + itemParcel.setDataPosition(0); + MediaBrowser.MediaItem item = + MediaBrowser.MediaItem.CREATOR.createFromParcel(itemParcel); + resultData.putParcelable(KEY_MEDIA_ITEM, item); + itemParcel.recycle(); + } + receiver.send(resultCode, resultData); + } + }); + } + } + } +} diff --git a/v4/api23/android/support/v4/text/ICUCompatApi23.java b/v4/api23/android/support/v4/text/ICUCompatApi23.java index f013522a90..1a3c54f17e 100644 --- a/v4/api23/android/support/v4/text/ICUCompatApi23.java +++ b/v4/api23/android/support/v4/text/ICUCompatApi23.java @@ -22,7 +22,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Locale; -public class ICUCompatApi23 { +class ICUCompatApi23 { private static final String TAG = "ICUCompatIcs"; diff --git a/v4/api23/android/support/v4/view/ViewCompatMarshmallow.java b/v4/api23/android/support/v4/view/ViewCompatMarshmallow.java new file mode 100644 index 0000000000..16d3ae88e1 --- /dev/null +++ b/v4/api23/android/support/v4/view/ViewCompatMarshmallow.java @@ -0,0 +1,33 @@ +/* + * 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.v4.view; + +import android.view.View; + +class ViewCompatMarshmallow { + public static void setScrollIndicators(View view, int indicators) { + view.setScrollIndicators(indicators); + } + + public static void setScrollIndicators(View view, int indicators, int mask) { + view.setScrollIndicators(indicators, mask); + } + + public static int getScrollIndicators(View view) { + return view.getScrollIndicators(); + } +} diff --git a/v4/api23/android/support/v4/widget/TextViewCompatApi23.java b/v4/api23/android/support/v4/widget/TextViewCompatApi23.java new file mode 100644 index 0000000000..8370bfcd87 --- /dev/null +++ b/v4/api23/android/support/v4/widget/TextViewCompatApi23.java @@ -0,0 +1,27 @@ +/* + * 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.v4.widget; + +import android.support.annotation.IdRes; +import android.support.annotation.NonNull; +import android.widget.TextView; + +class TextViewCompatApi23 { + public static void setTextAppearance(@NonNull TextView textView, @IdRes int resId) { + textView.setTextAppearance(resId); + } +} diff --git a/v4/build.gradle b/v4/build.gradle index a056973e6d..ba65cabb07 100644 --- a/v4/build.gradle +++ b/v4/build.gradle @@ -64,6 +64,10 @@ dependencies { // depend on the generation of this jar. This is done below // when manipulating the libraryVariants. compile files(internalJar.archivePath) + + androidTestCompile 'com.android.support.test:testing-support-lib:0.1' + androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0' + testCompile 'junit:junit:4.12' } android { @@ -73,6 +77,8 @@ android { minSdkVersion 4 // TODO: get target from branch //targetSdkVersion 19 + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } sourceSets { @@ -82,6 +88,8 @@ android { androidTest.setRoot('tests') androidTest.java.srcDir 'tests/java' + androidTest.res.srcDir 'tests/res' + androidTest.manifest.srcFile 'tests/AndroidManifest.xml' } lintOptions { @@ -93,6 +101,10 @@ android { sourceCompatibility JavaVersion.VERSION_1_7 targetCompatibility JavaVersion.VERSION_1_7 } + + testOptions { + unitTests.returnDefaultValues = true + } } android.libraryVariants.all { variant -> @@ -124,6 +136,8 @@ android.libraryVariants.all { variant -> def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) { classifier = 'sources' from android.sourceSets.main.java.srcDirs + exclude('android/content/pm/**') + exclude('android/service/media/**') } project.ext.allSS.each { ss -> @@ -135,6 +149,17 @@ android.libraryVariants.all { variant -> artifacts.add('archives', sourcesJarTask); } +// TODO make this generic for all projects +afterEvaluate { + def originalTask = tasks['packageDebugAndroidTest'] + tasks['assembleDebugAndroidTest'].doLast { + copy { + from(originalTask.outputFile) + into(rootProject.ext.testApkDistOut) + } + } +} + uploadArchives { repositories { mavenDeployer { diff --git a/v4/donut/android/support/v4/animation/ValueAnimatorCompat.java b/v4/donut/android/support/v4/animation/ValueAnimatorCompat.java index 6d98c97719..07cc602708 100644 --- a/v4/donut/android/support/v4/animation/ValueAnimatorCompat.java +++ b/v4/donut/android/support/v4/animation/ValueAnimatorCompat.java @@ -19,7 +19,7 @@ package android.support.v4.animation; import android.view.View; /** - * Compatibility implementation for {@link android.animation.Animator}. + * Compatibility implementation for {@code android.animation.ValueAnimator}. * * @hide */ diff --git a/v4/donut/android/support/v4/graphics/drawable/DrawableCompatBase.java b/v4/donut/android/support/v4/graphics/drawable/DrawableCompatBase.java index 4809618527..fe0163dd61 100644 --- a/v4/donut/android/support/v4/graphics/drawable/DrawableCompatBase.java +++ b/v4/donut/android/support/v4/graphics/drawable/DrawableCompatBase.java @@ -27,19 +27,19 @@ class DrawableCompatBase { public static void setTint(Drawable drawable, int tint) { if (drawable instanceof DrawableWrapper) { - ((DrawableWrapper) drawable).setTint(tint); + ((DrawableWrapper) drawable).setCompatTint(tint); } } public static void setTintList(Drawable drawable, ColorStateList tint) { if (drawable instanceof DrawableWrapper) { - ((DrawableWrapper) drawable).setTintList(tint); + ((DrawableWrapper) drawable).setCompatTintList(tint); } } public static void setTintMode(Drawable drawable, PorterDuff.Mode tintMode) { if (drawable instanceof DrawableWrapper) { - ((DrawableWrapper) drawable).setTintMode(tintMode); + ((DrawableWrapper) drawable).setCompatTintMode(tintMode); } } diff --git a/v4/donut/android/support/v4/graphics/drawable/DrawableWrapper.java b/v4/donut/android/support/v4/graphics/drawable/DrawableWrapper.java index 1073f344c6..edbe5ad9d0 100644 --- a/v4/donut/android/support/v4/graphics/drawable/DrawableWrapper.java +++ b/v4/donut/android/support/v4/graphics/drawable/DrawableWrapper.java @@ -28,11 +28,11 @@ import android.graphics.drawable.Drawable; */ public interface DrawableWrapper { - void setTint(int tint); + void setCompatTint(int tint); - void setTintList(ColorStateList tint); + void setCompatTintList(ColorStateList tint); - void setTintMode(PorterDuff.Mode tintMode); + void setCompatTintMode(PorterDuff.Mode tintMode); Drawable getWrappedDrawable(); diff --git a/v4/donut/android/support/v4/graphics/drawable/DrawableWrapperDonut.java b/v4/donut/android/support/v4/graphics/drawable/DrawableWrapperDonut.java index 9293520455..300e2e8ce1 100644 --- a/v4/donut/android/support/v4/graphics/drawable/DrawableWrapperDonut.java +++ b/v4/donut/android/support/v4/graphics/drawable/DrawableWrapperDonut.java @@ -89,7 +89,8 @@ class DrawableWrapperDonut extends Drawable implements Drawable.Callback, Drawab @Override public boolean isStateful() { - return (mTintList != null && mTintList.isStateful()) || mDrawable.isStateful(); + final ColorStateList tintList = isCompatTintEnabled() ? mTintList : null; + return (tintList != null && tintList.isStateful()) || mDrawable.isStateful(); } @Override @@ -188,30 +189,38 @@ class DrawableWrapperDonut extends Drawable implements Drawable.Callback, Drawab } @Override - public void setTint(int tint) { - setTintList(ColorStateList.valueOf(tint)); + public void setCompatTint(int tint) { + setCompatTintList(ColorStateList.valueOf(tint)); } @Override - public void setTintList(ColorStateList tint) { - mTintList = tint; - updateTint(getState()); + public void setCompatTintList(ColorStateList tint) { + if (mTintList != tint) { + mTintList = tint; + updateTint(getState()); + } } @Override - public void setTintMode(PorterDuff.Mode tintMode) { - mTintMode = tintMode; - updateTint(getState()); + public void setCompatTintMode(PorterDuff.Mode tintMode) { + if (mTintMode != tintMode) { + mTintMode = tintMode; + updateTint(getState()); + } } private boolean updateTint(int[] state) { + if (!isCompatTintEnabled()) { + // If compat tinting is not enabled, fail fast + return false; + } + if (mTintList != null && mTintMode != null) { final int color = mTintList.getColorForState(state, mTintList.getDefaultColor()); - final PorterDuff.Mode mode = mTintMode; - if (!mColorFilterSet || color != mCurrentColor || mode != mCurrentMode) { - setColorFilter(color, mode); + if (!mColorFilterSet || color != mCurrentColor || mTintMode != mCurrentMode) { + setColorFilter(color, mTintMode); mCurrentColor = color; - mCurrentMode = mode; + mCurrentMode = mTintMode; mColorFilterSet = true; return true; } @@ -236,13 +245,26 @@ class DrawableWrapperDonut extends Drawable implements Drawable.Callback, Drawab if (mDrawable != null) { mDrawable.setCallback(null); } - - mDrawable = drawable; + mDrawable = null; if (drawable != null) { + // Copy over the bounds from the drawable + setBounds(drawable.getBounds()); + // Set ourselves as the callback for invalidations drawable.setCallback(this); + } else { + // Clear our bounds + setBounds(0, 0, 0, 0); } + + mDrawable = drawable; + // Invalidate ourselves invalidateSelf(); } + + protected boolean isCompatTintEnabled() { + // It's enabled by default on Donut + return true; + } } diff --git a/v4/froyo/android/support/v4/media/session/MediaSessionCompatApi8.java b/v4/froyo/android/support/v4/media/session/MediaSessionCompatApi8.java index f49eb2b7cb..d03287fead 100644 --- a/v4/froyo/android/support/v4/media/session/MediaSessionCompatApi8.java +++ b/v4/froyo/android/support/v4/media/session/MediaSessionCompatApi8.java @@ -19,7 +19,7 @@ import android.content.ComponentName; import android.content.Context; import android.media.AudioManager; -public class MediaSessionCompatApi8 { +class MediaSessionCompatApi8 { public static void registerMediaButtonEventReceiver(Context context, ComponentName mbr) { AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); am.registerMediaButtonEventReceiver(mbr); diff --git a/v4/gingerbread/android/support/v4/widget/ScrollerCompatGingerbread.java b/v4/gingerbread/android/support/v4/widget/ScrollerCompatGingerbread.java index 429e864b08..61c9a03e23 100644 --- a/v4/gingerbread/android/support/v4/widget/ScrollerCompatGingerbread.java +++ b/v4/gingerbread/android/support/v4/widget/ScrollerCompatGingerbread.java @@ -87,4 +87,9 @@ class ScrollerCompatGingerbread { public static int getFinalY(Object scroller) { return ((OverScroller) scroller).getFinalY(); } + + public static boolean springBack(Object scroller, int startX, int startY, int minX, int maxX, + int minY, int maxY) { + return ((OverScroller) scroller).springBack(startX, startY, minX, maxX, minY, maxY); + } } diff --git a/v4/honeycomb/android/support/v4/content/ExecutorCompatHoneycomb.java b/v4/honeycomb/android/support/v4/content/ExecutorCompatHoneycomb.java new file mode 100644 index 0000000000..fa6af478f6 --- /dev/null +++ b/v4/honeycomb/android/support/v4/content/ExecutorCompatHoneycomb.java @@ -0,0 +1,31 @@ +/* + * 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.v4.content; + +import android.os.AsyncTask; + +import java.util.concurrent.Executor; + +/** + * Implementation of parallel executor compatibility that can call Honeycomb APIs. + * @hide + */ +class ExecutorCompatHoneycomb { + public static Executor getParallelExecutor() { + return AsyncTask.THREAD_POOL_EXECUTOR; + } +} diff --git a/v4/ics-mr1/android/support/v4/view/ViewCompatICSMr1.java b/v4/ics-mr1/android/support/v4/view/ViewCompatICSMr1.java new file mode 100644 index 0000000000..780345c61b --- /dev/null +++ b/v4/ics-mr1/android/support/v4/view/ViewCompatICSMr1.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2011 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.v4.view; + +import android.support.annotation.Nullable; +import android.view.View; +import android.view.View.AccessibilityDelegate; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; + +/** + * Helper for accessing newer features in View introduced in ICS Mr1. + */ +class ViewCompatICSMr1 { + public static boolean hasOnClickListeners(View v) { + return v.hasOnClickListeners(); + } +} diff --git a/v4/ics/android/support/v4/media/session/MediaSessionCompatApi14.java b/v4/ics/android/support/v4/media/session/MediaSessionCompatApi14.java index de644e695d..3378e1ec86 100644 --- a/v4/ics/android/support/v4/media/session/MediaSessionCompatApi14.java +++ b/v4/ics/android/support/v4/media/session/MediaSessionCompatApi14.java @@ -25,7 +25,7 @@ import android.media.RemoteControlClient; import android.os.Bundle; import android.os.ResultReceiver; -public class MediaSessionCompatApi14 { +class MediaSessionCompatApi14 { /***** RemoteControlClient States, we only need none as the others were public *******/ final static int RCC_PLAYSTATE_NONE = 0; @@ -224,7 +224,7 @@ public class MediaSessionCompatApi14 { } } - public static interface Callback { + static interface Callback { public void onCommand(String command, Bundle extras, ResultReceiver cb); public boolean onMediaButtonEvent(Intent mediaButtonIntent); diff --git a/v4/ics/android/support/v4/view/ViewParentCompatICS.java b/v4/ics/android/support/v4/view/ViewParentCompatICS.java index 251de0e0d0..f9fc5a5f74 100644 --- a/v4/ics/android/support/v4/view/ViewParentCompatICS.java +++ b/v4/ics/android/support/v4/view/ViewParentCompatICS.java @@ -23,7 +23,7 @@ import android.view.accessibility.AccessibilityEvent; /** * ICS-specific ViewParent API implementation. */ -public class ViewParentCompatICS { +class ViewParentCompatICS { public static boolean requestSendAccessibilityEvent( ViewParent parent, View child, AccessibilityEvent event) { return parent.requestSendAccessibilityEvent(child, event); diff --git a/v4/java/android/support/v4/animation/AnimatorCompatHelper.java b/v4/java/android/support/v4/animation/AnimatorCompatHelper.java index 8fa3679e6b..6ebe5c84ad 100644 --- a/v4/java/android/support/v4/animation/AnimatorCompatHelper.java +++ b/v4/java/android/support/v4/animation/AnimatorCompatHelper.java @@ -19,9 +19,12 @@ package android.support.v4.animation; import android.os.Build; import android.view.View; -abstract public class AnimatorCompatHelper { +/** + * @hide + */ +public final class AnimatorCompatHelper { - static AnimatorProvider IMPL; + private final static AnimatorProvider IMPL; static { if (Build.VERSION.SDK_INT >= 12) { @@ -35,9 +38,7 @@ abstract public class AnimatorCompatHelper { return IMPL.emptyValueAnimator(); } - AnimatorCompatHelper() { - - } + private AnimatorCompatHelper() {} public static void clearInterpolator(View view) { IMPL.clearInterpolator(view); diff --git a/v4/java/android/support/v4/app/FragmentActivity.java b/v4/java/android/support/v4/app/FragmentActivity.java index 1dd6d018d9..59881a21e6 100644 --- a/v4/java/android/support/v4/app/FragmentActivity.java +++ b/v4/java/android/support/v4/app/FragmentActivity.java @@ -27,6 +27,7 @@ import android.os.Message; import android.os.Parcelable; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.v4.media.session.MediaControllerCompat; import android.support.v4.util.SimpleArrayMap; import android.util.AttributeSet; import android.util.Log; @@ -124,6 +125,8 @@ public class FragmentActivity extends BaseFragmentActivityHoneycomb implements SimpleArrayMap<String, LoaderManager> loaders; } + MediaControllerCompat mMediaController; + // ------------------------------------------------------------------------ // HOOKS INTO ACTIVITY // ------------------------------------------------------------------------ @@ -169,6 +172,39 @@ public class FragmentActivity extends BaseFragmentActivityHoneycomb implements } /** + * Sets a {@link MediaControllerCompat} for later retrieval via + * {@link #getSupportMediaController()}. + * + * <p>On API 21 and later, this controller will be tied to the window of the activity and + * media key and volume events which are received while the Activity is in the foreground + * will be forwarded to the controller and used to invoke transport controls or adjust the + * volume. Prior to API 21, the global handling of media key and volume events through an + * active {@link android.support.v4.media.session.MediaSessionCompat} and media button receiver + * will still be respected.</p> + * + * @param mediaController The controller for the session which should receive + * media keys and volume changes on API 21 and later. + * @see #setMediaController(android.media.session.MediaController) + */ + final public void setSupportMediaController(MediaControllerCompat mediaController) { + mMediaController = mediaController; + if (android.os.Build.VERSION.SDK_INT >= 21) { + ActivityCompat21.setMediaController(this, mediaController.getMediaController()); + } + } + + /** + * Retrieves the current {@link MediaControllerCompat} for sending media key and volume events. + * + * @return The controller which should receive events. + * @see #setSupportMediaController(android.support.v4.media.session.MediaController) + * @see #getMediaController() + */ + final public MediaControllerCompat getSupportMediaController() { + return mMediaController; + } + + /** * Reverses the Activity Scene entry Transition and triggers the calling Activity * to reverse its exit Transition. When the exit Transition completes, * {@link #finish()} is called. If no entry Transition was used, finish() is called diff --git a/v4/java/android/support/v4/app/FragmentManager.java b/v4/java/android/support/v4/app/FragmentManager.java index adf8892a2f..50bc88bc20 100644 --- a/v4/java/android/support/v4/app/FragmentManager.java +++ b/v4/java/android/support/v4/app/FragmentManager.java @@ -52,6 +52,7 @@ import android.view.ViewGroup; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -414,8 +415,9 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate static class AnimateOnHWLayerIfNeededListener implements AnimationListener { + private AnimationListener mOrignalListener = null; private boolean mShouldRunOnHWLayer = false; - private View mView; + private View mView = null; public AnimateOnHWLayerIfNeededListener(final View v, Animation anim) { if (v == null || anim == null) { return; @@ -423,24 +425,38 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate mView = v; } + public AnimateOnHWLayerIfNeededListener(final View v, Animation anim, + AnimationListener listener) { + if (v == null || anim == null) { + return; + } + mOrignalListener = listener; + mView = v; + } + @Override @CallSuper public void onAnimationStart(Animation animation) { - mShouldRunOnHWLayer = shouldRunOnHWLayer(mView, animation); - if (mShouldRunOnHWLayer) { - mView.post(new Runnable() { - @Override - public void run() { - ViewCompat.setLayerType(mView, ViewCompat.LAYER_TYPE_HARDWARE, null); - } - }); + if (mView != null) { + mShouldRunOnHWLayer = shouldRunOnHWLayer(mView, animation); + if (mShouldRunOnHWLayer) { + mView.post(new Runnable() { + @Override + public void run() { + ViewCompat.setLayerType(mView, ViewCompat.LAYER_TYPE_HARDWARE, null); + } + }); + } + } + if (mOrignalListener != null) { + mOrignalListener.onAnimationStart(animation); } } @Override @CallSuper public void onAnimationEnd(Animation animation) { - if (mShouldRunOnHWLayer) { + if (mView != null && mShouldRunOnHWLayer) { mView.post(new Runnable() { @Override public void run() { @@ -448,10 +464,16 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate } }); } + if (mOrignalListener != null) { + mOrignalListener.onAnimationEnd(animation); + } } @Override public void onAnimationRepeat(Animation animation) { + if (mOrignalListener != null) { + mOrignalListener.onAnimationRepeat(animation); + } } } @@ -476,6 +498,8 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate FragmentController mController; FragmentContainer mContainer; Fragment mParent; + + static Field sAnimationListenerField = null; boolean mNeedMenuInvalidate; boolean mStateSaved; @@ -509,7 +533,8 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate } static boolean shouldRunOnHWLayer(View v, Animation anim) { - return ViewCompat.getLayerType(v) == ViewCompat.LAYER_TYPE_NONE + return Build.VERSION.SDK_INT >= 19 + && ViewCompat.getLayerType(v) == ViewCompat.LAYER_TYPE_NONE && ViewCompat.hasOverlappingRendering(v) && modifiesAlpha(anim); } @@ -914,7 +939,23 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate return; } if (shouldRunOnHWLayer(v, anim)) { - anim.setAnimationListener(new AnimateOnHWLayerIfNeededListener(v, anim)); + AnimationListener originalListener = null; + try { + if (sAnimationListenerField == null) { + sAnimationListenerField = Animation.class.getDeclaredField("mListener"); + sAnimationListenerField.setAccessible(true); + } + originalListener = (AnimationListener) sAnimationListenerField.get(anim); + } catch (NoSuchFieldException e) { + Log.e(TAG, "No field with the name mListener is found in Animation class", e); + } catch (IllegalAccessException e) { + Log.e(TAG, "Cannot access Animation's mListener field", e); + } + // If there's already a listener set on the animation, we need wrap the new listener + // around the existing listener, so that they will both get animation listener + // callbacks. + anim.setAnimationListener(new AnimateOnHWLayerIfNeededListener(v, anim, + originalListener)); } } diff --git a/v4/java/android/support/v4/app/NotificationCompat.java b/v4/java/android/support/v4/app/NotificationCompat.java index b206e95243..e23c11c8bd 100644 --- a/v4/java/android/support/v4/app/NotificationCompat.java +++ b/v4/java/android/support/v4/app/NotificationCompat.java @@ -2247,7 +2247,7 @@ public class NotificationCompat { * Size value for use with {@link #setCustomSizePreset} to show this notification with * default sizing. * <p>For custom display notifications created using {@link #setDisplayIntent}, - * the default is {@link #SIZE_LARGE}. All other notifications size automatically based + * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based * on their content. */ public static final int SIZE_DEFAULT = 0; diff --git a/v4/java/android/support/v4/content/ContextCompat.java b/v4/java/android/support/v4/content/ContextCompat.java index b8029d1b6c..29629e7323 100644 --- a/v4/java/android/support/v4/content/ContextCompat.java +++ b/v4/java/android/support/v4/content/ContextCompat.java @@ -403,7 +403,7 @@ public class ContextCompat { * * @see android.content.Context.getFilesDir */ - public final File getNoBackupFilesDir(Context context) { + public static File getNoBackupFilesDir(Context context) { final int version = Build.VERSION.SDK_INT; if (version >= 21) { return ContextCompatApi21.getNoBackupFilesDir(context); @@ -428,7 +428,7 @@ public class ContextCompat { * * @return The path of the directory holding application code cache files. */ - public final File getCodeCacheDir(Context context) { + public static File getCodeCacheDir(Context context) { final int version = Build.VERSION.SDK_INT; if (version >= 21) { return ContextCompatApi21.getCodeCacheDir(context); diff --git a/v4/java/android/support/v4/content/ParallelExecutorCompat.java b/v4/java/android/support/v4/content/ParallelExecutorCompat.java new file mode 100644 index 0000000000..c23470b46b --- /dev/null +++ b/v4/java/android/support/v4/content/ParallelExecutorCompat.java @@ -0,0 +1,36 @@ +/* + * 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.v4.content; + +import android.os.Build; + +import java.util.concurrent.Executor; + +/** + * Helper for accessing a shared parallel Executor instance + * introduced after API level 4 in a backwards compatible fashion. + */ +public class ParallelExecutorCompat { + public static Executor getParallelExecutor() { + if (Build.VERSION.SDK_INT >= 11) { + // From API 11 onwards, return AsyncTask.THREAD_POOL_EXECUTOR + return ExecutorCompatHoneycomb.getParallelExecutor(); + } else { + return ModernAsyncTask.THREAD_POOL_EXECUTOR; + } + } +} diff --git a/v4/java/android/support/v4/content/res/ResourcesCompat.java b/v4/java/android/support/v4/content/res/ResourcesCompat.java index 252977b4a8..ce5f658c09 100644 --- a/v4/java/android/support/v4/content/res/ResourcesCompat.java +++ b/v4/java/android/support/v4/content/res/ResourcesCompat.java @@ -16,11 +16,18 @@ package android.support.v4.content.res; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; import android.content.res.Resources.Theme; import android.graphics.drawable.Drawable; -import android.os.Build; +import android.support.annotation.ColorInt; +import android.support.annotation.ColorRes; +import android.support.annotation.DrawableRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import static android.os.Build.VERSION.SDK_INT; /** * Helper for accessing features in {@link android.content.res.Resources} @@ -45,11 +52,11 @@ public class ResourcesCompat { * @throws NotFoundException Throws NotFoundException if the given ID does * not exist. */ + @Nullable @SuppressWarnings("deprecation") - public static Drawable getDrawable(Resources res, int id, Theme theme) - throws NotFoundException { - final int version = Build.VERSION.SDK_INT; - if (version >= 21) { + public static Drawable getDrawable(@NonNull Resources res, @DrawableRes int id, + @Nullable Theme theme) throws NotFoundException { + if (SDK_INT >= 21) { return ResourcesCompatApi21.getDrawable(res, id, theme); } else { return res.getDrawable(id); @@ -65,7 +72,7 @@ public class ResourcesCompat { * this method simply calls through to {@link Resources#getDrawable(int)}. * <p> * Prior to API level 21, the theme will not be applied and this method - * calls through to Resources.getDrawableForDensity(int, int). + * calls through to Resources#getDrawableForDensity(int, int). * * @param id The desired resource identifier, as generated by the aapt * tool. This integer encodes the package, type, and resource @@ -76,18 +83,76 @@ public class ResourcesCompat { * {@code null}. * @return Drawable An object that can be used to draw this resource. * @throws NotFoundException Throws NotFoundException if the given ID does - * not exist. + * not exist. */ + @Nullable @SuppressWarnings("deprecation") - public static Drawable getDrawableForDensity(Resources res, int id, int density, Theme theme) - throws NotFoundException { - final int version = Build.VERSION.SDK_INT; - if (version >= 21) { + public static Drawable getDrawableForDensity(@NonNull Resources res, @DrawableRes int id, + int density, @Nullable Theme theme) throws NotFoundException { + if (SDK_INT >= 21) { return ResourcesCompatApi21.getDrawableForDensity(res, id, density, theme); - } else if (version >= 15) { + } else if (SDK_INT >= 15) { return ResourcesCompatIcsMr1.getDrawableForDensity(res, id, density); } else { return res.getDrawable(id); } } + + /** + * Returns a themed color integer associated with a particular resource ID. + * If the resource holds a complex {@link ColorStateList}, then the default + * color from the set is returned. + * <p> + * Prior to API level 23, the theme will not be applied and this method + * calls through to {@link Resources#getColor(int)}. + * + * @param id The desired resource identifier, as generated by the aapt + * tool. This integer encodes the package, type, and resource + * entry. The value 0 is an invalid identifier. + * @param theme The theme used to style the color attributes, may be + * {@code null}. + * @return A single color value in the form {@code 0xAARRGGBB}. + * @throws NotFoundException Throws NotFoundException if the given ID does + * not exist. + */ + @ColorInt + @SuppressWarnings("deprecation") + public static int getColor(@NonNull Resources res, @ColorRes int id, @Nullable Theme theme) + throws NotFoundException { + if (SDK_INT >= 23) { + return ResourcesCompatApi23.getColor(res, id, theme); + } else { + return res.getColor(id); + } + } + + /** + * Returns a themed color state list associated with a particular resource + * ID. The resource may contain either a single raw color value or a + * complex {@link ColorStateList} holding multiple possible colors. + * <p> + * Prior to API level 23, the theme will not be applied and this method + * calls through to {@link Resources#getColorStateList(int)}. + * + * @param id The desired resource identifier of a {@link ColorStateList}, + * as generated by the aapt tool. This integer encodes the + * package, type, and resource entry. The value 0 is an invalid + * identifier. + * @param theme The theme used to style the color attributes, may be + * {@code null}. + * @return A themed ColorStateList object containing either a single solid + * color or multiple colors that can be selected based on a state. + * @throws NotFoundException Throws NotFoundException if the given ID does + * not exist. + */ + @Nullable + @SuppressWarnings("deprecation") + public static ColorStateList getColorStateList(@NonNull Resources res, @ColorRes int id, + @Nullable Theme theme) throws NotFoundException { + if (SDK_INT >= 23) { + return ResourcesCompatApi23.getColorStateList(res, id, theme); + } else { + return res.getColorStateList(id); + } + } } diff --git a/v4/java/android/support/v4/graphics/ColorUtils.java b/v4/java/android/support/v4/graphics/ColorUtils.java index aac809bcea..4d9d9b279b 100644 --- a/v4/java/android/support/v4/graphics/ColorUtils.java +++ b/v4/java/android/support/v4/graphics/ColorUtils.java @@ -17,6 +17,10 @@ package android.support.v4.graphics; import android.graphics.Color; +import android.support.annotation.ColorInt; +import android.support.annotation.FloatRange; +import android.support.annotation.IntRange; +import android.support.annotation.NonNull; /** * A set of color-related utility methods, building upon those available in {@code Color}. @@ -24,14 +28,14 @@ import android.graphics.Color; public class ColorUtils { private static final int MIN_ALPHA_SEARCH_MAX_ITERATIONS = 10; - private static final int MIN_ALPHA_SEARCH_PRECISION = 10; + private static final int MIN_ALPHA_SEARCH_PRECISION = 1; private ColorUtils() {} /** * Composite two potentially translucent colors over each other and returns the result. */ - public static int compositeColors(int foreground, int background) { + public static int compositeColors(@ColorInt int foreground, @ColorInt int background) { int bgAlpha = Color.alpha(background); int fgAlpha = Color.alpha(foreground); int a = compositeAlpha(fgAlpha, bgAlpha); @@ -57,10 +61,13 @@ public class ColorUtils { /** * Returns the luminance of a color. - * - * Formula defined here: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef + * <p> + * Formula defined + * <a href="http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef">here</a>. + * </p> */ - public static double calculateLuminance(int color) { + @FloatRange(from = 0.0, to = 1.0) + public static double calculateLuminance(@ColorInt int color) { double red = Color.red(color) / 255d; red = red < 0.03928 ? red / 12.92 : Math.pow((red + 0.055) / 1.055, 2.4); @@ -80,9 +87,10 @@ public class ColorUtils { * Formula defined * <a href="http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef">here</a>. */ - public static double calculateContrast(int foreground, int background) { + public static double calculateContrast(@ColorInt int foreground, @ColorInt int background) { if (Color.alpha(background) != 255) { - throw new IllegalArgumentException("background can not be translucent"); + throw new IllegalArgumentException("background can not be translucent: #" + + Integer.toHexString(background)); } if (Color.alpha(foreground) < 255) { // If the foreground is translucent, composite the foreground over the background @@ -106,10 +114,11 @@ public class ColorUtils { * @param minContrastRatio the minimum contrast ratio. * @return the alpha value in the range 0-255, or -1 if no value could be calculated. */ - public static int calculateMinimumAlpha(int foreground, int background, + public static int calculateMinimumAlpha(@ColorInt int foreground, @ColorInt int background, float minContrastRatio) { if (Color.alpha(background) != 255) { - throw new IllegalArgumentException("background can not be translucent"); + throw new IllegalArgumentException("background can not be translucent: #" + + Integer.toHexString(background)); } // First lets check that a fully opaque foreground has sufficient contrast @@ -158,7 +167,9 @@ public class ColorUtils { * @param b blue component value [0..255] * @param hsl 3 element array which holds the resulting HSL components. */ - public static void RGBToHSL(int r, int g, int b, float[] hsl) { + public static void RGBToHSL(@IntRange(from = 0x0, to = 0xFF) int r, + @IntRange(from = 0x0, to = 0xFF) int g, @IntRange(from = 0x0, to = 0xFF) int b, + @NonNull float[] hsl) { final float rf = r / 255f; final float gf = g / 255f; final float bf = b / 255f; @@ -206,7 +217,7 @@ public class ColorUtils { * @param color the ARGB color to convert. The alpha component is ignored. * @param hsl 3 element array which holds the resulting HSL components. */ - public static void colorToHSL(int color, float[] hsl) { + public static void colorToHSL(@ColorInt int color, @NonNull float[] hsl) { RGBToHSL(Color.red(color), Color.green(color), Color.blue(color), hsl); } @@ -222,7 +233,8 @@ public class ColorUtils { * @param hsl 3 element array which holds the input HSL components. * @return the resulting RGB color */ - public static int HSLToColor(float[] hsl) { + @ColorInt + public static int HSLToColor(@NonNull float[] hsl) { final float h = hsl[0]; final float s = hsl[1]; final float l = hsl[2]; @@ -279,7 +291,9 @@ public class ColorUtils { /** * Set the alpha component of {@code color} to be {@code alpha}. */ - public static int setAlphaComponent(int color, int alpha) { + @ColorInt + public static int setAlphaComponent(@ColorInt int color, + @IntRange(from = 0x0, to = 0xFF) int alpha) { if (alpha < 0 || alpha > 255) { throw new IllegalArgumentException("alpha must be between 0 and 255."); } diff --git a/v4/java/android/support/v4/graphics/drawable/DrawableCompat.java b/v4/java/android/support/v4/graphics/drawable/DrawableCompat.java index 88b5f4a6c6..64ae075a00 100644 --- a/v4/java/android/support/v4/graphics/drawable/DrawableCompat.java +++ b/v4/java/android/support/v4/graphics/drawable/DrawableCompat.java @@ -123,7 +123,7 @@ public class DrawableCompat { @Override public int getLayoutDirection(Drawable drawable) { final int dir = DrawableCompatJellybeanMr1.getLayoutDirection(drawable); - return dir < 0 ? dir : ViewCompat.LAYOUT_DIRECTION_LTR; + return dir >= 0 ? dir : ViewCompat.LAYOUT_DIRECTION_LTR; } } diff --git a/v4/java/android/support/v4/media/MediaBrowserCompat.java b/v4/java/android/support/v4/media/MediaBrowserCompat.java new file mode 100644 index 0000000000..dcce7f57c2 --- /dev/null +++ b/v4/java/android/support/v4/media/MediaBrowserCompat.java @@ -0,0 +1,1243 @@ +/* + * 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.v4.media; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.RemoteException; +import android.support.annotation.IntDef; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.BundleCompat; +import android.support.v4.media.session.MediaControllerCompat; +import android.support.v4.media.session.MediaSessionCompat; +import android.support.v4.os.ResultReceiver; +import android.support.v4.util.ArrayMap; +import android.text.TextUtils; +import android.util.Log; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; + +import static android.support.v4.media.MediaBrowserProtocol.*; + +/** + * Browses media content offered by a {@link MediaBrowserServiceCompat}. + * <p> + * This object is not thread-safe. All calls should happen on the thread on which the browser + * was constructed. + * </p> + */ +public final class MediaBrowserCompat { + private static final String TAG = "MediaBrowserCompat"; + + private final MediaBrowserImpl mImpl; + + /** + * Creates a media browser for the specified media browse service. + * + * @param context The context. + * @param serviceComponent The component name of the media browse service. + * @param callback The connection callback. + * @param rootHints An optional bundle of service-specific arguments to send + * to the media browse service when connecting and retrieving the root id + * for browsing, or null if none. The contents of this bundle may affect + * the information returned when browsing. + */ + public MediaBrowserCompat(Context context, ComponentName serviceComponent, + ConnectionCallback callback, Bundle rootHints) { + if (android.os.Build.VERSION.SDK_INT >= 23) { + mImpl = new MediaBrowserImplApi23(context, serviceComponent, callback, rootHints); + } else if (android.os.Build.VERSION.SDK_INT >= 21) { + mImpl = new MediaBrowserImplApi21(context, serviceComponent, callback, rootHints); + } else { + mImpl = new MediaBrowserImplBase(context, serviceComponent, callback, rootHints); + } + } + + /** + * Connects to the media browse service. + * <p> + * The connection callback specified in the constructor will be invoked + * when the connection completes or fails. + * </p> + */ + public void connect() { + mImpl.connect(); + } + + /** + * Disconnects from the media browse service. + * After this, no more callbacks will be received. + */ + public void disconnect() { + mImpl.disconnect(); + } + + /** + * Returns whether the browser is connected to the service. + */ + public boolean isConnected() { + return mImpl.isConnected(); + } + + /** + * Gets the service component that the media browser is connected to. + */ + public @NonNull + ComponentName getServiceComponent() { + return mImpl.getServiceComponent(); + } + + /** + * Gets the root id. + * <p> + * Note that the root id may become invalid or change when when the + * browser is disconnected. + * </p> + * + * @throws IllegalStateException if not connected. + */ + public @NonNull String getRoot() { + return mImpl.getRoot(); + } + + /** + * Gets any extras for the media service. + * + * @throws IllegalStateException if not connected. + */ + public @Nullable + Bundle getExtras() { + return mImpl.getExtras(); + } + + /** + * Gets the media session token associated with the media browser. + * <p> + * Note that the session token may become invalid or change when when the + * browser is disconnected. + * </p> + * + * @return The session token for the browser, never null. + * + * @throws IllegalStateException if not connected. + */ + public @NonNull MediaSessionCompat.Token getSessionToken() { + return mImpl.getSessionToken(); + } + + /** + * Queries for information about the media items that are contained within + * the specified id and subscribes to receive updates when they change. + * <p> + * The list of subscriptions is maintained even when not connected and is + * restored after reconnection. It is ok to subscribe while not connected + * but the results will not be returned until the connection completes. + * </p> + * <p> + * If the id is already subscribed with a different callback then the new + * callback will replace the previous one and the child data will be + * reloaded. + * </p> + * + * @param parentId The id of the parent media item whose list of children + * will be subscribed. + * @param callback The callback to receive the list of children. + */ + public void subscribe(@NonNull String parentId, @NonNull SubscriptionCallback callback) { + mImpl.subscribe(parentId, callback); + } + + /** + * Unsubscribes for changes to the children of the specified media id. + * <p> + * The query callback will no longer be invoked for results associated with + * this id once this method returns. + * </p> + * + * @param parentId The id of the parent media item whose list of children + * will be unsubscribed. + */ + public void unsubscribe(@NonNull String parentId) { + mImpl.unsubscribe(parentId); + } + + /** + * Retrieves a specific {@link MediaItem} from the connected service. Not + * all services may support this, so falling back to subscribing to the + * parent's id should be used when unavailable. + * + * @param mediaId The id of the item to retrieve. + * @param cb The callback to receive the result on. + */ + public void getItem(final @NonNull String mediaId, @NonNull final ItemCallback cb) { + mImpl.getItem(mediaId, cb); + } + + public static class MediaItem implements Parcelable { + private final int mFlags; + private final MediaDescriptionCompat mDescription; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag=true, value = { FLAG_BROWSABLE, FLAG_PLAYABLE }) + public @interface Flags { } + + /** + * Flag: Indicates that the item has children of its own. + */ + public static final int FLAG_BROWSABLE = 1 << 0; + + /** + * Flag: Indicates that the item is playable. + * <p> + * The id of this item may be passed to + * {@link MediaControllerCompat.TransportControls#playFromMediaId(String, Bundle)} + * to start playing it. + * </p> + */ + public static final int FLAG_PLAYABLE = 1 << 1; + + /** + * Create a new MediaItem for use in browsing media. + * @param description The description of the media, which must include a + * media id. + * @param flags The flags for this item. + */ + public MediaItem(@NonNull MediaDescriptionCompat description, @Flags int flags) { + if (description == null) { + throw new IllegalArgumentException("description cannot be null"); + } + if (TextUtils.isEmpty(description.getMediaId())) { + throw new IllegalArgumentException("description must have a non-empty media id"); + } + mFlags = flags; + mDescription = description; + } + + /** + * Private constructor. + */ + private MediaItem(Parcel in) { + mFlags = in.readInt(); + mDescription = MediaDescriptionCompat.CREATOR.createFromParcel(in); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mFlags); + mDescription.writeToParcel(out, flags); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("MediaItem{"); + sb.append("mFlags=").append(mFlags); + sb.append(", mDescription=").append(mDescription); + sb.append('}'); + return sb.toString(); + } + + public static final Parcelable.Creator<MediaItem> CREATOR = + new Parcelable.Creator<MediaItem>() { + @Override + public MediaItem createFromParcel(Parcel in) { + return new MediaItem(in); + } + + @Override + public MediaItem[] newArray(int size) { + return new MediaItem[size]; + } + }; + + /** + * Gets the flags of the item. + */ + public @Flags int getFlags() { + return mFlags; + } + + /** + * Returns whether this item is browsable. + * @see #FLAG_BROWSABLE + */ + public boolean isBrowsable() { + return (mFlags & FLAG_BROWSABLE) != 0; + } + + /** + * Returns whether this item is playable. + * @see #FLAG_PLAYABLE + */ + public boolean isPlayable() { + return (mFlags & FLAG_PLAYABLE) != 0; + } + + /** + * Returns the description of the media. + */ + public @NonNull MediaDescriptionCompat getDescription() { + return mDescription; + } + + /** + * Returns the media id for this item. + */ + public @NonNull String getMediaId() { + return mDescription.getMediaId(); + } + } + + + /** + * Callbacks for connection related events. + */ + public static class ConnectionCallback { + final Object mConnectionCallbackObj; + + public ConnectionCallback() { + if (android.os.Build.VERSION.SDK_INT >= 21) { + mConnectionCallbackObj = + MediaBrowserCompatApi21.createConnectionCallback(new StubApi21()); + } else { + mConnectionCallbackObj = null; + } + } + + /** + * Invoked after {@link MediaBrowserCompat#connect()} when the request has successfully + * completed. + */ + public void onConnected() { + } + + /** + * Invoked when the client is disconnected from the media browser. + */ + public void onConnectionSuspended() { + } + + /** + * Invoked when the connection to the media browser failed. + */ + public void onConnectionFailed() { + } + + + private class StubApi21 implements MediaBrowserCompatApi21.ConnectionCallback { + @Override + public void onConnected() { + ConnectionCallback.this.onConnected(); + } + + @Override + public void onConnectionSuspended() { + ConnectionCallback.this.onConnectionSuspended(); + } + + @Override + public void onConnectionFailed() { + ConnectionCallback.this.onConnectionFailed(); + } + } + } + + /** + * Callbacks for subscription related events. + */ + public static abstract class SubscriptionCallback { + final Object mSubscriptionCallbackObj; + + public SubscriptionCallback() { + if (android.os.Build.VERSION.SDK_INT >= 21) { + mSubscriptionCallbackObj = + MediaBrowserCompatApi21.createSubscriptionCallback(new StubApi21()); + } else { + mSubscriptionCallbackObj = null; + } + } + + /** + * Called when the list of children is loaded or updated. + * + * @param parentId The media id of the parent media item. + * @param children The children which were loaded, or null if the id is invalid. + */ + public void onChildrenLoaded(@NonNull String parentId, List<MediaItem> children) { + } + + /** + * Called when the id doesn't exist or other errors in subscribing. + * <p> + * If this is called, the subscription remains until {@link MediaBrowserCompat#unsubscribe} + * called, because some errors may heal themselves. + * </p> + * + * @param parentId The media id of the parent media item whose children could + * not be loaded. + */ + public void onError(@NonNull String parentId) { + } + + private class StubApi21 implements MediaBrowserCompatApi21.SubscriptionCallback { + @Override + public void onChildrenLoaded(@NonNull String parentId, @NonNull List<Parcel> children) { + List<MediaBrowserCompat.MediaItem> mediaItems = null; + if (children != null) { + mediaItems = new ArrayList<>(); + for (Parcel parcel : children) { + parcel.setDataPosition(0); + mediaItems.add( + MediaBrowserCompat.MediaItem.CREATOR.createFromParcel(parcel)); + parcel.recycle(); + } + } + SubscriptionCallback.this.onChildrenLoaded(parentId, mediaItems); + } + + @Override + public void onError(@NonNull String parentId) { + SubscriptionCallback.this.onError(parentId); + } + } + } + + /** + * Callback for receiving the result of {@link #getItem}. + */ + public static abstract class ItemCallback { + final Object mItemCallbackObj; + + public ItemCallback() { + if (android.os.Build.VERSION.SDK_INT >= 23) { + mItemCallbackObj = MediaBrowserCompatApi23.createItemCallback(new StubApi23()); + } else { + mItemCallbackObj = null; + } + } + + /** + * Called when the item has been returned by the browser service. + * + * @param item The item that was returned or null if it doesn't exist. + */ + public void onItemLoaded(MediaItem item) { + } + + /** + * Called when the item doesn't exist or there was an error retrieving it. + * + * @param itemId The media id of the media item which could not be loaded. + */ + public void onError(@NonNull String itemId) { + } + + private class StubApi23 implements MediaBrowserCompatApi23.ItemCallback { + @Override + public void onItemLoaded(Parcel itemParcel) { + itemParcel.setDataPosition(0); + MediaItem item = MediaBrowserCompat.MediaItem.CREATOR.createFromParcel(itemParcel); + itemParcel.recycle(); + ItemCallback.this.onItemLoaded(item); + } + + @Override + public void onError(@NonNull String itemId) { + ItemCallback.this.onError(itemId); + } + } + } + + interface MediaBrowserImpl { + void connect(); + void disconnect(); + boolean isConnected(); + ComponentName getServiceComponent(); + @NonNull String getRoot(); + @Nullable Bundle getExtras(); + @NonNull MediaSessionCompat.Token getSessionToken(); + void subscribe(@NonNull String parentId, @NonNull SubscriptionCallback callback); + void unsubscribe(@NonNull String parentId); + void getItem(final @NonNull String mediaId, @NonNull final ItemCallback cb); + } + + static class MediaBrowserImplBase implements MediaBrowserImpl { + private static final boolean DBG = false; + + private static final int CONNECT_STATE_DISCONNECTED = 0; + private static final int CONNECT_STATE_CONNECTING = 1; + private static final int CONNECT_STATE_CONNECTED = 2; + private static final int CONNECT_STATE_SUSPENDED = 3; + + private final Context mContext; + private final ComponentName mServiceComponent; + private final ConnectionCallback mCallback; + private final Bundle mRootHints; + private final CallbackHandler mHandler = new CallbackHandler(); + private final ArrayMap<String,Subscription> mSubscriptions = new ArrayMap<>(); + + private int mState = CONNECT_STATE_DISCONNECTED; + private MediaServiceConnection mServiceConnection; + private ServiceBinderWrapper mServiceBinderWrapper; + private Messenger mCallbacksMessenger; + private String mRootId; + private MediaSessionCompat.Token mMediaSessionToken; + private Bundle mExtras; + + public MediaBrowserImplBase(Context context, ComponentName serviceComponent, + ConnectionCallback callback, Bundle rootHints) { + if (context == null) { + throw new IllegalArgumentException("context must not be null"); + } + if (serviceComponent == null) { + throw new IllegalArgumentException("service component must not be null"); + } + if (callback == null) { + throw new IllegalArgumentException("connection callback must not be null"); + } + mContext = context; + mServiceComponent = serviceComponent; + mCallback = callback; + mRootHints = rootHints; + } + + public void connect() { + if (mState != CONNECT_STATE_DISCONNECTED) { + throw new IllegalStateException("connect() called while not disconnected (state=" + + getStateLabel(mState) + ")"); + } + // TODO: remove this extra check. + if (DBG) { + if (mServiceConnection != null) { + throw new RuntimeException("mServiceConnection should be null. Instead it is " + + mServiceConnection); + } + } + if (mServiceBinderWrapper != null) { + throw new RuntimeException("mServiceBinderWrapper should be null. Instead it is " + + mServiceBinderWrapper); + } + if (mCallbacksMessenger != null) { + throw new RuntimeException("mCallbacksMessenger should be null. Instead it is " + + mCallbacksMessenger); + } + + mState = CONNECT_STATE_CONNECTING; + + final Intent intent = new Intent(MediaBrowserServiceCompat.SERVICE_INTERFACE); + intent.setComponent(mServiceComponent); + + final ServiceConnection thisConnection = mServiceConnection = + new MediaServiceConnection(); + + boolean bound = false; + try { + bound = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); + } catch (Exception ex) { + Log.e(TAG, "Failed binding to service " + mServiceComponent); + } + + if (!bound) { + // Tell them that it didn't work. We are already on the main thread, + // but we don't want to do callbacks inside of connect(). So post it, + // and then check that we are on the same ServiceConnection. We know + // we won't also get an onServiceConnected or onServiceDisconnected, + // so we won't be doing double callbacks. + mHandler.post(new Runnable() { + @Override + public void run() { + // Ensure that nobody else came in or tried to connect again. + if (thisConnection == mServiceConnection) { + forceCloseConnection(); + mCallback.onConnectionFailed(); + } + } + }); + } + + if (DBG) { + Log.d(TAG, "connect..."); + dump(); + } + } + + public void disconnect() { + // It's ok to call this any state, because allowing this lets apps not have + // to check isConnected() unnecessarily. They won't appreciate the extra + // assertions for this. We do everything we can here to go back to a sane state. + if (mCallbacksMessenger != null) { + try { + mServiceBinderWrapper.disconnect(); + } catch (RemoteException ex) { + // We are disconnecting anyway. Log, just for posterity but it's not + // a big problem. + Log.w(TAG, "RemoteException during connect for " + mServiceComponent); + } + } + forceCloseConnection(); + + if (DBG) { + Log.d(TAG, "disconnect..."); + dump(); + } + } + + /** + * Null out the variables and unbind from the service. This doesn't include + * calling disconnect on the service, because we only try to do that in the + * clean shutdown cases. + * <p> + * Everywhere that calls this EXCEPT for disconnect() should follow it with + * a call to mCallback.onConnectionFailed(). Disconnect doesn't do that callback + * for a clean shutdown, but everywhere else is a dirty shutdown and should + * notify the app. + */ + private void forceCloseConnection() { + if (mServiceConnection != null) { + mContext.unbindService(mServiceConnection); + } + mState = CONNECT_STATE_DISCONNECTED; + mServiceConnection = null; + mServiceBinderWrapper = null; + mCallbacksMessenger = null; + mRootId = null; + mMediaSessionToken = null; + } + + public boolean isConnected() { + return mState == CONNECT_STATE_CONNECTED; + } + + public @NonNull + ComponentName getServiceComponent() { + if (!isConnected()) { + throw new IllegalStateException("getServiceComponent() called while not connected" + + " (state=" + mState + ")"); + } + return mServiceComponent; + } + + public @NonNull String getRoot() { + if (!isConnected()) { + throw new IllegalStateException("getRoot() called while not connected" + + "(state=" + getStateLabel(mState) + ")"); + } + return mRootId; + } + + public @Nullable + Bundle getExtras() { + if (!isConnected()) { + throw new IllegalStateException("getExtras() called while not connected (state=" + + getStateLabel(mState) + ")"); + } + return mExtras; + } + + public @NonNull MediaSessionCompat.Token getSessionToken() { + if (!isConnected()) { + throw new IllegalStateException("getSessionToken() called while not connected" + + "(state=" + mState + ")"); + } + return mMediaSessionToken; + } + + public void subscribe(@NonNull String parentId, @NonNull SubscriptionCallback callback) { + // Check arguments. + if (parentId == null) { + throw new IllegalArgumentException("parentId is null"); + } + if (callback == null) { + throw new IllegalArgumentException("callback is null"); + } + + // Update or create the subscription. + Subscription sub = mSubscriptions.get(parentId); + boolean newSubscription = sub == null; + if (newSubscription) { + sub = new Subscription(parentId); + mSubscriptions.put(parentId, sub); + } + sub.callback = callback; + + // If we are connected, tell the service that we are watching. If we aren't + // connected, the service will be told when we connect. + if (mState == CONNECT_STATE_CONNECTED) { + try { + mServiceBinderWrapper.addSubscription(parentId); + } catch (RemoteException ex) { + // Process is crashing. We will disconnect, and upon reconnect we will + // automatically reregister. So nothing to do here. + Log.d(TAG, "addSubscription failed with RemoteException parentId=" + parentId); + } + } + } + + public void unsubscribe(@NonNull String parentId) { + // Check arguments. + if (TextUtils.isEmpty(parentId)) { + throw new IllegalArgumentException("parentId is empty."); + } + + // Remove from our list. + final Subscription sub = mSubscriptions.remove(parentId); + + // Tell the service if necessary. + if (mState == CONNECT_STATE_CONNECTED && sub != null) { + try { + mServiceBinderWrapper.removeSubscription(parentId); + } catch (RemoteException ex) { + // Process is crashing. We will disconnect, and upon reconnect we will + // automatically reregister. So nothing to do here. + Log.d(TAG, "removeSubscription failed with RemoteException parentId=" + + parentId); + } + } + } + + public void getItem(final @NonNull String mediaId, @NonNull final ItemCallback cb) { + if (TextUtils.isEmpty(mediaId)) { + throw new IllegalArgumentException("mediaId is empty."); + } + if (cb == null) { + throw new IllegalArgumentException("cb is null."); + } + if (mState != CONNECT_STATE_CONNECTED) { + Log.i(TAG, "Not connected, unable to retrieve the MediaItem."); + mHandler.post(new Runnable() { + @Override + public void run() { + cb.onError(mediaId); + } + }); + return; + } + ResultReceiver receiver = new ResultReceiver(mHandler) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + if (resultCode != 0 || resultData == null + || !resultData.containsKey(MediaBrowserServiceCompat.KEY_MEDIA_ITEM)) { + cb.onError(mediaId); + return; + } + Parcelable item = + resultData.getParcelable(MediaBrowserServiceCompat.KEY_MEDIA_ITEM); + if (!(item instanceof MediaItem)) { + cb.onError(mediaId); + return; + } + cb.onItemLoaded((MediaItem)item); + } + }; + try { + mServiceBinderWrapper.getMediaItem(mediaId, receiver); + } catch (RemoteException e) { + Log.i(TAG, "Remote error getting media item."); + mHandler.post(new Runnable() { + @Override + public void run() { + cb.onError(mediaId); + } + }); + } + } + + /** + * For debugging. + */ + private static String getStateLabel(int state) { + switch (state) { + case CONNECT_STATE_DISCONNECTED: + return "CONNECT_STATE_DISCONNECTED"; + case CONNECT_STATE_CONNECTING: + return "CONNECT_STATE_CONNECTING"; + case CONNECT_STATE_CONNECTED: + return "CONNECT_STATE_CONNECTED"; + case CONNECT_STATE_SUSPENDED: + return "CONNECT_STATE_SUSPENDED"; + default: + return "UNKNOWN/" + state; + } + } + + private final void onServiceConnected(final Messenger callback, final String root, + final MediaSessionCompat.Token session, final Bundle extra) { + // Check to make sure there hasn't been a disconnect or a different ServiceConnection. + if (!isCurrent(callback, "onConnect")) { + return; + } + // Don't allow them to call us twice. + if (mState != CONNECT_STATE_CONNECTING) { + Log.w(TAG, "onConnect from service while mState=" + getStateLabel(mState) + + "... ignoring"); + return; + } + mRootId = root; + mMediaSessionToken = session; + mExtras = extra; + mState = CONNECT_STATE_CONNECTED; + + if (DBG) { + Log.d(TAG, "ServiceCallbacks.onConnect..."); + dump(); + } + mCallback.onConnected(); + + // we may receive some subscriptions before we are connected, so re-subscribe + // everything now + for (String id : mSubscriptions.keySet()) { + try { + mServiceBinderWrapper.addSubscription(id); + } catch (RemoteException ex) { + // Process is crashing. We will disconnect, and upon reconnect we will + // automatically reregister. So nothing to do here. + Log.d(TAG, "addSubscription failed with RemoteException parentId=" + id); + } + } + } + + private final void onConnectionFailed(final Messenger callback) { + Log.e(TAG, "onConnectFailed for " + mServiceComponent); + + // Check to make sure there hasn't been a disconnect or a different ServiceConnection. + if (!isCurrent(callback, "onConnectFailed")) { + return; + } + // Don't allow them to call us twice. + if (mState != CONNECT_STATE_CONNECTING) { + Log.w(TAG, "onConnect from service while mState=" + getStateLabel(mState) + + "... ignoring"); + return; + } + + // Clean up + forceCloseConnection(); + + // Tell the app. + mCallback.onConnectionFailed(); + } + + private final void onLoadChildren(final Messenger callback, final String parentId, + final List list) { + // Check that there hasn't been a disconnect or a different ServiceConnection. + if (!isCurrent(callback, "onLoadChildren")) { + return; + } + + List<MediaItem> data = list; + if (DBG) { + Log.d(TAG, "onLoadChildren for " + mServiceComponent + " id=" + parentId); + } + + // Check that the subscription is still subscribed. + final Subscription subscription = mSubscriptions.get(parentId); + if (subscription == null) { + if (DBG) { + Log.d(TAG, "onLoadChildren for id that isn't subscribed id=" + parentId); + } + return; + } + + // Tell the app. + subscription.callback.onChildrenLoaded(parentId, data); + } + + /** + * Return true if {@code callback} is the current ServiceCallbacks. Also logs if it's not. + */ + private boolean isCurrent(Messenger callback, String funcName) { + if (mCallbacksMessenger != callback) { + if (mState != CONNECT_STATE_DISCONNECTED) { + Log.i(TAG, funcName + " for " + mServiceComponent + " with mCallbacksMessenger=" + + mCallbacksMessenger + " this=" + this); + } + return false; + } + return true; + } + + /** + * Log internal state. + * @hide + */ + void dump() { + Log.d(TAG, "MediaBrowserCompat..."); + Log.d(TAG, " mServiceComponent=" + mServiceComponent); + Log.d(TAG, " mCallback=" + mCallback); + Log.d(TAG, " mRootHints=" + mRootHints); + Log.d(TAG, " mState=" + getStateLabel(mState)); + Log.d(TAG, " mServiceConnection=" + mServiceConnection); + Log.d(TAG, " mServiceBinderWrapper=" + mServiceBinderWrapper); + Log.d(TAG, " mCallbacksMessenger=" + mCallbacksMessenger); + Log.d(TAG, " mRootId=" + mRootId); + Log.d(TAG, " mMediaSessionToken=" + mMediaSessionToken); + } + + private class ServiceBinderWrapper { + private Messenger mMessenger; + + public ServiceBinderWrapper(IBinder target) { + mMessenger = new Messenger(target); + } + + void connect() throws RemoteException { + sendRequest(CLIENT_MSG_CONNECT, mContext.getPackageName(), mRootHints, + mCallbacksMessenger); + } + + void disconnect() throws RemoteException { + sendRequest(CLIENT_MSG_DISCONNECT, null, null, mCallbacksMessenger); + } + + void addSubscription(String parentId) throws RemoteException { + sendRequest(CLIENT_MSG_ADD_SUBSCRIPTION, parentId, null, mCallbacksMessenger); + } + + void removeSubscription(String parentId) throws RemoteException { + sendRequest(CLIENT_MSG_REMOVE_SUBSCRIPTION, parentId, null, mCallbacksMessenger); + } + + void getMediaItem(String mediaId, ResultReceiver receiver) throws RemoteException { + Bundle data = new Bundle(); + data.putParcelable(SERVICE_DATA_RESULT_RECEIVER, receiver); + sendRequest(CLIENT_MSG_GET_MEDIA_ITEM, mediaId, data, null); + } + + private void sendRequest(int what, Object obj, Bundle data, Messenger cbMessenger) + throws RemoteException { + Message msg = Message.obtain(); + msg.what = what; + msg.arg1 = CLIENT_VERSION_CURRENT; + msg.obj = obj; + msg.setData(data); + msg.replyTo = cbMessenger; + mMessenger.send(msg); + } + } + + /** + * ServiceConnection to the other app. + */ + private class MediaServiceConnection implements ServiceConnection { + @Override + public void onServiceConnected(final ComponentName name, final IBinder binder) { + postOrRun(new Runnable() { + @Override + public void run() { + if (DBG) { + Log.d(TAG, "MediaServiceConnection.onServiceConnected name=" + name + + " binder=" + binder); + dump(); + } + + // Make sure we are still the current connection, and that they haven't + // called disconnect(). + if (!isCurrent("onServiceConnected")) { + return; + } + + // Save their binder + mServiceBinderWrapper = new ServiceBinderWrapper(binder); + + // We make a new mServiceCallbacks each time we connect so that we can drop + // responses from previous connections. + mCallbacksMessenger = new Messenger(mHandler); + + mState = CONNECT_STATE_CONNECTING; + + // Call connect, which is async. When we get a response from that we will + // say that we're connected. + try { + if (DBG) { + Log.d(TAG, "ServiceCallbacks.onConnect..."); + dump(); + } + mServiceBinderWrapper.connect(); + } catch (RemoteException ex) { + // Connect failed, which isn't good. But the auto-reconnect on the + // service will take over and we will come back. We will also get the + // onServiceDisconnected, which has all the cleanup code. So let that + // do it. + Log.w(TAG, "RemoteException during connect for " + mServiceComponent); + if (DBG) { + Log.d(TAG, "ServiceCallbacks.onConnect..."); + dump(); + } + } + } + }); + } + + @Override + public void onServiceDisconnected(final ComponentName name) { + postOrRun(new Runnable() { + @Override + public void run() { + if (DBG) { + Log.d(TAG, "MediaServiceConnection.onServiceDisconnected name=" + name + + " this=" + this + " mServiceConnection=" + + mServiceConnection); + dump(); + } + + // Make sure we are still the current connection, and that they haven't + // called disconnect(). + if (!isCurrent("onServiceDisconnected")) { + return; + } + + // Clear out what we set in onServiceConnected + mServiceBinderWrapper = null; + mCallbacksMessenger = null; + + // And tell the app that it's suspended. + mState = CONNECT_STATE_SUSPENDED; + mCallback.onConnectionSuspended(); + } + }); + } + + private void postOrRun(Runnable r) { + if (Thread.currentThread() == mHandler.getLooper().getThread()) { + r.run(); + } else { + mHandler.post(r); + } + } + + /** + * Return true if this is the current ServiceConnection. Also logs if it's not. + */ + private boolean isCurrent(String funcName) { + if (mServiceConnection != this) { + if (mState != CONNECT_STATE_DISCONNECTED) { + // Check mState, because otherwise this log is noisy. + Log.i(TAG, funcName + " for " + mServiceComponent + + " with mServiceConnection=" + mServiceConnection + " this=" + this); + } + return false; + } + return true; + } + } + + private class CallbackHandler extends Handler { + @Override + public void handleMessage(Message msg) { + Bundle data = msg.getData(); + switch (msg.what) { + case SERVICE_MSG_ON_CONNECT: + onServiceConnected(mCallbacksMessenger, (String) msg.obj, + (MediaSessionCompat.Token) data.getParcelable( + SERVICE_DATA_MEDIA_SESSION_TOKEN), + data.getBundle(SERVICE_DATA_EXTRAS)); + break; + case SERVICE_MSG_ON_CONNECT_FAILED: + onConnectionFailed(mCallbacksMessenger); + break; + case SERVICE_MSG_ON_LOAD_CHILDREN: + onLoadChildren(mCallbacksMessenger, (String) msg.obj, + data.getParcelableArrayList(SERVICE_DATA_MEDIA_ITEM_LIST)); + break; + default: + Log.w(TAG, "Unhandled message: " + msg + + "\n Client version: " + CLIENT_VERSION_CURRENT + + "\n Service version: " + msg.arg1); + } + } + } + + private static class Subscription { + final String id; + SubscriptionCallback callback; + + Subscription(String id) { + this.id = id; + } + } + } + + static class MediaBrowserImplApi21 implements MediaBrowserImpl { + protected Object mBrowserObj; + protected Messenger mMessenger; + protected Handler mHandler = new Handler(); + + public MediaBrowserImplApi21(Context context, ComponentName serviceComponent, + ConnectionCallback callback, Bundle rootHints) { + mBrowserObj = MediaBrowserCompatApi21.createBrowser(context, serviceComponent, + callback.mConnectionCallbackObj, rootHints); + } + + @Override + public void connect() { + MediaBrowserCompatApi21.connect(mBrowserObj); + } + + @Override + public void disconnect() { + MediaBrowserCompatApi21.disconnect(mBrowserObj); + } + + @Override + public boolean isConnected() { + return MediaBrowserCompatApi21.isConnected(mBrowserObj); + } + + @Override + public ComponentName getServiceComponent() { + return MediaBrowserCompatApi21.getServiceComponent(mBrowserObj); + } + + @NonNull + @Override + public String getRoot() { + return MediaBrowserCompatApi21.getRoot(mBrowserObj); + } + + @Nullable + @Override + public Bundle getExtras() { + return MediaBrowserCompatApi21.getExtras(mBrowserObj); + } + + @NonNull + @Override + public MediaSessionCompat.Token getSessionToken() { + return MediaSessionCompat.Token.fromToken( + MediaBrowserCompatApi21.getSessionToken(mBrowserObj)); + } + + @Override + public void subscribe(@NonNull String parentId, @NonNull SubscriptionCallback callback) { + MediaBrowserCompatApi21.subscribe( + mBrowserObj, parentId, callback.mSubscriptionCallbackObj); + } + + @Override + public void unsubscribe(@NonNull String parentId) { + MediaBrowserCompatApi21.unsubscribe(mBrowserObj, parentId); + } + + @Override + public void getItem(@NonNull final String mediaId, @NonNull final ItemCallback cb) { + if (TextUtils.isEmpty(mediaId)) { + throw new IllegalArgumentException("mediaId is empty."); + } + if (cb == null) { + throw new IllegalArgumentException("cb is null."); + } + if (!MediaBrowserCompatApi21.isConnected(mBrowserObj)) { + Log.i(TAG, "Not connected, unable to retrieve the MediaItem."); + mHandler.post(new Runnable() { + @Override + public void run() { + cb.onError(mediaId); + } + }); + return; + } + if (mMessenger == null) { + Bundle extras = MediaBrowserCompatApi21.getExtras(mBrowserObj); + IBinder serviceBinder = BundleCompat.getBinder(extras, EXTRA_MESSENGER_BINDER); + if (serviceBinder != null) { + mMessenger = new Messenger(serviceBinder); + } + } + if (mMessenger == null) { + mHandler.post(new Runnable() { + @Override + public void run() { + // Default framework implementation. + cb.onItemLoaded(null); + } + }); + return; + } + ResultReceiver receiver = new ResultReceiver(mHandler) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + if (resultCode != 0 || resultData == null + || !resultData.containsKey(MediaBrowserServiceCompat.KEY_MEDIA_ITEM)) { + cb.onError(mediaId); + return; + } + Parcelable item = + resultData.getParcelable(MediaBrowserServiceCompat.KEY_MEDIA_ITEM); + if (!(item instanceof MediaItem)) { + cb.onError(mediaId); + return; + } + cb.onItemLoaded((MediaItem)item); + } + }; + try { + Bundle data = new Bundle(); + data.putParcelable(SERVICE_DATA_RESULT_RECEIVER, receiver); + sendRequest(CLIENT_MSG_GET_MEDIA_ITEM, mediaId, data, null); + } catch (RemoteException e) { + Log.i(TAG, "Remote error getting media item."); + mHandler.post(new Runnable() { + @Override + public void run() { + cb.onError(mediaId); + } + }); + } + } + + private void sendRequest(int what, Object obj, Bundle data, Messenger cbMessenger) + throws RemoteException { + Message msg = Message.obtain(); + msg.what = what; + msg.arg1 = CLIENT_VERSION_CURRENT; + msg.obj = obj; + msg.setData(data); + msg.replyTo = cbMessenger; + mMessenger.send(msg); + } + } + + static class MediaBrowserImplApi23 extends MediaBrowserImplApi21 { + public MediaBrowserImplApi23(Context context, ComponentName serviceComponent, + ConnectionCallback callback, Bundle rootHints) { + super(context, serviceComponent, callback, rootHints); + } + + @Override + public void getItem(@NonNull String mediaId, @NonNull ItemCallback cb) { + MediaBrowserCompatApi23.getItem(mBrowserObj, mediaId, cb.mItemCallbackObj); + } + } +} diff --git a/v4/java/android/support/v4/media/MediaBrowserProtocol.java b/v4/java/android/support/v4/media/MediaBrowserProtocol.java new file mode 100644 index 0000000000..7c8feb31c4 --- /dev/null +++ b/v4/java/android/support/v4/media/MediaBrowserProtocol.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.v4.media; + +/*** + * Defines the communication protocol for media browsers and media browser services. + * @hide + */ +class MediaBrowserProtocol { + + /** + * MediaBrowserCompat will check the version of the connected MediaBrowserServiceCompat, + * and it will not send messages if they are introduced in the higher version of the + * MediaBrowserServiceCompat. + */ + public static final int SERVICE_VERSION_1 = 1; + public static final int SERVICE_VERSION_CURRENT = SERVICE_VERSION_1; + + /* + * Messages sent from the media browser service compat to the media browser compat. + * (Compat implementation for IMediaBrowserServiceCallbacks) + * DO NOT RENUMBER THESE! + */ + + /** (service v1) + * Sent after {@link MediaBrowserCompat#connect()} when the request has successfully + * completed. + * - arg1 : The service version + * - obj : The root media item id + * - data + * SERVICE_DATA_MEDIA_SESSION_TOKEN : Media session token + * SERVICE_DATA_EXTRAS : An extras bundle which contains EXTRA_SERVICE_VERSION + */ + public static final int SERVICE_MSG_ON_CONNECT = 1; + + /** (service v1) + * Sent after {@link MediaBrowserCompat#connect()} when the connection to the media browser + * failed. + * - arg1 : service version + */ + public static final int SERVICE_MSG_ON_CONNECT_FAILED = 2; + + /** (service v1) + * Sent when the list of children is loaded or updated. + * - arg1 : The service version + * - obj : The parent media item id + * - data + * SERVICE_DATA_MEDIA_ITEM_LIST : An array list for the media item children + */ + public static final int SERVICE_MSG_ON_LOAD_CHILDREN = 3; + + public static final String SERVICE_DATA_MEDIA_SESSION_TOKEN = "data_media_session_token"; + public static final String SERVICE_DATA_EXTRAS = "data_extras"; + public static final String SERVICE_DATA_MEDIA_ITEM_LIST = "data_media_item_list"; + public static final String SERVICE_DATA_RESULT_RECEIVER = "data_result_receiver"; + + public static final String EXTRA_SERVICE_VERSION = "extra_service_version"; + public static final String EXTRA_MESSENGER_BINDER = "extra_messenger"; + + /** + * MediaBrowserServiceCompat will check the version of the MediaBrowserCompat, and it will not + * send messages if they are introduced in the higher version of the MediaBrowserCompat. + */ + public static final int CLIENT_VERSION_1 = 1; + public static final int CLIENT_VERSION_CURRENT = CLIENT_VERSION_1; + + /* + * Messages sent from the media browser compat to the media browser service compat. + * (Compat implementation for IMediaBrowserService) + * DO NOT RENUMBER THESE! + */ + + /** (client v1) + * Sent to connect to the media browse service compat. + * - arg1 : The client version + * - obj : The package name + * - data : An optional root hints bundle of service-specific arguments + * - replayTo : Client messenger + */ + public static final int CLIENT_MSG_CONNECT = 1; + + /** (client v1) + * Sent to disconnect from the media browse service compat. + * - arg1 : The client version + * - replayTo : Client messenger + */ + public static final int CLIENT_MSG_DISCONNECT = 2; + + /** (client v1) + * Sent to subscribe for changes to the children of the specified media id. + * - arg1 : The client version + * - obj : The media item id + * - replayTo : Client messenger + */ + public static final int CLIENT_MSG_ADD_SUBSCRIPTION = 3; + + /** (client v1) + * Sent to unsubscribe for changes to the children of the specified media id. + * - arg1 : The client version + * - obj : The media item id + * - replayTo : Client messenger + */ + public static final int CLIENT_MSG_REMOVE_SUBSCRIPTION = 4; + + /** (client v1) + * Sent to retrieves a specific media item from the connected service. + * - arg1 : The client version + * - obj : The media item id + * - data + * SERVICE_DATA_RESULT_RECEIVER : Result receiver to get the result + */ + public static final int CLIENT_MSG_GET_MEDIA_ITEM = 5; +} diff --git a/v4/java/android/support/v4/media/MediaBrowserServiceCompat.java b/v4/java/android/support/v4/media/MediaBrowserServiceCompat.java new file mode 100644 index 0000000000..205e789989 --- /dev/null +++ b/v4/java/android/support/v4/media/MediaBrowserServiceCompat.java @@ -0,0 +1,803 @@ +/* + * 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.v4.media; + +import android.app.Service; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Binder; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; +import android.os.Parcel; +import android.os.RemoteException; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.BundleCompat; +import android.support.v4.media.session.MediaSessionCompat; +import android.support.v4.os.ResultReceiver; +import android.support.v4.util.ArrayMap; +import android.text.TextUtils; +import android.util.Log; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +import static android.support.v4.media.MediaBrowserProtocol.*; + +/** + * Base class for media browse services. + * <p> + * Media browse services enable applications to browse media content provided by an application + * and ask the application to start playing it. They may also be used to control content that + * is already playing by way of a {@link MediaSessionCompat}. + * </p> + * + * To extend this class, you must declare the service in your manifest file with + * an intent filter with the {@link #SERVICE_INTERFACE} action. + * + * For example: + * </p><pre> + * <service android:name=".MyMediaBrowserServiceCompat" + * android:label="@string/service_name" > + * <intent-filter> + * <action android:name="android.media.browse.MediaBrowserService" /> + * </intent-filter> + * </service> + * </pre> + */ +public abstract class MediaBrowserServiceCompat extends Service { + private static final String TAG = "MediaBrowserServiceCompat"; + private static final boolean DBG = false; + + private MediaBrowserServiceImpl mImpl; + + /** + * The {@link Intent} that must be declared as handled by the service. + */ + public static final String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService"; + + /** + * A key for passing the MediaItem to the ResultReceiver in getItem. + * + * @hide + */ + public static final String KEY_MEDIA_ITEM = "media_item"; + + private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap(); + private final ServiceHandler mHandler = new ServiceHandler(); + MediaSessionCompat.Token mSession; + + interface MediaBrowserServiceImpl { + void onCreate(); + IBinder onBind(Intent intent); + } + + class MediaBrowserServiceImplBase implements MediaBrowserServiceImpl { + private Messenger mMessenger; + + @Override + public void onCreate() { + mMessenger = new Messenger(mHandler); + } + + @Override + public IBinder onBind(Intent intent) { + if (SERVICE_INTERFACE.equals(intent.getAction())) { + return mMessenger.getBinder(); + } + return null; + } + } + + class MediaBrowserServiceImplApi21 implements MediaBrowserServiceImpl { + private Object mServiceObj; + + @Override + public void onCreate() { + mServiceObj = MediaBrowserServiceCompatApi21.createService(); + MediaBrowserServiceCompatApi21.onCreate(mServiceObj, new ServiceImplApi21()); + } + + @Override + public IBinder onBind(Intent intent) { + return MediaBrowserServiceCompatApi21.onBind(mServiceObj, intent); + } + } + + class MediaBrowserServiceImplApi23 implements MediaBrowserServiceImpl { + private Object mServiceObj; + + @Override + public void onCreate() { + mServiceObj = MediaBrowserServiceCompatApi23.createService(); + MediaBrowserServiceCompatApi23.onCreate(mServiceObj, new ServiceImplApi23()); + } + + @Override + public IBinder onBind(Intent intent) { + return MediaBrowserServiceCompatApi23.onBind(mServiceObj, intent); + } + } + + private final class ServiceHandler extends Handler { + private final ServiceImpl mServiceImpl = new ServiceImpl(); + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case CLIENT_MSG_CONNECT: + mServiceImpl.connect((String) msg.obj, msg.getData(), + new ServiceCallbacksCompat(msg.replyTo)); + break; + case CLIENT_MSG_DISCONNECT: + mServiceImpl.disconnect(new ServiceCallbacksCompat(msg.replyTo)); + break; + case CLIENT_MSG_ADD_SUBSCRIPTION: + mServiceImpl.addSubscription((String) msg.obj, + new ServiceCallbacksCompat(msg.replyTo)); + break; + case CLIENT_MSG_REMOVE_SUBSCRIPTION: + mServiceImpl.removeSubscription((String) msg.obj, + new ServiceCallbacksCompat(msg.replyTo)); + break; + case CLIENT_MSG_GET_MEDIA_ITEM: + mServiceImpl.getMediaItem((String) msg.obj, (ResultReceiver) msg.getData() + .getParcelable(SERVICE_DATA_RESULT_RECEIVER)); + break; + default: + Log.w(TAG, "Unhandled message: " + msg + + "\n Service version: " + SERVICE_VERSION_CURRENT + + "\n Client version: " + msg.arg1); + } + } + + public void postOrRun(Runnable r) { + if (Thread.currentThread() == getLooper().getThread()) { + r.run(); + } else { + post(r); + } + } + + public ServiceImpl getServiceImpl() { + return mServiceImpl; + } + } + + /** + * All the info about a connection. + */ + private class ConnectionRecord { + String pkg; + Bundle rootHints; + ServiceCallbacks callbacks; + BrowserRoot root; + HashSet<String> subscriptions = new HashSet(); + } + + /** + * Completion handler for asynchronous callback methods in {@link MediaBrowserServiceCompat}. + * <p> + * Each of the methods that takes one of these to send the result must call + * {@link #sendResult} to respond to the caller with the given results. If those + * functions return without calling {@link #sendResult}, they must instead call + * {@link #detach} before returning, and then may call {@link #sendResult} when + * they are done. If more than one of those methods is called, an exception will + * be thrown. + * + * @see MediaBrowserServiceCompat#onLoadChildren + * @see MediaBrowserServiceCompat#onLoadItem + */ + public static class Result<T> { + private Object mDebug; + private boolean mDetachCalled; + private boolean mSendResultCalled; + + Result(Object debug) { + mDebug = debug; + } + + /** + * Send the result back to the caller. + */ + public void sendResult(T result) { + if (mSendResultCalled) { + throw new IllegalStateException("sendResult() called twice for: " + mDebug); + } + mSendResultCalled = true; + onResultSent(result); + } + + /** + * Detach this message from the current thread and allow the {@link #sendResult} + * call to happen later. + */ + public void detach() { + if (mDetachCalled) { + throw new IllegalStateException("detach() called when detach() had already" + + " been called for: " + mDebug); + } + if (mSendResultCalled) { + throw new IllegalStateException("detach() called when sendResult() had already" + + " been called for: " + mDebug); + } + mDetachCalled = true; + } + + boolean isDone() { + return mDetachCalled || mSendResultCalled; + } + + /** + * Called when the result is sent, after assertions about not being called twice + * have happened. + */ + void onResultSent(T result) { + } + } + + private class ServiceImpl { + public void connect(final String pkg, final Bundle rootHints, + final ServiceCallbacks callbacks) { + + final int uid = Binder.getCallingUid(); + if (!isValidPackage(pkg, uid)) { + throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid + + " package=" + pkg); + } + + mHandler.postOrRun(new Runnable() { + @Override + public void run() { + final IBinder b = callbacks.asBinder(); + + // Clear out the old subscriptions. We are getting new ones. + mConnections.remove(b); + + final ConnectionRecord connection = new ConnectionRecord(); + connection.pkg = pkg; + connection.rootHints = rootHints; + connection.callbacks = callbacks; + + connection.root = + MediaBrowserServiceCompat.this.onGetRoot(pkg, uid, rootHints); + + // If they didn't return something, don't allow this client. + if (connection.root == null) { + Log.i(TAG, "No root for client " + pkg + " from service " + + getClass().getName()); + try { + callbacks.onConnectFailed(); + } catch (RemoteException ex) { + Log.w(TAG, "Calling onConnectFailed() failed. Ignoring. " + + "pkg=" + pkg); + } + } else { + try { + mConnections.put(b, connection); + if (mSession != null) { + callbacks.onConnect(connection.root.getRootId(), + mSession, connection.root.getExtras()); + } + } catch (RemoteException ex) { + Log.w(TAG, "Calling onConnect() failed. Dropping client. " + + "pkg=" + pkg); + mConnections.remove(b); + } + } + } + }); + } + + public void disconnect(final ServiceCallbacks callbacks) { + mHandler.postOrRun(new Runnable() { + @Override + public void run() { + final IBinder b = callbacks.asBinder(); + + // Clear out the old subscriptions. We are getting new ones. + final ConnectionRecord old = mConnections.remove(b); + if (old != null) { + // TODO + } + } + }); + } + + + public void addSubscription(final String id, final ServiceCallbacks callbacks) { + mHandler.postOrRun(new Runnable() { + @Override + public void run() { + final IBinder b = callbacks.asBinder(); + + // Get the record for the connection + final ConnectionRecord connection = mConnections.get(b); + if (connection == null) { + Log.w(TAG, "addSubscription for callback that isn't registered id=" + + id); + return; + } + + MediaBrowserServiceCompat.this.addSubscription(id, connection); + } + }); + } + + public void removeSubscription(final String id, final ServiceCallbacks callbacks) { + mHandler.postOrRun(new Runnable() { + @Override + public void run() { + final IBinder b = callbacks.asBinder(); + + ConnectionRecord connection = mConnections.get(b); + if (connection == null) { + Log.w(TAG, "removeSubscription for callback that isn't registered id=" + + id); + return; + } + if (!connection.subscriptions.remove(id)) { + Log.w(TAG, "removeSubscription called for " + id + + " which is not subscribed"); + } + } + }); + } + + public void getMediaItem(final String mediaId, final ResultReceiver receiver) { + if (TextUtils.isEmpty(mediaId) || receiver == null) { + return; + } + + mHandler.postOrRun(new Runnable() { + @Override + public void run() { + performLoadItem(mediaId, receiver); + } + }); + } + } + + private class ServiceImplApi21 implements MediaBrowserServiceCompatApi21.ServiceImplApi21 { + final ServiceImpl mServiceImpl; + + ServiceImplApi21() { + mServiceImpl = mHandler.getServiceImpl(); + } + + @Override + public void connect(final String pkg, final Bundle rootHints, + final MediaBrowserServiceCompatApi21.ServiceCallbacks callbacks) { + mServiceImpl.connect(pkg, rootHints, new ServiceCallbacksApi21(callbacks)); + } + + @Override + public void disconnect(final MediaBrowserServiceCompatApi21.ServiceCallbacks callbacks) { + mServiceImpl.disconnect(new ServiceCallbacksApi21(callbacks)); + } + + + @Override + public void addSubscription( + final String id, final MediaBrowserServiceCompatApi21.ServiceCallbacks callbacks) { + mServiceImpl.addSubscription(id, new ServiceCallbacksApi21(callbacks)); + } + + @Override + public void removeSubscription(final String id, + final MediaBrowserServiceCompatApi21.ServiceCallbacks callbacks) { + mServiceImpl.removeSubscription(id, new ServiceCallbacksApi21(callbacks)); + } + } + + private class ServiceImplApi23 extends ServiceImplApi21 + implements MediaBrowserServiceCompatApi23.ServiceImplApi23 { + @Override + public void getMediaItem(final String mediaId, + final MediaBrowserServiceCompatApi23.ItemCallback cb) { + ResultReceiver receiverCompat = new ResultReceiver(mHandler) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + MediaBrowserCompat.MediaItem item = resultData.getParcelable(KEY_MEDIA_ITEM); + Parcel itemParcel = null; + if (item != null) { + itemParcel = Parcel.obtain(); + item.writeToParcel(itemParcel, 0); + } + cb.onItemLoaded(resultCode, resultData, itemParcel); + } + }; + mServiceImpl.getMediaItem(mediaId, receiverCompat); + } + } + + private interface ServiceCallbacks { + IBinder asBinder(); + void onConnect(String root, MediaSessionCompat.Token session, Bundle extras) + throws RemoteException; + void onConnectFailed() throws RemoteException; + void onLoadChildren(String mediaId, List<MediaBrowserCompat.MediaItem> list) + throws RemoteException; + } + + private class ServiceCallbacksCompat implements ServiceCallbacks { + final Messenger mCallbacks; + + ServiceCallbacksCompat(Messenger callbacks) { + mCallbacks = callbacks; + } + + public IBinder asBinder() { + return mCallbacks.getBinder(); + } + + public void onConnect(String root, MediaSessionCompat.Token session, Bundle extras) + throws RemoteException { + if (extras == null) { + extras = new Bundle(); + } + extras.putInt(EXTRA_SERVICE_VERSION, SERVICE_VERSION_CURRENT); + Bundle data = new Bundle(); + data.putParcelable(SERVICE_DATA_MEDIA_SESSION_TOKEN, session); + data.putBundle(SERVICE_DATA_EXTRAS, extras); + sendRequest(SERVICE_MSG_ON_CONNECT, root, data); + } + + public void onConnectFailed() throws RemoteException { + sendRequest(SERVICE_MSG_ON_CONNECT_FAILED, null, null); + } + + public void onLoadChildren(String mediaId, List<MediaBrowserCompat.MediaItem> list) + throws RemoteException { + Bundle data = null; + if (list != null) { + data = new Bundle(); + data.putParcelableArrayList(SERVICE_DATA_MEDIA_ITEM_LIST, + list instanceof ArrayList ? (ArrayList) list : new ArrayList<>(list)); + } + sendRequest(SERVICE_MSG_ON_LOAD_CHILDREN, mediaId, data); + } + + private void sendRequest(int what, Object obj, Bundle data) + throws RemoteException { + Message msg = Message.obtain(); + msg.what = what; + msg.arg1 = SERVICE_VERSION_CURRENT; + msg.obj = obj; + msg.setData(data); + mCallbacks.send(msg); + } + } + + private class ServiceCallbacksApi21 implements ServiceCallbacks { + final MediaBrowserServiceCompatApi21.ServiceCallbacks mCallbacks; + Messenger mMessenger; + + ServiceCallbacksApi21(MediaBrowserServiceCompatApi21.ServiceCallbacks callbacks) { + mCallbacks = callbacks; + } + + public IBinder asBinder() { + return mCallbacks.asBinder(); + } + + public void onConnect(String root, MediaSessionCompat.Token session, Bundle extras) + throws RemoteException { + if (extras == null) { + extras = new Bundle(); + } + mMessenger = new Messenger(mHandler); + BundleCompat.putBinder(extras, EXTRA_MESSENGER_BINDER, mMessenger.getBinder()); + extras.putInt(EXTRA_SERVICE_VERSION, SERVICE_VERSION_CURRENT); + mCallbacks.onConnect(root, session.getToken(), extras); + } + + public void onConnectFailed() throws RemoteException { + mCallbacks.onConnectFailed(); + } + + public void onLoadChildren(String mediaId, List<MediaBrowserCompat.MediaItem> list) + throws RemoteException { + List<Parcel> parcelList = null; + if (list != null) { + parcelList = new ArrayList<>(); + for (MediaBrowserCompat.MediaItem item : list) { + Parcel parcel = Parcel.obtain(); + item.writeToParcel(parcel, 0); + parcelList.add(parcel); + } + } + mCallbacks.onLoadChildren(mediaId, parcelList); + } + } + + @Override + public void onCreate() { + super.onCreate(); + if (Build.VERSION.SDK_INT >= 23) { + mImpl = new MediaBrowserServiceImplApi23(); + } else if (Build.VERSION.SDK_INT >= 21) { + mImpl = new MediaBrowserServiceImplApi21(); + } else { + mImpl = new MediaBrowserServiceImplBase(); + } + mImpl.onCreate(); + } + + @Override + public IBinder onBind(Intent intent) { + return mImpl.onBind(intent); + } + + @Override + public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { + } + + /** + * Called to get the root information for browsing by a particular client. + * <p> + * The implementation should verify that the client package has permission + * to access browse media information before returning the root id; it + * should return null if the client is not allowed to access this + * information. + * </p> + * + * @param clientPackageName The package name of the application which is + * requesting access to browse media. + * @param clientUid The uid of the application which is requesting access to + * browse media. + * @param rootHints An optional bundle of service-specific arguments to send + * to the media browse service when connecting and retrieving the + * root id for browsing, or null if none. The contents of this + * bundle may affect the information returned when browsing. + * @return The {@link BrowserRoot} for accessing this app's content or null. + */ + public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName, + int clientUid, @Nullable Bundle rootHints); + + /** + * Called to get information about the children of a media item. + * <p> + * Implementations must call {@link Result#sendResult result.sendResult} + * with the list of children. If loading the children will be an expensive + * operation that should be performed on another thread, + * {@link Result#detach result.detach} may be called before returning from + * this function, and then {@link Result#sendResult result.sendResult} + * called when the loading is complete. + * + * @param parentId The id of the parent media item whose children are to be + * queried. + * @param result The Result to send the list of children to, or null if the + * id is invalid. + */ + public abstract void onLoadChildren(@NonNull String parentId, + @NonNull Result<List<MediaBrowserCompat.MediaItem>> result); + + /** + * Called to get information about a specific media item. + * <p> + * Implementations must call {@link Result#sendResult result.sendResult}. If + * loading the item will be an expensive operation {@link Result#detach + * result.detach} may be called before returning from this function, and + * then {@link Result#sendResult result.sendResult} called when the item has + * been loaded. + * <p> + * The default implementation sends a null result. + * + * @param itemId The id for the specific + * {@link MediaBrowserCompat.MediaItem}. + * @param result The Result to send the item to, or null if the id is + * invalid. + */ + public void onLoadItem(String itemId, Result<MediaBrowserCompat.MediaItem> result) { + result.sendResult(null); + } + + /** + * Call to set the media session. + * <p> + * This should be called as soon as possible during the service's startup. + * It may only be called once. + * + * @param token The token for the service's {@link MediaSessionCompat}. + */ + public void setSessionToken(final MediaSessionCompat.Token token) { + if (token == null) { + throw new IllegalArgumentException("Session token may not be null."); + } + if (mSession != null) { + throw new IllegalStateException("The session token has already been set."); + } + mSession = token; + mHandler.post(new Runnable() { + @Override + public void run() { + for (IBinder key : mConnections.keySet()) { + ConnectionRecord connection = mConnections.get(key); + try { + connection.callbacks.onConnect(connection.root.getRootId(), token, + connection.root.getExtras()); + } catch (RemoteException e) { + Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid."); + mConnections.remove(key); + } + } + } + }); + } + + /** + * Gets the session token, or null if it has not yet been created + * or if it has been destroyed. + */ + public @Nullable MediaSessionCompat.Token getSessionToken() { + return mSession; + } + + /** + * Notifies all connected media browsers that the children of + * the specified parent id have changed in some way. + * This will cause browsers to fetch subscribed content again. + * + * @param parentId The id of the parent media item whose + * children changed. + */ + public void notifyChildrenChanged(@NonNull final String parentId) { + if (parentId == null) { + throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged"); + } + mHandler.post(new Runnable() { + @Override + public void run() { + for (IBinder binder : mConnections.keySet()) { + ConnectionRecord connection = mConnections.get(binder); + if (connection.subscriptions.contains(parentId)) { + performLoadChildren(parentId, connection); + } + } + } + }); + } + + /** + * Return whether the given package is one of the ones that is owned by the uid. + */ + private boolean isValidPackage(String pkg, int uid) { + if (pkg == null) { + return false; + } + final PackageManager pm = getPackageManager(); + final String[] packages = pm.getPackagesForUid(uid); + final int N = packages.length; + for (int i=0; i<N; i++) { + if (packages[i].equals(pkg)) { + return true; + } + } + return false; + } + + /** + * Save the subscription and if it is a new subscription send the results. + */ + private void addSubscription(String id, ConnectionRecord connection) { + // Save the subscription + connection.subscriptions.add(id); + + // send the results + performLoadChildren(id, connection); + } + + /** + * Call onLoadChildren and then send the results back to the connection. + * <p> + * Callers must make sure that this connection is still connected. + */ + private void performLoadChildren(final String parentId, final ConnectionRecord connection) { + final Result<List<MediaBrowserCompat.MediaItem>> result + = new Result<List<MediaBrowserCompat.MediaItem>>(parentId) { + @Override + void onResultSent(List<MediaBrowserCompat.MediaItem> list) { + if (mConnections.get(connection.callbacks.asBinder()) != connection) { + if (DBG) { + Log.d(TAG, "Not sending onLoadChildren result for connection that has" + + " been disconnected. pkg=" + connection.pkg + " id=" + parentId); + } + return; + } + + try { + connection.callbacks.onLoadChildren(parentId, list); + } catch (RemoteException ex) { + // The other side is in the process of crashing. + Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId + + " package=" + connection.pkg); + } + } + }; + + onLoadChildren(parentId, result); + + if (!result.isDone()) { + throw new IllegalStateException("onLoadChildren must call detach() or sendResult()" + + " before returning for package=" + connection.pkg + " id=" + parentId); + } + } + + private void performLoadItem(String itemId, final ResultReceiver receiver) { + final Result<MediaBrowserCompat.MediaItem> result = + new Result<MediaBrowserCompat.MediaItem>(itemId) { + @Override + void onResultSent(MediaBrowserCompat.MediaItem item) { + Bundle bundle = new Bundle(); + bundle.putParcelable(KEY_MEDIA_ITEM, item); + receiver.send(0, bundle); + } + }; + + MediaBrowserServiceCompat.this.onLoadItem(itemId, result); + + if (!result.isDone()) { + throw new IllegalStateException("onLoadItem must call detach() or sendResult()" + + " before returning for id=" + itemId); + } + } + + /** + * Contains information that the browser service needs to send to the client + * when first connected. + */ + public static final class BrowserRoot { + final private String mRootId; + final private Bundle mExtras; + + /** + * Constructs a browser root. + * @param rootId The root id for browsing. + * @param extras Any extras about the browser service. + */ + public BrowserRoot(@NonNull String rootId, @Nullable Bundle extras) { + if (rootId == null) { + throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " + + "Use null for BrowserRoot instead."); + } + mRootId = rootId; + mExtras = extras; + } + + /** + * Gets the root id for browsing. + */ + public String getRootId() { + return mRootId; + } + + /** + * Gets any extras about the brwoser service. + */ + public Bundle getExtras() { + return mExtras; + } + } +} diff --git a/v4/java/android/support/v4/media/MediaMetadataCompat.java b/v4/java/android/support/v4/media/MediaMetadataCompat.java index 088b208a4f..0d078265c9 100644 --- a/v4/java/android/support/v4/media/MediaMetadataCompat.java +++ b/v4/java/android/support/v4/media/MediaMetadataCompat.java @@ -78,7 +78,8 @@ public final class MediaMetadataCompat implements Parcelable { public static final String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION"; /** - * The date the media was created or published as TODO determine format. + * The date the media was created or published. The format is unspecified + * but RFC 3339 is recommended. */ public static final String METADATA_KEY_DATE = "android.media.metadata.DATE"; @@ -360,7 +361,13 @@ public final class MediaMetadataCompat implements Parcelable { public RatingCompat getRating(@RatingKey String key) { RatingCompat rating = null; try { - rating = mBundle.getParcelable(key); + if (Build.VERSION.SDK_INT >= 21) { + // On platform version 21 or higher, mBundle stores a Rating object. Convert it to + // RatingCompat. + rating = RatingCompat.fromRating(mBundle.getParcelable(key)); + } else { + rating = mBundle.getParcelable(key); + } } catch (Exception e) { // ignore, value was not a bitmap Log.w(TAG, "Failed to retrieve a key as Rating.", e); @@ -509,31 +516,11 @@ public final class MediaMetadataCompat implements Parcelable { return null; } - Builder builder = new Builder(); - for (String key : MediaMetadataCompatApi21.keySet(metadataObj)) { - Integer type = METADATA_KEYS_TYPE.get(key); - if (type != null) { - switch (type) { - case METADATA_TYPE_BITMAP: - builder.putBitmap(key, - MediaMetadataCompatApi21.getBitmap(metadataObj, key)); - break; - case METADATA_TYPE_LONG: - builder.putLong(key, - MediaMetadataCompatApi21.getLong(metadataObj, key)); - break; - case METADATA_TYPE_RATING: - builder.putRating(key, RatingCompat.fromRating( - MediaMetadataCompatApi21.getRating(metadataObj, key))); - break; - case METADATA_TYPE_TEXT: - builder.putText(key, - MediaMetadataCompatApi21.getText(metadataObj, key)); - break; - } - } - } - MediaMetadataCompat metadata = builder.build(); + Parcel p = Parcel.obtain(); + MediaMetadataCompatApi21.writeToParcel(metadataObj, p, 0); + p.setDataPosition(0); + MediaMetadataCompat metadata = MediaMetadataCompat.CREATOR.createFromParcel(p); + p.recycle(); metadata.mMetadataObj = metadataObj; return metadata; } @@ -553,31 +540,11 @@ public final class MediaMetadataCompat implements Parcelable { return mMetadataObj; } - Object builderObj = MediaMetadataCompatApi21.Builder.newInstance(); - for (String key : keySet()) { - Integer type = METADATA_KEYS_TYPE.get(key); - if (type != null) { - switch (type) { - case METADATA_TYPE_BITMAP: - MediaMetadataCompatApi21.Builder.putBitmap(builderObj, key, - getBitmap(key)); - break; - case METADATA_TYPE_LONG: - MediaMetadataCompatApi21.Builder.putLong(builderObj, key, - getLong(key)); - break; - case METADATA_TYPE_RATING: - MediaMetadataCompatApi21.Builder.putRating(builderObj, key, - getRating(key).getRating()); - break; - case METADATA_TYPE_TEXT: - MediaMetadataCompatApi21.Builder.putText(builderObj, key, - getText(key)); - break; - } - } - } - mMetadataObj = MediaMetadataCompatApi21.Builder.build(builderObj); + Parcel p = Parcel.obtain(); + writeToParcel(p, 0); + p.setDataPosition(0); + mMetadataObj = MediaMetadataCompatApi21.createFromParcel(p); + p.recycle(); return mMetadataObj; } @@ -741,7 +708,13 @@ public final class MediaMetadataCompat implements Parcelable { + " key cannot be used to put a Rating"); } } - mBundle.putParcelable(key, value); + if (Build.VERSION.SDK_INT >= 21) { + // On platform version 21 or higher, use Rating instead of RatingCompat so mBundle + // can be unmarshalled. + mBundle.putParcelable(key, (Parcelable) value.getRating()); + } else { + mBundle.putParcelable(key, value); + } return this; } diff --git a/v4/java/android/support/v4/media/session/MediaButtonReceiver.java b/v4/java/android/support/v4/media/session/MediaButtonReceiver.java new file mode 100644 index 0000000000..d5eb0e839a --- /dev/null +++ b/v4/java/android/support/v4/media/session/MediaButtonReceiver.java @@ -0,0 +1,129 @@ +/* + * 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.v4.media.session; + +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.support.v4.media.session.MediaControllerCompat; +import android.support.v4.media.session.MediaSessionCompat; +import android.view.KeyEvent; + +import java.util.List; + +/** + * A media button receiver receives and helps translate hardware media playback buttons, + * such as those found on wired and wireless headsets, into the appropriate callbacks + * in your app. + * <p /> + * You can add this MediaButtonReceiver to your app by adding it directly to your + * AndroidManifest.xml: + * <pre> + * <receiver android:name="android.support.v4.media.session.MediaButtonReceiver" > + * <intent-filter> + * <action android:name="android.intent.action.MEDIA_BUTTON" /> + * </intent-filter> + * </receiver> + * </pre> + * This class assumes you have a {@link Service} in your app that controls + * media playback via a {@link MediaSessionCompat}. That {@link Service} must + * include an intent filter that also handles {@link Intent#ACTION_MEDIA_BUTTON}: + * <pre> + * <service android:name="com.example.android.MediaPlaybackService" > + * <intent-filter> + * <action android:name="android.intent.action.MEDIA_BUTTON" /> + * </intent-filter> + * </service> + * </pre> + * + * All {@link Intent}s sent to this MediaButtonReceiver will then be forwarded + * to the {@link Service}. Events can then be handled in + * {@link Service#onStartCommand(Intent, int, int)} by calling + * {@link MediaButtonReceiver#handleIntent(MediaSessionCompat, Intent)}, passing in + * your current {@link MediaSessionCompat}: + * <pre> + * private MediaSessionCompat mMediaSessionCompat = ...; + * + * public int onStartCommand(Intent intent, int flags, int startId) { + * MediaButtonReceiver.handleIntent(mMediaSessionCompat, intent); + * return super.onStartCommand(intent, flags, startId); + * } + * </pre> + * + * This ensures that the correct callbacks to {@link MediaSessionCompat.Callback} + * will be triggered based on the incoming {@link KeyEvent}. + */ +public class MediaButtonReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + Intent queryIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); + queryIntent.setPackage(context.getPackageName()); + PackageManager pm = context.getPackageManager(); + List<ResolveInfo> resolveInfos = pm.queryIntentServices(queryIntent, 0); + if (resolveInfos.size() != 1) { + throw new IllegalStateException("Expected 1 Service that handles " + + Intent.ACTION_MEDIA_BUTTON + ", found " + resolveInfos.size()); + } + ResolveInfo resolveInfo = resolveInfos.get(0); + ComponentName componentName = new ComponentName(resolveInfo.serviceInfo.packageName, + resolveInfo.serviceInfo.name); + intent.setComponent(componentName); + context.startService(intent); + } + + /** + * Extracts any available {@link KeyEvent} from an {@link Intent#ACTION_MEDIA_BUTTON} + * intent, passing it onto the {@link MediaSessionCompat} using + * {@link MediaControllerCompat#dispatchMediaButtonEvent(KeyEvent)}, which in turn + * will trigger callbacks to the {@link MediaSessionCompat.Callback} registered via + * {@link MediaSessionCompat#setCallback(MediaSessionCompat.Callback)}. + * <p /> + * The returned {@link KeyEvent} is non-null if any {@link KeyEvent} is found and can + * be used if any additional processing is needed beyond what is done in the + * {@link MediaSessionCompat.Callback}. An example of is to prevent redelivery of a + * {@link KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE} Intent in the case of the Service being + * restarted (which, by default, will redeliver the last received Intent). + * <pre> + * KeyEvent keyEvent = MediaButtonReceiver.handleIntent(mediaSession, intent); + * if (keyEvent != null && keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) { + * Intent emptyIntent = new Intent(intent); + * emptyIntent.setAction(""); + * startService(emptyIntent); + * } + * </pre> + * @param mediaSessionCompat A {@link MediaSessionCompat} that has a + * {@link MediaSessionCompat.Callback} set. + * @param intent The intent to parse. + * @return The extracted {@link KeyEvent} if found, or null. + */ + public static KeyEvent handleIntent(MediaSessionCompat mediaSessionCompat, Intent intent) { + if (mediaSessionCompat == null || intent == null + || !Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction()) + || !intent.hasExtra(Intent.EXTRA_KEY_EVENT)) { + return null; + } + KeyEvent ke = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); + MediaControllerCompat mediaController = mediaSessionCompat.getController(); + mediaController.dispatchMediaButtonEvent(ke); + return ke; + } +} + diff --git a/v4/java/android/support/v4/media/session/MediaControllerCompat.java b/v4/java/android/support/v4/media/session/MediaControllerCompat.java index 1d2180ce51..a6a5c88948 100644 --- a/v4/java/android/support/v4/media/session/MediaControllerCompat.java +++ b/v4/java/android/support/v4/media/session/MediaControllerCompat.java @@ -92,7 +92,9 @@ public final class MediaControllerCompat { } mToken = sessionToken; - if (android.os.Build.VERSION.SDK_INT >= 21) { + if (android.os.Build.VERSION.SDK_INT >= 23) { + mImpl = new MediaControllerImplApi23(context, sessionToken); + } else if (android.os.Build.VERSION.SDK_INT >= 21) { mImpl = new MediaControllerImplApi21(context, sessionToken); } else { mImpl = new MediaControllerImplBase(mToken); @@ -555,7 +557,9 @@ public final class MediaControllerCompat { } public void post(int what, Object obj, Bundle data) { - obtainMessage(what, obj).sendToTarget(); + Message msg = obtainMessage(what, obj); + msg.setData(data); + msg.sendToTarget(); } } } @@ -1328,6 +1332,14 @@ public final class MediaControllerCompat { @Override public void playFromUri(Uri uri, Bundle extras) { + if (uri == null || Uri.EMPTY.equals(uri)) { + throw new IllegalArgumentException( + "You must specify a non-empty Uri for playFromUri."); + } + Bundle bundle = new Bundle(); + bundle.putParcelable(MediaSessionCompat.ACTION_ARGUMENT_URI, uri); + bundle.putParcelable(MediaSessionCompat.ACTION_ARGUMENT_EXTRAS, extras); + sendCustomAction(MediaSessionCompat.ACTION_PLAY_FROM_URI, bundle); } @Override diff --git a/v4/java/android/support/v4/media/session/MediaSessionCompat.java b/v4/java/android/support/v4/media/session/MediaSessionCompat.java index fa963df305..d35ada60ee 100644 --- a/v4/java/android/support/v4/media/session/MediaSessionCompat.java +++ b/v4/java/android/support/v4/media/session/MediaSessionCompat.java @@ -19,9 +19,12 @@ package android.support.v4.media.session; import android.app.Activity; import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.media.AudioManager; import android.net.Uri; import android.os.Bundle; @@ -77,6 +80,8 @@ import java.util.List; * backwards compatible fashion. */ public class MediaSessionCompat { + private static final String TAG = "MediaSessionCompat"; + private final MediaSessionImpl mImpl; private final MediaControllerCompat mController; private final ArrayList<OnActiveChangeListener> @@ -102,12 +107,51 @@ public class MediaSessionCompat { public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1; /** + * Custom action to invoke playFromUri() for the forward compatibility. + * + * @hide + */ + public static final String ACTION_PLAY_FROM_URI = + "android.support.v4.media.session.action.PLAY_FROM_URI"; + + /** + * Argument for use with {@link #ACTION_PLAY_FROM_URI} indicating URI to play. + * + * @hide + */ + public static final String ACTION_ARGUMENT_URI = + "android.support.v4.media.session.action.ARGUMENT_URI"; + + /** + * Argument for use with {@link #ACTION_PLAY_FROM_URI} indicating extra bundle. + * + * @hide + */ + public static final String ACTION_ARGUMENT_EXTRAS = + "android.support.v4.media.session.action.ARGUMENT_EXTRAS"; + + /** + * Creates a new session using a media button receiver from your manifest. + * Note that a media button receiver is required to support platform versions + * earlier than {@link android.os.Build.VERSION_CODES#LOLLIPOP}. + * + * @param context The context. + * @param tag A short name for debugging purposes. + */ + public MediaSessionCompat(Context context, String tag) { + this(context, tag, null, null); + } + + /** * Creates a new session. * * @param context The context. * @param tag A short name for debugging purposes. * @param mediaButtonEventReceiver The component name for your receiver. - * This must be non-null to support platform versions earlier + * If null, this will attempt to find an appropriate + * {@link BroadcastReceiver} that handles + * {@link Intent#ACTION_MEDIA_BUTTON} from your manifest. + * A receiver is required to support platform versions earlier * than {@link android.os.Build.VERSION_CODES#LOLLIPOP}. * @param mbrIntent The PendingIntent for your receiver component that * handles media button events. This is optional and will be used @@ -123,6 +167,24 @@ public class MediaSessionCompat { throw new IllegalArgumentException("tag must not be null or empty"); } + if (mediaButtonEventReceiver == null) { + Intent queryIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); + queryIntent.setPackage(context.getPackageName()); + PackageManager pm = context.getPackageManager(); + List<ResolveInfo> resolveInfos = pm.queryBroadcastReceivers(queryIntent, 0); + // If none are found, assume we are running on a newer platform version that does + // not require a media button receiver ComponentName. Later code will double check + // this assumption and throw an error if needed + if (resolveInfos.size() == 1) { + ResolveInfo resolveInfo = resolveInfos.get(0); + mediaButtonEventReceiver = new ComponentName(resolveInfo.activityInfo.packageName, + resolveInfo.activityInfo.name); + } else if (resolveInfos.size() > 1) { + Log.w(TAG, "More than one BroadcastReceiver that handles " + + Intent.ACTION_MEDIA_BUTTON + " was found, using null. Provide a " + + "specific ComponentName to use as this session's media button receiver"); + } + } if (mediaButtonEventReceiver != null && mbrIntent == null) { // construct a PendingIntent for the media button Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); @@ -250,8 +312,8 @@ public class MediaSessionCompat { * <p> * On platforms earlier than * {@link android.os.Build.VERSION_CODES#LOLLIPOP}, - * {@link #setMediaButtonReceiver(PendingIntent)} must be called before - * setting this to true. + * a media button event receiver should be set via the constructor to + * receive media button events. * * @param active Whether this session is active or not. */ @@ -675,7 +737,13 @@ public class MediaSessionCompat { @Override public void onCustomAction(String action, Bundle extras) { - Callback.this.onCustomAction(action, extras); + if (action.equals(ACTION_PLAY_FROM_URI)) { + Uri uri = (Uri) extras.getParcelable(ACTION_ARGUMENT_URI); + Bundle bundle = (Bundle) extras.getParcelable(ACTION_ARGUMENT_EXTRAS); + Callback.this.onPlayFromUri(uri, bundle); + } else { + Callback.this.onCustomAction(action, extras); + } } } @@ -1309,7 +1377,7 @@ public class MediaSessionCompat { if (!mIsMbrRegistered && (mFlags & FLAG_HANDLES_MEDIA_BUTTONS) != 0) { if (android.os.Build.VERSION.SDK_INT >= 18) { MediaSessionCompatApi18.registerMediaButtonEventReceiver(mContext, - mMediaButtonEventReceiver); + mMediaButtonEventReceiver, mComponentName); } else { MediaSessionCompatApi8.registerMediaButtonEventReceiver(mContext, mComponentName); @@ -1318,7 +1386,7 @@ public class MediaSessionCompat { } else if (mIsMbrRegistered && (mFlags & FLAG_HANDLES_MEDIA_BUTTONS) == 0) { if (android.os.Build.VERSION.SDK_INT >= 18) { MediaSessionCompatApi18.unregisterMediaButtonEventReceiver(mContext, - mMediaButtonEventReceiver); + mMediaButtonEventReceiver, mComponentName); } else { MediaSessionCompatApi8.unregisterMediaButtonEventReceiver(mContext, mComponentName); @@ -1348,7 +1416,7 @@ public class MediaSessionCompat { if (mIsMbrRegistered) { if (android.os.Build.VERSION.SDK_INT >= 18) { MediaSessionCompatApi18.unregisterMediaButtonEventReceiver(mContext, - mMediaButtonEventReceiver); + mMediaButtonEventReceiver, mComponentName); } else { MediaSessionCompatApi8.unregisterMediaButtonEventReceiver(mContext, mComponentName); @@ -1373,7 +1441,7 @@ public class MediaSessionCompat { mVolumeProvider.onAdjustVolume(direction); } } else { - mAudioManager.adjustStreamVolume(direction, mLocalStream, flags); + mAudioManager.adjustStreamVolume(mLocalStream, direction, flags); } } @@ -1928,7 +1996,8 @@ public class MediaSessionCompat { @Override public void setCallback(Callback callback, Handler handler) { - MediaSessionCompatApi21.setCallback(mSessionObj, callback.mCallbackObj, handler); + MediaSessionCompatApi21.setCallback(mSessionObj, + callback == null ? null : callback.mCallbackObj, handler); } @Override @@ -1974,12 +2043,14 @@ public class MediaSessionCompat { @Override public void setPlaybackState(PlaybackStateCompat state) { - MediaSessionCompatApi21.setPlaybackState(mSessionObj, state.getPlaybackState()); + MediaSessionCompatApi21.setPlaybackState(mSessionObj, + state == null ? null : state.getPlaybackState()); } @Override public void setMetadata(MediaMetadataCompat metadata) { - MediaSessionCompatApi21.setMetadata(mSessionObj, metadata.getMediaMetadata()); + MediaSessionCompatApi21.setMetadata(mSessionObj, + metadata == null ? null : metadata.getMediaMetadata()); } @Override diff --git a/v4/java/android/support/v4/os/IResultReceiver.aidl b/v4/java/android/support/v4/os/IResultReceiver.aidl new file mode 100644 index 0000000000..cd23ff3450 --- /dev/null +++ b/v4/java/android/support/v4/os/IResultReceiver.aidl @@ -0,0 +1,24 @@ +/* +** Copyright 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.v4.os; + +import android.os.Bundle; + +/** @hide */ +oneway interface IResultReceiver { + void send(int resultCode, in Bundle resultData); +} diff --git a/v4/java/android/support/v4/os/ResultReceiver.aidl b/v4/java/android/support/v4/os/ResultReceiver.aidl new file mode 100644 index 0000000000..81c81f6a93 --- /dev/null +++ b/v4/java/android/support/v4/os/ResultReceiver.aidl @@ -0,0 +1,19 @@ +/* +** Copyright 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.v4.os; + +parcelable ResultReceiver; diff --git a/v4/java/android/support/v4/os/ResultReceiver.java b/v4/java/android/support/v4/os/ResultReceiver.java new file mode 100644 index 0000000000..f0dc00158a --- /dev/null +++ b/v4/java/android/support/v4/os/ResultReceiver.java @@ -0,0 +1,143 @@ +/* + * 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.v4.os; + +import android.os.Bundle; +import android.os.Handler; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.RemoteException; +import android.support.v4.os.IResultReceiver; + +/** + * Generic interface for receiving a callback result from someone. Use this + * by creating a subclass and implement {@link #onReceiveResult}, which you can + * then pass to others and send through IPC, and receive results they + * supply with {@link #send}. + * + * <p>Note: the implementation underneath is just a simple wrapper around + * a {@link Binder} that is used to perform the communication. This means + * semantically you should treat it as such: this class does not impact process + * lifecycle management (you must be using some higher-level component to tell + * the system that your process needs to continue running), the connection will + * break if your process goes away for any reason, etc.</p> + * @hide + */ +public class ResultReceiver implements Parcelable { + final boolean mLocal; + final Handler mHandler; + + IResultReceiver mReceiver; + + class MyRunnable implements Runnable { + final int mResultCode; + final Bundle mResultData; + + MyRunnable(int resultCode, Bundle resultData) { + mResultCode = resultCode; + mResultData = resultData; + } + + public void run() { + onReceiveResult(mResultCode, mResultData); + } + } + + class MyResultReceiver extends IResultReceiver.Stub { + public void send(int resultCode, Bundle resultData) { + if (mHandler != null) { + mHandler.post(new MyRunnable(resultCode, resultData)); + } else { + onReceiveResult(resultCode, resultData); + } + } + } + + /** + * Create a new ResultReceive to receive results. Your + * {@link #onReceiveResult} method will be called from the thread running + * <var>handler</var> if given, or from an arbitrary thread if null. + */ + public ResultReceiver(Handler handler) { + mLocal = true; + mHandler = handler; + } + + /** + * Deliver a result to this receiver. Will call {@link #onReceiveResult}, + * always asynchronously if the receiver has supplied a Handler in which + * to dispatch the result. + * @param resultCode Arbitrary result code to deliver, as defined by you. + * @param resultData Any additional data provided by you. + */ + public void send(int resultCode, Bundle resultData) { + if (mLocal) { + if (mHandler != null) { + mHandler.post(new MyRunnable(resultCode, resultData)); + } else { + onReceiveResult(resultCode, resultData); + } + return; + } + + if (mReceiver != null) { + try { + mReceiver.send(resultCode, resultData); + } catch (RemoteException e) { + } + } + } + + /** + * Override to receive results delivered to this object. + * + * @param resultCode Arbitrary result code delivered by the sender, as + * defined by the sender. + * @param resultData Any additional data provided by the sender. + */ + protected void onReceiveResult(int resultCode, Bundle resultData) { + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + synchronized (this) { + if (mReceiver == null) { + mReceiver = new MyResultReceiver(); + } + out.writeStrongBinder(mReceiver.asBinder()); + } + } + + ResultReceiver(Parcel in) { + mLocal = false; + mHandler = null; + mReceiver = IResultReceiver.Stub.asInterface(in.readStrongBinder()); + } + + public static final Parcelable.Creator<ResultReceiver> CREATOR + = new Parcelable.Creator<ResultReceiver>() { + public ResultReceiver createFromParcel(Parcel in) { + return new ResultReceiver(in); + } + public ResultReceiver[] newArray(int size) { + return new ResultReceiver[size]; + } + }; +} diff --git a/v4/java/android/support/v4/view/ActionProvider.java b/v4/java/android/support/v4/view/ActionProvider.java index d195a3a5b6..070ea46517 100644 --- a/v4/java/android/support/v4/view/ActionProvider.java +++ b/v4/java/android/support/v4/view/ActionProvider.java @@ -60,6 +60,63 @@ import android.view.View; * </code></pre> * </li></ul></p> * + * <h3>Creating a custom action provider</h3> + * + * <p>To create a custom action provider, extend ActionProvider and implement + * its callback methods as necessary. In particular, implement the following + * methods:</p> + * + * <dl> + * <dt>{@link #ActionProvider ActionProvider()} constructor</dt> + * <dd>This constructor is passed the application context. You should + * save the context in a member field to use in the other callback methods.</dd> + * + * <dt>{@link #onCreateActionView onCreateActionView(MenuItem)}</dt> + * <dd>The system calls this method when the action provider is created. + * You define the action provider's layout through the implementation of this + * method. Use the context acquired + * from the constructor to instantiate a {@link android.view.LayoutInflater} and + * inflate your action provider's layout from an XML resource, then hook up + * event listeners for the view's components. For example: + * + *<pre> + * public View onCreateActionView(MenuItem forItem) { + * // Inflate the action provider to be shown on the action bar. + * LayoutInflater layoutInflater = LayoutInflater.from(mContext); + * View providerView = + * layoutInflater.inflate(R.layout.my_action_provider, null); + * ImageButton button = + * (ImageButton) providerView.findViewById(R.id.button); + * button.setOnClickListener(new View.OnClickListener() { + * @Override + * public void onClick(View v) { + * // Do something... + * } + * }); + * return providerView; + * }</pre> + * </dd> + * + * <dt>{@link #onPerformDefaultAction onPerformDefaultAction()}</dt> + * <dd><p>The system calls this method when the user selects a menu item from the action + * overflow. The action provider should perform a default action for the + * menu item. The system does not call this method if the menu item opens a submenu.</p> + * + * <p>If your action provider presents a submenu through the + * {@link #onPrepareSubMenu onPrepareSubMenu()} callback, the submenu + * appears even if the action provider is in the overflow menu. + * Thus, the system never calls {@link #onPerformDefaultAction + * onPerformDefaultAction()} if there is a submenu.</p> + * + * <p class="note"> <strong>Note:</strong> An activity or a fragment that + * implements <code>onOptionsItemSelected()</code> can override the action + * provider's default behavior (unless it uses a submenu) by handling the + * item-selected event and returning <code>true</code>. In this case, the + * system does not call + * {@link #onPerformDefaultAction onPerformDefaultAction()}.</p></dd> + * </dl> + * + * * @see android.support.v4.view.MenuItemCompat#setActionProvider(android.view.MenuItem, ActionProvider) * @see android.support.v4.view.MenuItemCompat#getActionProvider(android.view.MenuItem) */ diff --git a/v4/java/android/support/v4/view/PagerAdapter.java b/v4/java/android/support/v4/view/PagerAdapter.java index ef524047d0..55fb9c1324 100644 --- a/v4/java/android/support/v4/view/PagerAdapter.java +++ b/v4/java/android/support/v4/view/PagerAdapter.java @@ -76,7 +76,8 @@ import android.view.ViewGroup; * the method {@link #getItemPosition(Object)}.</p> */ public abstract class PagerAdapter { - private DataSetObservable mObservable = new DataSetObservable(); + private final DataSetObservable mObservable = new DataSetObservable(); + private DataSetObserver mViewPagerObserver; public static final int POSITION_UNCHANGED = -1; public static final int POSITION_NONE = -2; @@ -273,6 +274,11 @@ public abstract class PagerAdapter { * and associated views should update. */ public void notifyDataSetChanged() { + synchronized (this) { + if (mViewPagerObserver != null) { + mViewPagerObserver.onChanged(); + } + } mObservable.notifyChanged(); } @@ -294,6 +300,12 @@ public abstract class PagerAdapter { mObservable.unregisterObserver(observer); } + void setViewPagerObserver(DataSetObserver observer) { + synchronized (this) { + mViewPagerObserver = observer; + } + } + /** * This method may be called by the ViewPager to obtain a title string * to describe the specified page. This method may return null diff --git a/v4/java/android/support/v4/view/ViewCompat.java b/v4/java/android/support/v4/view/ViewCompat.java index d3dce894bb..b88d7e5b92 100644 --- a/v4/java/android/support/v4/view/ViewCompat.java +++ b/v4/java/android/support/v4/view/ViewCompat.java @@ -27,6 +27,7 @@ import android.os.Bundle; import android.support.annotation.FloatRange; import android.support.annotation.IdRes; import android.support.annotation.IntDef; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat; @@ -276,6 +277,73 @@ public class ViewCompat { */ public static final int SCROLL_AXIS_VERTICAL = 1 << 1; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, + value = { + SCROLL_INDICATOR_TOP, + SCROLL_INDICATOR_BOTTOM, + SCROLL_INDICATOR_LEFT, + SCROLL_INDICATOR_RIGHT, + SCROLL_INDICATOR_START, + SCROLL_INDICATOR_END, + }) + public @interface ScrollIndicators {} + + /** + * Scroll indicator direction for the top edge of the view. + * + * @see #setScrollIndicators(int) + * @see #setScrollIndicators(int, int) + * @see #getScrollIndicators() + */ + public static final int SCROLL_INDICATOR_TOP = 0x1; + + /** + * Scroll indicator direction for the bottom edge of the view. + * + * @see #setScrollIndicators(int) + * @see #setScrollIndicators(int, int) + * @see #getScrollIndicators() + */ + public static final int SCROLL_INDICATOR_BOTTOM = 0x2; + + /** + * Scroll indicator direction for the left edge of the view. + * + * @see #setScrollIndicators(int) + * @see #setScrollIndicators(int, int) + * @see #getScrollIndicators() + */ + public static final int SCROLL_INDICATOR_LEFT = 0x4; + + /** + * Scroll indicator direction for the right edge of the view. + * + * @see #setScrollIndicators(int) + * @see #setScrollIndicators(int, int) + * @see #getScrollIndicators() + */ + public static final int SCROLL_INDICATOR_RIGHT = 0x8; + + /** + * Scroll indicator direction for the starting edge of the view. + * + * @see #setScrollIndicators(View, int) + * @see #setScrollIndicators(View, int, int) + * @see #getScrollIndicators(View) + */ + public static final int SCROLL_INDICATOR_START = 0x10; + + /** + * Scroll indicator direction for the ending edge of the view. + * + * @see #setScrollIndicators(int) + * @see #setScrollIndicators(int, int) + * @see #getScrollIndicators() + */ + public static final int SCROLL_INDICATOR_END = 0x20; + interface ViewCompatImpl { public boolean canScrollHorizontally(View v, int direction); public boolean canScrollVertically(View v, int direction); @@ -384,6 +452,10 @@ public class ViewCompat { int combineMeasuredStates(int curState, int newState); public float getZ(View view); public boolean isAttachedToWindow(View view); + public boolean hasOnClickListeners(View view); + public void setScrollIndicators(View view, int indicators); + public void setScrollIndicators(View view, int indicators, int mask); + public int getScrollIndicators(View view); } static class BaseViewCompatImpl implements ViewCompatImpl { @@ -963,6 +1035,26 @@ public class ViewCompat { public boolean isAttachedToWindow(View view) { return ViewCompatBase.isAttachedToWindow(view); } + + @Override + public boolean hasOnClickListeners(View view) { + return false; + } + + @Override + public int getScrollIndicators(View view) { + return 0; + } + + @Override + public void setScrollIndicators(View view, int indicators) { + // no-op + } + + @Override + public void setScrollIndicators(View view, int indicators, int mask) { + // no-op + } } static class EclairMr1ViewCompatImpl extends BaseViewCompatImpl { @@ -1222,7 +1314,14 @@ public class ViewCompat { } } - static class JBViewCompatImpl extends ICSViewCompatImpl { + static class ICSMr1ViewCompatImpl extends ICSViewCompatImpl { + @Override + public boolean hasOnClickListeners(View view) { + return ViewCompatICSMr1.hasOnClickListeners(view); + } + } + + static class JBViewCompatImpl extends ICSMr1ViewCompatImpl { @Override public boolean hasTransientState(View view) { return ViewCompatJB.hasTransientState(view); @@ -1529,10 +1628,29 @@ public class ViewCompat { } } + static class MarshmallowViewCompatImpl extends LollipopViewCompatImpl { + @Override + public void setScrollIndicators(View view, int indicators) { + ViewCompatMarshmallow.setScrollIndicators(view, indicators); + } + + @Override + public void setScrollIndicators(View view, int indicators, int mask) { + ViewCompatMarshmallow.setScrollIndicators(view, indicators, mask); + } + + @Override + public int getScrollIndicators(View view) { + return ViewCompatMarshmallow.getScrollIndicators(view); + } + } + static final ViewCompatImpl IMPL; static { final int version = android.os.Build.VERSION.SDK_INT; - if (version >= 21) { + if (version >= 23) { + IMPL = new MarshmallowViewCompatImpl(); + } else if (version >= 21) { IMPL = new LollipopViewCompatImpl(); } else if (version >= 19) { IMPL = new KitKatViewCompatImpl(); @@ -1540,6 +1658,8 @@ public class ViewCompat { IMPL = new JbMr1ViewCompatImpl(); } else if (version >= 16) { IMPL = new JBViewCompatImpl(); + } else if (version >= 15) { + IMPL = new ICSMr1ViewCompatImpl(); } else if (version >= 14) { IMPL = new ICSViewCompatImpl(); } else if (version >= 11) { @@ -2513,7 +2633,7 @@ public class ViewCompat { * @param value The y location of the pivot point. */ public static void setPivotY(View view, float value) { - IMPL.setPivotX(view, value); + IMPL.setPivotY(view, value); } public static float getRotation(View view) { @@ -3085,4 +3205,76 @@ public class ViewCompat { public static boolean isAttachedToWindow(View view) { return IMPL.isAttachedToWindow(view); } + + /** + * Returns whether the provided view has an attached {@link View.OnClickListener}. + * + * @return true if there is a listener, false if there is none. + */ + public static boolean hasOnClickListeners(View view) { + return IMPL.hasOnClickListeners(view); + } + + /** + * Sets the state of all scroll indicators. + * <p> + * See {@link #setScrollIndicators(View, int, int)} for usage information. + * + * @param indicators a bitmask of indicators that should be enabled, or + * {@code 0} to disable all indicators + * + * @see #setScrollIndicators(View, int, int) + * @see #getScrollIndicators(View) + */ + public static void setScrollIndicators(@NonNull View view, @ScrollIndicators int indicators) { + IMPL.setScrollIndicators(view, indicators); + } + + /** + * Sets the state of the scroll indicators specified by the mask. To change + * all scroll indicators at once, see {@link #setScrollIndicators(View, int)}. + * <p> + * When a scroll indicator is enabled, it will be displayed if the view + * can scroll in the direction of the indicator. + * <p> + * Multiple indicator types may be enabled or disabled by passing the + * logical OR of the desired types. If multiple types are specified, they + * will all be set to the same enabled state. + * <p> + * For example, to enable the top scroll indicatorExample: {@code setScrollIndicators} + * + * @param indicators the indicator direction, or the logical OR of multiple + * indicator directions. One or more of: + * <ul> + * <li>{@link #SCROLL_INDICATOR_TOP}</li> + * <li>{@link #SCROLL_INDICATOR_BOTTOM}</li> + * <li>{@link #SCROLL_INDICATOR_LEFT}</li> + * <li>{@link #SCROLL_INDICATOR_RIGHT}</li> + * <li>{@link #SCROLL_INDICATOR_START}</li> + * <li>{@link #SCROLL_INDICATOR_END}</li> + * </ul> + * + * @see #setScrollIndicators(View, int) + * @see #getScrollIndicators(View) + */ + public static void setScrollIndicators(@NonNull View view, @ScrollIndicators int indicators, + @ScrollIndicators int mask) { + IMPL.setScrollIndicators(view, indicators, mask); + } + + /** + * Returns a bitmask representing the enabled scroll indicators. + * <p> + * For example, if the top and left scroll indicators are enabled and all + * other indicators are disabled, the return value will be + * {@code ViewCompat.SCROLL_INDICATOR_TOP | ViewCompat.SCROLL_INDICATOR_LEFT}. + * <p> + * To check whether the bottom scroll indicator is enabled, use the value + * of {@code (ViewCompat.getScrollIndicators(view) & ViewCompat.SCROLL_INDICATOR_BOTTOM) != 0}. + * + * @return a bitmask representing the enabled scroll indicators + */ + public static int getScrollIndicators(@NonNull View view) { + return IMPL.getScrollIndicators(view); + } } diff --git a/v4/java/android/support/v4/view/ViewPager.java b/v4/java/android/support/v4/view/ViewPager.java index f19104a75c..c6f3648e32 100644 --- a/v4/java/android/support/v4/view/ViewPager.java +++ b/v4/java/android/support/v4/view/ViewPager.java @@ -144,7 +144,10 @@ public class ViewPager extends ViewGroup { private int mRestoredCurItem = -1; private Parcelable mRestoredAdapterState = null; private ClassLoader mRestoredClassLoader = null; + private Scroller mScroller; + private boolean mIsScrollStarted; + private PagerObserver mObserver; private int mPageMargin; @@ -388,6 +391,10 @@ public class ViewPager extends ViewGroup { @Override protected void onDetachedFromWindow() { removeCallbacks(mEndScrollRunnable); + // To be on the safe side, abort the scroller + if ((mScroller != null) && !mScroller.isFinished()) { + mScroller.abortAnimation(); + } super.onDetachedFromWindow(); } @@ -411,7 +418,7 @@ public class ViewPager extends ViewGroup { */ public void setAdapter(PagerAdapter adapter) { if (mAdapter != null) { - mAdapter.unregisterDataSetObserver(mObserver); + mAdapter.setViewPagerObserver(null); mAdapter.startUpdate(this); for (int i = 0; i < mItems.size(); i++) { final ItemInfo ii = mItems.get(i); @@ -432,7 +439,7 @@ public class ViewPager extends ViewGroup { if (mObserver == null) { mObserver = new PagerObserver(); } - mAdapter.registerDataSetObserver(mObserver); + mAdapter.setViewPagerObserver(mObserver); mPopulatePending = false; final boolean wasFirstLayout = mFirstLayout; mFirstLayout = true; @@ -829,7 +836,21 @@ public class ViewPager extends ViewGroup { setScrollingCacheEnabled(false); return; } - int sx = getScrollX(); + + int sx; + boolean wasScrolling = (mScroller != null) && !mScroller.isFinished(); + if (wasScrolling) { + // We're in the middle of a previously initiated scrolling. Check to see + // whether that scrolling has actually started (if we always call getStartX + // we can get a stale value from the scroller if it hadn't yet had its first + // computeScrollOffset call) to decide what is the current scrolling position. + sx = mIsScrollStarted ? mScroller.getCurrX() : mScroller.getStartX(); + // And abort the current scrolling. + mScroller.abortAnimation(); + setScrollingCacheEnabled(false); + } else { + sx = getScrollX(); + } int sy = getScrollY(); int dx = x - sx; int dy = y - sy; @@ -849,7 +870,7 @@ public class ViewPager extends ViewGroup { final float distance = halfWidth + halfWidth * distanceInfluenceForSnapDuration(distanceRatio); - int duration = 0; + int duration; velocity = Math.abs(velocity); if (velocity > 0) { duration = 4 * Math.round(1000 * Math.abs(distance / velocity)); @@ -860,6 +881,9 @@ public class ViewPager extends ViewGroup { } duration = Math.min(duration, MAX_SETTLE_DURATION); + // Reset the "scroll started" flag. It will be flipped to true in all places + // where we call computeScrollOffset(). + mIsScrollStarted = false; mScroller.startScroll(sx, sy, dx, dy, duration); ViewCompat.postInvalidateOnAnimation(this); } @@ -1642,6 +1666,7 @@ public class ViewPager extends ViewGroup { @Override public void computeScroll() { + mIsScrollStarted = true; if (!mScroller.isFinished() && mScroller.computeScrollOffset()) { int oldX = getScrollX(); int oldY = getScrollY(); @@ -1822,15 +1847,18 @@ public class ViewPager extends ViewGroup { if (needPopulate) { // Done with scroll, no longer want to cache view drawing. setScrollingCacheEnabled(false); - mScroller.abortAnimation(); - int oldX = getScrollX(); - int oldY = getScrollY(); - int x = mScroller.getCurrX(); - int y = mScroller.getCurrY(); - if (oldX != x || oldY != y) { - scrollTo(x, y); - if (x != oldX) { - pageScrolled(x); + boolean wasScrolling = !mScroller.isFinished(); + if (wasScrolling) { + mScroller.abortAnimation(); + int oldX = getScrollX(); + int oldY = getScrollY(); + int x = mScroller.getCurrX(); + int y = mScroller.getCurrY(); + if (oldX != x || oldY != y) { + scrollTo(x, y); + if (x != oldX) { + pageScrolled(x); + } } } } @@ -1878,13 +1906,7 @@ public class ViewPager extends ViewGroup { if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { // Release the drag. if (DEBUG) Log.v(TAG, "Intercept done!"); - mIsBeingDragged = false; - mIsUnableToDrag = false; - mActivePointerId = INVALID_POINTER; - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } + resetTouch(); return false; } @@ -1970,6 +1992,7 @@ public class ViewPager extends ViewGroup { mActivePointerId = MotionEventCompat.getPointerId(ev, 0); mIsUnableToDrag = false; + mIsScrollStarted = true; mScroller.computeScrollOffset(); if (mScrollState == SCROLL_STATE_SETTLING && Math.abs(mScroller.getFinalX() - mScroller.getCurrX()) > mCloseEnough) { @@ -2051,6 +2074,11 @@ public class ViewPager extends ViewGroup { case MotionEvent.ACTION_MOVE: if (!mIsBeingDragged) { final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); + if (pointerIndex == -1) { + // A child has consumed some touch events and put us into an inconsistent state. + needsInvalidate = resetTouch(); + break; + } final float x = MotionEventCompat.getX(ev, pointerIndex); final float xDiff = Math.abs(x - mLastMotionX); final float y = MotionEventCompat.getY(ev, pointerIndex); @@ -2102,17 +2130,13 @@ public class ViewPager extends ViewGroup { totalDelta); setCurrentItemInternal(nextPage, true, true, initialVelocity); - mActivePointerId = INVALID_POINTER; - endDrag(); - needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease(); + needsInvalidate = resetTouch(); } break; case MotionEvent.ACTION_CANCEL: if (mIsBeingDragged) { scrollToItem(mCurItem, true, 0, false); - mActivePointerId = INVALID_POINTER; - endDrag(); - needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease(); + needsInvalidate = resetTouch(); } break; case MotionEventCompat.ACTION_POINTER_DOWN: { @@ -2134,6 +2158,14 @@ public class ViewPager extends ViewGroup { return true; } + private boolean resetTouch() { + boolean needsInvalidate; + mActivePointerId = INVALID_POINTER; + endDrag(); + needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease(); + return needsInvalidate; + } + private void requestParentDisallowInterceptTouchEvent(boolean disallowIntercept) { final ViewParent parent = getParent(); if (parent != null) { diff --git a/v4/java/android/support/v4/widget/DrawerLayout.java b/v4/java/android/support/v4/widget/DrawerLayout.java index a9560ad7b0..549b8eadbc 100644 --- a/v4/java/android/support/v4/widget/DrawerLayout.java +++ b/v4/java/android/support/v4/widget/DrawerLayout.java @@ -59,17 +59,20 @@ import java.util.List; /** * DrawerLayout acts as a top-level container for window content that allows for - * interactive "drawer" views to be pulled out from the edge of the window. + * interactive "drawer" views to be pulled out from one or both vertical edges of the window. * * <p>Drawer positioning and layout is controlled using the <code>android:layout_gravity</code> * attribute on child views corresponding to which side of the view you want the drawer - * to emerge from: left or right. (Or start/end on platform versions that support layout direction.) + * to emerge from: left or right (or start/end on platform versions that support layout direction.) + * Note that you can only have one drawer view for each vertical edge of the window. If your + * layout configures more than one drawer view per vertical edge of the window, an exception will + * be thrown at runtime. * </p> * * <p>To use a DrawerLayout, position your primary content view as the first child with - * a width and height of <code>match_parent</code>. Add drawers as child views after the main - * content view and set the <code>layout_gravity</code> appropriately. Drawers commonly use - * <code>match_parent</code> for height with a fixed width.</p> + * width and height of <code>match_parent</code> and no <code>layout_gravity></code>. + * Add drawers as child views after the main content view and set the <code>layout_gravity</code> + * appropriately. Drawers commonly use <code>match_parent</code> for height with a fixed width.</p> * * <p>{@link DrawerListener} can be used to monitor the state and motion of drawer views. * Avoid performing expensive operations such as layout during animation as it can cause @@ -110,7 +113,8 @@ public class DrawerLayout extends ViewGroup implements DrawerLayoutImpl { public static final int STATE_SETTLING = ViewDragHelper.STATE_SETTLING; /** @hide */ - @IntDef({LOCK_MODE_UNLOCKED, LOCK_MODE_LOCKED_CLOSED, LOCK_MODE_LOCKED_OPEN}) + @IntDef({LOCK_MODE_UNLOCKED, LOCK_MODE_LOCKED_CLOSED, LOCK_MODE_LOCKED_OPEN, + LOCK_MODE_UNDEFINED}) @Retention(RetentionPolicy.SOURCE) private @interface LockMode {} @@ -131,6 +135,11 @@ public class DrawerLayout extends ViewGroup implements DrawerLayoutImpl { */ public static final int LOCK_MODE_LOCKED_OPEN = 2; + /** + * The drawer's lock state is reset to default. + */ + public static final int LOCK_MODE_UNDEFINED = 3; + /** @hide */ @IntDef({Gravity.LEFT, Gravity.RIGHT, GravityCompat.START, GravityCompat.END}) @Retention(RetentionPolicy.SOURCE) @@ -189,8 +198,12 @@ public class DrawerLayout extends ViewGroup implements DrawerLayoutImpl { private int mDrawerState; private boolean mInLayout; private boolean mFirstLayout = true; - private int mLockModeLeft; - private int mLockModeRight; + + private @LockMode int mLockModeLeft = LOCK_MODE_UNDEFINED; + private @LockMode int mLockModeRight = LOCK_MODE_UNDEFINED; + private @LockMode int mLockModeStart = LOCK_MODE_UNDEFINED; + private @LockMode int mLockModeEnd = LOCK_MODE_UNDEFINED; + private boolean mDisallowInterceptRequested; private boolean mChildrenCanceledTouch; @@ -544,11 +557,22 @@ public class DrawerLayout extends ViewGroup implements DrawerLayoutImpl { public void setDrawerLockMode(@LockMode int lockMode, @EdgeGravity int edgeGravity) { final int absGravity = GravityCompat.getAbsoluteGravity(edgeGravity, ViewCompat.getLayoutDirection(this)); - if (absGravity == Gravity.LEFT) { - mLockModeLeft = lockMode; - } else if (absGravity == Gravity.RIGHT) { - mLockModeRight = lockMode; + + switch (edgeGravity) { + case Gravity.LEFT: + mLockModeLeft = lockMode; + break; + case Gravity.RIGHT: + mLockModeRight = lockMode; + break; + case GravityCompat.START: + mLockModeStart = lockMode; + break; + case GravityCompat.END: + mLockModeEnd = lockMode; + break; } + if (lockMode != LOCK_MODE_UNLOCKED) { // Cancel interaction in progress final ViewDragHelper helper = absGravity == Gravity.LEFT ? mLeftDragger : mRightDragger; @@ -566,8 +590,8 @@ public class DrawerLayout extends ViewGroup implements DrawerLayoutImpl { if (toClose != null) { closeDrawer(toClose); } - break; - // default: do nothing + break; + // default: do nothing } } @@ -607,13 +631,51 @@ public class DrawerLayout extends ViewGroup implements DrawerLayoutImpl { */ @LockMode public int getDrawerLockMode(@EdgeGravity int edgeGravity) { - final int absGravity = GravityCompat.getAbsoluteGravity( - edgeGravity, ViewCompat.getLayoutDirection(this)); - if (absGravity == Gravity.LEFT) { - return mLockModeLeft; - } else if (absGravity == Gravity.RIGHT) { - return mLockModeRight; + int layoutDirection = ViewCompat.getLayoutDirection(this); + + switch (edgeGravity) { + case Gravity.LEFT: + if (mLockModeLeft != LOCK_MODE_UNDEFINED) { + return mLockModeLeft; + } + int leftLockMode = (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) ? + mLockModeStart : mLockModeEnd; + if (leftLockMode != LOCK_MODE_UNDEFINED) { + return leftLockMode; + } + break; + case Gravity.RIGHT: + if (mLockModeRight != LOCK_MODE_UNDEFINED) { + return mLockModeRight; + } + int rightLockMode = (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) ? + mLockModeEnd : mLockModeStart; + if (rightLockMode != LOCK_MODE_UNDEFINED) { + return rightLockMode; + } + break; + case GravityCompat.START: + if (mLockModeStart != LOCK_MODE_UNDEFINED) { + return mLockModeStart; + } + int startLockMode = (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) ? + mLockModeLeft : mLockModeRight; + if (startLockMode != LOCK_MODE_UNDEFINED) { + return startLockMode; + } + break; + case GravityCompat.END: + if (mLockModeEnd != LOCK_MODE_UNDEFINED) { + return mLockModeEnd; + } + int endLockMode = (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) ? + mLockModeRight : mLockModeLeft; + if (endLockMode != LOCK_MODE_UNDEFINED) { + return endLockMode; + } + break; } + return LOCK_MODE_UNLOCKED; } @@ -626,13 +688,8 @@ public class DrawerLayout extends ViewGroup implements DrawerLayoutImpl { */ @LockMode public int getDrawerLockMode(View drawerView) { - final int absGravity = getDrawerViewAbsoluteGravity(drawerView); - if (absGravity == Gravity.LEFT) { - return mLockModeLeft; - } else if (absGravity == Gravity.RIGHT) { - return mLockModeRight; - } - return LOCK_MODE_UNLOCKED; + final int drawerGravity = ((LayoutParams) drawerView.getLayoutParams()).gravity; + return getDrawerLockMode(drawerGravity); } /** @@ -712,8 +769,8 @@ public class DrawerLayout extends ViewGroup implements DrawerLayoutImpl { void dispatchOnDrawerClosed(View drawerView) { final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams(); - if (lp.knownOpen) { - lp.knownOpen = false; + if ((lp.openState & LayoutParams.FLAG_IS_OPENED) == 1) { + lp.openState = 0; if (mListener != null) { mListener.onDrawerClosed(drawerView); } @@ -734,8 +791,8 @@ public class DrawerLayout extends ViewGroup implements DrawerLayoutImpl { void dispatchOnDrawerOpened(View drawerView) { final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams(); - if (!lp.knownOpen) { - lp.knownOpen = true; + if ((lp.openState & LayoutParams.FLAG_IS_OPENED) == 0) { + lp.openState = LayoutParams.FLAG_IS_OPENED; if (mListener != null) { mListener.onDrawerOpened(drawerView); } @@ -806,7 +863,8 @@ public class DrawerLayout extends ViewGroup implements DrawerLayoutImpl { final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); - if (((LayoutParams) child.getLayoutParams()).knownOpen) { + final LayoutParams childLp = (LayoutParams) child.getLayoutParams(); + if ((childLp.openState & LayoutParams.FLAG_IS_OPENED) == 1) { return child; } } @@ -951,6 +1009,7 @@ public class DrawerLayout extends ViewGroup implements DrawerLayoutImpl { gravityToString(childGravity) + " but this " + TAG + " already has a " + "drawer view along that edge"); } + foundDrawers = foundDrawers | childGravity; final int drawerWidthSpec = getChildMeasureSpec(widthMeasureSpec, mMinDrawerMargin + lp.leftMargin + lp.rightMargin, lp.width); @@ -1435,13 +1494,15 @@ public class DrawerLayout extends ViewGroup implements DrawerLayoutImpl { throw new IllegalArgumentException("View " + drawerView + " is not a sliding drawer"); } + final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams(); if (mFirstLayout) { - final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams(); lp.onScreen = 1.f; - lp.knownOpen = true; + lp.openState = LayoutParams.FLAG_IS_OPENED; updateChildrenImportantForAccessibility(drawerView, true); } else { + lp.openState |= LayoutParams.FLAG_IS_OPENING; + if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.LEFT)) { mLeftDragger.smoothSlideViewTo(drawerView, 0, drawerView.getTop()); } else { @@ -1477,11 +1538,13 @@ public class DrawerLayout extends ViewGroup implements DrawerLayoutImpl { throw new IllegalArgumentException("View " + drawerView + " is not a sliding drawer"); } + final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams(); if (mFirstLayout) { - final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams(); lp.onScreen = 0.f; - lp.knownOpen = false; + lp.openState = 0; } else { + lp.openState |= LayoutParams.FLAG_IS_CLOSING; + if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.LEFT)) { mLeftDragger.smoothSlideViewTo(drawerView, -drawerView.getWidth(), drawerView.getTop()); @@ -1521,7 +1584,8 @@ public class DrawerLayout extends ViewGroup implements DrawerLayoutImpl { if (!isDrawerView(drawer)) { throw new IllegalArgumentException("View " + drawer + " is not a drawer"); } - return ((LayoutParams) drawer.getLayoutParams()).knownOpen; + LayoutParams drawerLp = (LayoutParams) drawer.getLayoutParams(); + return (drawerLp.openState & LayoutParams.FLAG_IS_OPENED) == 1; } /** @@ -1705,8 +1769,18 @@ public class DrawerLayout extends ViewGroup implements DrawerLayoutImpl { } } - setDrawerLockMode(ss.lockModeLeft, Gravity.LEFT); - setDrawerLockMode(ss.lockModeRight, Gravity.RIGHT); + if (ss.lockModeLeft != LOCK_MODE_UNDEFINED) { + setDrawerLockMode(ss.lockModeLeft, Gravity.LEFT); + } + if (ss.lockModeRight != LOCK_MODE_UNDEFINED) { + setDrawerLockMode(ss.lockModeRight, Gravity.RIGHT); + } + if (ss.lockModeStart != LOCK_MODE_UNDEFINED) { + setDrawerLockMode(ss.lockModeStart, GravityCompat.START); + } + if (ss.lockModeEnd != LOCK_MODE_UNDEFINED) { + setDrawerLockMode(ss.lockModeEnd, GravityCompat.END); + } } @Override @@ -1714,13 +1788,26 @@ public class DrawerLayout extends ViewGroup implements DrawerLayoutImpl { final Parcelable superState = super.onSaveInstanceState(); final SavedState ss = new SavedState(superState); - final View openDrawer = findOpenDrawer(); - if (openDrawer != null) { - ss.openDrawerGravity = ((LayoutParams) openDrawer.getLayoutParams()).gravity; + final int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + final View child = getChildAt(i); + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + // Is the current child fully opened (that is, not closing)? + boolean isOpenedAndNotClosing = (lp.openState == LayoutParams.FLAG_IS_OPENED); + // Is the current child opening? + boolean isClosedAndOpening = (lp.openState == LayoutParams.FLAG_IS_OPENING); + if (isOpenedAndNotClosing || isClosedAndOpening) { + // If one of the conditions above holds, save the child's gravity + // so that we open that child during state restore. + ss.openDrawerGravity = lp.gravity; + break; + } } ss.lockModeLeft = mLockModeLeft; ss.lockModeRight = mLockModeRight; + ss.lockModeStart = mLockModeStart; + ss.lockModeEnd = mLockModeEnd; return ss; } @@ -1766,12 +1853,18 @@ public class DrawerLayout extends ViewGroup implements DrawerLayoutImpl { */ protected static class SavedState extends BaseSavedState { int openDrawerGravity = Gravity.NO_GRAVITY; - int lockModeLeft = LOCK_MODE_UNLOCKED; - int lockModeRight = LOCK_MODE_UNLOCKED; + @LockMode int lockModeLeft; + @LockMode int lockModeRight; + @LockMode int lockModeStart; + @LockMode int lockModeEnd; public SavedState(Parcel in) { super(in); openDrawerGravity = in.readInt(); + lockModeLeft = in.readInt(); + lockModeRight = in.readInt(); + lockModeStart = in.readInt(); + lockModeEnd = in.readInt(); } public SavedState(Parcelable superState) { @@ -1782,6 +1875,10 @@ public class DrawerLayout extends ViewGroup implements DrawerLayoutImpl { public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(openDrawerGravity); + dest.writeInt(lockModeLeft); + dest.writeInt(lockModeRight); + dest.writeInt(lockModeStart); + dest.writeInt(lockModeEnd); } public static final Parcelable.Creator<SavedState> CREATOR = @@ -1965,11 +2062,14 @@ public class DrawerLayout extends ViewGroup implements DrawerLayoutImpl { } public static class LayoutParams extends ViewGroup.MarginLayoutParams { + private static final int FLAG_IS_OPENED = 0x1; + private static final int FLAG_IS_OPENING = 0x2; + private static final int FLAG_IS_CLOSING = 0x4; public int gravity = Gravity.NO_GRAVITY; - float onScreen; - boolean isPeeking; - boolean knownOpen; + private float onScreen; + private boolean isPeeking; + private int openState; public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); diff --git a/v4/java/android/support/v4/widget/NestedScrollView.java b/v4/java/android/support/v4/widget/NestedScrollView.java index 788761a571..27e43076d9 100644 --- a/v4/java/android/support/v4/widget/NestedScrollView.java +++ b/v4/java/android/support/v4/widget/NestedScrollView.java @@ -31,6 +31,7 @@ import android.support.v4.view.NestedScrollingChild; import android.support.v4.view.NestedScrollingChildHelper; import android.support.v4.view.NestedScrollingParent; import android.support.v4.view.NestedScrollingParentHelper; +import android.support.v4.view.ScrollingView; import android.support.v4.view.VelocityTrackerCompat; import android.support.v4.view.ViewCompat; import android.support.v4.view.accessibility.AccessibilityEventCompat; @@ -61,13 +62,35 @@ import java.util.List; * Nested scrolling is enabled by default. */ public class NestedScrollView extends FrameLayout implements NestedScrollingParent, - NestedScrollingChild { + NestedScrollingChild, ScrollingView { static final int ANIMATED_SCROLL_GAP = 250; static final float MAX_SCROLL_FACTOR = 0.5f; private static final String TAG = "NestedScrollView"; + /** + * Interface definition for a callback to be invoked when the scroll + * X or Y positions of a view change. + * + * <p>This version of the interface works on all versions of Android, back to API v4.</p> + * + * @see #setOnScrollChangeListener(OnScrollChangeListener) + */ + public interface OnScrollChangeListener { + /** + * Called when the scroll position of a view changes. + * + * @param v The view whose scroll position has changed. + * @param scrollX Current horizontal scroll origin. + * @param scrollY Current vertical scroll origin. + * @param oldScrollX Previous horizontal scroll origin. + * @param oldScrollY Previous vertical scroll origin. + */ + void onScrollChange(NestedScrollView v, int scrollX, int scrollY, + int oldScrollX, int oldScrollY); + } + private long mLastScroll; private final Rect mTempRect = new Rect(); @@ -153,6 +176,8 @@ public class NestedScrollView extends FrameLayout implements NestedScrollingPare private float mVerticalScrollFactor; + private OnScrollChangeListener mOnScrollChangeListener; + public NestedScrollView(Context context) { this(context, null); } @@ -245,6 +270,7 @@ public class NestedScrollView extends FrameLayout implements NestedScrollingPare @Override public void onStopNestedScroll(View target) { + mParentHelper.onStopNestedScroll(target); stopNestedScroll(); } @@ -376,6 +402,19 @@ public class NestedScrollView extends FrameLayout implements NestedScrollingPare } /** + * Register a callback to be invoked when the scroll X or Y positions of + * this view change. + * <p>This version of the method works on all versions of Android, back to API v4.</p> + * + * @param l The listener to notify when the scroll X or Y position changes. + * @see android.view.View#getScrollX() + * @see android.view.View#getScrollY() + */ + public void setOnScrollChangeListener(OnScrollChangeListener l) { + mOnScrollChangeListener = l; + } + + /** * @return Returns true this ScrollView can be scrolled */ private boolean canScroll() { @@ -430,6 +469,15 @@ public class NestedScrollView extends FrameLayout implements NestedScrollingPare } @Override + protected void onScrollChanged(int l, int t, int oldl, int oldt) { + super.onScrollChanged(l, t, oldl, oldt); + + if (mOnScrollChangeListener != null) { + mOnScrollChangeListener.onScrollChange(this, l, t, oldl, oldt); + } + } + + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); @@ -576,13 +624,6 @@ public class NestedScrollView extends FrameLayout implements NestedScrollingPare return true; } - /* - * Don't try to intercept touch if we can't scroll anyway. - */ - if (getScrollY() == 0 && !ViewCompat.canScrollVertically(this, 1)) { - return false; - } - switch (action & MotionEventCompat.ACTION_MASK) { case MotionEvent.ACTION_MOVE: { /* @@ -657,6 +698,9 @@ public class NestedScrollView extends FrameLayout implements NestedScrollingPare mIsBeingDragged = false; mActivePointerId = INVALID_POINTER; recycleVelocityTracker(); + if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0, getScrollRange())) { + ViewCompat.postInvalidateOnAnimation(this); + } stopNestedScroll(); break; case MotionEventCompat.ACTION_POINTER_UP: @@ -795,17 +839,23 @@ public class NestedScrollView extends FrameLayout implements NestedScrollingPare if ((Math.abs(initialVelocity) > mMinimumVelocity)) { flingWithNestedDispatch(-initialVelocity); + } else if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0, + getScrollRange())) { + ViewCompat.postInvalidateOnAnimation(this); } - - mActivePointerId = INVALID_POINTER; - endDrag(); } + mActivePointerId = INVALID_POINTER; + endDrag(); break; case MotionEvent.ACTION_CANCEL: if (mIsBeingDragged && getChildCount() > 0) { - mActivePointerId = INVALID_POINTER; - endDrag(); + if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0, + getScrollRange())) { + ViewCompat.postInvalidateOnAnimation(this); + } } + mActivePointerId = INVALID_POINTER; + endDrag(); break; case MotionEventCompat.ACTION_POINTER_DOWN: { final int index = MotionEventCompat.getActionIndex(ev); @@ -942,6 +992,10 @@ public class NestedScrollView extends FrameLayout implements NestedScrollingPare clampedY = true; } + if (clampedY) { + mScroller.springBack(newScrollX, newScrollY, 0, 0, 0, getScrollRange()); + } + onOverScrolled(newScrollX, newScrollY, clampedX, clampedY); return clampedX || clampedY; @@ -1280,9 +1334,10 @@ public class NestedScrollView extends FrameLayout implements NestedScrollingPare /** * <p>The scroll range of a scroll view is the overall height of all of its * children.</p> + * @hide */ @Override - protected int computeVerticalScrollRange() { + public int computeVerticalScrollRange() { final int count = getChildCount(); final int contentHeight = getHeight() - getPaddingBottom() - getPaddingTop(); if (count == 0) { @@ -1301,11 +1356,36 @@ public class NestedScrollView extends FrameLayout implements NestedScrollingPare return scrollRange; } + /** @hide */ @Override - protected int computeVerticalScrollOffset() { + public int computeVerticalScrollOffset() { return Math.max(0, super.computeVerticalScrollOffset()); } + /** @hide */ + @Override + public int computeVerticalScrollExtent() { + return super.computeVerticalScrollExtent(); + } + + /** @hide */ + @Override + public int computeHorizontalScrollRange() { + return super.computeHorizontalScrollRange(); + } + + /** @hide */ + @Override + public int computeHorizontalScrollOffset() { + return super.computeHorizontalScrollOffset(); + } + + /** @hide */ + @Override + public int computeHorizontalScrollExtent() { + return super.computeHorizontalScrollExtent(); + } + @Override protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { ViewGroup.LayoutParams lp = child.getLayoutParams(); diff --git a/v4/java/android/support/v4/widget/ScrollerCompat.java b/v4/java/android/support/v4/widget/ScrollerCompat.java index afbf89704e..3115a415ec 100644 --- a/v4/java/android/support/v4/widget/ScrollerCompat.java +++ b/v4/java/android/support/v4/widget/ScrollerCompat.java @@ -54,6 +54,8 @@ public class ScrollerCompat { boolean isOverScrolled(Object scroller); int getFinalX(Object scroller); int getFinalY(Object scroller); + boolean springBack(Object scroller, int startX, int startY, int minX, int maxX, + int minY, int maxY); } static final int CHASE_FRAME_TIME = 16; // ms per target frame @@ -145,6 +147,12 @@ public class ScrollerCompat { public int getFinalY(Object scroller) { return ((Scroller) scroller).getFinalY(); } + + @Override + public boolean springBack(Object scroller, int startX, int startY, int minX, int maxX, + int minY, int maxY) { + return false; + } } static class ScrollerCompatImplGingerbread implements ScrollerCompatImpl { @@ -233,6 +241,13 @@ public class ScrollerCompat { public int getFinalY(Object scroller) { return ScrollerCompatGingerbread.getFinalY(scroller); } + + @Override + public boolean springBack(Object scroller, int startX, int startY, int minX, int maxX, + int minY, int maxY) { + return ScrollerCompatGingerbread.springBack(scroller, startX, startY, minX, maxX, + minY, maxY); + } } static class ScrollerCompatImplIcs extends ScrollerCompatImplGingerbread { @@ -423,6 +438,22 @@ public class ScrollerCompat { } /** + * Call this when you want to 'spring back' into a valid coordinate range. + * + * @param startX Starting X coordinate + * @param startY Starting Y coordinate + * @param minX Minimum valid X value + * @param maxX Maximum valid X value + * @param minY Minimum valid Y value + * @param maxY Maximum valid Y value + * @return true if a springback was initiated, false if startX and startY were + * already within the valid range. + */ + public boolean springBack(int startX, int startY, int minX, int maxX, int minY, int maxY) { + return mImpl.springBack(mScroller, startX, startY, minX, maxX, minY, maxY); + } + + /** * Stops the animation. Aborting the animation causes the scroller to move to the final x and y * position. */ diff --git a/v4/java/android/support/v4/widget/SwipeRefreshLayout.java b/v4/java/android/support/v4/widget/SwipeRefreshLayout.java index d6b35ef1be..06db6f2f29 100644 --- a/v4/java/android/support/v4/widget/SwipeRefreshLayout.java +++ b/v4/java/android/support/v4/widget/SwipeRefreshLayout.java @@ -102,6 +102,7 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare private boolean mRefreshing = false; private int mTouchSlop; private float mTotalDragDistance = -1; + // If nested scrolling is enabled, the total amount that needed to be // consumed by this as the nested scrolling parent is used in place of the // overscroll determined by MOVE events in the onTouch handler @@ -109,6 +110,8 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare private final NestedScrollingParentHelper mNestedScrollingParentHelper; private final NestedScrollingChildHelper mNestedScrollingChildHelper; private final int[] mParentScrollConsumed = new int[2]; + private final int[] mParentOffsetInWindow = new int[2]; + private boolean mNestedScrollInProgress; private int mMediumAnimationDuration; private int mCurrentTargetOffsetTop; @@ -182,22 +185,34 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare mListener.onRefresh(); } } + mCurrentTargetOffsetTop = mCircleView.getTop(); } else { - mProgress.stop(); - mCircleView.setVisibility(View.GONE); - setColorViewAlpha(MAX_ALPHA); - // Return the circle to its start position - if (mScale) { - setAnimationProgress(0 /* animation complete and view is hidden */); - } else { - setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCurrentTargetOffsetTop, - true /* requires update */); - } + reset(); } - mCurrentTargetOffsetTop = mCircleView.getTop(); } }; + private void reset() { + mCircleView.clearAnimation(); + mProgress.stop(); + mCircleView.setVisibility(View.GONE); + setColorViewAlpha(MAX_ALPHA); + // Return the circle to its start position + if (mScale) { + setAnimationProgress(0 /* animation complete and view is hidden */); + } else { + setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCurrentTargetOffsetTop, + true /* requires update */); + } + mCurrentTargetOffsetTop = mCircleView.getTop(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + reset(); + } + private void setColorViewAlpha(int targetAlpha) { mCircleView.getBackground().setAlpha(targetAlpha); mProgress.setAlpha(targetAlpha); @@ -654,7 +669,8 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare mReturningToStart = false; } - if (!isEnabled() || mReturningToStart || canChildScrollUp() || mRefreshing) { + if (!isEnabled() || mReturningToStart || canChildScrollUp() + || mRefreshing || mNestedScrollInProgress) { // Fail fast if we're not in a state where a swipe is possible return false; } @@ -728,19 +744,18 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare @Override public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { - if (isEnabled() && (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0) { - // Dispatch up to the nested parent - startNestedScroll(nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL); - return true; - } - return false; + return isEnabled() && canChildScrollUp() && !mReturningToStart && !mRefreshing + && (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0; } @Override public void onNestedScrollAccepted(View child, View target, int axes) { // Reset the counter of how much leftover scroll needs to be consumed. mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes); + // Dispatch up to the nested parent + startNestedScroll(axes & ViewCompat.SCROLL_AXIS_VERTICAL); mTotalUnconsumed = 0; + mNestedScrollInProgress = true; } @Override @@ -759,6 +774,15 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare moveSpinner(mTotalUnconsumed); } + // If a client layout is using a custom start position for the circle + // view, they mean to hide it again before scrolling the child view + // If we get back to mTotalUnconsumed == 0 and there is more to go, hide + // the circle so it isn't exposed if its blocking content is moved + if (mUsingCustomStart && dy > 0 && mTotalUnconsumed == 0 + && Math.abs(dy - consumed[1]) > 0) { + mCircleView.setVisibility(View.GONE); + } + // Now let our nested parent consume the leftovers final int[] parentConsumed = mParentScrollConsumed; if (dispatchNestedPreScroll(dx - consumed[0], dy - consumed[1], parentConsumed, null)) { @@ -775,6 +799,7 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare @Override public void onStopNestedScroll(View target) { mNestedScrollingParentHelper.onStopNestedScroll(target); + mNestedScrollInProgress = false; // Finish the spinner for nested scrolling if we ever consumed any // unconsumed nested scroll if (mTotalUnconsumed > 0) { @@ -786,30 +811,27 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare } @Override - public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, - int dyUnconsumed) { - if (dyUnconsumed < 0) { - dyUnconsumed = Math.abs(dyUnconsumed); - mTotalUnconsumed += dyUnconsumed; + public void onNestedScroll(final View target, final int dxConsumed, final int dyConsumed, + final int dxUnconsumed, final int dyUnconsumed) { + // Dispatch up to the nested parent first + dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, + mParentOffsetInWindow); + + // This is a bit of a hack. Nested scrolling works from the bottom up, and as we are + // sometimes between two nested scrolling views, we need a way to be able to know when any + // nested scrolling parent has stopped handling events. We do that by using the + // 'offset in window 'functionality to see if we have been moved from the event. + // This is a decent indication of whether we should take over the event stream or not. + final int dy = dyUnconsumed + mParentOffsetInWindow[1]; + if (dy < 0) { + mTotalUnconsumed += Math.abs(dy); moveSpinner(mTotalUnconsumed); } - // Dispatch up to the nested parent - dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dxConsumed, null); } // NestedScrollingChild @Override - public boolean onNestedPreFling(View target, float velocityX, float velocityY) { - return false; - } - - @Override - public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) { - return false; - } - - @Override public void setNestedScrollingEnabled(boolean enabled) { mNestedScrollingChildHelper.setNestedScrollingEnabled(enabled); } @@ -847,6 +869,18 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare } @Override + public boolean onNestedPreFling(View target, float velocityX, + float velocityY) { + return dispatchNestedPreFling(velocityX, velocityY); + } + + @Override + public boolean onNestedFling(View target, float velocityX, float velocityY, + boolean consumed) { + return dispatchNestedFling(velocityX, velocityY, consumed); + } + + @Override public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) { return mNestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed); } @@ -943,12 +977,13 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare @Override public boolean onTouchEvent(MotionEvent ev) { final int action = MotionEventCompat.getActionMasked(ev); + int pointerIndex = -1; if (mReturningToStart && action == MotionEvent.ACTION_DOWN) { mReturningToStart = false; } - if (!isEnabled() || mReturningToStart || canChildScrollUp()) { + if (!isEnabled() || mReturningToStart || canChildScrollUp() || mNestedScrollInProgress) { // Fail fast if we're not in a state where a swipe is possible return false; } @@ -960,7 +995,7 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare break; case MotionEvent.ACTION_MOVE: { - final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); + pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); if (pointerIndex < 0) { Log.e(LOG_TAG, "Got ACTION_MOVE event but have an invalid active pointer id."); return false; @@ -978,8 +1013,12 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare break; } case MotionEventCompat.ACTION_POINTER_DOWN: { - final int index = MotionEventCompat.getActionIndex(ev); - mActivePointerId = MotionEventCompat.getPointerId(ev, index); + pointerIndex = MotionEventCompat.getActionIndex(ev); + if (pointerIndex < 0) { + Log.e(LOG_TAG, "Got ACTION_POINTER_DOWN event but have an invalid action index."); + return false; + } + mActivePointerId = MotionEventCompat.getPointerId(ev, pointerIndex); break; } @@ -987,15 +1026,13 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare onSecondaryPointerUp(ev); break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: { - if (mActivePointerId == INVALID_POINTER) { - if (action == MotionEvent.ACTION_UP) { - Log.e(LOG_TAG, "Got ACTION_UP event but don't have an active pointer id."); - } + case MotionEvent.ACTION_UP: { + pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); + if (pointerIndex < 0) { + Log.e(LOG_TAG, "Got ACTION_UP event but don't have an active pointer id."); return false; } - final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); + final float y = MotionEventCompat.getY(ev, pointerIndex); final float overscrollTop = (y - mInitialMotionY) * DRAG_RATE; mIsBeingDragged = false; @@ -1003,6 +1040,8 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare mActivePointerId = INVALID_POINTER; return false; } + case MotionEvent.ACTION_CANCEL: + return false; } return true; @@ -1020,18 +1059,6 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare mCircleView.startAnimation(mAnimateToCorrectPosition); } - private void peek(int from, AnimationListener listener) { - mFrom = from; - mPeek.reset(); - mPeek.setDuration(500); - mPeek.setInterpolator(mDecelerateInterpolator); - if (listener != null) { - mCircleView.setAnimationListener(listener); - } - mCircleView.clearAnimation(); - mCircleView.startAnimation(mPeek); - } - private void animateOffsetToStartPosition(int from, AnimationListener listener) { if (mScale) { // Scale the item back down @@ -1066,23 +1093,6 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare } }; - private final Animation mPeek = new Animation() { - @Override - public void applyTransformation(float interpolatedTime, Transformation t) { - int targetTop = 0; - int endTarget = 0; - if (!mUsingCustomStart) { - endTarget = (int) (mSpinnerFinalOffset - Math.abs(mOriginalOffsetTop)); - } else { - endTarget = (int) mSpinnerFinalOffset; //mSpinnerFinalOffset; - } - targetTop = (mFrom + (int) ((endTarget - mFrom) * interpolatedTime)); - int offset = targetTop - mCircleView.getTop(); - setTargetOffsetTopAndBottom(offset, false /* requires update */); - mProgress.setArrowScale(1 - interpolatedTime); - } - }; - private void moveToStart(float interpolatedTime) { int targetTop = 0; targetTop = (mFrom + (int) ((mOriginalOffsetTop - mFrom) * interpolatedTime)); diff --git a/v4/java/android/support/v4/widget/TextViewCompat.java b/v4/java/android/support/v4/widget/TextViewCompat.java index c31196aeac..621ee66eab 100644 --- a/v4/java/android/support/v4/widget/TextViewCompat.java +++ b/v4/java/android/support/v4/widget/TextViewCompat.java @@ -18,6 +18,7 @@ package android.support.v4.widget; import android.graphics.drawable.Drawable; import android.os.Build; +import android.support.annotation.IdRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.widget.TextView; @@ -29,26 +30,23 @@ import android.widget.TextView; public class TextViewCompat { // Hide constructor - private TextViewCompat() { - } + private TextViewCompat() {} interface TextViewCompatImpl { - - public void setCompoundDrawablesRelative(@NonNull TextView textView, + void setCompoundDrawablesRelative(@NonNull TextView textView, @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end, @Nullable Drawable bottom); - - public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView, + void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView, @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end, @Nullable Drawable bottom); - - public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView, + void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView, int start, int top, int end, int bottom); - + int getMaxLines(TextView textView); + int getMinLines(TextView textView); + void setTextAppearance(@NonNull TextView textView, @IdRes int resId); } static class BaseTextViewCompatImpl implements TextViewCompatImpl { - @Override public void setCompoundDrawablesRelative(@NonNull TextView textView, @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end, @@ -69,10 +67,35 @@ public class TextViewCompat { textView.setCompoundDrawablesWithIntrinsicBounds(start, top, end, bottom); } + @Override + public int getMaxLines(TextView textView) { + return TextViewCompatDonut.getMaxLines(textView); + } + + @Override + public int getMinLines(TextView textView) { + return TextViewCompatDonut.getMinLines(textView); + } + + @Override + public void setTextAppearance(TextView textView, int resId) { + TextViewCompatDonut.setTextAppearance(textView, resId); + } } - static class JbMr1TextViewCompatImpl extends BaseTextViewCompatImpl { + static class JbTextViewCompatImpl extends BaseTextViewCompatImpl { + @Override + public int getMaxLines(TextView textView) { + return TextViewCompatJb.getMaxLines(textView); + } + + @Override + public int getMinLines(TextView textView) { + return TextViewCompatJb.getMinLines(textView); + } + } + static class JbMr1TextViewCompatImpl extends JbTextViewCompatImpl { @Override public void setCompoundDrawablesRelative(@NonNull TextView textView, @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end, @@ -94,11 +117,9 @@ public class TextViewCompat { TextViewCompatJbMr1.setCompoundDrawablesRelativeWithIntrinsicBounds(textView, start, top, end, bottom); } - } static class JbMr2TextViewCompatImpl extends JbMr1TextViewCompatImpl { - @Override public void setCompoundDrawablesRelative(@NonNull TextView textView, @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end, @@ -123,14 +144,25 @@ public class TextViewCompat { } } + static class Api23TextViewCompatImpl extends JbMr2TextViewCompatImpl { + @Override + public void setTextAppearance(@NonNull TextView textView, @IdRes int resId) { + TextViewCompatApi23.setTextAppearance(textView, resId); + } + } + static final TextViewCompatImpl IMPL; static { final int version = Build.VERSION.SDK_INT; - if (version >= 18) { + if (version >= 23) { + IMPL = new Api23TextViewCompatImpl(); + } else if (version >= 18) { IMPL = new JbMr2TextViewCompatImpl(); } else if (version >= 17) { IMPL = new JbMr1TextViewCompatImpl(); + } else if (version >= 16) { + IMPL = new JbTextViewCompatImpl(); } else { IMPL = new BaseTextViewCompatImpl(); } @@ -200,4 +232,34 @@ public class TextViewCompat { IMPL.setCompoundDrawablesRelativeWithIntrinsicBounds(textView, start, top, end, bottom); } + /** + * Returns the maximum number of lines displayed in the given TextView, or -1 if the maximum + * height was set in pixels instead. + */ + public static int getMaxLines(@NonNull TextView textView) { + return IMPL.getMaxLines(textView); + } + + /** + * Returns the minimum number of lines displayed in the given TextView, or -1 if the minimum + * height was set in pixels instead. + */ + public static int getMinLines(@NonNull TextView textView) { + return IMPL.getMinLines(textView); + } + + /** + * Sets the text appearance from the specified style resource. + * <p> + * Use a framework-defined {@code TextAppearance} style like + * {@link android.R.style#TextAppearance_Material_Body1 @android:style/TextAppearance.Material.Body1} + * or see {@link android.R.styleable#TextAppearance TextAppearance} for the + * set of attributes that can be used in a custom style. + * + * @param textView The TextView against which to invoke the method. + * @param resId The resource identifier of the style to apply. + */ + public static void setTextAppearance(@NonNull TextView textView, @IdRes int resId) { + IMPL.setTextAppearance(textView, resId); + } } diff --git a/v4/java/android/support/v4/widget/TextViewCompatDonut.java b/v4/java/android/support/v4/widget/TextViewCompatDonut.java new file mode 100644 index 0000000000..ba155fe342 --- /dev/null +++ b/v4/java/android/support/v4/widget/TextViewCompatDonut.java @@ -0,0 +1,98 @@ +/* + * 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.v4.widget; + +import android.util.Log; +import android.widget.TextView; + +import java.lang.reflect.Field; + +class TextViewCompatDonut { + + private static final String LOG_TAG = "TextViewCompatDonut"; + private static final int LINES = 1; + + private static Field sMaximumField; + private static boolean sMaximumFieldFetched; + private static Field sMaxModeField; + private static boolean sMaxModeFieldFetched; + + private static Field sMinimumField; + private static boolean sMinimumFieldFetched; + private static Field sMinModeField; + private static boolean sMinModeFieldFetched; + + static int getMaxLines(TextView textView) { + if (!sMaxModeFieldFetched) { + sMaxModeField = retrieveField("mMaxMode"); + sMaxModeFieldFetched = true; + } + if (sMaxModeField != null && retrieveIntFromField(sMaxModeField, textView) == LINES) { + // If the max mode is using lines, we can grab the maximum value + if (!sMaximumFieldFetched) { + sMaximumField = retrieveField("mMaximum"); + sMaximumFieldFetched = true; + } + if (sMaximumField != null) { + return retrieveIntFromField(sMaximumField, textView); + } + } + return -1; + } + + static int getMinLines(TextView textView) { + if (!sMinModeFieldFetched) { + sMinModeField = retrieveField("mMinMode"); + sMinModeFieldFetched = true; + } + if (sMinModeField != null && retrieveIntFromField(sMinModeField, textView) == LINES) { + // If the min mode is using lines, we can grab the maximum value + if (!sMinimumFieldFetched) { + sMinimumField = retrieveField("mMinimum"); + sMinimumFieldFetched = true; + } + if (sMinimumField != null) { + return retrieveIntFromField(sMinimumField, textView); + } + } + return -1; + } + + private static Field retrieveField(String fieldName) { + Field field = null; + try { + field = TextView.class.getDeclaredField(fieldName); + field.setAccessible(true); + } catch (NoSuchFieldException e) { + Log.e(LOG_TAG, "Could not retrieve " + fieldName + " field."); + } + return field; + } + + private static int retrieveIntFromField(Field field, TextView textView) { + try { + return field.getInt(textView); + } catch (IllegalAccessException e) { + Log.d(LOG_TAG, "Could not retrieve value of " + field.getName() + " field."); + } + return -1; + } + + static void setTextAppearance(TextView textView, int resId) { + textView.setTextAppearance(textView.getContext(), resId); + } +} diff --git a/v4/jellybean-mr1/android/support/v4/text/TextUtilsCompatJellybeanMr1.java b/v4/jellybean-mr1/android/support/v4/text/TextUtilsCompatJellybeanMr1.java index fcdf8b5675..d5b675b4d6 100644 --- a/v4/jellybean-mr1/android/support/v4/text/TextUtilsCompatJellybeanMr1.java +++ b/v4/jellybean-mr1/android/support/v4/text/TextUtilsCompatJellybeanMr1.java @@ -25,7 +25,7 @@ import java.util.Locale; /** * Jellybean MR1 - specific TextUtils API access. */ -public class TextUtilsCompatJellybeanMr1 { +class TextUtilsCompatJellybeanMr1 { @NonNull public static String htmlEncode(@NonNull String s) { return TextUtils.htmlEncode(s); diff --git a/v4/jellybean-mr2/android/support/v4/media/TransportMediatorJellybeanMR2.java b/v4/jellybean-mr2/android/support/v4/media/TransportMediatorJellybeanMR2.java index 38b6778f23..be3555e428 100644 --- a/v4/jellybean-mr2/android/support/v4/media/TransportMediatorJellybeanMR2.java +++ b/v4/jellybean-mr2/android/support/v4/media/TransportMediatorJellybeanMR2.java @@ -28,9 +28,7 @@ import android.view.KeyEvent; import android.view.View; import android.view.ViewTreeObserver; -class TransportMediatorJellybeanMR2 - implements RemoteControlClient.OnGetPlaybackPositionListener, - RemoteControlClient.OnPlaybackPositionUpdateListener { +class TransportMediatorJellybeanMR2 { final Context mContext; final AudioManager mAudioManager; final View mTargetView; @@ -75,7 +73,20 @@ class TransportMediatorJellybeanMR2 mTransportCallback.handleAudioFocusChange(focusChange); } }; - + final RemoteControlClient.OnGetPlaybackPositionListener mGetPlaybackPositionListener + = new RemoteControlClient.OnGetPlaybackPositionListener() { + @Override + public long onGetPlaybackPosition() { + return mTransportCallback.getPlaybackPosition(); + } + }; + final RemoteControlClient.OnPlaybackPositionUpdateListener mPlaybackPositionUpdateListener + = new RemoteControlClient.OnPlaybackPositionUpdateListener() { + public void onPlaybackPositionUpdate(long newPositionMs) { + mTransportCallback.playbackPositionUpdate(newPositionMs); + } + }; + PendingIntent mPendingIntent; RemoteControlClient mRemoteControl; boolean mFocused; @@ -112,8 +123,8 @@ class TransportMediatorJellybeanMR2 mPendingIntent = PendingIntent.getBroadcast(mContext, 0, mIntent, PendingIntent.FLAG_CANCEL_CURRENT); mRemoteControl = new RemoteControlClient(mPendingIntent); - mRemoteControl.setOnGetPlaybackPositionListener(this); - mRemoteControl.setPlaybackPositionUpdateListener(this); + mRemoteControl.setOnGetPlaybackPositionListener(mGetPlaybackPositionListener); + mRemoteControl.setPlaybackPositionUpdateListener(mPlaybackPositionUpdateListener); } void gainFocus() { @@ -145,16 +156,6 @@ class TransportMediatorJellybeanMR2 } } - @Override - public long onGetPlaybackPosition() { - return mTransportCallback.getPlaybackPosition(); - } - - @Override - public void onPlaybackPositionUpdate(long newPositionMs) { - mTransportCallback.playbackPositionUpdate(newPositionMs); - } - public void refreshState(boolean playing, long position, int transportControls) { if (mRemoteControl != null) { mRemoteControl.setPlaybackState(playing ? RemoteControlClient.PLAYSTATE_PLAYING diff --git a/v4/jellybean-mr2/android/support/v4/media/session/MediaSessionCompatApi18.java b/v4/jellybean-mr2/android/support/v4/media/session/MediaSessionCompatApi18.java index 1c5dca78ed..8ceb38c226 100644 --- a/v4/jellybean-mr2/android/support/v4/media/session/MediaSessionCompatApi18.java +++ b/v4/jellybean-mr2/android/support/v4/media/session/MediaSessionCompatApi18.java @@ -16,28 +16,56 @@ package android.support.v4.media.session; import android.app.PendingIntent; +import android.content.ComponentName; import android.content.Context; import android.media.AudioManager; import android.media.RemoteControlClient; +import android.util.Log; import android.os.SystemClock; -public class MediaSessionCompatApi18 { +class MediaSessionCompatApi18 { + private static final String TAG = "MediaSessionCompatApi18"; + /***** PlaybackState actions *****/ private static final long ACTION_SEEK_TO = 1 << 8; + private static boolean sIsMbrPendingIntentSupported = true; + public static Object createPlaybackPositionUpdateListener( MediaSessionCompatApi14.Callback callback) { return new OnPlaybackPositionUpdateListener<MediaSessionCompatApi14.Callback>(callback); } - public static void registerMediaButtonEventReceiver(Context context, PendingIntent pi) { + public static void registerMediaButtonEventReceiver(Context context, PendingIntent pi, + ComponentName cn) { AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); - am.registerMediaButtonEventReceiver(pi); + + // Some Android implementations are not able to register a media button event receiver + // using a PendingIntent but need a ComponentName instead. These will raise a + // NullPointerException. + if (sIsMbrPendingIntentSupported) { + try { + am.registerMediaButtonEventReceiver(pi); + } catch (NullPointerException e) { + Log.w(TAG, "Unable to register media button event receiver with " + + "PendingIntent, falling back to ComponentName."); + sIsMbrPendingIntentSupported = false; + } + } + + if (!sIsMbrPendingIntentSupported) { + am.registerMediaButtonEventReceiver(cn); + } } - public static void unregisterMediaButtonEventReceiver(Context context, PendingIntent pi) { + public static void unregisterMediaButtonEventReceiver(Context context, PendingIntent pi, + ComponentName cn) { AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); - am.unregisterMediaButtonEventReceiver(pi); + if (sIsMbrPendingIntentSupported) { + am.unregisterMediaButtonEventReceiver(pi); + } else { + am.unregisterMediaButtonEventReceiver(cn); + } } public static void setState(Object rccObj, int state, long position, float speed, diff --git a/v4/jellybean/android/support/v4/widget/TextViewCompatJb.java b/v4/jellybean/android/support/v4/widget/TextViewCompatJb.java new file mode 100644 index 0000000000..1658874c68 --- /dev/null +++ b/v4/jellybean/android/support/v4/widget/TextViewCompatJb.java @@ -0,0 +1,31 @@ +/* + * 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.v4.widget; + +import android.graphics.drawable.Drawable; +import android.view.View; +import android.widget.TextView; + +class TextViewCompatJb { + static int getMaxLines(TextView textView) { + return textView.getMaxLines(); + } + + static int getMinLines(TextView textView) { + return textView.getMinLines(); + } +} diff --git a/v4/kitkat/android/support/v4/media/session/MediaSessionCompatApi19.java b/v4/kitkat/android/support/v4/media/session/MediaSessionCompatApi19.java index 61039d37fc..f3c19e292e 100644 --- a/v4/kitkat/android/support/v4/media/session/MediaSessionCompatApi19.java +++ b/v4/kitkat/android/support/v4/media/session/MediaSessionCompatApi19.java @@ -15,14 +15,13 @@ */ package android.support.v4.media.session; -import android.graphics.Bitmap; import android.media.MediaMetadataEditor; import android.media.MediaMetadataRetriever; import android.media.Rating; import android.media.RemoteControlClient; import android.os.Bundle; -public class MediaSessionCompatApi19 { +class MediaSessionCompatApi19 { /***** PlaybackState actions *****/ private static final long ACTION_SET_RATING = 1 << 7; diff --git a/v4/kitkat/android/support/v4/view/ViewPropertyAnimatorCompatKK.java b/v4/kitkat/android/support/v4/view/ViewPropertyAnimatorCompatKK.java index 5439972d65..64f1969dad 100644 --- a/v4/kitkat/android/support/v4/view/ViewPropertyAnimatorCompatKK.java +++ b/v4/kitkat/android/support/v4/view/ViewPropertyAnimatorCompatKK.java @@ -22,12 +22,16 @@ class ViewPropertyAnimatorCompatKK { public static void setUpdateListener(final View view, final ViewPropertyAnimatorUpdateListener listener) { - view.animate().setUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator valueAnimator) { - listener.onAnimationUpdate(view); - } - }); + ValueAnimator.AnimatorUpdateListener wrapped = null; + if (listener != null) { + wrapped = new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator valueAnimator) { + listener.onAnimationUpdate(view); + } + }; + } + view.animate().setUpdateListener(wrapped); } } diff --git a/v4/tests/AndroidManifest.xml b/v4/tests/AndroidManifest.xml new file mode 100644 index 0000000000..556c885344 --- /dev/null +++ b/v4/tests/AndroidManifest.xml @@ -0,0 +1,32 @@ +<?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. + --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="android.support.v4.test"> + <uses-sdk android:minSdkVersion="4" tools:overrideLibrary="android.support.test, + android.support.test.espresso, android.support.test.espresso.idling"/> + + <application> + <uses-library android:name="android.test.runner" /> + <activity android:name="android.support.v4.widget.test.TextViewTestActivity"/> + <activity android:name="android.support.v4.widget.TestActivity"/> + </application> + + <instrumentation android:name="android.test.InstrumentationTestRunner" + android:targetPackage="android.support.v4.test" + /> +</manifest> diff --git a/v4/tests/java/android/support/v4/graphics/ColorUtilsTest.java b/v4/tests/java/android/support/v4/graphics/ColorUtilsTest.java index 855cdba3a5..56cb6fbf43 100644 --- a/v4/tests/java/android/support/v4/graphics/ColorUtilsTest.java +++ b/v4/tests/java/android/support/v4/graphics/ColorUtilsTest.java @@ -17,9 +17,9 @@ package android.support.v4.graphics; import android.graphics.Color; -import android.support.v4.graphics.ColorUtils; import android.test.AndroidTestCase; +import java.lang.Integer; import java.util.ArrayList; /** @@ -31,21 +31,31 @@ public class ColorUtilsTest extends AndroidTestCase { private static final float ALLOWED_OFFSET_HUE = 360 * 0.005f; private static final float ALLOWED_OFFSET_SATURATION = 0.005f; private static final float ALLOWED_OFFSET_LIGHTNESS = 0.005f; + private static final float ALLOWED_OFFSET_MIN_ALPHA = 0.01f; private static final int ALLOWED_OFFSET_RGB_COMPONENT = 2; private static final ArrayList<TestEntry> sEntryList = new ArrayList<>(); static { - sEntryList.add(new TestEntry(Color.BLACK).setHsl(0f, 0f, 0f)); - sEntryList.add(new TestEntry(Color.WHITE).setHsl(0f, 0f, 1f)); - sEntryList.add(new TestEntry(Color.BLUE).setHsl(240f, 1f, 0.5f)); - sEntryList.add(new TestEntry(Color.GREEN).setHsl(120f, 1f, 0.5f)); - sEntryList.add(new TestEntry(Color.RED).setHsl(0f, 1f, 0.5f)); - sEntryList.add(new TestEntry(Color.CYAN).setHsl(180f, 1f, 0.5f)); - sEntryList.add(new TestEntry(0x2196F3).setHsl(207f, 0.9f, 0.54f)); - sEntryList.add(new TestEntry(0xD1C4E9).setHsl(261f, 0.46f, 0.84f)); - sEntryList.add(new TestEntry(0x311B92).setHsl(251.09f, 0.687f, 0.339f)); + sEntryList.add(new TestEntry(Color.BLACK).setHsl(0f, 0f, 0f) + .setWhiteMinAlpha30(0.35f).setWhiteMinAlpha45(0.46f)); + sEntryList.add(new TestEntry(Color.WHITE).setHsl(0f, 0f, 1f) + .setBlackMinAlpha30(0.42f).setBlackMinAlpha45(0.54f)); + sEntryList.add(new TestEntry(Color.BLUE).setHsl(240f, 1f, 0.5f) + .setWhiteMinAlpha30(0.55f).setWhiteMinAlpha45(0.71f)); + sEntryList.add(new TestEntry(Color.GREEN).setHsl(120f, 1f, 0.5f) + .setBlackMinAlpha30(0.43f).setBlackMinAlpha45(0.55f)); + sEntryList.add(new TestEntry(Color.RED).setHsl(0f, 1f, 0.5f) + .setWhiteMinAlpha30(0.84f).setBlackMinAlpha30(0.55f).setBlackMinAlpha45(0.78f)); + sEntryList.add(new TestEntry(Color.CYAN).setHsl(180f, 1f, 0.5f) + .setBlackMinAlpha30(0.43f).setBlackMinAlpha45(0.55f)); + sEntryList.add(new TestEntry(0xFF2196F3).setHsl(207f, 0.9f, 0.54f) + .setBlackMinAlpha30(0.52f).setWhiteMinAlpha30(0.97f).setBlackMinAlpha45(0.7f)); + sEntryList.add(new TestEntry(0xFFD1C4E9).setHsl(261f, 0.46f, 0.84f) + .setBlackMinAlpha30(0.45f).setBlackMinAlpha45(0.58f)); + sEntryList.add(new TestEntry(0xFF311B92).setHsl(251.09f, 0.687f, 0.339f) + .setWhiteMinAlpha30(0.39f).setWhiteMinAlpha45(0.54f)); } public void testToHSL() { @@ -72,6 +82,28 @@ public class ColorUtilsTest extends AndroidTestCase { } } + public void testMinAlphas() { + for (TestEntry entry : sEntryList) { + testMinAlpha("Black title", entry.rgb, entry.blackMinAlpha30, + ColorUtils.calculateMinimumAlpha(Color.BLACK, entry.rgb, 3.0f)); + testMinAlpha("Black body", entry.rgb, entry.blackMinAlpha45, + ColorUtils.calculateMinimumAlpha(Color.BLACK, entry.rgb, 4.5f)); + testMinAlpha("White title", entry.rgb, entry.whiteMinAlpha30, + ColorUtils.calculateMinimumAlpha(Color.WHITE, entry.rgb, 3.0f)); + testMinAlpha("White body", entry.rgb, entry.whiteMinAlpha45, + ColorUtils.calculateMinimumAlpha(Color.WHITE, entry.rgb, 4.5f)); + } + } + + private static void testMinAlpha(String title, int color, float expected, int actual) { + final String message = title + " text within error for #" + Integer.toHexString(color); + if (expected < 0) { + assertEquals(message, actual, -1); + } else { + assertClose(message, expected, actual / 255f, ALLOWED_OFFSET_MIN_ALPHA); + } + } + private static void assertClose(String message, float expected, float actual, float allowedOffset) { StringBuilder sb = new StringBuilder(message); @@ -114,6 +146,10 @@ public class ColorUtilsTest extends AndroidTestCase { private static class TestEntry { final int rgb; final float[] hsl = new float[3]; + float blackMinAlpha45 = -1; + float blackMinAlpha30 = -1; + float whiteMinAlpha45 = -1; + float whiteMinAlpha30 = -1; TestEntry(int rgb) { this.rgb = rgb; @@ -125,5 +161,25 @@ public class ColorUtilsTest extends AndroidTestCase { hsl[2] = l; return this; } + + TestEntry setBlackMinAlpha30(float minAlpha) { + blackMinAlpha30 = minAlpha; + return this; + } + + TestEntry setBlackMinAlpha45(float minAlpha) { + blackMinAlpha45 = minAlpha; + return this; + } + + TestEntry setWhiteMinAlpha30(float minAlpha) { + whiteMinAlpha30 = minAlpha; + return this; + } + + TestEntry setWhiteMinAlpha45(float minAlpha) { + whiteMinAlpha45 = minAlpha; + return this; + } } }
\ No newline at end of file diff --git a/v4/tests/java/android/support/v4/widget/TestActivity.java b/v4/tests/java/android/support/v4/widget/TestActivity.java new file mode 100644 index 0000000000..9ab5188db1 --- /dev/null +++ b/v4/tests/java/android/support/v4/widget/TestActivity.java @@ -0,0 +1,35 @@ +/* + * 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.v4.widget; + +import android.app.Activity; +import android.os.Bundle; +import android.view.WindowManager; +import android.widget.FrameLayout; + +public class TestActivity extends Activity { + FrameLayout mContainer; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mContainer = new FrameLayout(this); + + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + setContentView(mContainer); + } +} diff --git a/v4/tests/java/android/support/v4/widget/TextViewCompatTest.java b/v4/tests/java/android/support/v4/widget/TextViewCompatTest.java new file mode 100644 index 0000000000..a7d233909c --- /dev/null +++ b/v4/tests/java/android/support/v4/widget/TextViewCompatTest.java @@ -0,0 +1,79 @@ +/* + * 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.v4.widget; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import android.app.Instrumentation; +import android.test.ActivityInstrumentationTestCase2; +import android.support.test.InstrumentationRegistry; +import android.support.v4.widget.TextViewCompat; +import android.util.Log; +import android.widget.TextView; + +import android.support.test.runner.AndroidJUnit4; + +@RunWith(AndroidJUnit4.class) +public class TextViewCompatTest extends ActivityInstrumentationTestCase2<TestActivity> { + private boolean mDebug; + + Throwable mainThreadException; + + Thread mInstrumentationThread; + + public TextViewCompatTest() { + super("android.support.v4.widget", TestActivity.class); + mDebug = false; + } + + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + mInstrumentationThread = Thread.currentThread(); + + // Note that injectInstrumentation was added in v5. Since this is v4 we have to use + // the misspelled (and deprecated) inject API. + injectInsrumentation(InstrumentationRegistry.getInstrumentation()); + } + + @After + @Override + public void tearDown() throws Exception { + getInstrumentation().waitForIdleSync(); + super.tearDown(); + } + + @Test + public void testMaxLines() throws Throwable { + final TextView textView = new TextView(getActivity()); + textView.setMaxLines(4); + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + getActivity().mContainer.addView(textView); + } + }); + + assertEquals("Max lines must match", TextViewCompat.getMaxLines(textView), 4); + } +} diff --git a/v4/tests/java/android/support/v4/widget/test/TextViewTestActivity.java b/v4/tests/java/android/support/v4/widget/test/TextViewTestActivity.java new file mode 100644 index 0000000000..7366127815 --- /dev/null +++ b/v4/tests/java/android/support/v4/widget/test/TextViewTestActivity.java @@ -0,0 +1,24 @@ +/* + * 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.v4.widget.test; + + +import android.app.Activity; + +public class TextViewTestActivity extends Activity { + +} |
