aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJorge Ruesga <jorge@ruesga.com>2013-11-03 20:27:42 +0100
committerJorge Ruesga <jorge@ruesga.com>2013-11-03 20:27:42 +0100
commitcfa23b580d7d50eb4c1e739f590bedd6b0a84bd8 (patch)
tree8aeb1f472ba965913d0f06ec203db10e3515fd00
parentcf54b6212fdfd3bc0a0a1cab3b2d8415e07b382b (diff)
downloadandroid_packages_wallpapers_PhotoPhase-cfa23b580d7d50eb4c1e739f590bedd6b0a84bd8.tar.gz
android_packages_wallpapers_PhotoPhase-cfa23b580d7d50eb4c1e739f590bedd6b0a84bd8.tar.bz2
android_packages_wallpapers_PhotoPhase-cfa23b580d7d50eb4c1e739f590bedd6b0a84bd8.zip
Dispositions templates
Signed-off-by: Jorge Ruesga <jorge@ruesga.com>
-rw-r--r--res/layout/disposition_view.xml21
-rw-r--r--src/afzkl/development/mColorPicker/drawables/AlphaPatternDrawable.java129
-rw-r--r--src/afzkl/development/mColorPicker/views/ColorDialogView.java517
-rw-r--r--src/afzkl/development/mColorPicker/views/ColorPanelView.java174
-rw-r--r--src/afzkl/development/mColorPicker/views/ColorPickerView.java998
-rw-r--r--src/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompat.java452
-rw-r--r--src/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatIcs.java49
-rw-r--r--src/android/support/v4/os/ParcelableCompat.java59
-rw-r--r--src/android/support/v4/os/ParcelableCompatCreatorCallbacks.java47
-rw-r--r--src/android/support/v4/os/ParcelableCompatHoneycombMR2.java49
-rw-r--r--src/android/support/v4/view/AccessibilityDelegateCompat.java433
-rw-r--r--src/android/support/v4/view/AccessibilityDelegateCompatIcs.java119
-rw-r--r--src/android/support/v4/view/KeyEventCompat.java215
-rw-r--r--src/android/support/v4/view/KeyEventCompatEclair.java40
-rw-r--r--src/android/support/v4/view/KeyEventCompatHoneycomb.java36
-rw-r--r--src/android/support/v4/view/MotionEventCompat.java238
-rw-r--r--src/android/support/v4/view/MotionEventCompatEclair.java40
-rw-r--r--src/android/support/v4/view/PagerAdapter.java340
-rw-r--r--src/android/support/v4/view/VelocityTrackerCompat.java93
-rw-r--r--src/android/support/v4/view/VelocityTrackerCompatHoneycomb.java31
-rw-r--r--src/android/support/v4/view/ViewCompat.java1112
-rw-r--r--src/android/support/v4/view/ViewCompatEclairMr1.java26
-rw-r--r--src/android/support/v4/view/ViewCompatGingerbread.java29
-rw-r--r--src/android/support/v4/view/ViewCompatHC.java55
-rw-r--r--src/android/support/v4/view/ViewCompatICS.java52
-rw-r--r--src/android/support/v4/view/ViewConfigurationCompat.java75
-rw-r--r--src/android/support/v4/view/ViewConfigurationCompatFroyo.java28
-rw-r--r--src/android/support/v4/view/ViewPager.java2908
-rw-r--r--src/android/support/v4/view/accessibility/AccessibilityEventCompat.java238
-rw-r--r--src/android/support/v4/view/accessibility/AccessibilityEventCompatIcs.java38
-rw-r--r--src/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java1960
-rw-r--r--src/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatIcs.java219
-rw-r--r--src/android/support/v4/view/accessibility/AccessibilityNodeProviderCompat.java153
-rw-r--r--src/android/support/v4/view/accessibility/AccessibilityRecordCompat.java1083
-rw-r--r--src/android/support/v4/view/accessibility/AccessibilityRecordCompatIcs.java193
-rw-r--r--src/android/support/v4/view/accessibility/AccessibilityRecordCompatIcsMr1.java40
-rw-r--r--src/android/support/v4/widget/EdgeEffectCompat.java238
-rw-r--r--src/android/support/v4/widget/EdgeEffectCompatIcs.java64
-rw-r--r--src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseRenderer.java3
-rw-r--r--src/org/cyanogenmod/wallpapers/photophase/adapters/DispositionAdapter.java128
-rw-r--r--src/org/cyanogenmod/wallpapers/photophase/model/Dispositions.java72
-rw-r--r--src/org/cyanogenmod/wallpapers/photophase/preferences/DispositionFragment.java190
-rw-r--r--src/org/cyanogenmod/wallpapers/photophase/preferences/LandscapeDispositionFragment.java9
-rw-r--r--src/org/cyanogenmod/wallpapers/photophase/preferences/PortraitDispositionFragment.java9
-rw-r--r--src/org/cyanogenmod/wallpapers/photophase/widgets/DispositionView.java56
45 files changed, 13004 insertions, 54 deletions
diff --git a/res/layout/disposition_view.xml b/res/layout/disposition_view.xml
new file mode 100644
index 0000000..a26b9a0
--- /dev/null
+++ b/res/layout/disposition_view.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2013 The CyanogenMod 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.
+-->
+<org.cyanogenmod.wallpapers.photophase.widgets.DispositionView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="@dimen/disposition_frame_margin" /> \ No newline at end of file
diff --git a/src/afzkl/development/mColorPicker/drawables/AlphaPatternDrawable.java b/src/afzkl/development/mColorPicker/drawables/AlphaPatternDrawable.java
new file mode 100644
index 0000000..e878756
--- /dev/null
+++ b/src/afzkl/development/mColorPicker/drawables/AlphaPatternDrawable.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2010 Daniel Nilsson
+ *
+ * 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 afzkl.development.mColorPicker.drawables;
+
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+
+/**
+ * This drawable that draws a simple white and gray chessboard pattern.
+ * It's pattern you will often see as a background behind a
+ * partly transparent image in many applications.
+ * @author Daniel Nilsson
+ */
+@SuppressWarnings("all")
+public class AlphaPatternDrawable extends Drawable {
+
+ private int mRectangleSize = 10;
+
+ private final Paint mPaint = new Paint();
+ private final Paint mPaintWhite = new Paint();
+ private final Paint mPaintGray = new Paint();
+
+ private int numRectanglesHorizontal;
+ private int numRectanglesVertical;
+
+ /**
+ * Bitmap in which the pattern will be cahched.
+ */
+ private Bitmap mBitmap;
+
+ public AlphaPatternDrawable(int rectangleSize) {
+ mRectangleSize = rectangleSize;
+ mPaintWhite.setColor(0xffffffff);
+ mPaintGray.setColor(0xffcbcbcb);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ canvas.drawBitmap(mBitmap, null, getBounds(), mPaint);
+ }
+
+ @Override
+ public int getOpacity() {
+ return 0;
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ throw new UnsupportedOperationException("Alpha is not supported by this drawwable.");
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ throw new UnsupportedOperationException("ColorFilter is not supported by this drawwable.");
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+
+ int height = bounds.height();
+ int width = bounds.width();
+
+ numRectanglesHorizontal = (int) Math.ceil((width / mRectangleSize));
+ numRectanglesVertical = (int) Math.ceil(height / mRectangleSize);
+
+ generatePatternBitmap();
+
+ }
+
+ /**
+ * This will generate a bitmap with the pattern
+ * as big as the rectangle we were allow to draw on.
+ * We do this to chache the bitmap so we don't need to
+ * recreate it each time draw() is called since it
+ * takes a few milliseconds.
+ */
+ private void generatePatternBitmap() {
+
+ if (getBounds().width() <= 0 || getBounds().height() <= 0) {
+ return;
+ }
+
+ mBitmap = Bitmap.createBitmap(getBounds().width(), getBounds().height(), Config.ARGB_8888);
+ Canvas canvas = new Canvas(mBitmap);
+
+ Rect r = new Rect();
+ boolean verticalStartWhite = true;
+ for (int i = 0; i <= numRectanglesVertical; i++) {
+
+ boolean isWhite = verticalStartWhite;
+ for (int j = 0; j <= numRectanglesHorizontal; j++) {
+
+ r.top = i * mRectangleSize;
+ r.left = j * mRectangleSize;
+ r.bottom = r.top + mRectangleSize;
+ r.right = r.left + mRectangleSize;
+
+ canvas.drawRect(r, isWhite ? mPaintWhite : mPaintGray);
+
+ isWhite = !isWhite;
+ }
+
+ verticalStartWhite = !verticalStartWhite;
+
+ }
+
+ }
+
+}
diff --git a/src/afzkl/development/mColorPicker/views/ColorDialogView.java b/src/afzkl/development/mColorPicker/views/ColorDialogView.java
new file mode 100644
index 0000000..ad0b9cf
--- /dev/null
+++ b/src/afzkl/development/mColorPicker/views/ColorDialogView.java
@@ -0,0 +1,517 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod 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 afzkl.development.mColorPicker.views;
+
+import afzkl.development.mColorPicker.views.ColorPickerView.OnColorChangedListener;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.text.Editable;
+import android.text.InputFilter;
+import android.text.InputType;
+import android.text.Spanned;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import java.util.Locale;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * A view use directly into a dialog. It contains a one {@link ColorPickerView}
+ * and two {@link ColorPanelView} (the current color and the new color)
+ */
+public class ColorDialogView extends RelativeLayout
+ implements OnColorChangedListener, TextWatcher {
+
+ private static final int DEFAULT_MARGIN_DP = 16;
+ private static final int DEFAULT_PANEL_HEIGHT_DP = 32;
+ private static final int DEFAULT_TEXT_SIZE_SP = 12;
+ private static final int DEFAULT_LABEL_TEXT_SIZE_SP = 18;
+
+ private ColorPickerView mPickerView;
+ private ColorPanelView mCurrentColorView;
+ private ColorPanelView mNewColorView;
+ private TextView tvCurrent;
+ private TextView tvNew;
+ private TextView tvColorLabel;
+ private EditText etColor;
+
+ private String mCurrentLabelText = "Current:"; //$NON-NLS-1$
+ private String mNewLabelText = "New:"; //$NON-NLS-1$
+
+ private String mColorLabelText = "Color:"; //$NON-NLS-1$
+
+ private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
+
+ /**
+ * Constructor of <code>ColorDialogView</code>
+ *
+ * @param context The current context
+ */
+ public ColorDialogView(Context context) {
+ this(context, null);
+ }
+
+ /**
+ * Constructor of <code>ColorDialogView</code>
+ *
+ * @param context The current context
+ * @param attrs The attributes of the XML tag that is inflating the view.
+ */
+ public ColorDialogView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ /**
+ * Constructor of <code>ColorDialogView</code>
+ *
+ * @param context The current context
+ * @param attrs The attributes of the XML tag that is inflating the view.
+ * @param defStyle The default style to apply to this view. If 0, no style
+ * will be applied (beyond what is included in the theme). This may
+ * either be an attribute resource, whose value will be retrieved
+ * from the current theme, or an explicit style resource.
+ */
+ public ColorDialogView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init();
+ }
+
+ /**
+ * Method that initializes the view. This method loads all the necessary
+ * information and create an appropriate layout for the view
+ */
+ private void init() {
+ // To fight color branding.
+ ((Activity)getContext()).getWindow().setFormat(PixelFormat.RGBA_8888);
+
+ // Create the scrollview over the dialog
+ final int dlgMarging = (int)convertDpToPixel(DEFAULT_MARGIN_DP);
+ ScrollView sv = new ScrollView(getContext());
+ sv.setId(internalGenerateViewId());
+ RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT,
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
+ lp.setMargins(dlgMarging, 0, dlgMarging, 0);
+ sv.setLayoutParams(lp);
+ sv.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_INSET);
+
+ // Now the vertical layout
+ LinearLayout ll = new LinearLayout(getContext());
+ ll.setId(internalGenerateViewId());
+ lp = new RelativeLayout.LayoutParams(
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT,
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT);
+ ll.setLayoutParams(lp);
+ ll.setOrientation(LinearLayout.VERTICAL);
+ sv.addView(ll);
+
+ // Creates the color input field
+ int id = createColorInput(ll);
+
+ // Creates the color picker
+ id = createColorPicker(ll, id);
+
+ // Creates the current color and new color panels
+ id = createColorsPanel(ll, id);
+
+ // Add the scrollview
+ addView(sv);
+
+ // Sets the input color
+ this.etColor.setText(toHex(this.mNewColorView.getColor()));
+ }
+
+ /**
+ * Method that creates the color input
+ *
+ * @param parent The parent layout
+ */
+ private int createColorInput(ViewGroup parent) {
+ final int dlgMarging = (int)convertDpToPixel(DEFAULT_MARGIN_DP);
+ LinearLayout.LayoutParams lp2 = new LinearLayout.LayoutParams(
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT);
+ lp2.setMargins(0, 0, dlgMarging, 0);
+ this.tvColorLabel = new TextView(getContext());
+ this.tvColorLabel.setText(this.mColorLabelText);
+ this.tvColorLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, DEFAULT_LABEL_TEXT_SIZE_SP);
+ this.tvColorLabel.setGravity(Gravity.BOTTOM | Gravity.LEFT);
+ this.tvColorLabel.setLayoutParams(lp2);
+
+ lp2 = new LinearLayout.LayoutParams(
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
+ this.etColor = new EditText(getContext());
+ this.etColor.setSingleLine();
+ this.etColor.setGravity(Gravity.TOP | Gravity.LEFT);
+ this.etColor.setCursorVisible(true);
+ this.etColor.setImeOptions(
+ EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_FULLSCREEN);
+ this.etColor.setInputType(
+ InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
+ this.etColor.setLayoutParams(lp2);
+ InputFilter[] filters = new InputFilter[2];
+ filters[0] = new InputFilter.LengthFilter(8);
+ filters[1] = new InputFilter() {
+ @Override
+ public CharSequence filter(CharSequence source, int start,
+ int end, Spanned dest, int dstart, int dend) {
+ if (start >= end) return ""; //$NON-NLS-1$
+ String s = source.subSequence(start, end).toString();
+ StringBuilder sb = new StringBuilder();
+ int cc = s.length();
+ for (int i = 0; i < cc; i++) {
+ char c = s.charAt(i);
+ if ((c >= '0' && c <= '9') ||
+ (c >= 'a' && c <= 'f') ||
+ (c >= 'A' && c <= 'F')) {
+ sb.append(c);
+ }
+ }
+ return sb.toString().toUpperCase(Locale.getDefault());
+ }
+ };
+ this.etColor.setFilters(filters);
+ this.etColor.addTextChangedListener(this);
+
+ LinearLayout ll1 = new LinearLayout(getContext());
+ ll1.setId(internalGenerateViewId());
+ RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT,
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
+ lp.setMargins(dlgMarging, 0, dlgMarging, 0);
+ lp.addRule(RelativeLayout.ALIGN_PARENT_TOP);
+ ll1.setLayoutParams(lp);
+ ll1.addView(this.tvColorLabel);
+ ll1.addView(this.etColor);
+ parent.addView(ll1);
+
+ return ll1.getId();
+ }
+
+ /**
+ * Method that creates the color picker
+ *
+ * @param parent The parent layout
+ * @param belowOf The anchor view
+ * @return id The layout id
+ */
+ private int createColorPicker(ViewGroup parent, int belowOf) {
+ final int dlgMarging = (int)convertDpToPixel(DEFAULT_MARGIN_DP);
+ this.mPickerView = new ColorPickerView(getContext());
+ this.mPickerView.setId(internalGenerateViewId());
+ this.mPickerView.setOnColorChangedListener(this);
+ RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT,
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
+ lp.setMargins(dlgMarging, 0, dlgMarging, 0);
+ lp.addRule(RelativeLayout.BELOW, belowOf);
+ this.mPickerView.setLayoutParams(lp);
+ parent.addView(this.mPickerView);
+ return this.mPickerView.getId();
+ }
+
+ /**
+ * Method that creates the colors panel (current and new)
+ *
+ * @param parent The parent layout
+ * @param belowOf The anchor view
+ * @return id The layout id
+ */
+ private int createColorsPanel(ViewGroup parent, int belowOf) {
+ final int dlgMarging = (int)convertDpToPixel(DEFAULT_MARGIN_DP);
+ final int panelHeight = (int)convertDpToPixel(DEFAULT_PANEL_HEIGHT_DP);
+ LinearLayout.LayoutParams lp2 = new LinearLayout.LayoutParams(
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT,
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT,
+ 1);
+
+ // Titles
+ this.tvCurrent = new TextView(getContext());
+ lp2 = new LinearLayout.LayoutParams(
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT,
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
+ 1);
+ this.tvCurrent.setLayoutParams(lp2);
+ this.tvCurrent.setText(this.mCurrentLabelText);
+ this.tvCurrent.setTextSize(TypedValue.COMPLEX_UNIT_SP, DEFAULT_TEXT_SIZE_SP);
+ this.tvNew = new TextView(getContext());
+ this.tvNew.setLayoutParams(lp2);
+ this.tvNew.setText(this.mNewLabelText);
+ this.tvNew.setTextSize(TypedValue.COMPLEX_UNIT_SP, DEFAULT_TEXT_SIZE_SP);
+ TextView sep1 = new TextView(getContext());
+ lp2 = new LinearLayout.LayoutParams(
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
+ 0);
+ lp2.setMargins(dlgMarging, 0, dlgMarging, 0);
+ sep1.setLayoutParams(lp2);
+ sep1.setText(" "); //$NON-NLS-1$
+ sep1.setTextSize(TypedValue.COMPLEX_UNIT_SP, DEFAULT_TEXT_SIZE_SP);
+
+ LinearLayout ll1 = new LinearLayout(getContext());
+ ll1.setId(internalGenerateViewId());
+ RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT,
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
+ lp.setMargins(dlgMarging, 0, dlgMarging, dlgMarging/2);
+ lp.addRule(RelativeLayout.BELOW, belowOf);
+ ll1.setLayoutParams(lp);
+ ll1.addView(this.tvCurrent);
+ ll1.addView(sep1);
+ ll1.addView(this.tvNew);
+ parent.addView(ll1);
+
+ // Color panels
+ lp2 = new LinearLayout.LayoutParams(
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT,
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
+ 1);
+ this.mCurrentColorView = new ColorPanelView(getContext());
+ this.mCurrentColorView.setLayoutParams(lp2);
+ this.mNewColorView = new ColorPanelView(getContext());
+ this.mNewColorView.setLayoutParams(lp2);
+ TextView sep2 = new TextView(getContext());
+ lp2 = new LinearLayout.LayoutParams(
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
+ 0);
+ lp2.setMargins(dlgMarging, 0, dlgMarging, 0);
+ sep2.setLayoutParams(lp2);
+ sep2.setText("-"); //$NON-NLS-1$
+ sep2.setTextSize(TypedValue.COMPLEX_UNIT_SP, DEFAULT_TEXT_SIZE_SP);
+
+ LinearLayout ll2 = new LinearLayout(getContext());
+ ll2.setId(internalGenerateViewId());
+ lp = new RelativeLayout.LayoutParams(
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT, panelHeight);
+ lp.setMargins(dlgMarging, 0, dlgMarging, dlgMarging/2);
+ lp.addRule(RelativeLayout.BELOW, ll1.getId());
+ ll2.setLayoutParams(lp);
+ ll2.addView(this.mCurrentColorView);
+ ll2.addView(sep2);
+ ll2.addView(this.mNewColorView);
+ parent.addView(ll2);
+
+ return ll2.getId();
+ }
+
+ /**
+ * Method that returns the color of the picker
+ *
+ * @return The ARGB color
+ */
+ public int getColor() {
+ return this.mPickerView.getColor();
+ }
+
+ /**
+ * Method that set the color of the picker
+ *
+ * @param argb The ARGB color
+ */
+ public void setColor(int argb) {
+ setColor(argb, false);
+ }
+
+ /**
+ * Method that set the color of the picker
+ *
+ * @param argb The ARGB color
+ * @param fromEditText If the call comes from the <code>EditText</code>
+ */
+ private void setColor(int argb, boolean fromEditText) {
+ this.mPickerView.setColor(argb, false);
+ this.mCurrentColorView.setColor(argb);
+ this.mNewColorView.setColor(argb);
+ if (!fromEditText) {
+ this.etColor.setText(toHex(this.mNewColorView.getColor()));
+ }
+ }
+
+ /**
+ * Method that display/hide the alpha slider
+ *
+ * @param show If the alpha slider should be shown
+ */
+ public void showAlphaSlider(boolean show) {
+ this.mPickerView.setAlphaSliderVisible(show);
+ }
+
+ /**
+ * Set the text that should be shown in the alpha slider.
+ * Set to null to disable text.
+ *
+ * @param text Text that should be shown.
+ */
+ public void setAlphaSliderText(String text) {
+ this.mPickerView.setAlphaSliderText(text);
+ }
+
+ /**
+ * Set the text that should be shown in the actual color panel.
+ * Set to null to disable text.
+ *
+ * @param text Text that should be shown.
+ */
+ public void setCurrentColorText(String text) {
+ this.mCurrentLabelText = text;
+ this.tvCurrent.setText(this.mCurrentLabelText);
+ }
+
+ /**
+ * Set the text that should be shown in the new color panel.
+ * Set to null to disable text.
+ *
+ * @param text Text that should be shown.
+ */
+ public void setNewColorText(String text) {
+ this.mNewLabelText = text;
+ this.tvNew.setText(this.mNewLabelText);
+ }
+
+ /**
+ * Set the text that should be shown in the label of the color input.
+ * Set to null to disable text.
+ *
+ * @param text Text that should be shown.
+ */
+ public void setColorLabelText(String text) {
+ this.mColorLabelText = text;
+ this.tvColorLabel.setText(this.mColorLabelText);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onColorChanged(int color) {
+ this.mNewColorView.setColor(color);
+ this.etColor.removeTextChangedListener(this);
+ this.etColor.setText(toHex(this.mNewColorView.getColor()));
+ this.etColor.addTextChangedListener(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (s.length() == 8) {
+ try {
+ setColor(toARGB(s.toString()), true);
+ } catch (Exception e) {/**NON BLOCK**/}
+ }
+ }
+
+ /**
+ * This method converts dp unit to equivalent device specific value in pixels.
+ *
+ * @param ctx The current context
+ * @param dp A value in dp (Device independent pixels) unit
+ * @return float A float value to represent Pixels equivalent to dp according to device
+ */
+ private float convertDpToPixel(float dp) {
+ Resources resources = getContext().getResources();
+ DisplayMetrics metrics = resources.getDisplayMetrics();
+ return dp * (metrics.densityDpi / 160f);
+ }
+
+ /**
+ * Method that converts an ARGB color to its hex string color representation
+ *
+ * @param argb The ARGB color
+ * @return String The hex string representation of the color
+ */
+ private static String toHex(int argb) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(toHexString((byte)Color.alpha(argb)));
+ sb.append(toHexString((byte)Color.red(argb)));
+ sb.append(toHexString((byte)Color.green(argb)));
+ sb.append(toHexString((byte)Color.blue(argb)));
+ return sb.toString();
+ }
+
+ /**
+ * Method that converts an hex string color representation to an ARGB color
+ *
+ * @param hex The hex string representation of the color
+ * @return int The ARGB color
+ */
+ private static int toARGB(String hex) {
+ return Color.parseColor("#" + hex); //$NON-NLS-1$
+ }
+
+ /**
+ * Method that converts a byte into its hex string representation
+ *
+ * @param v The value to convert
+ * @return String The hex string representation
+ */
+ private static String toHexString(byte v) {
+ String hex = Integer.toHexString(v & 0xff);
+ if (hex.length() == 1) {
+ hex = "0" + hex; //$NON-NLS-1$
+ }
+ return hex.toUpperCase(Locale.getDefault());
+ }
+
+ /**
+ * Generate a value suitable for use in {@link #setId(int)}.
+ * This value will not collide with ID values generated at build time by aapt for R.id.
+ *
+ * @return a generated ID value
+ */
+ private static int internalGenerateViewId() {
+ for (;;) {
+ final int result = sNextGeneratedId.get();
+ // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
+ int newValue = result + 1;
+ if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
+ if (sNextGeneratedId.compareAndSet(result, newValue)) {
+ return result;
+ }
+ }
+ }
+}
diff --git a/src/afzkl/development/mColorPicker/views/ColorPanelView.java b/src/afzkl/development/mColorPicker/views/ColorPanelView.java
new file mode 100644
index 0000000..9764ff1
--- /dev/null
+++ b/src/afzkl/development/mColorPicker/views/ColorPanelView.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2010 Daniel Nilsson
+ *
+ * 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 afzkl.development.mColorPicker.views;
+
+import afzkl.development.mColorPicker.drawables.AlphaPatternDrawable;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * This class draws a panel which which will be filled with a color which can be set.
+ * It can be used to show the currently selected color which you will get from
+ * the {@link ColorPickerView}.
+ * @author Daniel Nilsson
+ *
+ */
+@SuppressWarnings("all")
+public class ColorPanelView extends View{
+
+ /**
+ * The width in pixels of the border
+ * surrounding the color panel.
+ */
+ private final static float BORDER_WIDTH_PX = 1;
+
+ private static float mDensity = 1f;
+
+ private int mBorderColor = 0xff6E6E6E;
+ private int mColor = 0xff000000;
+
+ private Paint mBorderPaint;
+ private Paint mColorPaint;
+
+ private RectF mDrawingRect;
+ private RectF mColorRect;
+
+ private AlphaPatternDrawable mAlphaPattern;
+
+
+ public ColorPanelView(Context context) {
+ this(context, null);
+ }
+
+ public ColorPanelView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ColorPanelView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ init();
+ }
+
+ private void init() {
+ mBorderPaint = new Paint();
+ mColorPaint = new Paint();
+ mDensity = getContext().getResources().getDisplayMetrics().density;
+ }
+
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+
+ final RectF rect = mColorRect;
+
+ if (BORDER_WIDTH_PX > 0) {
+ mBorderPaint.setColor(mBorderColor);
+ canvas.drawRect(mDrawingRect, mBorderPaint);
+ }
+
+ if (mAlphaPattern != null) {
+ mAlphaPattern.draw(canvas);
+ }
+
+ mColorPaint.setColor(mColor);
+
+ canvas.drawRect(rect, mColorPaint);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ int height = MeasureSpec.getSize(heightMeasureSpec);
+
+ setMeasuredDimension(width, height);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ mDrawingRect = new RectF();
+ mDrawingRect.left = getPaddingLeft();
+ mDrawingRect.right = w - getPaddingRight();
+ mDrawingRect.top = getPaddingTop();
+ mDrawingRect.bottom = h - getPaddingBottom();
+
+ setUpColorRect();
+
+ }
+
+ private void setUpColorRect() {
+ final RectF dRect = mDrawingRect;
+
+ float left = dRect.left + BORDER_WIDTH_PX;
+ float top = dRect.top + BORDER_WIDTH_PX;
+ float bottom = dRect.bottom - BORDER_WIDTH_PX;
+ float right = dRect.right - BORDER_WIDTH_PX;
+
+ mColorRect = new RectF(left,top, right, bottom);
+
+ mAlphaPattern = new AlphaPatternDrawable((int)(5 * mDensity));
+
+ mAlphaPattern.setBounds(Math.round(mColorRect.left),
+ Math.round(mColorRect.top),
+ Math.round(mColorRect.right),
+ Math.round(mColorRect.bottom));
+
+ }
+
+ /**
+ * Set the color that should be shown by this view.
+ * @param color
+ */
+ public void setColor(int color) {
+ mColor = color;
+ invalidate();
+ }
+
+ /**
+ * Get the color currently show by this view.
+ * @return
+ */
+ public int getColor() {
+ return mColor;
+ }
+
+ /**
+ * Set the color of the border surrounding the panel.
+ * @param color
+ */
+ public void setBorderColor(int color) {
+ mBorderColor = color;
+ invalidate();
+ }
+
+ /**
+ * Get the color of the border surrounding the panel.
+ */
+ public int getBorderColor() {
+ return mBorderColor;
+ }
+
+}
diff --git a/src/afzkl/development/mColorPicker/views/ColorPickerView.java b/src/afzkl/development/mColorPicker/views/ColorPickerView.java
new file mode 100644
index 0000000..fd901c5
--- /dev/null
+++ b/src/afzkl/development/mColorPicker/views/ColorPickerView.java
@@ -0,0 +1,998 @@
+/*
+ * Copyright (C) 2010 Daniel Nilsson
+ *
+ * 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 afzkl.development.mColorPicker.views;
+
+import afzkl.development.mColorPicker.drawables.AlphaPatternDrawable;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ComposeShader;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.Paint.Align;
+import android.graphics.Paint.Style;
+import android.graphics.Point;
+import android.graphics.PorterDuff;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.Shader.TileMode;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+import java.lang.reflect.Method;
+
+/**
+ * Displays a color picker to the user and allow them
+ * to select a color. A slider for the alpha channel is
+ * also available. Enable it by setting
+ * setAlphaSliderVisible(boolean) to true.
+ * @author Daniel Nilsson
+ */
+@SuppressWarnings("all")
+public class ColorPickerView extends View{
+
+ public interface OnColorChangedListener{
+ public void onColorChanged(int color);
+ }
+
+ private final static int PANEL_SAT_VAL = 0;
+ private final static int PANEL_HUE = 1;
+ private final static int PANEL_ALPHA = 2;
+
+ /**
+ * The width in pixels of the border
+ * surrounding all color panels.
+ */
+ private final static float BORDER_WIDTH_PX = 1;
+
+ /**
+ * The width in dp of the hue panel.
+ */
+ private float HUE_PANEL_WIDTH = 30f;
+ /**
+ * The height in dp of the alpha panel
+ */
+ private float ALPHA_PANEL_HEIGHT = 20f;
+ /**
+ * The distance in dp between the different
+ * color panels.
+ */
+ private float PANEL_SPACING = 10f;
+ /**
+ * The radius in dp of the color palette tracker circle.
+ */
+ private float PALETTE_CIRCLE_TRACKER_RADIUS = 5f;
+ /**
+ * The dp which the tracker of the hue or alpha panel
+ * will extend outside of its bounds.
+ */
+ private float RECTANGLE_TRACKER_OFFSET = 2f;
+
+
+ private static float mDensity = 1f;
+
+ private OnColorChangedListener mListener;
+
+ private Paint mSatValPaint;
+ private Paint mSatValTrackerPaint;
+
+ private Paint mHuePaint;
+ private Paint mHueTrackerPaint;
+
+ private Paint mAlphaPaint;
+ private Paint mAlphaTextPaint;
+
+ private Paint mBorderPaint;
+
+ private Shader mValShader;
+ private Shader mSatShader;
+ private Shader mHueShader;
+ private Shader mAlphaShader;
+
+ private int mAlpha = 0xff;
+ private float mHue = 360f;
+ private float mSat = 0f;
+ private float mVal = 0f;
+
+ private String mAlphaSliderText = "Alpha";
+ private int mSliderTrackerColor = 0xff1c1c1c;
+ private int mBorderColor = 0xff6E6E6E;
+ private boolean mShowAlphaPanel = false;
+
+ /*
+ * To remember which panel that has the "focus" when
+ * processing hardware button data.
+ */
+ private int mLastTouchedPanel = PANEL_SAT_VAL;
+
+ /**
+ * Offset from the edge we must have or else
+ * the finger tracker will get clipped when
+ * it is drawn outside of the view.
+ */
+ private float mDrawingOffset;
+
+
+ /*
+ * Distance form the edges of the view
+ * of where we are allowed to draw.
+ */
+ private RectF mDrawingRect;
+
+ private RectF mSatValRect;
+ private RectF mHueRect;
+ private RectF mAlphaRect;
+
+ private AlphaPatternDrawable mAlphaPattern;
+
+ private Point mStartTouchPoint = null;
+
+
+ public ColorPickerView(Context context) {
+ this(context, null);
+ }
+
+ public ColorPickerView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ColorPickerView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init();
+ }
+
+ private void init() {
+ mDensity = getContext().getResources().getDisplayMetrics().density;
+ PALETTE_CIRCLE_TRACKER_RADIUS *= mDensity;
+ RECTANGLE_TRACKER_OFFSET *= mDensity;
+ HUE_PANEL_WIDTH *= mDensity;
+ ALPHA_PANEL_HEIGHT *= mDensity;
+ PANEL_SPACING = PANEL_SPACING * mDensity;
+
+ mDrawingOffset = calculateRequiredOffset();
+
+ initPaintTools();
+
+ //Needed for receiving trackball motion events.
+ setFocusable(true);
+ setFocusableInTouchMode(true);
+ }
+
+ private void initPaintTools() {
+
+ mSatValPaint = new Paint();
+ mSatValTrackerPaint = new Paint();
+ mHuePaint = new Paint();
+ mHueTrackerPaint = new Paint();
+ mAlphaPaint = new Paint();
+ mAlphaTextPaint = new Paint();
+ mBorderPaint = new Paint();
+
+
+ mSatValTrackerPaint.setStyle(Style.STROKE);
+ mSatValTrackerPaint.setStrokeWidth(2f * mDensity);
+ mSatValTrackerPaint.setAntiAlias(true);
+
+ mHueTrackerPaint.setColor(mSliderTrackerColor);
+ mHueTrackerPaint.setStyle(Style.STROKE);
+ mHueTrackerPaint.setStrokeWidth(2f * mDensity);
+ mHueTrackerPaint.setAntiAlias(true);
+
+ mAlphaTextPaint.setColor(0xff1c1c1c);
+ mAlphaTextPaint.setTextSize(14f * mDensity);
+ mAlphaTextPaint.setAntiAlias(true);
+ mAlphaTextPaint.setTextAlign(Align.CENTER);
+ mAlphaTextPaint.setFakeBoldText(true);
+
+
+ }
+
+ private float calculateRequiredOffset() {
+ float offset = Math.max(PALETTE_CIRCLE_TRACKER_RADIUS, RECTANGLE_TRACKER_OFFSET);
+ offset = Math.max(offset, BORDER_WIDTH_PX * mDensity);
+
+ return offset * 1.5f;
+ }
+
+ private int[] buildHueColorArray() {
+
+ int[] hue = new int[361];
+
+ int count = 0;
+ for (int i = hue.length -1; i >= 0; i--, count++) {
+ hue[count] = Color.HSVToColor(new float[]{i, 1f, 1f});
+ }
+
+ return hue;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ checkHardwareAccelerationSupport();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+
+ if (mDrawingRect.width() <= 0 || mDrawingRect.height() <= 0) return;
+
+ drawSatValPanel(canvas);
+ drawHuePanel(canvas);
+ drawAlphaPanel(canvas);
+
+ }
+
+ private void drawSatValPanel(Canvas canvas) {
+
+ final RectF rect = mSatValRect;
+
+ if (BORDER_WIDTH_PX > 0) {
+ mBorderPaint.setColor(mBorderColor);
+ canvas.drawRect(mDrawingRect.left, mDrawingRect.top, rect.right + BORDER_WIDTH_PX, rect.bottom + BORDER_WIDTH_PX, mBorderPaint);
+ }
+
+ if (mValShader == null) {
+ mValShader = new LinearGradient(rect.left, rect.top, rect.left, rect.bottom,
+ 0xffffffff, 0xff000000, TileMode.CLAMP);
+ }
+
+ int rgb = Color.HSVToColor(new float[]{mHue,1f,1f});
+
+ mSatShader = new LinearGradient(rect.left, rect.top, rect.right, rect.top,
+ 0xffffffff, rgb, TileMode.CLAMP);
+ ComposeShader mShader = new ComposeShader(mValShader, mSatShader, PorterDuff.Mode.MULTIPLY);
+ mSatValPaint.setShader(mShader);
+
+ canvas.drawRect(rect, mSatValPaint);
+
+ Point p = satValToPoint(mSat, mVal);
+
+ mSatValTrackerPaint.setColor(0xff000000);
+ canvas.drawCircle(p.x, p.y, PALETTE_CIRCLE_TRACKER_RADIUS - 1f * mDensity, mSatValTrackerPaint);
+
+ mSatValTrackerPaint.setColor(0xffdddddd);
+ canvas.drawCircle(p.x, p.y, PALETTE_CIRCLE_TRACKER_RADIUS, mSatValTrackerPaint);
+
+ }
+
+ private void drawHuePanel(Canvas canvas) {
+
+ final RectF rect = mHueRect;
+
+ if (BORDER_WIDTH_PX > 0) {
+ mBorderPaint.setColor(mBorderColor);
+ canvas.drawRect(rect.left - BORDER_WIDTH_PX,
+ rect.top - BORDER_WIDTH_PX,
+ rect.right + BORDER_WIDTH_PX,
+ rect.bottom + BORDER_WIDTH_PX,
+ mBorderPaint);
+ }
+
+ if (mHueShader == null) {
+ mHueShader = new LinearGradient(rect.left, rect.top, rect.left, rect.bottom, buildHueColorArray(), null, TileMode.CLAMP);
+ mHuePaint.setShader(mHueShader);
+ }
+
+ canvas.drawRect(rect, mHuePaint);
+
+ float rectHeight = 4 * mDensity / 2;
+
+ Point p = hueToPoint(mHue);
+
+ RectF r = new RectF();
+ r.left = rect.left - RECTANGLE_TRACKER_OFFSET;
+ r.right = rect.right + RECTANGLE_TRACKER_OFFSET;
+ r.top = p.y - rectHeight;
+ r.bottom = p.y + rectHeight;
+
+
+ canvas.drawRoundRect(r, 2, 2, mHueTrackerPaint);
+
+ }
+
+ private void drawAlphaPanel(Canvas canvas) {
+
+ if (!mShowAlphaPanel || mAlphaRect == null || mAlphaPattern == null) return;
+
+ final RectF rect = mAlphaRect;
+
+ if (BORDER_WIDTH_PX > 0) {
+ mBorderPaint.setColor(mBorderColor);
+ canvas.drawRect(rect.left - BORDER_WIDTH_PX,
+ rect.top - BORDER_WIDTH_PX,
+ rect.right + BORDER_WIDTH_PX,
+ rect.bottom + BORDER_WIDTH_PX,
+ mBorderPaint);
+ }
+
+
+ mAlphaPattern.draw(canvas);
+
+ float[] hsv = new float[]{mHue,mSat,mVal};
+ int color = Color.HSVToColor(hsv);
+ int acolor = Color.HSVToColor(0, hsv);
+
+ mAlphaShader = new LinearGradient(rect.left, rect.top, rect.right, rect.top,
+ color, acolor, TileMode.CLAMP);
+
+
+ mAlphaPaint.setShader(mAlphaShader);
+
+ canvas.drawRect(rect, mAlphaPaint);
+
+ if (mAlphaSliderText != null && mAlphaSliderText!= "") {
+ canvas.drawText(mAlphaSliderText, rect.centerX(), rect.centerY() + 4 * mDensity, mAlphaTextPaint);
+ }
+
+ float rectWidth = 4 * mDensity / 2;
+
+ Point p = alphaToPoint(mAlpha);
+
+ RectF r = new RectF();
+ r.left = p.x - rectWidth;
+ r.right = p.x + rectWidth;
+ r.top = rect.top - RECTANGLE_TRACKER_OFFSET;
+ r.bottom = rect.bottom + RECTANGLE_TRACKER_OFFSET;
+
+ canvas.drawRoundRect(r, 2, 2, mHueTrackerPaint);
+
+ }
+
+
+ private Point hueToPoint(float hue) {
+
+ final RectF rect = mHueRect;
+ final float height = rect.height();
+
+ Point p = new Point();
+
+ p.y = (int) (height - (hue * height / 360f) + rect.top);
+ p.x = (int) rect.left;
+
+ return p;
+ }
+
+ private Point satValToPoint(float sat, float val) {
+
+ final RectF rect = mSatValRect;
+ final float height = rect.height();
+ final float width = rect.width();
+
+ Point p = new Point();
+
+ p.x = (int) (sat * width + rect.left);
+ p.y = (int) ((1f - val) * height + rect.top);
+
+ return p;
+ }
+
+ private Point alphaToPoint(int alpha) {
+
+ final RectF rect = mAlphaRect;
+ final float width = rect.width();
+
+ Point p = new Point();
+
+ p.x = (int) (width - (alpha * width / 0xff) + rect.left);
+ p.y = (int) rect.top;
+
+ return p;
+
+ }
+
+ private float[] pointToSatVal(float x, float y) {
+
+ final RectF rect = mSatValRect;
+ float[] result = new float[2];
+
+ float width = rect.width();
+ float height = rect.height();
+
+ if (x < rect.left) {
+ x = 0f;
+ }
+ else if (x > rect.right) {
+ x = width;
+ }
+ else{
+ x = x - rect.left;
+ }
+
+ if (y < rect.top) {
+ y = 0f;
+ }
+ else if (y > rect.bottom) {
+ y = height;
+ }
+ else{
+ y = y - rect.top;
+ }
+
+
+ result[0] = 1.f / width * x;
+ result[1] = 1.f - (1.f / height * y);
+
+ return result;
+ }
+
+ private float pointToHue(float y) {
+
+ final RectF rect = mHueRect;
+
+ float height = rect.height();
+
+ if (y < rect.top) {
+ y = 0f;
+ }
+ else if (y > rect.bottom) {
+ y = height;
+ }
+ else{
+ y = y - rect.top;
+ }
+
+ return 360f - (y * 360f / height);
+ }
+
+ private int pointToAlpha(int x) {
+
+ final RectF rect = mAlphaRect;
+ final int width = (int) rect.width();
+
+ if (x < rect.left) {
+ x = 0;
+ }
+ else if (x > rect.right) {
+ x = width;
+ }
+ else{
+ x = x - (int)rect.left;
+ }
+
+ return 0xff - (x * 0xff / width);
+
+ }
+
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent event) {
+
+ float x = event.getX();
+ float y = event.getY();
+
+ boolean update = false;
+
+
+ if (event.getAction() == MotionEvent.ACTION_MOVE) {
+
+ switch(mLastTouchedPanel) {
+
+ case PANEL_SAT_VAL:
+
+ float sat, val;
+
+ sat = mSat + x/50f;
+ val = mVal - y/50f;
+
+ if (sat < 0f) {
+ sat = 0f;
+ }
+ else if (sat > 1f) {
+ sat = 1f;
+ }
+
+ if (val < 0f) {
+ val = 0f;
+ }
+ else if (val > 1f) {
+ val = 1f;
+ }
+
+ mSat = sat;
+ mVal = val;
+
+ update = true;
+
+ break;
+
+ case PANEL_HUE:
+
+ float hue = mHue - y * 10f;
+
+ if (hue < 0f) {
+ hue = 0f;
+ }
+ else if (hue > 360f) {
+ hue = 360f;
+ }
+
+ mHue = hue;
+
+ update = true;
+
+ break;
+
+ case PANEL_ALPHA:
+
+ if (!mShowAlphaPanel || mAlphaRect == null) {
+ update = false;
+ }
+ else{
+
+ int alpha = (int) (mAlpha - x*10);
+
+ if (alpha < 0) {
+ alpha = 0;
+ }
+ else if (alpha > 0xff) {
+ alpha = 0xff;
+ }
+
+ mAlpha = alpha;
+
+
+ update = true;
+ }
+
+ break;
+ }
+
+
+ }
+
+
+ if (update) {
+
+ if (mListener != null) {
+ mListener.onColorChanged(Color.HSVToColor(mAlpha, new float[]{mHue, mSat, mVal}));
+ }
+
+ invalidate();
+ return true;
+ }
+
+
+ return super.onTrackballEvent(event);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+
+ boolean update = false;
+
+ switch(event.getAction()) {
+
+ case MotionEvent.ACTION_DOWN:
+
+ mStartTouchPoint = new Point((int)event.getX(), (int)event.getY());
+
+ update = moveTrackersIfNeeded(event);
+
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+
+ update = moveTrackersIfNeeded(event);
+
+ break;
+
+ case MotionEvent.ACTION_UP:
+
+ mStartTouchPoint = null;
+
+ update = moveTrackersIfNeeded(event);
+
+ break;
+
+ }
+
+ if (update) {
+
+ if (mListener != null) {
+ mListener.onColorChanged(Color.HSVToColor(mAlpha, new float[]{mHue, mSat, mVal}));
+ }
+
+ invalidate();
+ return true;
+ }
+
+
+ return super.onTouchEvent(event);
+ }
+
+ private boolean moveTrackersIfNeeded(MotionEvent event) {
+
+ if (mStartTouchPoint == null) return false;
+
+ boolean update = false;
+
+ int startX = mStartTouchPoint.x;
+ int startY = mStartTouchPoint.y;
+
+
+ if (mHueRect.contains(startX, startY)) {
+ mLastTouchedPanel = PANEL_HUE;
+
+ mHue = pointToHue(event.getY());
+
+ update = true;
+ }
+ else if (mSatValRect.contains(startX, startY)) {
+
+ mLastTouchedPanel = PANEL_SAT_VAL;
+
+ float[] result = pointToSatVal(event.getX(), event.getY());
+
+ mSat = result[0];
+ mVal = result[1];
+
+ update = true;
+ }
+ else if (mAlphaRect != null && mAlphaRect.contains(startX, startY)) {
+
+ mLastTouchedPanel = PANEL_ALPHA;
+
+ mAlpha = pointToAlpha((int)event.getX());
+
+ update = true;
+ }
+
+
+ return update;
+ }
+
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+ int width = 0;
+ int height = 0;
+
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+
+ int widthAllowed = MeasureSpec.getSize(widthMeasureSpec);
+ int heightAllowed = MeasureSpec.getSize(heightMeasureSpec);
+
+
+ widthAllowed = chooseWidth(widthMode, widthAllowed);
+ heightAllowed = chooseHeight(heightMode, heightAllowed);
+
+
+ if (!mShowAlphaPanel) {
+ height = (int) (widthAllowed - PANEL_SPACING - HUE_PANEL_WIDTH);
+
+ //If calculated height (based on the width) is more than the allowed height.
+ if (height > heightAllowed) {
+ height = heightAllowed;
+ width = (int) (height + PANEL_SPACING + HUE_PANEL_WIDTH);
+ }
+ else{
+ width = widthAllowed;
+ }
+ }
+ else{
+
+ width = (int) (heightAllowed - ALPHA_PANEL_HEIGHT + HUE_PANEL_WIDTH);
+
+ if (width > widthAllowed) {
+ width = widthAllowed;
+ height = (int) (widthAllowed - HUE_PANEL_WIDTH + ALPHA_PANEL_HEIGHT);
+ }
+ else{
+ height = heightAllowed;
+ }
+
+
+ }
+
+
+ setMeasuredDimension(width, height);
+ }
+
+ private int chooseWidth(int mode, int size) {
+ if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) {
+ return size;
+ } else { // (mode == MeasureSpec.UNSPECIFIED)
+ return getPrefferedWidth();
+ }
+ }
+
+ private int chooseHeight(int mode, int size) {
+ if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) {
+ return size;
+ } else { // (mode == MeasureSpec.UNSPECIFIED)
+ return getPreferedHeight();
+ }
+ }
+
+ private int getPrefferedWidth() {
+
+ int width = getPreferedHeight();
+
+ if (mShowAlphaPanel) {
+ width -= (PANEL_SPACING + ALPHA_PANEL_HEIGHT);
+ }
+
+
+ return (int) (width + HUE_PANEL_WIDTH + PANEL_SPACING);
+
+ }
+
+ private int getPreferedHeight() {
+
+ int height = (int)(200 * mDensity);
+
+ if (mShowAlphaPanel) {
+ height += PANEL_SPACING + ALPHA_PANEL_HEIGHT;
+ }
+
+ return height;
+ }
+
+
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ mDrawingRect = new RectF();
+ mDrawingRect.left = mDrawingOffset + getPaddingLeft();
+ mDrawingRect.right = w - mDrawingOffset - getPaddingRight();
+ mDrawingRect.top = mDrawingOffset + getPaddingTop();
+ mDrawingRect.bottom = h - mDrawingOffset - getPaddingBottom();
+
+ setUpSatValRect();
+ setUpHueRect();
+ setUpAlphaRect();
+ }
+
+ private void setUpSatValRect() {
+
+ final RectF dRect = mDrawingRect;
+ float panelSide = dRect.height() - BORDER_WIDTH_PX * 2;
+
+ if (mShowAlphaPanel) {
+ panelSide -= PANEL_SPACING + ALPHA_PANEL_HEIGHT;
+ }
+
+ float left = dRect.left + BORDER_WIDTH_PX;
+ float top = dRect.top + BORDER_WIDTH_PX;
+ float bottom = top + panelSide;
+ float right = left + panelSide;
+
+ mSatValRect = new RectF(left,top, right, bottom);
+ }
+
+ private void setUpHueRect() {
+ final RectF dRect = mDrawingRect;
+
+ float left = dRect.right - HUE_PANEL_WIDTH + BORDER_WIDTH_PX;
+ float top = dRect.top + BORDER_WIDTH_PX;
+ float bottom = dRect.bottom - BORDER_WIDTH_PX - (mShowAlphaPanel ? (PANEL_SPACING + ALPHA_PANEL_HEIGHT) : 0);
+ float right = dRect.right - BORDER_WIDTH_PX;
+
+ mHueRect = new RectF(left, top, right, bottom);
+ }
+
+ private void setUpAlphaRect() {
+
+ if (!mShowAlphaPanel) return;
+
+ final RectF dRect = mDrawingRect;
+
+ float left = dRect.left + BORDER_WIDTH_PX;
+ float top = dRect.bottom - ALPHA_PANEL_HEIGHT + BORDER_WIDTH_PX;
+ float bottom = dRect.bottom - BORDER_WIDTH_PX;
+ float right = dRect.right - BORDER_WIDTH_PX;
+
+ mAlphaRect = new RectF(left, top, right, bottom);
+
+
+ mAlphaPattern = new AlphaPatternDrawable((int) (5 * mDensity));
+ mAlphaPattern.setBounds(Math.round(mAlphaRect.left), Math
+ .round(mAlphaRect.top), Math.round(mAlphaRect.right), Math
+ .round(mAlphaRect.bottom));
+
+
+
+ }
+
+
+ /**
+ * Set a OnColorChangedListener to get notified when the color
+ * selected by the user has changed.
+ * @param listener
+ */
+ public void setOnColorChangedListener(OnColorChangedListener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Set the color of the border surrounding all panels.
+ * @param color
+ */
+ public void setBorderColor(int color) {
+ mBorderColor = color;
+ invalidate();
+ }
+
+ /**
+ * Get the color of the border surrounding all panels.
+ */
+ public int getBorderColor() {
+ return mBorderColor;
+ }
+
+ /**
+ * Get the current color this view is showing.
+ * @return the current color.
+ */
+ public int getColor() {
+ return Color.HSVToColor(mAlpha, new float[]{mHue,mSat,mVal});
+ }
+
+ /**
+ * Set the color the view should show.
+ * @param color The color that should be selected.
+ */
+ public void setColor(int color) {
+ setColor(color, false);
+ }
+
+ /**
+ * Set the color this view should show.
+ * @param color The color that should be selected.
+ * @param callback If you want to get a callback to
+ * your OnColorChangedListener.
+ */
+ public void setColor(int color, boolean callback) {
+
+ int alpha = Color.alpha(color);
+ int red = Color.red(color);
+ int blue = Color.blue(color);
+ int green = Color.green(color);
+
+ float[] hsv = new float[3];
+
+ Color.RGBToHSV(red, green, blue, hsv);
+
+ mAlpha = alpha;
+ mHue = hsv[0];
+ mSat = hsv[1];
+ mVal = hsv[2];
+
+ if (callback && mListener != null) {
+ mListener.onColorChanged(Color.HSVToColor(mAlpha, new float[]{mHue, mSat, mVal}));
+ }
+
+ invalidate();
+ }
+
+ /**
+ * Get the drawing offset of the color picker view.
+ * The drawing offset is the distance from the side of
+ * a panel to the side of the view minus the padding.
+ * Useful if you want to have your own panel below showing
+ * the currently selected color and want to align it perfectly.
+ * @return The offset in pixels.
+ */
+ public float getDrawingOffset() {
+ return mDrawingOffset;
+ }
+
+ /**
+ * Set if the user is allowed to adjust the alpha panel. Default is false.
+ * If it is set to false no alpha will be set.
+ * @param visible
+ */
+ public void setAlphaSliderVisible(boolean visible) {
+
+ if (mShowAlphaPanel != visible) {
+ mShowAlphaPanel = visible;
+
+ /*
+ * Reset all shader to force a recreation.
+ * Otherwise they will not look right after
+ * the size of the view has changed.
+ */
+ mValShader = null;
+ mSatShader = null;
+ mHueShader = null;
+ mAlphaShader = null;;
+
+ requestLayout();
+ }
+
+ }
+
+ public void setSliderTrackerColor(int color) {
+ mSliderTrackerColor = color;
+
+ mHueTrackerPaint.setColor(mSliderTrackerColor);
+
+ invalidate();
+ }
+
+ public int getSliderTrackerColor() {
+ return mSliderTrackerColor;
+ }
+
+ /**
+ * Set the text that should be shown in the
+ * alpha slider. Set to null to disable text.
+ * @param res string resource id.
+ */
+ public void setAlphaSliderText(int res) {
+ String text = getContext().getString(res);
+ setAlphaSliderText(text);
+ }
+
+ /**
+ * Set the text that should be shown in the
+ * alpha slider. Set to null to disable text.
+ * @param text Text that should be shown.
+ */
+ public void setAlphaSliderText(String text) {
+ mAlphaSliderText = text;
+ invalidate();
+ }
+
+ /**
+ * Get the current value of the text
+ * that will be shown in the alpha
+ * slider.
+ * @return
+ */
+ public String getAlphaSliderText() {
+ return mAlphaSliderText;
+ }
+
+ /**
+ * Method that checks the support for HardwareAcceleration. Check AOSP notice<br/>
+ * <br/>
+ * <pre>
+ * 'ComposeShader can only contain shaders of different types (a BitmapShader and a
+ * LinearGradient for instance, but not two instances of BitmapShader)'. But, 'If your
+ * application is affected by any of these missing features or limitations, you can turn
+ * off hardware acceleration for just the affected portion of your application by calling
+ * setLayerType(View.LAYER_TYPE_SOFTWARE, null).'
+ */
+ private void checkHardwareAccelerationSupport() {
+ // HardwareAcceleration sit is only available since ICS. 14 = ICS_VERSION_CODE
+ if (android.os.Build.VERSION.SDK_INT >= 14) {
+ try{
+ // We need to use reflection to get that method to avoid compilation errors
+ Method isHardwareAccelerated =
+ getClass().getMethod("isHardwareAccelerated", new Class[]{});
+ Object o = isHardwareAccelerated.invoke(this, new Object[]{});
+ if (null != o && o instanceof Boolean && (Boolean)o) {
+ // HardwareAcceleration is supported. Use SoftwareAcceleration
+ Method setLayerType =
+ getClass().getMethod(
+ "setLayerType", int.class, android.graphics.Paint.class);
+ setLayerType.invoke(this, 1, (android.graphics.Paint)null);
+ }
+ } catch (Exception e) { /** NON BLOCK **/}
+ }
+ }
+
+}
diff --git a/src/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompat.java b/src/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompat.java
new file mode 100644
index 0000000..a67e162
--- /dev/null
+++ b/src/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompat.java
@@ -0,0 +1,452 @@
+/*
+ * 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.accessibilityservice;
+
+import android.accessibilityservice.AccessibilityService;
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.content.pm.ResolveInfo;
+import android.os.Build;
+import android.view.View;
+
+/**
+ * Helper for accessing features in {@link android.accessibilityservice.AccessibilityService}
+ * introduced after API level 4 in a backwards compatible fashion.
+ */
+public class AccessibilityServiceInfoCompat {
+
+ static interface AccessibilityServiceInfoVersionImpl {
+ public String getId(AccessibilityServiceInfo info);
+ public ResolveInfo getResolveInfo(AccessibilityServiceInfo info);
+ public boolean getCanRetrieveWindowContent(AccessibilityServiceInfo info);
+ public String getDescription(AccessibilityServiceInfo info);
+ public String getSettingsActivityName(AccessibilityServiceInfo info);
+ public int getCapabilities(AccessibilityServiceInfo info);
+ }
+
+ static class AccessibilityServiceInfoStubImpl implements AccessibilityServiceInfoVersionImpl {
+
+ @Override
+ public boolean getCanRetrieveWindowContent(AccessibilityServiceInfo info) {
+ return false;
+ }
+
+ @Override
+ public String getDescription(AccessibilityServiceInfo info) {
+ return null;
+ }
+
+ @Override
+ public String getId(AccessibilityServiceInfo info) {
+ return null;
+ }
+
+ @Override
+ public ResolveInfo getResolveInfo(AccessibilityServiceInfo info) {
+ return null;
+ }
+
+ @Override
+ public String getSettingsActivityName(AccessibilityServiceInfo info) {
+ return null;
+ }
+
+ @Override
+ public int getCapabilities(AccessibilityServiceInfo info) {
+ return 0;
+ }
+ }
+
+ static class AccessibilityServiceInfoIcsImpl extends AccessibilityServiceInfoStubImpl {
+
+ @Override
+ public boolean getCanRetrieveWindowContent(AccessibilityServiceInfo info) {
+ return AccessibilityServiceInfoCompatIcs.getCanRetrieveWindowContent(info);
+ }
+
+ @Override
+ public String getDescription(AccessibilityServiceInfo info) {
+ return AccessibilityServiceInfoCompatIcs.getDescription(info);
+ }
+
+ @Override
+ public String getId(AccessibilityServiceInfo info) {
+ return AccessibilityServiceInfoCompatIcs.getId(info);
+ }
+
+ @Override
+ public ResolveInfo getResolveInfo(AccessibilityServiceInfo info) {
+ return AccessibilityServiceInfoCompatIcs.getResolveInfo(info);
+ }
+
+ @Override
+ public String getSettingsActivityName(AccessibilityServiceInfo info) {
+ return AccessibilityServiceInfoCompatIcs.getSettingsActivityName(info);
+ }
+
+ @Override
+ public int getCapabilities(AccessibilityServiceInfo info) {
+ if (getCanRetrieveWindowContent(info)) {
+ return CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT;
+ }
+ return 0;
+ }
+ }
+
+ static {
+ if (Build.VERSION.SDK_INT >= 14) { // ICS
+ IMPL = new AccessibilityServiceInfoIcsImpl();
+ } else {
+ IMPL = new AccessibilityServiceInfoStubImpl();
+ }
+ }
+
+ // Capabilities
+
+ private static final AccessibilityServiceInfoVersionImpl IMPL;
+
+ /**
+ * Capability: This accessibility service can retrieve the active window content.
+ */
+ public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 0x00000001;
+
+ /**
+ * Capability: This accessibility service can request touch exploration mode in which
+ * touched items are spoken aloud and the UI can be explored via gestures.
+ */
+ public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 0x00000002;
+
+ /**
+ * Capability: This accessibility service can request enhanced web accessibility
+ * enhancements. For example, installing scripts to make app content more accessible.
+ */
+ public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 0x00000004;
+
+ /**
+ * Capability: This accessibility service can filter the key event stream.
+ */
+ public static final int CAPABILITY_CAN_FILTER_KEY_EVENTS = 0x00000008;
+
+ // Feedback types
+
+ /**
+ * Denotes braille feedback.
+ */
+ public static final int FEEDBACK_BRAILLE = 0x0000020;
+
+ /**
+ * Mask for all feedback types.
+ *
+ * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN
+ * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC
+ * @see AccessibilityServiceInfo#FEEDBACK_AUDIBLE
+ * @see AccessibilityServiceInfo#FEEDBACK_VISUAL
+ * @see AccessibilityServiceInfo#FEEDBACK_GENERIC
+ * @see FEEDBACK_BRAILLE
+ */
+ @SuppressWarnings("javadoc")
+ public static final int FEEDBACK_ALL_MASK = 0xFFFFFFFF;
+
+ // Flags
+
+ /**
+ * If an {@link AccessibilityService} is the default for a given type.
+ * Default service is invoked only if no package specific one exists. In case of
+ * more than one package specific service only the earlier registered is notified.
+ */
+ public static final int DEFAULT = 0x0000001;
+
+ /**
+ * If this flag is set the system will regard views that are not important
+ * for accessibility in addition to the ones that are important for accessibility.
+ * That is, views that are marked as not important for accessibility via
+ * {@link View#IMPORTANT_FOR_ACCESSIBILITY_NO} or
+ * {@link View#IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS} and views that are
+ * marked as potentially important for accessibility via
+ * {@link View#IMPORTANT_FOR_ACCESSIBILITY_AUTO} for which the system has determined
+ * that are not important for accessibility, are both reported while querying the
+ * window content and also the accessibility service will receive accessibility events
+ * from them.
+ * <p>
+ * <strong>Note:</strong> For accessibility services targeting API version
+ * {@link Build.VERSION_CODES#JELLY_BEAN} or higher this flag has to be explicitly
+ * set for the system to regard views that are not important for accessibility. For
+ * accessibility services targeting API version lower than
+ * {@link Build.VERSION_CODES#JELLY_BEAN} this flag is ignored and all views are
+ * regarded for accessibility purposes.
+ * </p>
+ * <p>
+ * Usually views not important for accessibility are layout managers that do not
+ * react to user actions, do not draw any content, and do not have any special
+ * semantics in the context of the screen content. For example, a three by three
+ * grid can be implemented as three horizontal linear layouts and one vertical,
+ * or three vertical linear layouts and one horizontal, or one grid layout, etc.
+ * In this context the actual layout mangers used to achieve the grid configuration
+ * are not important, rather it is important that there are nine evenly distributed
+ * elements.
+ * </p>
+ */
+ @SuppressWarnings("javadoc")
+ public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x0000002;
+
+ /**
+ * This flag requests that the system gets into touch exploration mode.
+ * In this mode a single finger moving on the screen behaves as a mouse
+ * pointer hovering over the user interface. The system will also detect
+ * certain gestures performed on the touch screen and notify this service.
+ * The system will enable touch exploration mode if there is at least one
+ * accessibility service that has this flag set. Hence, clearing this
+ * flag does not guarantee that the device will not be in touch exploration
+ * mode since there may be another enabled service that requested it.
+ * <p>
+ * For accessibility services targeting API version higher than
+ * {@link Build.VERSION_CODES#JELLY_BEAN_MR1} that want to set
+ * this flag have to declare this capability in their meta-data by setting
+ * the attribute canRequestTouchExplorationMode to true, otherwise this flag
+ * will be ignored. For how to declare the meta-data of a service refer to
+ * {@value AccessibilityService#SERVICE_META_DATA}.
+ * </p>
+ * <p>
+ * Services targeting API version equal to or lower than
+ * {@link Build.VERSION_CODES#JELLY_BEAN_MR1} will work normally, i.e.
+ * the first time they are run, if this flag is specified, a dialog is
+ * shown to the user to confirm enabling explore by touch.
+ * </p>
+ */
+ @SuppressWarnings("javadoc")
+ public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 0x0000004;
+
+ /**
+ * This flag requests from the system to enable web accessibility enhancing
+ * extensions. Such extensions aim to provide improved accessibility support
+ * for content presented in a {@link android.webkit.WebView}. An example of such
+ * an extension is injecting JavaScript from a secure source. The system will enable
+ * enhanced web accessibility if there is at least one accessibility service
+ * that has this flag set. Hence, clearing this flag does not guarantee that the
+ * device will not have enhanced web accessibility enabled since there may be
+ * another enabled service that requested it.
+ * <p>
+ * Services that want to set this flag have to declare this capability
+ * in their meta-data by setting the attribute canRequestEnhancedWebAccessibility
+ * to true, otherwise this flag will be ignored. For how to declare the meta-data
+ * of a service refer to {@value AccessibilityService#SERVICE_META_DATA}.
+ * </p>
+ */
+ public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 0x00000008;
+
+ /**
+ * This flag requests that the AccessibilityNodeInfos obtained
+ * by an {@link AccessibilityService} contain the id of the source view.
+ * The source view id will be a fully qualified resource name of the
+ * form "package:id/name", for example "foo.bar:id/my_list", and it is
+ * useful for UI test automation. This flag is not set by default.
+ */
+ public static final int FLAG_REPORT_VIEW_IDS = 0x00000010;
+
+ /**
+ * This flag requests from the system to filter key events. If this flag
+ * is set the accessibility service will receive the key events before
+ * applications allowing it implement global shortcuts. Setting this flag
+ * does not guarantee that this service will filter key events since only
+ * one service can do so at any given time. This avoids user confusion due
+ * to behavior change in case different key filtering services are enabled.
+ * If there is already another key filtering service enabled, this one will
+ * not receive key events.
+ * <p>
+ * Services that want to set this flag have to declare this capability
+ * in their meta-data by setting the attribute canRequestFilterKeyEvents
+ * to true, otherwise this flag will be ignored. For how to declare the meta
+ * -data of a service refer to {@value AccessibilityService#SERVICE_META_DATA}.
+ * </p>
+ */
+ public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 0x00000020;
+
+ /*
+ * Hide constructor
+ */
+ private AccessibilityServiceInfoCompat() {
+
+ }
+
+ /**
+ * The accessibility service id.
+ * <p>
+ * <strong>Generated by the system.</strong>
+ * </p>
+ *
+ * @return The id.
+ */
+ public static String getId(AccessibilityServiceInfo info) {
+ return IMPL.getId(info);
+ }
+
+ /**
+ * The service {@link ResolveInfo}.
+ * <p>
+ * <strong>Generated by the system.</strong>
+ * </p>
+ *
+ * @return The info.
+ */
+ public static ResolveInfo getResolveInfo(AccessibilityServiceInfo info) {
+ return IMPL.getResolveInfo(info);
+ }
+
+ /**
+ * The settings activity name.
+ * <p>
+ * <strong>Statically set from {@link AccessibilityService#SERVICE_META_DATA
+ * meta-data}.</strong>
+ * </p>
+ *
+ * @return The settings activity name.
+ */
+ public static String getSettingsActivityName(AccessibilityServiceInfo info) {
+ return IMPL.getSettingsActivityName(info);
+ }
+
+ /**
+ * Whether this service can retrieve the current window's content.
+ * <p>
+ * <strong>Statically set from {@link AccessibilityService#SERVICE_META_DATA
+ * meta-data}.</strong>
+ * </p>
+ *
+ * @return True window content can be retrieved.
+ */
+ public static boolean getCanRetrieveWindowContent(AccessibilityServiceInfo info) {
+ return IMPL.getCanRetrieveWindowContent(info);
+ }
+
+ /**
+ * Description of the accessibility service.
+ * <p>
+ * <strong>Statically set from {@link AccessibilityService#SERVICE_META_DATA
+ * meta-data}.</strong>
+ * </p>
+ *
+ * @return The description.
+ */
+ public static String getDescription(AccessibilityServiceInfo info) {
+ return IMPL.getDescription(info);
+ }
+
+ /**
+ * Returns the string representation of a feedback type. For example,
+ * {@link AccessibilityServiceInfo#FEEDBACK_SPOKEN} is represented by the
+ * string FEEDBACK_SPOKEN.
+ *
+ * @param feedbackType The feedback type.
+ * @return The string representation.
+ */
+ public static String feedbackTypeToString(int feedbackType) {
+ int fbt = feedbackType;
+ StringBuilder builder = new StringBuilder();
+ builder.append("[");
+ while (fbt > 0) {
+ final int feedbackTypeFlag = 1 << Integer.numberOfTrailingZeros(fbt);
+ fbt &= ~feedbackTypeFlag;
+ if (builder.length() > 1) {
+ builder.append(", ");
+ }
+ switch (feedbackTypeFlag) {
+ case AccessibilityServiceInfo.FEEDBACK_AUDIBLE:
+ builder.append("FEEDBACK_AUDIBLE");
+ break;
+ case AccessibilityServiceInfo.FEEDBACK_HAPTIC:
+ builder.append("FEEDBACK_HAPTIC");
+ break;
+ case AccessibilityServiceInfo.FEEDBACK_GENERIC:
+ builder.append("FEEDBACK_GENERIC");
+ break;
+ case AccessibilityServiceInfo.FEEDBACK_SPOKEN:
+ builder.append("FEEDBACK_SPOKEN");
+ break;
+ case AccessibilityServiceInfo.FEEDBACK_VISUAL:
+ builder.append("FEEDBACK_VISUAL");
+ break;
+ }
+ }
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /**
+ * Returns the string representation of a flag. For example,
+ * {@link AccessibilityServiceInfo#DEFAULT} is represented by the
+ * string DEFAULT.
+ *
+ * @param flag The flag.
+ * @return The string representation.
+ */
+ public static String flagToString(int flag) {
+ switch (flag) {
+ case DEFAULT:
+ return "DEFAULT";
+ case FLAG_INCLUDE_NOT_IMPORTANT_VIEWS:
+ return "FLAG_INCLUDE_NOT_IMPORTANT_VIEWS";
+ case FLAG_REQUEST_TOUCH_EXPLORATION_MODE:
+ return "FLAG_REQUEST_TOUCH_EXPLORATION_MODE";
+ case FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY:
+ return "FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY";
+ case FLAG_REPORT_VIEW_IDS:
+ return "FLAG_REPORT_VIEW_IDS";
+ case FLAG_REQUEST_FILTER_KEY_EVENTS:
+ return "FLAG_REQUEST_FILTER_KEY_EVENTS";
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Returns the bit mask of capabilities this accessibility service has such as
+ * being able to retrieve the active window content, etc.
+ *
+ * @param info The service info whose capabilities to get.
+ * @return The capability bit mask.
+ *
+ * @see #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
+ * @see #CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
+ * @see #CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
+ * @see #CAPABILITY_CAN_FILTER_KEY_EVENTS
+ */
+ public static int getCapabilities(AccessibilityServiceInfo info) {
+ return IMPL.getCapabilities(info);
+ }
+
+ /**
+ * Returns the string representation of a capability. For example,
+ * {@link #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT} is represented
+ * by the string CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT.
+ *
+ * @param capability The capability.
+ * @return The string representation.
+ */
+ public static String capabilityToString(int capability) {
+ switch (capability) {
+ case CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT:
+ return "CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT";
+ case CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION:
+ return "CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION";
+ case CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY:
+ return "CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY";
+ case CAPABILITY_CAN_FILTER_KEY_EVENTS:
+ return "CAPABILITY_CAN_FILTER_KEY_EVENTS";
+ default:
+ return "UNKNOWN";
+ }
+ }
+}
diff --git a/src/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatIcs.java b/src/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatIcs.java
new file mode 100644
index 0000000..b1c203f
--- /dev/null
+++ b/src/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatIcs.java
@@ -0,0 +1,49 @@
+/*
+ * 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.accessibilityservice;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.content.pm.ResolveInfo;
+
+/**
+ * ICS implementation of the new APIs in AccessibilityServiceInfo.
+ */
+class AccessibilityServiceInfoCompatIcs {
+
+ @SuppressWarnings("deprecation")
+ public static boolean getCanRetrieveWindowContent(AccessibilityServiceInfo info) {
+ return info.getCanRetrieveWindowContent();
+ }
+
+ @SuppressWarnings("deprecation")
+ public static String getDescription(AccessibilityServiceInfo info) {
+ return info.getDescription();
+ }
+
+ public static String getId(AccessibilityServiceInfo info) {
+ return info.getId();
+ }
+
+ public static ResolveInfo getResolveInfo(AccessibilityServiceInfo info) {
+ return info.getResolveInfo();
+ }
+
+ public static String getSettingsActivityName(AccessibilityServiceInfo info) {
+ return info.getSettingsActivityName();
+ }
+}
diff --git a/src/android/support/v4/os/ParcelableCompat.java b/src/android/support/v4/os/ParcelableCompat.java
new file mode 100644
index 0000000..eb40359
--- /dev/null
+++ b/src/android/support/v4/os/ParcelableCompat.java
@@ -0,0 +1,59 @@
+/*
+ * 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.os;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Helper for accessing features in {@link android.os.Parcelable}
+ * introduced after API level 4 in a backwards compatible fashion.
+ */
+public class ParcelableCompat {
+
+ /**
+ * Factory method for {@link android.os.Parcelable.Creator}.
+ *
+ * @param callbacks Creator callbacks implementation.
+ * @return New creator.
+ */
+ public static <T> Parcelable.Creator<T> newCreator(
+ ParcelableCompatCreatorCallbacks<T> callbacks) {
+ if (android.os.Build.VERSION.SDK_INT >= 13) {
+ ParcelableCompatCreatorHoneycombMR2Stub.instantiate(callbacks);
+ }
+ return new CompatCreator<T>(callbacks);
+ }
+
+ static class CompatCreator<T> implements Parcelable.Creator<T> {
+ final ParcelableCompatCreatorCallbacks<T> mCallbacks;
+
+ public CompatCreator(ParcelableCompatCreatorCallbacks<T> callbacks) {
+ mCallbacks = callbacks;
+ }
+
+ @Override
+ public T createFromParcel(Parcel source) {
+ return mCallbacks.createFromParcel(source, null);
+ }
+
+ @Override
+ public T[] newArray(int size) {
+ return mCallbacks.newArray(size);
+ }
+ }
+}
diff --git a/src/android/support/v4/os/ParcelableCompatCreatorCallbacks.java b/src/android/support/v4/os/ParcelableCompatCreatorCallbacks.java
new file mode 100644
index 0000000..a577d6f
--- /dev/null
+++ b/src/android/support/v4/os/ParcelableCompatCreatorCallbacks.java
@@ -0,0 +1,47 @@
+/*
+ * 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.os;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Callbacks a {@link Parcelable} creator should implement.
+ */
+public interface ParcelableCompatCreatorCallbacks<T> {
+
+ /**
+ * Create a new instance of the Parcelable class, instantiating it
+ * from the given Parcel whose data had previously been written by
+ * {@link Parcelable#writeToParcel Parcelable.writeToParcel()} and
+ * using the given ClassLoader.
+ *
+ * @param in The Parcel to read the object's data from.
+ * @param loader The ClassLoader that this object is being created in.
+ * @return Returns a new instance of the Parcelable class.
+ */
+ public T createFromParcel(Parcel in, ClassLoader loader);
+
+ /**
+ * Create a new array of the Parcelable class.
+ *
+ * @param size Size of the array.
+ * @return Returns an array of the Parcelable class, with every entry
+ * initialized to null.
+ */
+ public T[] newArray(int size);
+}
diff --git a/src/android/support/v4/os/ParcelableCompatHoneycombMR2.java b/src/android/support/v4/os/ParcelableCompatHoneycombMR2.java
new file mode 100644
index 0000000..dd7ea81
--- /dev/null
+++ b/src/android/support/v4/os/ParcelableCompatHoneycombMR2.java
@@ -0,0 +1,49 @@
+/*
+ * 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.os;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+class ParcelableCompatCreatorHoneycombMR2Stub {
+ static <T> Parcelable.Creator<T> instantiate(ParcelableCompatCreatorCallbacks<T> callbacks) {
+ return new ParcelableCompatCreatorHoneycombMR2<T>(callbacks);
+ }
+}
+
+class ParcelableCompatCreatorHoneycombMR2<T> implements Parcelable.ClassLoaderCreator<T> {
+ private final ParcelableCompatCreatorCallbacks<T> mCallbacks;
+
+ public ParcelableCompatCreatorHoneycombMR2(ParcelableCompatCreatorCallbacks<T> callbacks) {
+ mCallbacks = callbacks;
+ }
+
+ @Override
+ public T createFromParcel(Parcel in) {
+ return mCallbacks.createFromParcel(in, null);
+ }
+
+ @Override
+ public T createFromParcel(Parcel in, ClassLoader loader) {
+ return mCallbacks.createFromParcel(in, loader);
+ }
+
+ @Override
+ public T[] newArray(int size) {
+ return mCallbacks.newArray(size);
+ }
+}
diff --git a/src/android/support/v4/view/AccessibilityDelegateCompat.java b/src/android/support/v4/view/AccessibilityDelegateCompat.java
new file mode 100644
index 0000000..59d823d
--- /dev/null
+++ b/src/android/support/v4/view/AccessibilityDelegateCompat.java
@@ -0,0 +1,433 @@
+/*
+ * 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.os.Build;
+import android.os.Bundle;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
+import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+
+/**
+ * Helper for accessing {@link android.view.View.AccessibilityDelegate} introduced after
+ * API level 4 in a backwards compatible fashion.
+ */
+public class AccessibilityDelegateCompat {
+
+ static interface AccessibilityDelegateImpl {
+ public Object newAccessiblityDelegateDefaultImpl();
+ public Object newAccessiblityDelegateBridge(AccessibilityDelegateCompat listener);
+ public boolean dispatchPopulateAccessibilityEvent(Object delegate, View host,
+ AccessibilityEvent event);
+ public void onInitializeAccessibilityEvent(Object delegate, View host,
+ AccessibilityEvent event);
+ public void onInitializeAccessibilityNodeInfo(Object delegate, View host,
+ AccessibilityNodeInfoCompat info);
+ public void onPopulateAccessibilityEvent(Object delegate, View host,
+ AccessibilityEvent event);
+ public boolean onRequestSendAccessibilityEvent(Object delegate, ViewGroup host, View child,
+ AccessibilityEvent event);
+ public void sendAccessibilityEvent(Object delegate, View host, int eventType);
+ public void sendAccessibilityEventUnchecked(Object delegate, View host,
+ AccessibilityEvent event);
+ public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(Object delegate,
+ View host);
+ public boolean performAccessibilityAction(Object delegate, View host, int action,
+ Bundle args);
+ }
+
+ static class AccessibilityDelegateStubImpl implements AccessibilityDelegateImpl {
+ @Override
+ public Object newAccessiblityDelegateDefaultImpl() {
+ return null;
+ }
+
+ @Override
+ public Object newAccessiblityDelegateBridge(AccessibilityDelegateCompat listener) {
+ return null;
+ }
+
+ @Override
+ public boolean dispatchPopulateAccessibilityEvent(Object delegate, View host,
+ AccessibilityEvent event) {
+ return false;
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(Object delegate, View host,
+ AccessibilityEvent event) {
+ return;
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(Object delegate, View host,
+ AccessibilityNodeInfoCompat info) {
+ return;
+ }
+
+ @Override
+ public void onPopulateAccessibilityEvent(Object delegate, View host,
+ AccessibilityEvent event) {
+ return;
+ }
+
+ @Override
+ public boolean onRequestSendAccessibilityEvent(Object delegate, ViewGroup host, View child,
+ AccessibilityEvent event) {
+ return true;
+ }
+
+ @Override
+ public void sendAccessibilityEvent(Object delegate, View host, int eventType) {
+ return;
+ }
+
+ @Override
+ public void sendAccessibilityEventUnchecked(Object delegate, View host,
+ AccessibilityEvent event) {
+ return;
+ }
+
+ @Override
+ public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(Object delegate,
+ View host) {
+ return null;
+ }
+
+ @Override
+ public boolean performAccessibilityAction(Object delegate, View host, int action,
+ Bundle args) {
+ return false;
+ }
+ }
+
+ static class AccessibilityDelegateIcsImpl extends AccessibilityDelegateStubImpl {
+ @Override
+ public Object newAccessiblityDelegateDefaultImpl() {
+ return AccessibilityDelegateCompatIcs.newAccessibilityDelegateDefaultImpl();
+ }
+
+ @Override
+ public Object newAccessiblityDelegateBridge(final AccessibilityDelegateCompat compat) {
+ return AccessibilityDelegateCompatIcs.newAccessibilityDelegateBridge(
+ new AccessibilityDelegateCompatIcs.AccessibilityDelegateBridge() {
+ @Override
+ public boolean dispatchPopulateAccessibilityEvent(View host,
+ AccessibilityEvent event) {
+ return compat.dispatchPopulateAccessibilityEvent(host, event);
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
+ compat.onInitializeAccessibilityEvent(host, event);
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, Object info) {
+ compat.onInitializeAccessibilityNodeInfo(host,
+ new AccessibilityNodeInfoCompat(info));
+ }
+
+ @Override
+ public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+ compat.onPopulateAccessibilityEvent(host, event);
+ }
+
+ @Override
+ public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
+ AccessibilityEvent event) {
+ return compat.onRequestSendAccessibilityEvent(host, child, event);
+ }
+
+ @Override
+ public void sendAccessibilityEvent(View host, int eventType) {
+ compat.sendAccessibilityEvent(host, eventType);
+ }
+
+ @Override
+ public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
+ compat.sendAccessibilityEventUnchecked(host, event);
+ }
+ });
+ }
+
+ @Override
+ public boolean dispatchPopulateAccessibilityEvent(Object delegate, View host,
+ AccessibilityEvent event) {
+ return AccessibilityDelegateCompatIcs.dispatchPopulateAccessibilityEvent(delegate,
+ host, event);
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(Object delegate, View host,
+ AccessibilityEvent event) {
+ AccessibilityDelegateCompatIcs.onInitializeAccessibilityEvent(delegate, host, event);
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(Object delegate, View host,
+ AccessibilityNodeInfoCompat info) {
+ AccessibilityDelegateCompatIcs.onInitializeAccessibilityNodeInfo(delegate, host,
+ info.getInfo());
+ }
+
+ @Override
+ public void onPopulateAccessibilityEvent(Object delegate, View host,
+ AccessibilityEvent event) {
+ AccessibilityDelegateCompatIcs.onPopulateAccessibilityEvent(delegate, host, event);
+ }
+
+ @Override
+ public boolean onRequestSendAccessibilityEvent(Object delegate, ViewGroup host, View child,
+ AccessibilityEvent event) {
+ return AccessibilityDelegateCompatIcs.onRequestSendAccessibilityEvent(delegate, host,
+ child, event);
+ }
+
+ @Override
+ public void sendAccessibilityEvent(Object delegate, View host, int eventType) {
+ AccessibilityDelegateCompatIcs.sendAccessibilityEvent(delegate, host, eventType);
+ }
+
+ @Override
+ public void sendAccessibilityEventUnchecked(Object delegate, View host,
+ AccessibilityEvent event) {
+ AccessibilityDelegateCompatIcs.sendAccessibilityEventUnchecked(delegate, host, event);
+ }
+ }
+
+ private static final AccessibilityDelegateImpl IMPL;
+ private static final Object DEFAULT_DELEGATE;
+
+ static {
+ if (Build.VERSION.SDK_INT >= 14) { // ICS
+ IMPL = new AccessibilityDelegateIcsImpl();
+ } else {
+ IMPL = new AccessibilityDelegateStubImpl();
+ }
+ DEFAULT_DELEGATE = IMPL.newAccessiblityDelegateDefaultImpl();
+ }
+
+ final Object mBridge;
+
+ /**
+ * Creates a new instance.
+ */
+ public AccessibilityDelegateCompat() {
+ mBridge = IMPL.newAccessiblityDelegateBridge(this);
+ }
+
+ /**
+ * @return The wrapped bridge implementation.
+ */
+ Object getBridge() {
+ return mBridge;
+ }
+
+ /**
+ * Sends an accessibility event of the given type. If accessibility is not
+ * enabled this method has no effect.
+ * <p>
+ * The default implementation behaves as {@link View#sendAccessibilityEvent(int)
+ * View#sendAccessibilityEvent(int)} for the case of no accessibility delegate
+ * been set.
+ * </p>
+ *
+ * @param host The View hosting the delegate.
+ * @param eventType The type of the event to send.
+ *
+ * @see View#sendAccessibilityEvent(int) View#sendAccessibilityEvent(int)
+ */
+ @SuppressWarnings("static-method")
+ public void sendAccessibilityEvent(View host, int eventType) {
+ IMPL.sendAccessibilityEvent(DEFAULT_DELEGATE, host, eventType);
+ }
+
+ /**
+ * Sends an accessibility event. This method behaves exactly as
+ * {@link #sendAccessibilityEvent(View, int)} but takes as an argument an
+ * empty {@link AccessibilityEvent} and does not perform a check whether
+ * accessibility is enabled.
+ * <p>
+ * The default implementation behaves as
+ * {@link View#sendAccessibilityEventUnchecked(AccessibilityEvent)
+ * View#sendAccessibilityEventUnchecked(AccessibilityEvent)} for
+ * the case of no accessibility delegate been set.
+ * </p>
+ *
+ * @param host The View hosting the delegate.
+ * @param event The event to send.
+ *
+ * @see View#sendAccessibilityEventUnchecked(AccessibilityEvent)
+ * View#sendAccessibilityEventUnchecked(AccessibilityEvent)
+ */
+ @SuppressWarnings("static-method")
+ public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
+ IMPL.sendAccessibilityEventUnchecked(DEFAULT_DELEGATE, host, event);
+ }
+
+ /**
+ * Dispatches an {@link AccessibilityEvent} to the host {@link View} first and then
+ * to its children for adding their text content to the event.
+ * <p>
+ * The default implementation behaves as
+ * {@link View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
+ * View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)} for
+ * the case of no accessibility delegate been set.
+ * </p>
+ *
+ * @param host The View hosting the delegate.
+ * @param event The event.
+ * @return True if the event population was completed.
+ *
+ * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
+ * View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
+ */
+ @SuppressWarnings("static-method")
+ public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+ return IMPL.dispatchPopulateAccessibilityEvent(DEFAULT_DELEGATE, host, event);
+ }
+
+ /**
+ * Gives a chance to the host View to populate the accessibility event with its
+ * text content.
+ * <p>
+ * The default implementation behaves as
+ * {@link ViewCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent)
+ * ViewCompat#onPopulateAccessibilityEvent(AccessibilityEvent)} for
+ * the case of no accessibility delegate been set.
+ * </p>
+ *
+ * @param host The View hosting the delegate.
+ * @param event The accessibility event which to populate.
+ *
+ * @see ViewCompat#onPopulateAccessibilityEvent(View ,AccessibilityEvent)
+ * ViewCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent)
+ */
+ @SuppressWarnings("static-method")
+ public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+ IMPL.onPopulateAccessibilityEvent(DEFAULT_DELEGATE, host, event);
+ }
+
+ /**
+ * Initializes an {@link AccessibilityEvent} with information about the
+ * the host View which is the event source.
+ * <p>
+ * The default implementation behaves as
+ * {@link ViewCompat#onInitializeAccessibilityEvent(View v, AccessibilityEvent event)
+ * ViewCompat#onInitalizeAccessibilityEvent(View v, AccessibilityEvent event)} for
+ * the case of no accessibility delegate been set.
+ * </p>
+ *
+ * @param host The View hosting the delegate.
+ * @param event The event to initialize.
+ *
+ * @see ViewCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent)
+ * ViewCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent)
+ */
+ @SuppressWarnings("static-method")
+ public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
+ IMPL.onInitializeAccessibilityEvent(DEFAULT_DELEGATE, host, event);
+ }
+
+ /**
+ * Initializes an {@link AccessibilityNodeInfoCompat} with information about the host view.
+ * <p>
+ * The default implementation behaves as
+ * {@link ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)
+ * ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)} for
+ * the case of no accessibility delegate been set.
+ * </p>
+ *
+ * @param host The View hosting the delegate.
+ * @param info The instance to initialize.
+ *
+ * @see ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)
+ * ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)
+ */
+ @SuppressWarnings("static-method")
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
+ IMPL.onInitializeAccessibilityNodeInfo(DEFAULT_DELEGATE, host, info);
+ }
+
+ /**
+ * Called when a child of the host View has requested sending an
+ * {@link AccessibilityEvent} and gives an opportunity to the parent (the host)
+ * to augment the event.
+ * <p>
+ * The default implementation behaves as
+ * {@link ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)
+ * ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)} for
+ * the case of no accessibility delegate been set.
+ * </p>
+ *
+ * @param host The View hosting the delegate.
+ * @param child The child which requests sending the event.
+ * @param event The event to be sent.
+ * @return True if the event should be sent
+ *
+ * @see ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)
+ * ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)
+ */
+ @SuppressWarnings({"static-method", "javadoc"})
+ public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
+ AccessibilityEvent event) {
+ return IMPL.onRequestSendAccessibilityEvent(DEFAULT_DELEGATE, host, child, event);
+ }
+
+ /**
+ * Gets the provider for managing a virtual view hierarchy rooted at this View
+ * and reported to {@link android.accessibilityservice.AccessibilityService}s
+ * that explore the window content.
+ * <p>
+ * The default implementation behaves as
+ * {@link ViewCompat#getAccessibilityNodeProvider(View) ViewCompat#getAccessibilityNodeProvider(View)}
+ * for the case of no accessibility delegate been set.
+ * </p>
+ *
+ * @return The provider.
+ *
+ * @see AccessibilityNodeProviderCompat
+ */
+ @SuppressWarnings("static-method")
+ public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host) {
+ return IMPL.getAccessibilityNodeProvider(DEFAULT_DELEGATE, host);
+ }
+
+ /**
+ * Performs the specified accessibility action on the view. For
+ * possible accessibility actions look at {@link AccessibilityNodeInfoCompat}.
+ * <p>
+ * The default implementation behaves as
+ * {@link View#performAccessibilityAction(int, Bundle)
+ * View#performAccessibilityAction(int, Bundle)} for the case of
+ * no accessibility delegate been set.
+ * </p>
+ *
+ * @param action The action to perform.
+ * @return Whether the action was performed.
+ *
+ * @see View#performAccessibilityAction(int, Bundle)
+ * View#performAccessibilityAction(int, Bundle)
+ */
+ @SuppressWarnings("static-method")
+ public boolean performAccessibilityAction(View host, int action, Bundle args) {
+ return IMPL.performAccessibilityAction(DEFAULT_DELEGATE, host, action, args);
+ }
+}
diff --git a/src/android/support/v4/view/AccessibilityDelegateCompatIcs.java b/src/android/support/v4/view/AccessibilityDelegateCompatIcs.java
new file mode 100644
index 0000000..4adeb0c
--- /dev/null
+++ b/src/android/support/v4/view/AccessibilityDelegateCompatIcs.java
@@ -0,0 +1,119 @@
+/*
+ * 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.view.View;
+import android.view.View.AccessibilityDelegate;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+/**
+ * ICS specific AccessibilityDelegate API implementation.
+ */
+class AccessibilityDelegateCompatIcs {
+
+ public interface AccessibilityDelegateBridge {
+ public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event);
+ public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event);
+ public void onInitializeAccessibilityNodeInfo(View host, Object info);
+ public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event);
+ public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
+ AccessibilityEvent event);
+ public void sendAccessibilityEvent(View host, int eventType);
+ public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event);
+ }
+
+ public static Object newAccessibilityDelegateDefaultImpl() {
+ return new AccessibilityDelegate();
+ }
+
+ public static Object newAccessibilityDelegateBridge(final AccessibilityDelegateBridge bridge) {
+ return new AccessibilityDelegate() {
+ @Override
+ public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+ return bridge.dispatchPopulateAccessibilityEvent(host, event);
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
+ bridge.onInitializeAccessibilityEvent(host, event);
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ bridge.onInitializeAccessibilityNodeInfo(host, info);
+ }
+
+ @Override
+ public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+ bridge.onPopulateAccessibilityEvent(host, event);
+ }
+
+ @Override
+ public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
+ AccessibilityEvent event) {
+ return bridge.onRequestSendAccessibilityEvent(host, child, event);
+ }
+
+ @Override
+ public void sendAccessibilityEvent(View host, int eventType) {
+ bridge.sendAccessibilityEvent(host, eventType);
+ }
+
+ @Override
+ public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
+ bridge.sendAccessibilityEventUnchecked(host, event);
+ }
+ };
+ }
+
+ public static boolean dispatchPopulateAccessibilityEvent(Object delegate, View host,
+ AccessibilityEvent event) {
+ return ((AccessibilityDelegate) delegate).dispatchPopulateAccessibilityEvent(host, event);
+ }
+
+ public static void onInitializeAccessibilityEvent(Object delegate, View host,
+ AccessibilityEvent event) {
+ ((AccessibilityDelegate) delegate).onInitializeAccessibilityEvent(host, event);
+ }
+
+ public static void onInitializeAccessibilityNodeInfo(Object delegate, View host, Object info) {
+ ((AccessibilityDelegate) delegate).onInitializeAccessibilityNodeInfo(host,
+ (AccessibilityNodeInfo) info);
+ }
+
+ public static void onPopulateAccessibilityEvent(Object delegate, View host,
+ AccessibilityEvent event) {
+ ((AccessibilityDelegate) delegate).onPopulateAccessibilityEvent(host, event);
+ }
+
+ public static boolean onRequestSendAccessibilityEvent(Object delegate, ViewGroup host,
+ View child, AccessibilityEvent event) {
+ return ((AccessibilityDelegate) delegate).onRequestSendAccessibilityEvent(host, child,
+ event);
+ }
+
+ public static void sendAccessibilityEvent(Object delegate, View host, int eventType) {
+ ((AccessibilityDelegate) delegate).sendAccessibilityEvent(host, eventType);
+ }
+
+ public static void sendAccessibilityEventUnchecked(Object delegate, View host,
+ AccessibilityEvent event) {
+ ((AccessibilityDelegate) delegate).sendAccessibilityEventUnchecked(host, event);
+ }
+}
diff --git a/src/android/support/v4/view/KeyEventCompat.java b/src/android/support/v4/view/KeyEventCompat.java
new file mode 100644
index 0000000..7dd5428
--- /dev/null
+++ b/src/android/support/v4/view/KeyEventCompat.java
@@ -0,0 +1,215 @@
+/*
+ * 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.view.KeyEvent;
+import android.view.View;
+
+/**
+ * Helper for accessing features in {@link KeyEvent} introduced after
+ * API level 4 in a backwards compatible fashion.
+ */
+public class KeyEventCompat {
+ /**
+ * Interface for the full API.
+ */
+ interface KeyEventVersionImpl {
+ public int normalizeMetaState(int metaState);
+ public boolean metaStateHasModifiers(int metaState, int modifiers);
+ public boolean metaStateHasNoModifiers(int metaState);
+ public void startTracking(KeyEvent event);
+ public boolean isTracking(KeyEvent event);
+ public Object getKeyDispatcherState(View view);
+ public boolean dispatch(KeyEvent event, KeyEvent.Callback receiver, Object state,
+ Object target);
+ }
+
+ /**
+ * Interface implementation that doesn't use anything about v4 APIs.
+ */
+ static class BaseKeyEventVersionImpl implements KeyEventVersionImpl {
+ private static final int META_MODIFIER_MASK =
+ KeyEvent.META_SHIFT_ON | KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON
+ | KeyEvent.META_ALT_ON | KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON
+ | KeyEvent.META_SYM_ON;
+
+ // Mask of all lock key meta states.
+ private static final int META_ALL_MASK = META_MODIFIER_MASK;
+
+ private static int metaStateFilterDirectionalModifiers(int metaState,
+ int modifiers, int basic, int left, int right) {
+ final boolean wantBasic = (modifiers & basic) != 0;
+ final int directional = left | right;
+ final boolean wantLeftOrRight = (modifiers & directional) != 0;
+
+ if (wantBasic) {
+ if (wantLeftOrRight) {
+ throw new IllegalArgumentException("bad arguments");
+ }
+ return metaState & ~directional;
+ } else if (wantLeftOrRight) {
+ return metaState & ~basic;
+ } else {
+ return metaState;
+ }
+ }
+
+ @Override
+ public int normalizeMetaState(int metaState) {
+ int ms = metaState;
+ if ((ms & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON)) != 0) {
+ ms |= KeyEvent.META_SHIFT_ON;
+ }
+ if ((ms & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON)) != 0) {
+ ms |= KeyEvent.META_ALT_ON;
+ }
+ return ms & META_ALL_MASK;
+ }
+
+ @Override
+ public boolean metaStateHasModifiers(int metaState, int modifiers) {
+ int ms = metaState;
+ ms = normalizeMetaState(ms) & META_MODIFIER_MASK;
+ ms = metaStateFilterDirectionalModifiers(ms, modifiers,
+ KeyEvent.META_SHIFT_ON, KeyEvent.META_SHIFT_LEFT_ON, KeyEvent.META_SHIFT_RIGHT_ON);
+ ms = metaStateFilterDirectionalModifiers(ms, modifiers,
+ KeyEvent.META_ALT_ON, KeyEvent.META_ALT_LEFT_ON, KeyEvent.META_ALT_RIGHT_ON);
+ return ms == modifiers;
+ }
+
+ @Override
+ public boolean metaStateHasNoModifiers(int metaState) {
+ return (normalizeMetaState(metaState) & META_MODIFIER_MASK) == 0;
+ }
+
+ @Override
+ public void startTracking(KeyEvent event) {
+ return;
+ }
+
+ @Override
+ public boolean isTracking(KeyEvent event) {
+ return false;
+ }
+
+ @Override
+ public Object getKeyDispatcherState(View view) {
+ return null;
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public boolean dispatch(KeyEvent event, KeyEvent.Callback receiver, Object state,
+ Object target) {
+ return event.dispatch(receiver);
+ }
+ }
+
+ static class EclairKeyEventVersionImpl extends BaseKeyEventVersionImpl {
+ @Override
+ public void startTracking(KeyEvent event) {
+ KeyEventCompatEclair.startTracking(event);
+ }
+
+ @Override
+ public boolean isTracking(KeyEvent event) {
+ return KeyEventCompatEclair.isTracking(event);
+ }
+
+ @Override
+ public Object getKeyDispatcherState(View view) {
+ return KeyEventCompatEclair.getKeyDispatcherState(view);
+ }
+
+ @Override
+ public boolean dispatch(KeyEvent event, KeyEvent.Callback receiver, Object state,
+ Object target) {
+ return KeyEventCompatEclair.dispatch(event, receiver, state, target);
+ }
+ }
+
+ /**
+ * Interface implementation for devices with at least v11 APIs.
+ */
+ static class HoneycombKeyEventVersionImpl extends EclairKeyEventVersionImpl {
+ @Override
+ public int normalizeMetaState(int metaState) {
+ return KeyEventCompatHoneycomb.normalizeMetaState(metaState);
+ }
+
+ @Override
+ public boolean metaStateHasModifiers(int metaState, int modifiers) {
+ return KeyEventCompatHoneycomb.metaStateHasModifiers(metaState, modifiers);
+ }
+
+ @Override
+ public boolean metaStateHasNoModifiers(int metaState) {
+ return KeyEventCompatHoneycomb.metaStateHasNoModifiers(metaState);
+ }
+ }
+
+ /**
+ * Select the correct implementation to use for the current platform.
+ */
+ static final KeyEventVersionImpl IMPL;
+ static {
+ if (android.os.Build.VERSION.SDK_INT >= 11) {
+ IMPL = new HoneycombKeyEventVersionImpl();
+ } else {
+ IMPL = new BaseKeyEventVersionImpl();
+ }
+ }
+
+ // -------------------------------------------------------------------
+
+ public static int normalizeMetaState(int metaState) {
+ return IMPL.normalizeMetaState(metaState);
+ }
+
+ public static boolean metaStateHasModifiers(int metaState, int modifiers) {
+ return IMPL.metaStateHasModifiers(metaState, modifiers);
+ }
+
+ public static boolean metaStateHasNoModifiers(int metaState) {
+ return IMPL.metaStateHasNoModifiers(metaState);
+ }
+
+ public static boolean hasModifiers(KeyEvent event, int modifiers) {
+ return IMPL.metaStateHasModifiers(event.getMetaState(), modifiers);
+ }
+
+ public static boolean hasNoModifiers(KeyEvent event) {
+ return IMPL.metaStateHasNoModifiers(event.getMetaState());
+ }
+
+ public static void startTracking(KeyEvent event) {
+ IMPL.startTracking(event);
+ }
+
+ public static boolean isTracking(KeyEvent event) {
+ return IMPL.isTracking(event);
+ }
+
+ public static Object getKeyDispatcherState(View view) {
+ return IMPL.getKeyDispatcherState(view);
+ }
+
+ public static boolean dispatch(KeyEvent event, KeyEvent.Callback receiver, Object state,
+ Object target) {
+ return IMPL.dispatch(event, receiver, state, target);
+ }
+}
diff --git a/src/android/support/v4/view/KeyEventCompatEclair.java b/src/android/support/v4/view/KeyEventCompatEclair.java
new file mode 100644
index 0000000..b3b0d11
--- /dev/null
+++ b/src/android/support/v4/view/KeyEventCompatEclair.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 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.KeyEvent;
+import android.view.View;
+
+class KeyEventCompatEclair {
+ public static Object getKeyDispatcherState(View view) {
+ return view.getKeyDispatcherState();
+ }
+
+ public static boolean dispatch(KeyEvent event, KeyEvent.Callback receiver, Object state,
+ Object target) {
+ return event.dispatch(receiver, (KeyEvent.DispatcherState)state, target);
+ }
+
+ public static void startTracking(KeyEvent event) {
+ event.startTracking();
+ }
+
+ public static boolean isTracking(KeyEvent event) {
+ return event.isTracking();
+ }
+}
diff --git a/src/android/support/v4/view/KeyEventCompatHoneycomb.java b/src/android/support/v4/view/KeyEventCompatHoneycomb.java
new file mode 100644
index 0000000..8819357
--- /dev/null
+++ b/src/android/support/v4/view/KeyEventCompatHoneycomb.java
@@ -0,0 +1,36 @@
+/*
+ * 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.view.KeyEvent;
+
+/**
+ * Implementation of key event compatibility that can call Honeycomb APIs.
+ */
+class KeyEventCompatHoneycomb {
+ public static int normalizeMetaState(int metaState) {
+ return KeyEvent.normalizeMetaState(metaState);
+ }
+
+ public static boolean metaStateHasModifiers(int metaState, int modifiers) {
+ return KeyEvent.metaStateHasModifiers(metaState, modifiers);
+ }
+
+ public static boolean metaStateHasNoModifiers(int metaState) {
+ return KeyEvent.metaStateHasNoModifiers(metaState);
+ }
+}
diff --git a/src/android/support/v4/view/MotionEventCompat.java b/src/android/support/v4/view/MotionEventCompat.java
new file mode 100644
index 0000000..18e9e5d
--- /dev/null
+++ b/src/android/support/v4/view/MotionEventCompat.java
@@ -0,0 +1,238 @@
+/*
+ * 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.view.MotionEvent;
+
+/**
+ * Helper for accessing features in {@link MotionEvent} introduced
+ * after API level 4 in a backwards compatible fashion.
+ */
+public class MotionEventCompat {
+ /**
+ * Interface for the full API.
+ */
+ interface MotionEventVersionImpl {
+ public int findPointerIndex(MotionEvent event, int pointerId);
+ public int getPointerId(MotionEvent event, int pointerIndex);
+ public float getX(MotionEvent event, int pointerIndex);
+ public float getY(MotionEvent event, int pointerIndex);
+ public int getPointerCount(MotionEvent event);
+ }
+
+ /**
+ * Interface implementation that doesn't use anything about v4 APIs.
+ */
+ static class BaseMotionEventVersionImpl implements MotionEventVersionImpl {
+ @Override
+ public int findPointerIndex(MotionEvent event, int pointerId) {
+ if (pointerId == 0) {
+ // id 0 == index 0 and vice versa.
+ return 0;
+ }
+ return -1;
+ }
+ @Override
+ public int getPointerId(MotionEvent event, int pointerIndex) {
+ if (pointerIndex == 0) {
+ // index 0 == id 0 and vice versa.
+ return 0;
+ }
+ throw new IndexOutOfBoundsException("Pre-Eclair does not support multiple pointers");
+ }
+ @Override
+ public float getX(MotionEvent event, int pointerIndex) {
+ if (pointerIndex == 0) {
+ return event.getX();
+ }
+ throw new IndexOutOfBoundsException("Pre-Eclair does not support multiple pointers");
+ }
+ @Override
+ public float getY(MotionEvent event, int pointerIndex) {
+ if (pointerIndex == 0) {
+ return event.getY();
+ }
+ throw new IndexOutOfBoundsException("Pre-Eclair does not support multiple pointers");
+ }
+ @Override
+ public int getPointerCount(MotionEvent event) {
+ return 1;
+ }
+ }
+
+ /**
+ * Interface implementation for devices with at least v11 APIs.
+ */
+ static class EclairMotionEventVersionImpl implements MotionEventVersionImpl {
+ @Override
+ public int findPointerIndex(MotionEvent event, int pointerId) {
+ return MotionEventCompatEclair.findPointerIndex(event, pointerId);
+ }
+ @Override
+ public int getPointerId(MotionEvent event, int pointerIndex) {
+ return MotionEventCompatEclair.getPointerId(event, pointerIndex);
+ }
+ @Override
+ public float getX(MotionEvent event, int pointerIndex) {
+ return MotionEventCompatEclair.getX(event, pointerIndex);
+ }
+ @Override
+ public float getY(MotionEvent event, int pointerIndex) {
+ return MotionEventCompatEclair.getY(event, pointerIndex);
+ }
+ @Override
+ public int getPointerCount(MotionEvent event) {
+ return MotionEventCompatEclair.getPointerCount(event);
+ }
+ }
+
+ /**
+ * Select the correct implementation to use for the current platform.
+ */
+ static final MotionEventVersionImpl IMPL;
+ static {
+ if (android.os.Build.VERSION.SDK_INT >= 5) {
+ IMPL = new EclairMotionEventVersionImpl();
+ } else {
+ IMPL = new BaseMotionEventVersionImpl();
+ }
+ }
+
+ // -------------------------------------------------------------------
+
+ /**
+ * Synonym for {@link MotionEvent#ACTION_MASK}.
+ */
+ public static final int ACTION_MASK = 0xff;
+
+ /**
+ * Synonym for {@link MotionEvent#ACTION_POINTER_DOWN}.
+ */
+ public static final int ACTION_POINTER_DOWN = 5;
+
+ /**
+ * Synonym for {@link MotionEvent#ACTION_POINTER_UP}.
+ */
+ public static final int ACTION_POINTER_UP = 6;
+
+ /**
+ * Synonym for {@link MotionEvent#ACTION_HOVER_MOVE}.
+ */
+ public static final int ACTION_HOVER_MOVE = 7;
+
+ /**
+ * Synonym for {@link MotionEvent#ACTION_SCROLL}.
+ */
+ public static final int ACTION_SCROLL = 8;
+
+ /**
+ * Synonym for {@link MotionEvent#ACTION_POINTER_INDEX_MASK}.
+ */
+ public static final int ACTION_POINTER_INDEX_MASK = 0xff00;
+
+ /**
+ * Synonym for {@link MotionEvent#ACTION_POINTER_INDEX_SHIFT}.
+ */
+ public static final int ACTION_POINTER_INDEX_SHIFT = 8;
+
+ /**
+ * Constant for {@link #getActionMasked}: The pointer is not down but has entered the
+ * boundaries of a window or view.
+ * <p>
+ * This action is always delivered to the window or view under the pointer.
+ * </p><p>
+ * This action is not a touch event so it is delivered to
+ * {@link android.view.View#onGenericMotionEvent(MotionEvent)} rather than
+ * {@link android.view.View#onTouchEvent(MotionEvent)}.
+ * </p>
+ */
+ public static final int ACTION_HOVER_ENTER = 9;
+
+ /**
+ * Constant for {@link #getActionMasked}: The pointer is not down but has exited the
+ * boundaries of a window or view.
+ * <p>
+ * This action is always delivered to the window or view that was previously under the pointer.
+ * </p><p>
+ * This action is not a touch event so it is delivered to
+ * {@link android.view.View#onGenericMotionEvent(MotionEvent)} rather than
+ * {@link android.view.View#onTouchEvent(MotionEvent)}.
+ * </p>
+ */
+ public static final int ACTION_HOVER_EXIT = 10;
+
+ /**
+ * Call {@link MotionEvent#getAction}, returning only the {@link #ACTION_MASK}
+ * portion.
+ */
+ public static int getActionMasked(MotionEvent event) {
+ return event.getAction() & ACTION_MASK;
+ }
+
+ /**
+ * Call {@link MotionEvent#getAction}, returning only the pointer index
+ * portion
+ */
+ public static int getActionIndex(MotionEvent event) {
+ return (event.getAction() & ACTION_POINTER_INDEX_MASK)
+ >> ACTION_POINTER_INDEX_SHIFT;
+ }
+
+ /**
+ * Call {@link MotionEvent#findPointerIndex(int)}.
+ * If running on a pre-{@link android.os.Build.VERSION_CODES#ECLAIR} device,
+ * does nothing and returns -1.
+ */
+ public static int findPointerIndex(MotionEvent event, int pointerId) {
+ return IMPL.findPointerIndex(event, pointerId);
+ }
+
+ /**
+ * Call {@link MotionEvent#getPointerId(int)}.
+ * If running on a pre-{@link android.os.Build.VERSION_CODES#ECLAIR} device,
+ * {@link IndexOutOfBoundsException} is thrown.
+ */
+ public static int getPointerId(MotionEvent event, int pointerIndex) {
+ return IMPL.getPointerId(event, pointerIndex);
+ }
+
+ /**
+ * Call {@link MotionEvent#getX(int)}.
+ * If running on a pre-{@link android.os.Build.VERSION_CODES#ECLAIR} device,
+ * {@link IndexOutOfBoundsException} is thrown.
+ */
+ public static float getX(MotionEvent event, int pointerIndex) {
+ return IMPL.getX(event, pointerIndex);
+ }
+
+ /**
+ * Call {@link MotionEvent#getY(int)}.
+ * If running on a pre-{@link android.os.Build.VERSION_CODES#ECLAIR} device,
+ * {@link IndexOutOfBoundsException} is thrown.
+ */
+ public static float getY(MotionEvent event, int pointerIndex) {
+ return IMPL.getY(event, pointerIndex);
+ }
+
+ /**
+ * The number of pointers of data contained in this event. Always
+ * >= 1.
+ */
+ public static int getPointerCount(MotionEvent event) {
+ return IMPL.getPointerCount(event);
+ }
+}
diff --git a/src/android/support/v4/view/MotionEventCompatEclair.java b/src/android/support/v4/view/MotionEventCompatEclair.java
new file mode 100644
index 0000000..7ecfb5c
--- /dev/null
+++ b/src/android/support/v4/view/MotionEventCompatEclair.java
@@ -0,0 +1,40 @@
+/*
+ * 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.view.MotionEvent;
+
+/**
+ * Implementation of motion event compatibility that can call Eclair APIs.
+ */
+class MotionEventCompatEclair {
+ public static int findPointerIndex(MotionEvent event, int pointerId) {
+ return event.findPointerIndex(pointerId);
+ }
+ public static int getPointerId(MotionEvent event, int pointerIndex) {
+ return event.getPointerId(pointerIndex);
+ }
+ public static float getX(MotionEvent event, int pointerIndex) {
+ return event.getX(pointerIndex);
+ }
+ public static float getY(MotionEvent event, int pointerIndex) {
+ return event.getY(pointerIndex);
+ }
+ public static int getPointerCount(MotionEvent event) {
+ return event.getPointerCount();
+ }
+}
diff --git a/src/android/support/v4/view/PagerAdapter.java b/src/android/support/v4/view/PagerAdapter.java
new file mode 100644
index 0000000..c321292
--- /dev/null
+++ b/src/android/support/v4/view/PagerAdapter.java
@@ -0,0 +1,340 @@
+/*
+ * 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.database.DataSetObservable;
+import android.database.DataSetObserver;
+import android.os.Parcelable;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Base class providing the adapter to populate pages inside of
+ * a {@link ViewPager}. You will most likely want to use a more
+ * specific implementation of this, such as
+ * {@link android.support.v4.app.FragmentPagerAdapter} or
+ * {@link android.support.v4.app.FragmentStatePagerAdapter}.
+ *
+ * <p>When you implement a PagerAdapter, you must override the following methods
+ * at minimum:</p>
+ * <ul>
+ * <li>{@link #instantiateItem(ViewGroup, int)}</li>
+ * <li>{@link #destroyItem(ViewGroup, int, Object)}</li>
+ * <li>{@link #getCount()}</li>
+ * <li>{@link #isViewFromObject(View, Object)}</li>
+ * </ul>
+ *
+ * <p>PagerAdapter is more general than the adapters used for
+ * {@link android.widget.AdapterView AdapterViews}. Instead of providing a
+ * View recycling mechanism directly ViewPager uses callbacks to indicate the
+ * steps taken during an update. A PagerAdapter may implement a form of View
+ * recycling if desired or use a more sophisticated method of managing page
+ * Views such as Fragment transactions where each page is represented by its
+ * own Fragment.</p>
+ *
+ * <p>ViewPager associates each page with a key Object instead of working with
+ * Views directly. This key is used to track and uniquely identify a given page
+ * independent of its position in the adapter. A call to the PagerAdapter method
+ * {@link #startUpdate(ViewGroup)} indicates that the contents of the ViewPager
+ * are about to change. One or more calls to {@link #instantiateItem(ViewGroup, int)}
+ * and/or {@link #destroyItem(ViewGroup, int, Object)} will follow, and the end
+ * of an update will be signaled by a call to {@link #finishUpdate(ViewGroup)}.
+ * By the time {@link #finishUpdate(ViewGroup) finishUpdate} returns the views
+ * associated with the key objects returned by
+ * {@link #instantiateItem(ViewGroup, int) instantiateItem} should be added to
+ * the parent ViewGroup passed to these methods and the views associated with
+ * the keys passed to {@link #destroyItem(ViewGroup, int, Object) destroyItem}
+ * should be removed. The method {@link #isViewFromObject(View, Object)} identifies
+ * whether a page View is associated with a given key object.</p>
+ *
+ * <p>A very simple PagerAdapter may choose to use the page Views themselves
+ * as key objects, returning them from {@link #instantiateItem(ViewGroup, int)}
+ * after creation and adding them to the parent ViewGroup. A matching
+ * {@link #destroyItem(ViewGroup, int, Object)} implementation would remove the
+ * View from the parent ViewGroup and {@link #isViewFromObject(View, Object)}
+ * could be implemented as <code>return view == object;</code>.</p>
+ *
+ * <p>PagerAdapter supports data set changes. Data set changes must occur on the
+ * main thread and must end with a call to {@link #notifyDataSetChanged()} similar
+ * to AdapterView adapters derived from {@link android.widget.BaseAdapter}. A data
+ * set change may involve pages being added, removed, or changing position. The
+ * ViewPager will keep the current page active provided the adapter implements
+ * the method {@link #getItemPosition(Object)}.</p>
+ */
+@SuppressWarnings("javadoc")
+public abstract class PagerAdapter {
+ private DataSetObservable mObservable = new DataSetObservable();
+
+ public static final int POSITION_UNCHANGED = -1;
+ public static final int POSITION_NONE = -2;
+
+ /**
+ * Return the number of views available.
+ */
+ public abstract int getCount();
+
+ /**
+ * Called when a change in the shown pages is going to start being made.
+ * @param container The containing View which is displaying this adapter's
+ * page views.
+ */
+ public void startUpdate(ViewGroup container) {
+ startUpdate((View) container);
+ }
+
+ /**
+ * Create the page for the given position. The adapter is responsible
+ * for adding the view to the container given here, although it only
+ * must ensure this is done by the time it returns from
+ * {@link #finishUpdate(ViewGroup)}.
+ *
+ * @param container The containing View in which the page will be shown.
+ * @param position The page position to be instantiated.
+ * @return Returns an Object representing the new page. This does not
+ * need to be a View, but can be some other container of the page.
+ */
+ public Object instantiateItem(ViewGroup container, int position) {
+ return instantiateItem((View) container, position);
+ }
+
+ /**
+ * Remove a page for the given position. The adapter is responsible
+ * for removing the view from its container, although it only must ensure
+ * this is done by the time it returns from {@link #finishUpdate(ViewGroup)}.
+ *
+ * @param container The containing View from which the page will be removed.
+ * @param position The page position to be removed.
+ * @param object The same object that was returned by
+ * {@link #instantiateItem(View, int)}.
+ */
+ public void destroyItem(ViewGroup container, int position, Object object) {
+ destroyItem((View) container, position, object);
+ }
+
+ /**
+ * Called to inform the adapter of which item is currently considered to
+ * be the "primary", that is the one show to the user as the current page.
+ *
+ * @param container The containing View from which the page will be removed.
+ * @param position The page position that is now the primary.
+ * @param object The same object that was returned by
+ * {@link #instantiateItem(View, int)}.
+ */
+ public void setPrimaryItem(ViewGroup container, int position, Object object) {
+ setPrimaryItem((View) container, position, object);
+ }
+
+ /**
+ * Called when the a change in the shown pages has been completed. At this
+ * point you must ensure that all of the pages have actually been added or
+ * removed from the container as appropriate.
+ * @param container The containing View which is displaying this adapter's
+ * page views.
+ */
+ public void finishUpdate(ViewGroup container) {
+ finishUpdate((View) container);
+ }
+
+ /**
+ * Called when a change in the shown pages is going to start being made.
+ * @param container The containing View which is displaying this adapter's
+ * page views.
+ *
+ * @deprecated Use {@link #startUpdate(ViewGroup)}
+ */
+ @Deprecated
+ @SuppressWarnings("static-method")
+ public void startUpdate(View container) {
+ return;
+ }
+
+ /**
+ * Create the page for the given position. The adapter is responsible
+ * for adding the view to the container given here, although it only
+ * must ensure this is done by the time it returns from
+ * {@link #finishUpdate(ViewGroup)}.
+ *
+ * @param container The containing View in which the page will be shown.
+ * @param position The page position to be instantiated.
+ * @return Returns an Object representing the new page. This does not
+ * need to be a View, but can be some other container of the page.
+ *
+ * @deprecated Use {@link #instantiateItem(ViewGroup, int)}
+ */
+ @Deprecated
+ @SuppressWarnings("static-method")
+ public Object instantiateItem(View container, int position) {
+ throw new UnsupportedOperationException(
+ "Required method instantiateItem was not overridden");
+ }
+
+ /**
+ * Remove a page for the given position. The adapter is responsible
+ * for removing the view from its container, although it only must ensure
+ * this is done by the time it returns from {@link #finishUpdate(View)}.
+ *
+ * @param container The containing View from which the page will be removed.
+ * @param position The page position to be removed.
+ * @param object The same object that was returned by
+ * {@link #instantiateItem(View, int)}.
+ *
+ * @deprecated Use {@link #destroyItem(ViewGroup, int, Object)}
+ */
+ @Deprecated
+ @SuppressWarnings("static-method")
+ public void destroyItem(View container, int position, Object object) {
+ throw new UnsupportedOperationException("Required method destroyItem was not overridden");
+ }
+
+ /**
+ * Called to inform the adapter of which item is currently considered to
+ * be the "primary", that is the one show to the user as the current page.
+ *
+ * @param container The containing View from which the page will be removed.
+ * @param position The page position that is now the primary.
+ * @param object The same object that was returned by
+ * {@link #instantiateItem(View, int)}.
+ *
+ * @deprecated Use {@link #setPrimaryItem(ViewGroup, int, Object)}
+ */
+ @Deprecated
+ @SuppressWarnings("static-method")
+ public void setPrimaryItem(View container, int position, Object object) {
+ return;
+ }
+
+ /**
+ * Called when the a change in the shown pages has been completed. At this
+ * point you must ensure that all of the pages have actually been added or
+ * removed from the container as appropriate.
+ * @param container The containing View which is displaying this adapter's
+ * page views.
+ *
+ * @deprecated Use {@link #finishUpdate(ViewGroup)}
+ */
+ @Deprecated
+ @SuppressWarnings("static-method")
+ public void finishUpdate(View container) {
+ return;
+ }
+
+ /**
+ * Determines whether a page View is associated with a specific key object
+ * as returned by {@link #instantiateItem(ViewGroup, int)}. This method is
+ * required for a PagerAdapter to function properly.
+ *
+ * @param view Page View to check for association with <code>object</code>
+ * @param object Object to check for association with <code>view</code>
+ * @return true if <code>view</code> is associated with the key object <code>object</code>
+ */
+ public abstract boolean isViewFromObject(View view, Object object);
+
+ /**
+ * Save any instance state associated with this adapter and its pages that should be
+ * restored if the current UI state needs to be reconstructed.
+ *
+ * @return Saved state for this adapter
+ */
+ @SuppressWarnings("static-method")
+ public Parcelable saveState() {
+ return null;
+ }
+
+ /**
+ * Restore any instance state associated with this adapter and its pages
+ * that was previously saved by {@link #saveState()}.
+ *
+ * @param state State previously saved by a call to {@link #saveState()}
+ * @param loader A ClassLoader that should be used to instantiate any restored objects
+ */
+ @SuppressWarnings("static-method")
+ public void restoreState(Parcelable state, ClassLoader loader) {
+ return;
+ }
+
+ /**
+ * Called when the host view is attempting to determine if an item's position
+ * has changed. Returns {@link #POSITION_UNCHANGED} if the position of the given
+ * item has not changed or {@link #POSITION_NONE} if the item is no longer present
+ * in the adapter.
+ *
+ * <p>The default implementation assumes that items will never
+ * change position and always returns {@link #POSITION_UNCHANGED}.
+ *
+ * @param object Object representing an item, previously returned by a call to
+ * {@link #instantiateItem(View, int)}.
+ * @return object's new position index from [0, {@link #getCount()}),
+ * {@link #POSITION_UNCHANGED} if the object's position has not changed,
+ * or {@link #POSITION_NONE} if the item is no longer present.
+ */
+ @SuppressWarnings("static-method")
+ public int getItemPosition(Object object) {
+ return POSITION_UNCHANGED;
+ }
+
+ /**
+ * This method should be called by the application if the data backing this adapter has changed
+ * and associated views should update.
+ */
+ public void notifyDataSetChanged() {
+ mObservable.notifyChanged();
+ }
+
+ /**
+ * Register an observer to receive callbacks related to the adapter's data changing.
+ *
+ * @param observer The {@link android.database.DataSetObserver} which will receive callbacks.
+ */
+ public void registerDataSetObserver(DataSetObserver observer) {
+ mObservable.registerObserver(observer);
+ }
+
+ /**
+ * Unregister an observer from callbacks related to the adapter's data changing.
+ *
+ * @param observer The {@link android.database.DataSetObserver} which will be unregistered.
+ */
+ public void unregisterDataSetObserver(DataSetObserver observer) {
+ mObservable.unregisterObserver(observer);
+ }
+
+ /**
+ * This method may be called by the ViewPager to obtain a title string
+ * to describe the specified page. This method may return null
+ * indicating no title for this page. The default implementation returns
+ * null.
+ *
+ * @param position The position of the title requested
+ * @return A title for the requested page
+ */
+ @SuppressWarnings("static-method")
+ public CharSequence getPageTitle(int position) {
+ return null;
+ }
+
+ /**
+ * Returns the proportional width of a given page as a percentage of the
+ * ViewPager's measured width from (0.f-1.f]
+ *
+ * @param position The position of the page requested
+ * @return Proportional width for the given page position
+ */
+ @SuppressWarnings("static-method")
+ public float getPageWidth(int position) {
+ return 1.f;
+ }
+}
diff --git a/src/android/support/v4/view/VelocityTrackerCompat.java b/src/android/support/v4/view/VelocityTrackerCompat.java
new file mode 100644
index 0000000..99286d0
--- /dev/null
+++ b/src/android/support/v4/view/VelocityTrackerCompat.java
@@ -0,0 +1,93 @@
+/*
+ * 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.view.VelocityTracker;
+
+/**
+ * Helper for accessing features in {@link VelocityTracker}
+ * introduced after API level 4 in a backwards compatible fashion.
+ */
+public class VelocityTrackerCompat {
+ /**
+ * Interface for the full API.
+ */
+ interface VelocityTrackerVersionImpl {
+ public float getXVelocity(VelocityTracker tracker, int pointerId);
+ public float getYVelocity(VelocityTracker tracker, int pointerId);
+ }
+
+ /**
+ * Interface implementation that doesn't use anything about v4 APIs.
+ */
+ static class BaseVelocityTrackerVersionImpl implements VelocityTrackerVersionImpl {
+ @Override
+ public float getXVelocity(VelocityTracker tracker, int pointerId) {
+ return tracker.getXVelocity();
+ }
+ @Override
+ public float getYVelocity(VelocityTracker tracker, int pointerId) {
+ return tracker.getYVelocity();
+ }
+ }
+
+ /**
+ * Interface implementation for devices with at least v11 APIs.
+ */
+ static class HoneycombVelocityTrackerVersionImpl implements VelocityTrackerVersionImpl {
+ @Override
+ public float getXVelocity(VelocityTracker tracker, int pointerId) {
+ return VelocityTrackerCompatHoneycomb.getXVelocity(tracker, pointerId);
+ }
+ @Override
+ public float getYVelocity(VelocityTracker tracker, int pointerId) {
+ return VelocityTrackerCompatHoneycomb.getYVelocity(tracker, pointerId);
+ }
+ }
+
+ /**
+ * Select the correct implementation to use for the current platform.
+ */
+ static final VelocityTrackerVersionImpl IMPL;
+ static {
+ if (android.os.Build.VERSION.SDK_INT >= 11) {
+ IMPL = new HoneycombVelocityTrackerVersionImpl();
+ } else {
+ IMPL = new BaseVelocityTrackerVersionImpl();
+ }
+ }
+
+ // -------------------------------------------------------------------
+
+ /**
+ * Call {@link VelocityTracker#getXVelocity(int)}.
+ * If running on a pre-{@link android.os.Build.VERSION_CODES#HONEYCOMB} device,
+ * returns {@link VelocityTracker#getXVelocity()}.
+ */
+ public static float getXVelocity(VelocityTracker tracker, int pointerId) {
+ return IMPL.getXVelocity(tracker, pointerId);
+ }
+
+ /**
+ * Call {@link VelocityTracker#getYVelocity(int)}.
+ * If running on a pre-{@link android.os.Build.VERSION_CODES#HONEYCOMB} device,
+ * returns {@link VelocityTracker#getYVelocity()}.
+ */
+ public static float getYVelocity(VelocityTracker tracker, int pointerId) {
+ return IMPL.getYVelocity(tracker, pointerId);
+ }
+}
diff --git a/src/android/support/v4/view/VelocityTrackerCompatHoneycomb.java b/src/android/support/v4/view/VelocityTrackerCompatHoneycomb.java
new file mode 100644
index 0000000..4f9d326
--- /dev/null
+++ b/src/android/support/v4/view/VelocityTrackerCompatHoneycomb.java
@@ -0,0 +1,31 @@
+/*
+ * 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.view.VelocityTracker;
+
+/**
+ * Implementation of velocity tracker compatibility that can call Honeycomb APIs.
+ */
+class VelocityTrackerCompatHoneycomb {
+ public static float getXVelocity(VelocityTracker tracker, int pointerId) {
+ return tracker.getXVelocity(pointerId);
+ }
+ public static float getYVelocity(VelocityTracker tracker, int pointerId) {
+ return tracker.getYVelocity(pointerId);
+ }
+}
diff --git a/src/android/support/v4/view/ViewCompat.java b/src/android/support/v4/view/ViewCompat.java
new file mode 100644
index 0000000..8dc9965
--- /dev/null
+++ b/src/android/support/v4/view/ViewCompat.java
@@ -0,0 +1,1112 @@
+/*
+ * 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.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
+import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat;
+import android.view.View;
+import android.view.ViewParent;
+import android.view.accessibility.AccessibilityEvent;
+
+/**
+ * Helper for accessing features in {@link View} introduced after API
+ * level 4 in a backwards compatible fashion.
+ */
+public class ViewCompat {
+ /**
+ * Always allow a user to over-scroll this view, provided it is a
+ * view that can scroll.
+ */
+ public static final int OVER_SCROLL_ALWAYS = 0;
+
+ /**
+ * Allow a user to over-scroll this view only if the content is large
+ * enough to meaningfully scroll, provided it is a view that can scroll.
+ */
+ public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1;
+
+ /**
+ * Never allow a user to over-scroll this view.
+ */
+ public static final int OVER_SCROLL_NEVER = 2;
+
+ private static final long FAKE_FRAME_TIME = 10;
+
+ /**
+ * Automatically determine whether a view is important for accessibility.
+ */
+ public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0x00000000;
+
+ /**
+ * The view is important for accessibility.
+ */
+ public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 0x00000001;
+
+ /**
+ * The view is not important for accessibility.
+ */
+ public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 0x00000002;
+
+ /**
+ * The view is not important for accessibility, nor are any of its
+ * descendant views.
+ */
+ public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 0x00000004;
+
+ /**
+ * Live region mode specifying that accessibility services should not
+ * automatically announce changes to this view. This is the default live
+ * region mode for most views.
+ * <p>
+ * Use with {@link ViewCompat#setAccessibilityLiveRegion(View, int)}.
+ */
+ public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0x00000000;
+
+ /**
+ * Live region mode specifying that accessibility services should announce
+ * changes to this view.
+ * <p>
+ * Use with {@link ViewCompat#setAccessibilityLiveRegion(View, int)}.
+ */
+ public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 0x00000001;
+
+ /**
+ * Live region mode specifying that accessibility services should interrupt
+ * ongoing speech to immediately announce changes to this view.
+ * <p>
+ * Use with {@link ViewCompat#setAccessibilityLiveRegion(View, int)}.
+ */
+ public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 0x00000002;
+
+ /**
+ * Indicates that the view does not have a layer.
+ */
+ public static final int LAYER_TYPE_NONE = 0;
+
+ /**
+ * <p>Indicates that the view has a software layer. A software layer is backed
+ * by a bitmap and causes the view to be rendered using Android's software
+ * rendering pipeline, even if hardware acceleration is enabled.</p>
+ *
+ * <p>Software layers have various usages:</p>
+ * <p>When the application is not using hardware acceleration, a software layer
+ * is useful to apply a specific color filter and/or blending mode and/or
+ * translucency to a view and all its children.</p>
+ * <p>When the application is using hardware acceleration, a software layer
+ * is useful to render drawing primitives not supported by the hardware
+ * accelerated pipeline. It can also be used to cache a complex view tree
+ * into a texture and reduce the complexity of drawing operations. For instance,
+ * when animating a complex view tree with a translation, a software layer can
+ * be used to render the view tree only once.</p>
+ * <p>Software layers should be avoided when the affected view tree updates
+ * often. Every update will require to re-render the software layer, which can
+ * potentially be slow (particularly when hardware acceleration is turned on
+ * since the layer will have to be uploaded into a hardware texture after every
+ * update.)</p>
+ */
+ public static final int LAYER_TYPE_SOFTWARE = 1;
+
+ /**
+ * <p>Indicates that the view has a hardware layer. A hardware layer is backed
+ * by a hardware specific texture (generally Frame Buffer Objects or FBO on
+ * OpenGL hardware) and causes the view to be rendered using Android's hardware
+ * rendering pipeline, but only if hardware acceleration is turned on for the
+ * view hierarchy. When hardware acceleration is turned off, hardware layers
+ * behave exactly as {@link #LAYER_TYPE_SOFTWARE software layers}.</p>
+ *
+ * <p>A hardware layer is useful to apply a specific color filter and/or
+ * blending mode and/or translucency to a view and all its children.</p>
+ * <p>A hardware layer can be used to cache a complex view tree into a
+ * texture and reduce the complexity of drawing operations. For instance,
+ * when animating a complex view tree with a translation, a hardware layer can
+ * be used to render the view tree only once.</p>
+ * <p>A hardware layer can also be used to increase the rendering quality when
+ * rotation transformations are applied on a view. It can also be used to
+ * prevent potential clipping issues when applying 3D transforms on a view.</p>
+ */
+ public static final int LAYER_TYPE_HARDWARE = 2;
+
+ /**
+ * Horizontal layout direction of this view is from Left to Right.
+ */
+ public static final int LAYOUT_DIRECTION_LTR = 0;
+
+ /**
+ * Horizontal layout direction of this view is from Right to Left.
+ */
+ public static final int LAYOUT_DIRECTION_RTL = 1;
+
+ /**
+ * Horizontal layout direction of this view is inherited from its parent.
+ * Use with {@link #setLayoutDirection}.
+ */
+ public static final int LAYOUT_DIRECTION_INHERIT = 2;
+
+ /**
+ * Horizontal layout direction of this view is from deduced from the default language
+ * script for the locale. Use with {@link #setLayoutDirection}.
+ */
+ public static final int LAYOUT_DIRECTION_LOCALE = 3;
+
+ /**
+ * Bits of {@link #getMeasuredWidthAndState} and
+ * {@link #getMeasuredWidthAndState} that provide the actual measured size.
+ */
+ public static final int MEASURED_SIZE_MASK = 0x00ffffff;
+
+ /**
+ * Bits of {@link #getMeasuredWidthAndState} and
+ * {@link #getMeasuredWidthAndState} that provide the additional state bits.
+ */
+ public static final int MEASURED_STATE_MASK = 0xff000000;
+
+ /**
+ * Bit shift of {@link #MEASURED_STATE_MASK} to get to the height bits
+ * for functions that combine both width and height into a single int,
+ * such as {@link #getMeasuredState} and the childState argument of
+ * {@link #resolveSizeAndState(int, int, int)}.
+ */
+ public static final int MEASURED_HEIGHT_STATE_SHIFT = 16;
+
+ /**
+ * Bit of {@link #getMeasuredWidthAndState} and
+ * {@link #getMeasuredWidthAndState} that indicates the measured size
+ * is smaller that the space the view would like to have.
+ */
+ public static final int MEASURED_STATE_TOO_SMALL = 0x01000000;
+
+ interface ViewCompatImpl {
+ public boolean canScrollHorizontally(View v, int direction);
+ public boolean canScrollVertically(View v, int direction);
+ public int getOverScrollMode(View v);
+ public void setOverScrollMode(View v, int mode);
+ public void onInitializeAccessibilityEvent(View v, AccessibilityEvent event);
+ public void onPopulateAccessibilityEvent(View v, AccessibilityEvent event);
+ public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info);
+ public void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate);
+ public boolean hasTransientState(View view);
+ public void setHasTransientState(View view, boolean hasTransientState);
+ public void postInvalidateOnAnimation(View view);
+ public void postInvalidateOnAnimation(View view, int left, int top, int right, int bottom);
+ public void postOnAnimation(View view, Runnable action);
+ public void postOnAnimationDelayed(View view, Runnable action, long delayMillis);
+ public int getImportantForAccessibility(View view);
+ public void setImportantForAccessibility(View view, int mode);
+ public boolean performAccessibilityAction(View view, int action, Bundle arguments);
+ public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View view);
+ public float getAlpha(View view);
+ public void setLayerType(View view, int layerType, Paint paint);
+ public int getLayerType(View view);
+ public int getLabelFor(View view);
+ public void setLabelFor(View view, int id);
+ public void setLayerPaint(View view, Paint paint);
+ public int getLayoutDirection(View view);
+ public void setLayoutDirection(View view, int layoutDirection);
+ public ViewParent getParentForAccessibility(View view);
+ public boolean isOpaque(View view);
+ public int resolveSizeAndState(int size, int measureSpec, int childMeasuredState);
+ public int getMeasuredWidthAndState(View view);
+ public int getMeasuredHeightAndState(View view);
+ public int getMeasuredState(View view);
+ public int getAccessibilityLiveRegion(View view);
+ public void setAccessibilityLiveRegion(View view, int mode);
+ }
+
+ static class BaseViewCompatImpl implements ViewCompatImpl {
+ @Override
+ public boolean canScrollHorizontally(View v, int direction) {
+ return false;
+ }
+ @Override
+ public boolean canScrollVertically(View v, int direction) {
+ return false;
+ }
+ @Override
+ public int getOverScrollMode(View v) {
+ return OVER_SCROLL_NEVER;
+ }
+ @Override
+ public void setOverScrollMode(View v, int mode) {
+ // Do nothing; API doesn't exist
+ }
+ @Override
+ public void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate) {
+ // Do nothing; API doesn't exist
+ }
+ @Override
+ public void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) {
+ // Do nothing; API doesn't exist
+ }
+ @Override
+ public void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) {
+ // Do nothing; API doesn't exist
+ }
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info) {
+ // Do nothing; API doesn't exist
+ }
+ @Override
+ public boolean hasTransientState(View view) {
+ // A view can't have transient state if transient state wasn't supported.
+ return false;
+ }
+ @Override
+ public void setHasTransientState(View view, boolean hasTransientState) {
+ // Do nothing; API doesn't exist
+ }
+ @Override
+ public void postInvalidateOnAnimation(View view) {
+ view.postInvalidateDelayed(getFrameTime());
+ }
+ @Override
+ public void postInvalidateOnAnimation(View view, int left, int top, int right, int bottom) {
+ view.postInvalidateDelayed(getFrameTime(), left, top, right, bottom);
+ }
+ @Override
+ public void postOnAnimation(View view, Runnable action) {
+ view.postDelayed(action, getFrameTime());
+ }
+ @Override
+ public void postOnAnimationDelayed(View view, Runnable action, long delayMillis) {
+ view.postDelayed(action, getFrameTime() + delayMillis);
+ }
+ @SuppressWarnings("static-method")
+ long getFrameTime() {
+ return FAKE_FRAME_TIME;
+ }
+ @Override
+ public int getImportantForAccessibility(View view) {
+ return 0;
+ }
+ @Override
+ public void setImportantForAccessibility(View view, int mode) {
+ return;
+ }
+ @Override
+ public boolean performAccessibilityAction(View view, int action, Bundle arguments) {
+ return false;
+ }
+ @Override
+ public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View view) {
+ return null;
+ }
+ @Override
+ public float getAlpha(View view) {
+ return 1.0f;
+ }
+ @Override
+ public void setLayerType(View view, int layerType, Paint paint) {
+ // No-op until layers became available (HC)
+ }
+ @Override
+ public int getLayerType(View view) {
+ return LAYER_TYPE_NONE;
+ }
+ @Override
+ public int getLabelFor(View view) {
+ return 0;
+ }
+ @Override
+ public void setLabelFor(View view, int id) {
+ return;
+ }
+ @Override
+ public void setLayerPaint(View view, Paint p) {
+ // No-op until layers became available (HC)
+ }
+
+ @Override
+ public int getLayoutDirection(View view) {
+ return LAYOUT_DIRECTION_LTR;
+ }
+
+ @Override
+ public void setLayoutDirection(View view, int layoutDirection) {
+ // No-op
+ }
+
+ @Override
+ public ViewParent getParentForAccessibility(View view) {
+ return view.getParent();
+ }
+
+ @Override
+ public boolean isOpaque(View view) {
+ final Drawable bg = view.getBackground();
+ if (bg != null) {
+ return bg.getOpacity() == PixelFormat.OPAQUE;
+ }
+ return false;
+ }
+
+ @Override
+ public int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
+ return View.resolveSize(size, measureSpec);
+ }
+
+ @Override
+ public int getMeasuredWidthAndState(View view) {
+ return view.getMeasuredWidth();
+ }
+
+ @Override
+ public int getMeasuredHeightAndState(View view) {
+ return view.getMeasuredHeight();
+ }
+
+ @Override
+ public int getMeasuredState(View view) {
+ return 0;
+ }
+
+ @Override
+ public int getAccessibilityLiveRegion(View view) {
+ return ACCESSIBILITY_LIVE_REGION_NONE;
+ }
+
+ @Override
+ public void setAccessibilityLiveRegion(View view, int mode) {
+ // No-op
+ }
+ }
+
+ static class EclairMr1ViewCompatImpl extends BaseViewCompatImpl {
+ @Override
+ public boolean isOpaque(View view) {
+ return ViewCompatEclairMr1.isOpaque(view);
+ }
+ }
+
+ static class GBViewCompatImpl extends EclairMr1ViewCompatImpl {
+ @Override
+ public int getOverScrollMode(View v) {
+ return ViewCompatGingerbread.getOverScrollMode(v);
+ }
+ @Override
+ public void setOverScrollMode(View v, int mode) {
+ ViewCompatGingerbread.setOverScrollMode(v, mode);
+ }
+ }
+
+ static class HCViewCompatImpl extends GBViewCompatImpl {
+ @Override
+ long getFrameTime() {
+ return ViewCompatHC.getFrameTime();
+ }
+ @Override
+ public float getAlpha(View view) {
+ return ViewCompatHC.getAlpha(view);
+ }
+ @Override
+ public void setLayerType(View view, int layerType, Paint paint) {
+ ViewCompatHC.setLayerType(view, layerType, paint);
+ }
+ @Override
+ public int getLayerType(View view) {
+ return ViewCompatHC.getLayerType(view);
+ }
+ @Override
+ public void setLayerPaint(View view, Paint paint) {
+ // Make sure the paint is correct; this will be cheap if it's the same
+ // instance as was used to call setLayerType earlier.
+ setLayerType(view, getLayerType(view), paint);
+ // This is expensive, but the only way to accomplish this before JB-MR1.
+ view.invalidate();
+ }
+ @Override
+ public int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
+ return ViewCompatHC.resolveSizeAndState(size, measureSpec, childMeasuredState);
+ }
+ @Override
+ public int getMeasuredWidthAndState(View view) {
+ return ViewCompatHC.getMeasuredWidthAndState(view);
+ }
+ @Override
+ public int getMeasuredHeightAndState(View view) {
+ return ViewCompatHC.getMeasuredHeightAndState(view);
+ }
+ @Override
+ public int getMeasuredState(View view) {
+ return ViewCompatHC.getMeasuredState(view);
+ }
+ }
+
+ static class ICSViewCompatImpl extends HCViewCompatImpl {
+ @Override
+ public boolean canScrollHorizontally(View v, int direction) {
+ return ViewCompatICS.canScrollHorizontally(v, direction);
+ }
+ @Override
+ public boolean canScrollVertically(View v, int direction) {
+ return ViewCompatICS.canScrollVertically(v, direction);
+ }
+ @Override
+ public void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) {
+ ViewCompatICS.onPopulateAccessibilityEvent(v, event);
+ }
+ @Override
+ public void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) {
+ ViewCompatICS.onInitializeAccessibilityEvent(v, event);
+ }
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info) {
+ ViewCompatICS.onInitializeAccessibilityNodeInfo(v, info.getInfo());
+ }
+ @Override
+ public void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate) {
+ ViewCompatICS.setAccessibilityDelegate(v, delegate.getBridge());
+ }
+ }
+
+ static final ViewCompatImpl IMPL;
+ static {
+ final int version = android.os.Build.VERSION.SDK_INT;
+ if (version >= 14) {
+ IMPL = new ICSViewCompatImpl();
+ } else if (version >= 11) {
+ IMPL = new HCViewCompatImpl();
+ } else if (version >= 9) {
+ IMPL = new GBViewCompatImpl();
+ } else {
+ IMPL = new BaseViewCompatImpl();
+ }
+ }
+
+ /**
+ * Check if this view can be scrolled horizontally in a certain direction.
+ *
+ * @param v The View against which to invoke the method.
+ * @param direction Negative to check scrolling left, positive to check scrolling right.
+ * @return true if this view can be scrolled in the specified direction, false otherwise.
+ */
+ public static boolean canScrollHorizontally(View v, int direction) {
+ return IMPL.canScrollHorizontally(v, direction);
+ }
+
+ /**
+ * Check if this view can be scrolled vertically in a certain direction.
+ *
+ * @param v The View against which to invoke the method.
+ * @param direction Negative to check scrolling up, positive to check scrolling down.
+ * @return true if this view can be scrolled in the specified direction, false otherwise.
+ */
+ public static boolean canScrollVertically(View v, int direction) {
+ return IMPL.canScrollVertically(v, direction);
+ }
+
+ /**
+ * Returns the over-scroll mode for this view. The result will be
+ * one of {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}
+ * (allow over-scrolling only if the view content is larger than the container),
+ * or {@link #OVER_SCROLL_NEVER}.
+ *
+ * @param v The View against which to invoke the method.
+ * @return This view's over-scroll mode.
+ */
+ public static int getOverScrollMode(View v) {
+ return IMPL.getOverScrollMode(v);
+ }
+
+ /**
+ * Set the over-scroll mode for this view. Valid over-scroll modes are
+ * {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}
+ * (allow over-scrolling only if the view content is larger than the container),
+ * or {@link #OVER_SCROLL_NEVER}.
+ *
+ * Setting the over-scroll mode of a view will have an effect only if the
+ * view is capable of scrolling.
+ *
+ * @param v The View against which to invoke the method.
+ * @param overScrollMode The new over-scroll mode for this view.
+ */
+ public static void setOverScrollMode(View v, int overScrollMode) {
+ IMPL.setOverScrollMode(v, overScrollMode);
+ }
+
+ /**
+ * Called from {@link View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)}
+ * giving a chance to this View to populate the accessibility event with its
+ * text content. While this method is free to modify event
+ * attributes other than text content, doing so should normally be performed in
+ * {@link View#onInitializeAccessibilityEvent(AccessibilityEvent)}.
+ * <p>
+ * Example: Adding formatted date string to an accessibility event in addition
+ * to the text added by the super implementation:
+ * <pre> public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+ * super.onPopulateAccessibilityEvent(event);
+ * final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY;
+ * String selectedDateUtterance = DateUtils.formatDateTime(mContext,
+ * mCurrentDate.getTimeInMillis(), flags);
+ * event.getText().add(selectedDateUtterance);
+ * }</pre>
+ * <p>
+ * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling
+ * {@link View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its
+ * {@link android.view.View.AccessibilityDelegate#onPopulateAccessibilityEvent(View,
+ * AccessibilityEvent)}
+ * is responsible for handling this call.
+ * </p>
+ * <p class="note"><strong>Note:</strong> Always call the super implementation before adding
+ * information to the event, in case the default implementation has basic information to add.
+ * </p>
+ *
+ * @param v The View against which to invoke the method.
+ * @param event The accessibility event which to populate.
+ *
+ * @see View#sendAccessibilityEvent(int)
+ * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
+ */
+ public static void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) {
+ IMPL.onPopulateAccessibilityEvent(v, event);
+ }
+
+ /**
+ * Initializes an {@link AccessibilityEvent} with information about
+ * this View which is the event source. In other words, the source of
+ * an accessibility event is the view whose state change triggered firing
+ * the event.
+ * <p>
+ * Example: Setting the password property of an event in addition
+ * to properties set by the super implementation:
+ * <pre> public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ * super.onInitializeAccessibilityEvent(event);
+ * event.setPassword(true);
+ * }</pre>
+ * <p>
+ * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling
+ * {@link View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its
+ * {@link android.view.View.AccessibilityDelegate#onInitializeAccessibilityEvent(View,
+ * AccessibilityEvent)}
+ * is responsible for handling this call.
+ * </p>
+ * <p class="note"><strong>Note:</strong> Always call the super implementation before adding
+ * information to the event, in case the default implementation has basic information to add.
+ * </p>
+ *
+ * @param v The View against which to invoke the method.
+ * @param event The event to initialize.
+ *
+ * @see View#sendAccessibilityEvent(int)
+ * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
+ */
+ public static void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) {
+ IMPL.onInitializeAccessibilityEvent(v, event);
+ }
+
+ /**
+ * Initializes an {@link android.view.accessibility.AccessibilityNodeInfo} with information
+ * about this view. The base implementation sets:
+ * <ul>
+ * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setParent(View)},</li>
+ * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setBoundsInParent(Rect)},</li>
+ * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setBoundsInScreen(Rect)},</li>
+ * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setPackageName(CharSequence)},</li>
+ * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setClassName(CharSequence)},</li>
+ * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setContentDescription(CharSequence)},</li>
+ * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setEnabled(boolean)},</li>
+ * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setClickable(boolean)},</li>
+ * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setFocusable(boolean)},</li>
+ * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setFocused(boolean)},</li>
+ * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setLongClickable(boolean)},</li>
+ * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setSelected(boolean)},</li>
+ * </ul>
+ * <p>
+ * Subclasses should override this method, call the super implementation,
+ * and set additional attributes.
+ * </p>
+ * <p>
+ * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling
+ * {@link View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its
+ * {@link android.view.View.AccessibilityDelegate#onInitializeAccessibilityNodeInfo(View,
+ * android.view.accessibility.AccessibilityNodeInfo)}
+ * is responsible for handling this call.
+ * </p>
+ *
+ * @param v The View against which to invoke the method.
+ * @param info The instance to initialize.
+ */
+ public static void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info) {
+ IMPL.onInitializeAccessibilityNodeInfo(v, info);
+ }
+
+ /**
+ * Sets a delegate for implementing accessibility support via compositon as
+ * opposed to inheritance. The delegate's primary use is for implementing
+ * backwards compatible widgets. For more details see
+ * {@link android.view.View.AccessibilityDelegate}.
+ *
+ * @param v The View against which to invoke the method.
+ * @param delegate The delegate instance.
+ *
+ * @see android.view.View.AccessibilityDelegate
+ */
+ public static void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate) {
+ IMPL.setAccessibilityDelegate(v, delegate);
+ }
+
+ /**
+ * Indicates whether the view is currently tracking transient state that the
+ * app should not need to concern itself with saving and restoring, but that
+ * the framework should take special note to preserve when possible.
+ *
+ * @param view View to check for transient state
+ * @return true if the view has transient state
+ */
+ public static boolean hasTransientState(View view) {
+ return IMPL.hasTransientState(view);
+ }
+
+ /**
+ * Set whether this view is currently tracking transient state that the
+ * framework should attempt to preserve when possible.
+ *
+ * @param view View tracking transient state
+ * @param hasTransientState true if this view has transient state
+ */
+ public static void setHasTransientState(View view, boolean hasTransientState) {
+ IMPL.setHasTransientState(view, hasTransientState);
+ }
+
+ /**
+ * <p>Cause an invalidate to happen on the next animation time step, typically the
+ * next display frame.</p>
+ *
+ * <p>This method can be invoked from outside of the UI thread
+ * only when this View is attached to a window.</p>
+ *
+ * @param view View to invalidate
+ */
+ public static void postInvalidateOnAnimation(View view) {
+ IMPL.postInvalidateOnAnimation(view);
+ }
+
+ /**
+ * <p>Cause an invalidate of the specified area to happen on the next animation
+ * time step, typically the next display frame.</p>
+ *
+ * <p>This method can be invoked from outside of the UI thread
+ * only when this View is attached to a window.</p>
+ *
+ * @param view View to invalidate
+ * @param left The left coordinate of the rectangle to invalidate.
+ * @param top The top coordinate of the rectangle to invalidate.
+ * @param right The right coordinate of the rectangle to invalidate.
+ * @param bottom The bottom coordinate of the rectangle to invalidate.
+ */
+ public static void postInvalidateOnAnimation(View view, int left, int top,
+ int right, int bottom) {
+ IMPL.postInvalidateOnAnimation(view, left, top, right, bottom);
+ }
+
+ /**
+ * <p>Causes the Runnable to execute on the next animation time step.
+ * The runnable will be run on the user interface thread.</p>
+ *
+ * <p>This method can be invoked from outside of the UI thread
+ * only when this View is attached to a window.</p>
+ *
+ * @param view View to post this Runnable to
+ * @param action The Runnable that will be executed.
+ */
+ public static void postOnAnimation(View view, Runnable action) {
+ IMPL.postOnAnimation(view, action);
+ }
+
+ /**
+ * <p>Causes the Runnable to execute on the next animation time step,
+ * after the specified amount of time elapses.
+ * The runnable will be run on the user interface thread.</p>
+ *
+ * <p>This method can be invoked from outside of the UI thread
+ * only when this View is attached to a window.</p>
+ *
+ * @param view The view to post this Runnable to
+ * @param action The Runnable that will be executed.
+ * @param delayMillis The delay (in milliseconds) until the Runnable
+ * will be executed.
+ */
+ public static void postOnAnimationDelayed(View view, Runnable action, long delayMillis) {
+ IMPL.postOnAnimationDelayed(view, action, delayMillis);
+ }
+
+ /**
+ * Gets the mode for determining whether this View is important for accessibility
+ * which is if it fires accessibility events and if it is reported to
+ * accessibility services that query the screen.
+ *
+ * @param view The view whose property to get.
+ * @return The mode for determining whether a View is important for accessibility.
+ *
+ * @see #IMPORTANT_FOR_ACCESSIBILITY_YES
+ * @see #IMPORTANT_FOR_ACCESSIBILITY_NO
+ * @see #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO
+ */
+ public static int getImportantForAccessibility(View view) {
+ return IMPL.getImportantForAccessibility(view);
+ }
+
+ /**
+ * Sets how to determine whether this view is important for accessibility
+ * which is if it fires accessibility events and if it is reported to
+ * accessibility services that query the screen.
+ *
+ * @param view The view whose property to set.
+ * @param mode How to determine whether this view is important for accessibility.
+ *
+ * @see #IMPORTANT_FOR_ACCESSIBILITY_YES
+ * @see #IMPORTANT_FOR_ACCESSIBILITY_NO
+ * @see #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO
+ */
+ public static void setImportantForAccessibility(View view, int mode) {
+ IMPL.setImportantForAccessibility(view, mode);
+ }
+
+ /**
+ * Performs the specified accessibility action on the view. For
+ * possible accessibility actions look at {@link AccessibilityNodeInfoCompat}.
+ * <p>
+ * If an {@link AccessibilityDelegateCompat} has been specified via calling
+ * {@link #setAccessibilityDelegate(View, AccessibilityDelegateCompat)} its
+ * {@link AccessibilityDelegateCompat#performAccessibilityAction(View, int, Bundle)}
+ * is responsible for handling this call.
+ * </p>
+ *
+ * @param action The action to perform.
+ * @param arguments Optional action arguments.
+ * @return Whether the action was performed.
+ */
+ public static boolean performAccessibilityAction(View view, int action, Bundle arguments) {
+ return IMPL.performAccessibilityAction(view, action, arguments);
+ }
+
+ /**
+ * Gets the provider for managing a virtual view hierarchy rooted at this View
+ * and reported to {@link android.accessibilityservice.AccessibilityService}s
+ * that explore the window content.
+ * <p>
+ * If this method returns an instance, this instance is responsible for managing
+ * {@link AccessibilityNodeInfoCompat}s describing the virtual sub-tree rooted at
+ * this View including the one representing the View itself. Similarly the returned
+ * instance is responsible for performing accessibility actions on any virtual
+ * view or the root view itself.
+ * </p>
+ * <p>
+ * If an {@link AccessibilityDelegateCompat} has been specified via calling
+ * {@link #setAccessibilityDelegate(View, AccessibilityDelegateCompat)} its
+ * {@link AccessibilityDelegateCompat#getAccessibilityNodeProvider(View)}
+ * is responsible for handling this call.
+ * </p>
+ *
+ * @param view The view whose property to get.
+ * @return The provider.
+ *
+ * @see AccessibilityNodeProviderCompat
+ */
+ public static AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View view) {
+ return IMPL.getAccessibilityNodeProvider(view);
+ }
+
+ /**
+ * The opacity of the view. This is a value from 0 to 1, where 0 means the view is
+ * completely transparent and 1 means the view is completely opaque.
+ *
+ * <p>By default this is 1.0f. Prior to API 11, the returned value is always 1.0f.
+ * @return The opacity of the view.
+ */
+ public static float getAlpha(View view) {
+ return IMPL.getAlpha(view);
+ }
+
+ /**
+ * <p>Specifies the type of layer backing this view. The layer can be
+ * {@link #LAYER_TYPE_NONE disabled}, {@link #LAYER_TYPE_SOFTWARE software} or
+ * {@link #LAYER_TYPE_HARDWARE hardware}.</p>
+ *
+ * <p>A layer is associated with an optional {@link android.graphics.Paint}
+ * instance that controls how the layer is composed on screen. The following
+ * properties of the paint are taken into account when composing the layer:</p>
+ * <ul>
+ * <li>{@link android.graphics.Paint#getAlpha() Translucency (alpha)}</li>
+ * <li>{@link android.graphics.Paint#getXfermode() Blending mode}</li>
+ * <li>{@link android.graphics.Paint#getColorFilter() Color filter}</li>
+ * </ul>
+ *
+ * <p>If this view has an alpha value set to < 1.0 by calling
+ * setAlpha(float), the alpha value of the layer's paint is replaced by
+ * this view's alpha value. Calling setAlpha(float) is therefore
+ * equivalent to setting a hardware layer on this view and providing a paint with
+ * the desired alpha value.<p>
+ *
+ * <p>Refer to the documentation of {@link #LAYER_TYPE_NONE disabled},
+ * {@link #LAYER_TYPE_SOFTWARE software} and {@link #LAYER_TYPE_HARDWARE hardware}
+ * for more information on when and how to use layers.</p>
+ *
+ * @param layerType The ype of layer to use with this view, must be one of
+ * {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
+ * {@link #LAYER_TYPE_HARDWARE}
+ * @param paint The paint used to compose the layer. This argument is optional
+ * and can be null. It is ignored when the layer type is
+ * {@link #LAYER_TYPE_NONE}
+ */
+ public static void setLayerType(View view, int layerType, Paint paint) {
+ IMPL.setLayerType(view, layerType, paint);
+ }
+
+ /**
+ * Indicates what type of layer is currently associated with this view. By default
+ * a view does not have a layer, and the layer type is {@link #LAYER_TYPE_NONE}.
+ * Refer to the documentation of
+ * {@link #setLayerType(android.view.View, int, android.graphics.Paint)}
+ * for more information on the different types of layers.
+ *
+ * @param view The view to fetch the layer type from
+ * @return {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
+ * {@link #LAYER_TYPE_HARDWARE}
+ *
+ * @see #setLayerType(android.view.View, int, android.graphics.Paint)
+ * @see #LAYER_TYPE_NONE
+ * @see #LAYER_TYPE_SOFTWARE
+ * @see #LAYER_TYPE_HARDWARE
+ */
+ public static int getLayerType(View view) {
+ return IMPL.getLayerType(view);
+ }
+
+ /**
+ * Gets the id of a view for which a given view serves as a label for
+ * accessibility purposes.
+ *
+ * @param view The view on which to invoke the corresponding method.
+ * @return The labeled view id.
+ */
+ public static int getLabelFor(View view) {
+ return IMPL.getLabelFor(view);
+ }
+
+ /**
+ * Sets the id of a view for which a given view serves as a label for
+ * accessibility purposes.
+ *
+ * @param view The view on which to invoke the corresponding method.
+ * @param labeledId The labeled view id.
+ */
+ public static void setLabelFor(View view, int labeledId) {
+ IMPL.setLabelFor(view, labeledId);
+ }
+
+ /**
+ * Updates the {@link Paint} object used with the current layer (used only if the current
+ * layer type is not set to {@link #LAYER_TYPE_NONE}). Changed properties of the Paint
+ * provided to {@link #setLayerType(android.view.View, int, android.graphics.Paint)}
+ * will be used the next time the View is redrawn, but
+ * {@link #setLayerPaint(android.view.View, android.graphics.Paint)}
+ * must be called to ensure that the view gets redrawn immediately.
+ *
+ * <p>A layer is associated with an optional {@link android.graphics.Paint}
+ * instance that controls how the layer is composed on screen. The following
+ * properties of the paint are taken into account when composing the layer:</p>
+ * <ul>
+ * <li>{@link android.graphics.Paint#getAlpha() Translucency (alpha)}</li>
+ * <li>{@link android.graphics.Paint#getXfermode() Blending mode}</li>
+ * <li>{@link android.graphics.Paint#getColorFilter() Color filter}</li>
+ * </ul>
+ *
+ * <p>If this view has an alpha value set to < 1.0 by calling
+ * View#setAlpha(float), the alpha value of the layer's paint is replaced by
+ * this view's alpha value. Calling View#setAlpha(float) is therefore
+ * equivalent to setting a hardware layer on this view and providing a paint with
+ * the desired alpha value.</p>
+ *
+ * @param view View to set a layer paint for
+ * @param paint The paint used to compose the layer. This argument is optional
+ * and can be null. It is ignored when the layer type is
+ * {@link #LAYER_TYPE_NONE}
+ *
+ * @see #setLayerType(View, int, android.graphics.Paint)
+ */
+ public static void setLayerPaint(View view, Paint paint) {
+ IMPL.setLayerPaint(view, paint);
+ }
+
+ /**
+ * Returns the resolved layout direction for this view.
+ *
+ * @param view View to get layout direction for
+ * @return {@link #LAYOUT_DIRECTION_RTL} if the layout direction is RTL or returns
+ * {@link #LAYOUT_DIRECTION_LTR} if the layout direction is not RTL.
+ *
+ * For compatibility, this will return {@link #LAYOUT_DIRECTION_LTR} if API version
+ * is lower than Jellybean MR1 (API 17)
+ */
+ public static int getLayoutDirection(View view) {
+ return IMPL.getLayoutDirection(view);
+ }
+
+ /**
+ * Set the layout direction for this view. This will propagate a reset of layout direction
+ * resolution to the view's children and resolve layout direction for this view.
+ *
+ * @param view View to set layout direction for
+ * @param layoutDirection the layout direction to set. Should be one of:
+ *
+ * {@link #LAYOUT_DIRECTION_LTR},
+ * {@link #LAYOUT_DIRECTION_RTL},
+ * {@link #LAYOUT_DIRECTION_INHERIT},
+ * {@link #LAYOUT_DIRECTION_LOCALE}.
+ *
+ * Resolution will be done if the value is set to LAYOUT_DIRECTION_INHERIT. The resolution
+ * proceeds up the parent chain of the view to get the value. If there is no parent, then it
+ * will return the default {@link #LAYOUT_DIRECTION_LTR}.
+ */
+ public static void setLayoutDirection(View view, int layoutDirection) {
+ IMPL.setLayoutDirection(view, layoutDirection);
+ }
+
+ /**
+ * Gets the parent for accessibility purposes. Note that the parent for
+ * accessibility is not necessary the immediate parent. It is the first
+ * predecessor that is important for accessibility.
+ *
+ * @param view View to retrieve parent for
+ * @return The parent for use in accessibility inspection
+ */
+ public static ViewParent getParentForAccessibility(View view) {
+ return IMPL.getParentForAccessibility(view);
+ }
+
+ /**
+ * Indicates whether this View is opaque. An opaque View guarantees that it will
+ * draw all the pixels overlapping its bounds using a fully opaque color.
+ *
+ * On API 7 and above this will call View's true isOpaque method. On previous platform
+ * versions it will check the opacity of the view's background drawable if present.
+ *
+ * @return True if this View is guaranteed to be fully opaque, false otherwise.
+ */
+ public static boolean isOpaque(View view) {
+ return IMPL.isOpaque(view);
+ }
+
+ /**
+ * Utility to reconcile a desired size and state, with constraints imposed
+ * by a MeasureSpec. Will take the desired size, unless a different size
+ * is imposed by the constraints. The returned value is a compound integer,
+ * with the resolved size in the {@link #MEASURED_SIZE_MASK} bits and
+ * optionally the bit {@link #MEASURED_STATE_TOO_SMALL} set if the resulting
+ * size is smaller than the size the view wants to be.
+ *
+ * @param size How big the view wants to be
+ * @param measureSpec Constraints imposed by the parent
+ * @return Size information bit mask as defined by
+ * {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}.
+ */
+ public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
+ return IMPL.resolveSizeAndState(size, measureSpec, childMeasuredState);
+ }
+
+ /**
+ * Return the full width measurement information for this view as computed
+ * by the most recent call to {@link android.view.View#measure(int, int)}.
+ * This result is a bit mask as defined by {@link #MEASURED_SIZE_MASK} and
+ * {@link #MEASURED_STATE_TOO_SMALL}.
+ * This should be used during measurement and layout calculations only. Use
+ * {@link android.view.View#getWidth()} to see how wide a view is after layout.
+ *
+ * @return The measured width of this view as a bit mask.
+ */
+ public static int getMeasuredWidthAndState(View view) {
+ return IMPL.getMeasuredWidthAndState(view);
+ }
+
+ /**
+ * Return the full height measurement information for this view as computed
+ * by the most recent call to {@link android.view.View#measure(int, int)}.
+ * This result is a bit mask as defined by {@link #MEASURED_SIZE_MASK} and
+ * {@link #MEASURED_STATE_TOO_SMALL}.
+ * This should be used during measurement and layout calculations only. Use
+ * {@link android.view.View#getHeight()} to see how wide a view is after layout.
+ *
+ * @return The measured width of this view as a bit mask.
+ */
+ public static int getMeasuredHeightAndState(View view) {
+ return IMPL.getMeasuredHeightAndState(view);
+ }
+
+ /**
+ * Return only the state bits of {@link #getMeasuredWidthAndState}
+ * and {@link #getMeasuredHeightAndState}, combined into one integer.
+ * The width component is in the regular bits {@link #MEASURED_STATE_MASK}
+ * and the height component is at the shifted bits
+ * {@link #MEASURED_HEIGHT_STATE_SHIFT}>>{@link #MEASURED_STATE_MASK}.
+ */
+ public static int getMeasuredState(View view) {
+ return IMPL.getMeasuredState(view);
+ }
+
+ /**
+ * Gets the live region mode for the specified View.
+ *
+ * @param view The view from which to obtain the live region mode
+ * @return The live region mode for the view.
+ *
+ * @see ViewCompat#setAccessibilityLiveRegion(View, int)
+ */
+ @SuppressWarnings("static-method")
+ public int getAccessibilityLiveRegion(View view) {
+ return IMPL.getAccessibilityLiveRegion(view);
+ }
+
+ /**
+ * Sets the live region mode for the specified view. This indicates to
+ * accessibility services whether they should automatically notify the user
+ * about changes to the view's content description or text, or to the
+ * content descriptions or text of the view's children (where applicable).
+ * <p>
+ * For example, in a login screen with a TextView that displays an "incorrect
+ * password" notification, that view should be marked as a live region with
+ * mode {@link #ACCESSIBILITY_LIVE_REGION_POLITE}.
+ * <p>
+ * To disable change notifications for this view, use
+ * {@link #ACCESSIBILITY_LIVE_REGION_NONE}. This is the default live region
+ * mode for most views.
+ * <p>
+ * To indicate that the user should be notified of changes, use
+ * {@link #ACCESSIBILITY_LIVE_REGION_POLITE}.
+ * <p>
+ * If the view's changes should interrupt ongoing speech and notify the user
+ * immediately, use {@link #ACCESSIBILITY_LIVE_REGION_ASSERTIVE}.
+ *
+ * @param view The view on which to set the live region mode
+ * @param mode The live region mode for this view, one of:
+ * <ul>
+ * <li>{@link #ACCESSIBILITY_LIVE_REGION_NONE}
+ * <li>{@link #ACCESSIBILITY_LIVE_REGION_POLITE}
+ * <li>{@link #ACCESSIBILITY_LIVE_REGION_ASSERTIVE}
+ * </ul>
+ */
+ @SuppressWarnings("static-method")
+ public void setAccessibilityLiveRegion(View view, int mode) {
+ IMPL.setAccessibilityLiveRegion(view, mode);
+ }
+}
diff --git a/src/android/support/v4/view/ViewCompatEclairMr1.java b/src/android/support/v4/view/ViewCompatEclairMr1.java
new file mode 100644
index 0000000..87000dd
--- /dev/null
+++ b/src/android/support/v4/view/ViewCompatEclairMr1.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2013 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 ViewCompatEclairMr1 {
+ public static boolean isOpaque(View view) {
+ return view.isOpaque();
+ }
+}
diff --git a/src/android/support/v4/view/ViewCompatGingerbread.java b/src/android/support/v4/view/ViewCompatGingerbread.java
new file mode 100644
index 0000000..f410a69
--- /dev/null
+++ b/src/android/support/v4/view/ViewCompatGingerbread.java
@@ -0,0 +1,29 @@
+/*
+ * 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.view.View;
+
+class ViewCompatGingerbread {
+ public static int getOverScrollMode(View v) {
+ return v.getOverScrollMode();
+ }
+
+ public static void setOverScrollMode(View v, int mode) {
+ v.setOverScrollMode(mode);
+ }
+}
diff --git a/src/android/support/v4/view/ViewCompatHC.java b/src/android/support/v4/view/ViewCompatHC.java
new file mode 100644
index 0000000..a237831
--- /dev/null
+++ b/src/android/support/v4/view/ViewCompatHC.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2012 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.animation.ValueAnimator;
+import android.graphics.Paint;
+import android.view.View;
+
+class ViewCompatHC {
+ static long getFrameTime() {
+ return ValueAnimator.getFrameDelay();
+ }
+
+ public static float getAlpha(View view) {
+ return view.getAlpha();
+ }
+
+ public static void setLayerType(View view, int layerType, Paint paint) {
+ view.setLayerType(layerType, paint);
+ }
+
+ public static int getLayerType(View view) {
+ return view.getLayerType();
+ }
+
+ public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
+ return View.resolveSizeAndState(size, measureSpec, childMeasuredState);
+ }
+
+ public static int getMeasuredWidthAndState(View view) {
+ return view.getMeasuredWidthAndState();
+ }
+
+ public static int getMeasuredHeightAndState(View view) {
+ return view.getMeasuredHeightAndState();
+ }
+
+ public static int getMeasuredState(View view) {
+ return view.getMeasuredState();
+ }
+}
diff --git a/src/android/support/v4/view/ViewCompatICS.java b/src/android/support/v4/view/ViewCompatICS.java
new file mode 100644
index 0000000..82aeaf3
--- /dev/null
+++ b/src/android/support/v4/view/ViewCompatICS.java
@@ -0,0 +1,52 @@
+/*
+ * 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.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.
+ */
+class ViewCompatICS {
+
+ public static boolean canScrollHorizontally(View v, int direction) {
+ return v.canScrollHorizontally(direction);
+ }
+
+ public static boolean canScrollVertically(View v, int direction) {
+ return v.canScrollVertically(direction);
+ }
+
+ public static void setAccessibilityDelegate(View v, Object delegate) {
+ v.setAccessibilityDelegate((AccessibilityDelegate) delegate);
+ }
+
+ public static void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) {
+ v.onPopulateAccessibilityEvent(event);
+ }
+
+ public static void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) {
+ v.onInitializeAccessibilityEvent(event);
+ }
+
+ public static void onInitializeAccessibilityNodeInfo(View v, Object info) {
+ v.onInitializeAccessibilityNodeInfo((AccessibilityNodeInfo) info);
+ }
+}
diff --git a/src/android/support/v4/view/ViewConfigurationCompat.java b/src/android/support/v4/view/ViewConfigurationCompat.java
new file mode 100644
index 0000000..fd26a27
--- /dev/null
+++ b/src/android/support/v4/view/ViewConfigurationCompat.java
@@ -0,0 +1,75 @@
+/*
+ * 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.view.ViewConfiguration;
+
+/**
+ * Helper for accessing features in {@link ViewConfiguration}
+ * introduced after API level 4 in a backwards compatible fashion.
+ */
+public class ViewConfigurationCompat {
+ /**
+ * Interface for the full API.
+ */
+ interface ViewConfigurationVersionImpl {
+ public int getScaledPagingTouchSlop(ViewConfiguration config);
+ }
+
+ /**
+ * Interface implementation that doesn't use anything about v4 APIs.
+ */
+ static class BaseViewConfigurationVersionImpl implements ViewConfigurationVersionImpl {
+ @Override
+ public int getScaledPagingTouchSlop(ViewConfiguration config) {
+ return config.getScaledTouchSlop();
+ }
+ }
+
+ /**
+ * Interface implementation for devices with at least v11 APIs.
+ */
+ static class FroyoViewConfigurationVersionImpl implements ViewConfigurationVersionImpl {
+ @Override
+ public int getScaledPagingTouchSlop(ViewConfiguration config) {
+ return ViewConfigurationCompatFroyo.getScaledPagingTouchSlop(config);
+ }
+ }
+
+ /**
+ * Select the correct implementation to use for the current platform.
+ */
+ static final ViewConfigurationVersionImpl IMPL;
+ static {
+ if (android.os.Build.VERSION.SDK_INT >= 11) {
+ IMPL = new FroyoViewConfigurationVersionImpl();
+ } else {
+ IMPL = new BaseViewConfigurationVersionImpl();
+ }
+ }
+
+ // -------------------------------------------------------------------
+
+ /**
+ * Call {@link ViewConfiguration#getScaledPagingTouchSlop()}.
+ * If running on a pre-{@link android.os.Build.VERSION_CODES#FROYO} device,
+ * returns {@link ViewConfiguration#getScaledTouchSlop()}.
+ */
+ public static int getScaledPagingTouchSlop(ViewConfiguration config) {
+ return IMPL.getScaledPagingTouchSlop(config);
+ }
+}
diff --git a/src/android/support/v4/view/ViewConfigurationCompatFroyo.java b/src/android/support/v4/view/ViewConfigurationCompatFroyo.java
new file mode 100644
index 0000000..e97a2a2
--- /dev/null
+++ b/src/android/support/v4/view/ViewConfigurationCompatFroyo.java
@@ -0,0 +1,28 @@
+/*
+ * 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.view.ViewConfiguration;
+
+/**
+ * Implementation of menu compatibility that can call Honeycomb APIs.
+ */
+class ViewConfigurationCompatFroyo {
+ public static int getScaledPagingTouchSlop(ViewConfiguration config) {
+ return config.getScaledPagingTouchSlop();
+ }
+}
diff --git a/src/android/support/v4/view/ViewPager.java b/src/android/support/v4/view/ViewPager.java
new file mode 100644
index 0000000..b67545d
--- /dev/null
+++ b/src/android/support/v4/view/ViewPager.java
@@ -0,0 +1,2908 @@
+/*
+ * 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.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.database.DataSetObserver;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.support.v4.os.ParcelableCompat;
+import android.support.v4.os.ParcelableCompatCreatorCallbacks;
+import android.support.v4.view.accessibility.AccessibilityEventCompat;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
+import android.support.v4.view.accessibility.AccessibilityRecordCompat;
+import android.support.v4.widget.EdgeEffectCompat;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.FocusFinder;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.SoundEffectConstants;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.animation.Interpolator;
+import android.widget.Scroller;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+/**
+ * Layout manager that allows the user to flip left and right
+ * through pages of data. You supply an implementation of a
+ * {@link PagerAdapter} to generate the pages that the view shows.
+ *
+ * <p>Note this class is currently under early design and
+ * development. The API will likely change in later updates of
+ * the compatibility library, requiring changes to the source code
+ * of apps when they are compiled against the newer version.</p>
+ *
+ * <p>ViewPager is most often used in conjunction with {@link android.app.Fragment},
+ * which is a convenient way to supply and manage the lifecycle of each page.
+ * There are standard adapters implemented for using fragments with the ViewPager,
+ * which cover the most common use cases. These are
+ * {@link android.support.v4.app.FragmentPagerAdapter} and
+ * {@link android.support.v4.app.FragmentStatePagerAdapter}; each of these
+ * classes have simple code showing how to build a full user interface
+ * with them.
+ *
+ * <p>Here is a more complicated example of ViewPager, using it in conjuction
+ * with {@link android.app.ActionBar} tabs. You can find other examples of using
+ * ViewPager in the API 4+ Support Demos and API 13+ Support Demos sample code.
+ *
+ * {@sample development/samples/Support13Demos/src/com/example/android/supportv13/app/ActionBarTabsPager.java
+ * complete}
+ */
+@SuppressWarnings("javadoc")
+public class ViewPager extends ViewGroup {
+ private static final String TAG = "ViewPager";
+ private static final boolean DEBUG = false;
+
+ private static final boolean USE_CACHE = false;
+
+ private static final int DEFAULT_OFFSCREEN_PAGES = 1;
+ private static final int MAX_SETTLE_DURATION = 600; // ms
+ private static final int MIN_DISTANCE_FOR_FLING = 25; // dips
+
+ private static final int DEFAULT_GUTTER_SIZE = 16; // dips
+
+ private static final int MIN_FLING_VELOCITY = 400; // dips
+
+ private static final int[] LAYOUT_ATTRS = new int[] {
+ android.R.attr.layout_gravity
+ };
+
+ /**
+ * Used to track what the expected number of items in the adapter should be.
+ * If the app changes this when we don't expect it, we'll throw a big obnoxious exception.
+ */
+ private int mExpectedAdapterCount;
+
+ static class ItemInfo {
+ Object object;
+ int position;
+ boolean scrolling;
+ float widthFactor;
+ float offset;
+ }
+
+ private static final Comparator<ItemInfo> COMPARATOR = new Comparator<ItemInfo>(){
+ @Override
+ public int compare(ItemInfo lhs, ItemInfo rhs) {
+ return lhs.position - rhs.position;
+ }
+ };
+
+ private static final Interpolator sInterpolator = new Interpolator() {
+ @Override
+ public float getInterpolation(float t) {
+ float tEx = t;
+ tEx -= 1.0f;
+ return tEx * tEx * tEx * tEx * tEx + 1.0f;
+ }
+ };
+
+ private final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
+ private final ItemInfo mTempItem = new ItemInfo();
+
+ private final Rect mTempRect = new Rect();
+
+ private PagerAdapter mAdapter;
+ private int mCurItem; // Index of currently displayed page.
+ private int mRestoredCurItem = -1;
+ private Parcelable mRestoredAdapterState = null;
+ private ClassLoader mRestoredClassLoader = null;
+ private Scroller mScroller;
+ private PagerObserver mObserver;
+
+ private int mPageMargin;
+ private Drawable mMarginDrawable;
+ private int mTopPageBounds;
+ private int mBottomPageBounds;
+
+ // Offsets of the first and last items, if known.
+ // Set during population, used to determine if we are at the beginning
+ // or end of the pager data set during touch scrolling.
+ private float mFirstOffset = -Float.MAX_VALUE;
+ private float mLastOffset = Float.MAX_VALUE;
+
+ private int mChildWidthMeasureSpec;
+ private int mChildHeightMeasureSpec;
+ private boolean mInLayout;
+
+ private boolean mScrollingCacheEnabled;
+
+ private boolean mPopulatePending;
+ private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES;
+
+ private boolean mIsBeingDragged;
+ private boolean mIsUnableToDrag;
+ private int mDefaultGutterSize;
+ private int mGutterSize;
+ private int mTouchSlop;
+ /**
+ * Position of the last motion event.
+ */
+ private float mLastMotionX;
+ private float mLastMotionY;
+ private float mInitialMotionX;
+ private float mInitialMotionY;
+ /**
+ * ID of the active pointer. This is used to retain consistency during
+ * drags/flings if multiple pointers are used.
+ */
+ private int mActivePointerId = INVALID_POINTER;
+ /**
+ * Sentinel value for no current active pointer.
+ * Used by {@link #mActivePointerId}.
+ */
+ private static final int INVALID_POINTER = -1;
+
+ /**
+ * Determines speed during touch scrolling
+ */
+ private VelocityTracker mVelocityTracker;
+ private int mMinimumVelocity;
+ private int mMaximumVelocity;
+ private int mFlingDistance;
+ private int mCloseEnough;
+
+ // If the pager is at least this close to its final position, complete the scroll
+ // on touch down and let the user interact with the content inside instead of
+ // "catching" the flinging pager.
+ private static final int CLOSE_ENOUGH = 2; // dp
+
+ private boolean mFakeDragging;
+ private long mFakeDragBeginTime;
+
+ private EdgeEffectCompat mLeftEdge;
+ private EdgeEffectCompat mRightEdge;
+
+ private boolean mFirstLayout = true;
+ private boolean mCalledSuper;
+ private int mDecorChildCount;
+
+ private OnPageChangeListener mOnPageChangeListener;
+ private OnPageChangeListener mInternalPageChangeListener;
+ private OnAdapterChangeListener mAdapterChangeListener;
+ private PageTransformer mPageTransformer;
+ private Method mSetChildrenDrawingOrderEnabled;
+
+ private static final int DRAW_ORDER_DEFAULT = 0;
+ private static final int DRAW_ORDER_FORWARD = 1;
+ private static final int DRAW_ORDER_REVERSE = 2;
+ private int mDrawingOrder;
+ private ArrayList<View> mDrawingOrderedChildren;
+ private static final ViewPositionComparator sPositionComparator = new ViewPositionComparator();
+
+ /**
+ * Indicates that the pager is in an idle, settled state. The current page
+ * is fully in view and no animation is in progress.
+ */
+ public static final int SCROLL_STATE_IDLE = 0;
+
+ /**
+ * Indicates that the pager is currently being dragged by the user.
+ */
+ public static final int SCROLL_STATE_DRAGGING = 1;
+
+ /**
+ * Indicates that the pager is in the process of settling to a final position.
+ */
+ public static final int SCROLL_STATE_SETTLING = 2;
+
+ private final Runnable mEndScrollRunnable = new Runnable() {
+ @Override
+ @SuppressWarnings("synthetic-access")
+ public void run() {
+ setScrollState(SCROLL_STATE_IDLE);
+ populate();
+ }
+ };
+
+ private int mScrollState = SCROLL_STATE_IDLE;
+
+ /**
+ * Callback interface for responding to changing state of the selected page.
+ */
+ public interface OnPageChangeListener {
+
+ /**
+ * This method will be invoked when the current page is scrolled, either as part
+ * of a programmatically initiated smooth scroll or a user initiated touch scroll.
+ *
+ * @param position Position index of the first page currently being displayed.
+ * Page position+1 will be visible if positionOffset is nonzero.
+ * @param positionOffset Value from [0, 1) indicating the offset from the page at position.
+ * @param positionOffsetPixels Value in pixels indicating the offset from position.
+ */
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);
+
+ /**
+ * This method will be invoked when a new page becomes selected. Animation is not
+ * necessarily complete.
+ *
+ * @param position Position index of the new selected page.
+ */
+ public void onPageSelected(int position);
+
+ /**
+ * Called when the scroll state changes. Useful for discovering when the user
+ * begins dragging, when the pager is automatically settling to the current page,
+ * or when it is fully stopped/idle.
+ *
+ * @param state The new scroll state.
+ * @see ViewPager#SCROLL_STATE_IDLE
+ * @see ViewPager#SCROLL_STATE_DRAGGING
+ * @see ViewPager#SCROLL_STATE_SETTLING
+ */
+ public void onPageScrollStateChanged(int state);
+ }
+
+ /**
+ * Simple implementation of the {@link OnPageChangeListener} interface with stub
+ * implementations of each method. Extend this if you do not intend to override
+ * every method of {@link OnPageChangeListener}.
+ */
+ public static class SimpleOnPageChangeListener implements OnPageChangeListener {
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ // This space for rent
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ // This space for rent
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ // This space for rent
+ }
+ }
+
+ /**
+ * A PageTransformer is invoked whenever a visible/attached page is scrolled.
+ * This offers an opportunity for the application to apply a custom transformation
+ * to the page views using animation properties.
+ *
+ * <p>As property animation is only supported as of Android 3.0 and forward,
+ * setting a PageTransformer on a ViewPager on earlier platform versions will
+ * be ignored.</p>
+ */
+ public interface PageTransformer {
+ /**
+ * Apply a property transformation to the given page.
+ *
+ * @param page Apply the transformation to this page
+ * @param position Position of page relative to the current front-and-center
+ * position of the pager. 0 is front and center. 1 is one full
+ * page position to the right, and -1 is one page position to the left.
+ */
+ public void transformPage(View page, float position);
+ }
+
+ /**
+ * Used internally to monitor when adapters are switched.
+ */
+ interface OnAdapterChangeListener {
+ public void onAdapterChanged(PagerAdapter oldAdapter, PagerAdapter newAdapter);
+ }
+
+ /**
+ * Used internally to tag special types of child views that should be added as
+ * pager decorations by default.
+ */
+ interface Decor {
+ // Empty
+ }
+
+ public ViewPager(Context context) {
+ super(context);
+ initViewPager();
+ }
+
+ public ViewPager(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initViewPager();
+ }
+
+ void initViewPager() {
+ setWillNotDraw(false);
+ setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
+ setFocusable(true);
+ final Context context = getContext();
+ mScroller = new Scroller(context, sInterpolator);
+ final ViewConfiguration configuration = ViewConfiguration.get(context);
+ final float density = context.getResources().getDisplayMetrics().density;
+
+ mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
+ mMinimumVelocity = (int) (MIN_FLING_VELOCITY * density);
+ mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+ mLeftEdge = new EdgeEffectCompat(context);
+ mRightEdge = new EdgeEffectCompat(context);
+
+ mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density);
+ mCloseEnough = (int) (CLOSE_ENOUGH * density);
+ mDefaultGutterSize = (int) (DEFAULT_GUTTER_SIZE * density);
+
+ ViewCompat.setAccessibilityDelegate(this, new MyAccessibilityDelegate());
+
+ if (ViewCompat.getImportantForAccessibility(this)
+ == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ ViewCompat.setImportantForAccessibility(this,
+ ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ removeCallbacks(mEndScrollRunnable);
+ super.onDetachedFromWindow();
+ }
+
+ private void setScrollState(int newState) {
+ if (mScrollState == newState) {
+ return;
+ }
+
+ mScrollState = newState;
+ if (mPageTransformer != null) {
+ // PageTransformers can do complex things that benefit from hardware layers.
+ enableLayers(newState != SCROLL_STATE_IDLE);
+ }
+ if (mOnPageChangeListener != null) {
+ mOnPageChangeListener.onPageScrollStateChanged(newState);
+ }
+ }
+
+ /**
+ * Set a PagerAdapter that will supply views for this pager as needed.
+ *
+ * @param adapter Adapter to use
+ */
+ @SuppressWarnings("synthetic-access")
+ public void setAdapter(PagerAdapter adapter) {
+ if (mAdapter != null) {
+ mAdapter.unregisterDataSetObserver(mObserver);
+ mAdapter.startUpdate(this);
+ for (int i = 0; i < mItems.size(); i++) {
+ final ItemInfo ii = mItems.get(i);
+ mAdapter.destroyItem(this, ii.position, ii.object);
+ }
+ mAdapter.finishUpdate(this);
+ mItems.clear();
+ removeNonDecorViews();
+ mCurItem = 0;
+ scrollTo(0, 0);
+ }
+
+ final PagerAdapter oldAdapter = mAdapter;
+ mAdapter = adapter;
+ mExpectedAdapterCount = 0;
+
+ if (mAdapter != null) {
+ if (mObserver == null) {
+ mObserver = new PagerObserver();
+ }
+ mAdapter.registerDataSetObserver(mObserver);
+ mPopulatePending = false;
+ final boolean wasFirstLayout = mFirstLayout;
+ mFirstLayout = true;
+ mExpectedAdapterCount = mAdapter.getCount();
+ if (mRestoredCurItem >= 0) {
+ mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader);
+ setCurrentItemInternal(mRestoredCurItem, false, true);
+ mRestoredCurItem = -1;
+ mRestoredAdapterState = null;
+ mRestoredClassLoader = null;
+ } else if (!wasFirstLayout) {
+ populate();
+ } else {
+ requestLayout();
+ }
+ }
+
+ if (mAdapterChangeListener != null && oldAdapter != adapter) {
+ mAdapterChangeListener.onAdapterChanged(oldAdapter, adapter);
+ }
+ }
+
+ private void removeNonDecorViews() {
+ for (int i = 0; i < getChildCount(); i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (!lp.isDecor) {
+ removeViewAt(i);
+ i--;
+ }
+ }
+ }
+
+ /**
+ * Retrieve the current adapter supplying pages.
+ *
+ * @return The currently registered PagerAdapter
+ */
+ public PagerAdapter getAdapter() {
+ return mAdapter;
+ }
+
+ void setOnAdapterChangeListener(OnAdapterChangeListener listener) {
+ mAdapterChangeListener = listener;
+ }
+
+ private int getClientWidth() {
+ return getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
+ }
+
+ /**
+ * Set the currently selected page. If the ViewPager has already been through its first
+ * layout with its current adapter there will be a smooth animated transition between
+ * the current item and the specified item.
+ *
+ * @param item Item index to select
+ */
+ public void setCurrentItem(int item) {
+ mPopulatePending = false;
+ setCurrentItemInternal(item, !mFirstLayout, false);
+ }
+
+ /**
+ * Set the currently selected page.
+ *
+ * @param item Item index to select
+ * @param smoothScroll True to smoothly scroll to the new item, false to transition immediately
+ */
+ public void setCurrentItem(int item, boolean smoothScroll) {
+ mPopulatePending = false;
+ setCurrentItemInternal(item, smoothScroll, false);
+ }
+
+ public int getCurrentItem() {
+ return mCurItem;
+ }
+
+ void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {
+ setCurrentItemInternal(item, smoothScroll, always, 0);
+ }
+
+ void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
+ int itemEx = item;
+ if (mAdapter == null || mAdapter.getCount() <= 0) {
+ setScrollingCacheEnabled(false);
+ return;
+ }
+ if (!always && mCurItem == itemEx && mItems.size() != 0) {
+ setScrollingCacheEnabled(false);
+ return;
+ }
+
+ if (itemEx < 0) {
+ itemEx = 0;
+ } else if (itemEx >= mAdapter.getCount()) {
+ itemEx = mAdapter.getCount() - 1;
+ }
+ final int pageLimit = mOffscreenPageLimit;
+ if (itemEx > (mCurItem + pageLimit) || itemEx < (mCurItem - pageLimit)) {
+ // We are doing a jump by more than one page. To avoid
+ // glitches, we want to keep all current pages in the view
+ // until the scroll ends.
+ for (int i=0; i<mItems.size(); i++) {
+ mItems.get(i).scrolling = true;
+ }
+ }
+ final boolean dispatchSelected = mCurItem != itemEx;
+
+ if (mFirstLayout) {
+ // We don't have any idea how big we are yet and shouldn't have any pages either.
+ // Just set things up and let the pending layout handle things.
+ mCurItem = itemEx;
+ if (dispatchSelected && mOnPageChangeListener != null) {
+ mOnPageChangeListener.onPageSelected(itemEx);
+ }
+ if (dispatchSelected && mInternalPageChangeListener != null) {
+ mInternalPageChangeListener.onPageSelected(itemEx);
+ }
+ requestLayout();
+ } else {
+ populate(itemEx);
+ scrollToItem(itemEx, smoothScroll, velocity, dispatchSelected);
+ }
+ }
+
+ private void scrollToItem(int item, boolean smoothScroll, int velocity,
+ boolean dispatchSelected) {
+ final ItemInfo curInfo = infoForPosition(item);
+ int destX = 0;
+ if (curInfo != null) {
+ final int width = getClientWidth();
+ destX = (int) (width * Math.max(mFirstOffset,
+ Math.min(curInfo.offset, mLastOffset)));
+ }
+ if (smoothScroll) {
+ smoothScrollTo(destX, 0, velocity);
+ if (dispatchSelected && mOnPageChangeListener != null) {
+ mOnPageChangeListener.onPageSelected(item);
+ }
+ if (dispatchSelected && mInternalPageChangeListener != null) {
+ mInternalPageChangeListener.onPageSelected(item);
+ }
+ } else {
+ if (dispatchSelected && mOnPageChangeListener != null) {
+ mOnPageChangeListener.onPageSelected(item);
+ }
+ if (dispatchSelected && mInternalPageChangeListener != null) {
+ mInternalPageChangeListener.onPageSelected(item);
+ }
+ completeScroll(false);
+ scrollTo(destX, 0);
+ pageScrolled(destX);
+ }
+ }
+
+ /**
+ * Set a listener that will be invoked whenever the page changes or is incrementally
+ * scrolled. See {@link OnPageChangeListener}.
+ *
+ * @param listener Listener to set
+ */
+ public void setOnPageChangeListener(OnPageChangeListener listener) {
+ mOnPageChangeListener = listener;
+ }
+
+ /**
+ * Set a {@link PageTransformer} that will be called for each attached page whenever
+ * the scroll position is changed. This allows the application to apply custom property
+ * transformations to each page, overriding the default sliding look and feel.
+ *
+ * <p><em>Note:</em> Prior to Android 3.0 the property animation APIs did not exist.
+ * As a result, setting a PageTransformer prior to Android 3.0 (API 11) will have no effect.</p>
+ *
+ * @param reverseDrawingOrder true if the supplied PageTransformer requires page views
+ * to be drawn from last to first instead of first to last.
+ * @param transformer PageTransformer that will modify each page's animation properties
+ */
+ public void setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer) {
+ if (Build.VERSION.SDK_INT >= 11) {
+ final boolean hasTransformer = transformer != null;
+ final boolean needsPopulate = hasTransformer != (mPageTransformer != null);
+ mPageTransformer = transformer;
+ setChildrenDrawingOrderEnabledCompat(hasTransformer);
+ if (hasTransformer) {
+ mDrawingOrder = reverseDrawingOrder ? DRAW_ORDER_REVERSE : DRAW_ORDER_FORWARD;
+ } else {
+ mDrawingOrder = DRAW_ORDER_DEFAULT;
+ }
+ if (needsPopulate) populate();
+ }
+ }
+
+ void setChildrenDrawingOrderEnabledCompat(boolean enable) {
+ if (Build.VERSION.SDK_INT >= 7) {
+ if (mSetChildrenDrawingOrderEnabled == null) {
+ try {
+ mSetChildrenDrawingOrderEnabled = ViewGroup.class.getDeclaredMethod(
+ "setChildrenDrawingOrderEnabled", new Class[] { Boolean.TYPE });
+ } catch (NoSuchMethodException e) {
+ Log.e(TAG, "Can't find setChildrenDrawingOrderEnabled", e);
+ }
+ }
+ try {
+ mSetChildrenDrawingOrderEnabled.invoke(this, Boolean.valueOf(enable));
+ } catch (Exception e) {
+ Log.e(TAG, "Error changing children drawing order", e);
+ }
+ }
+ }
+
+ @Override
+ protected int getChildDrawingOrder(int childCount, int i) {
+ final int index = mDrawingOrder == DRAW_ORDER_REVERSE ? childCount - 1 - i : i;
+ final int result = ((LayoutParams) mDrawingOrderedChildren.get(index).getLayoutParams()).childIndex;
+ return result;
+ }
+
+ /**
+ * Set a separate OnPageChangeListener for internal use by the support library.
+ *
+ * @param listener Listener to set
+ * @return The old listener that was set, if any.
+ */
+ OnPageChangeListener setInternalPageChangeListener(OnPageChangeListener listener) {
+ OnPageChangeListener oldListener = mInternalPageChangeListener;
+ mInternalPageChangeListener = listener;
+ return oldListener;
+ }
+
+ /**
+ * Returns the number of pages that will be retained to either side of the
+ * current page in the view hierarchy in an idle state. Defaults to 1.
+ *
+ * @return How many pages will be kept offscreen on either side
+ * @see #setOffscreenPageLimit(int)
+ */
+ public int getOffscreenPageLimit() {
+ return mOffscreenPageLimit;
+ }
+
+ /**
+ * Set the number of pages that should be retained to either side of the
+ * current page in the view hierarchy in an idle state. Pages beyond this
+ * limit will be recreated from the adapter when needed.
+ *
+ * <p>This is offered as an optimization. If you know in advance the number
+ * of pages you will need to support or have lazy-loading mechanisms in place
+ * on your pages, tweaking this setting can have benefits in perceived smoothness
+ * of paging animations and interaction. If you have a small number of pages (3-4)
+ * that you can keep active all at once, less time will be spent in layout for
+ * newly created view subtrees as the user pages back and forth.</p>
+ *
+ * <p>You should keep this limit low, especially if your pages have complex layouts.
+ * This setting defaults to 1.</p>
+ *
+ * @param limit How many pages will be kept offscreen in an idle state.
+ */
+ public void setOffscreenPageLimit(int limit) {
+ int limitEx = limit;
+ if (limitEx < DEFAULT_OFFSCREEN_PAGES) {
+ Log.w(TAG, "Requested offscreen page limit " + limitEx + " too small; defaulting to " +
+ DEFAULT_OFFSCREEN_PAGES);
+ limitEx = DEFAULT_OFFSCREEN_PAGES;
+ }
+ if (limitEx != mOffscreenPageLimit) {
+ mOffscreenPageLimit = limitEx;
+ populate();
+ }
+ }
+
+ /**
+ * Set the margin between pages.
+ *
+ * @param marginPixels Distance between adjacent pages in pixels
+ * @see #getPageMargin()
+ * @see #setPageMarginDrawable(Drawable)
+ * @see #setPageMarginDrawable(int)
+ */
+ public void setPageMargin(int marginPixels) {
+ final int oldMargin = mPageMargin;
+ mPageMargin = marginPixels;
+
+ final int width = getWidth();
+ recomputeScrollPosition(width, width, marginPixels, oldMargin);
+
+ requestLayout();
+ }
+
+ /**
+ * Return the margin between pages.
+ *
+ * @return The size of the margin in pixels
+ */
+ public int getPageMargin() {
+ return mPageMargin;
+ }
+
+ /**
+ * Set a drawable that will be used to fill the margin between pages.
+ *
+ * @param d Drawable to display between pages
+ */
+ public void setPageMarginDrawable(Drawable d) {
+ mMarginDrawable = d;
+ if (d != null) refreshDrawableState();
+ setWillNotDraw(d == null);
+ invalidate();
+ }
+
+ /**
+ * Set a drawable that will be used to fill the margin between pages.
+ *
+ * @param resId Resource ID of a drawable to display between pages
+ */
+ public void setPageMarginDrawable(int resId) {
+ setPageMarginDrawable(getContext().getResources().getDrawable(resId));
+ }
+
+ @Override
+ protected boolean verifyDrawable(Drawable who) {
+ return super.verifyDrawable(who) || who == mMarginDrawable;
+ }
+
+ @Override
+ protected void drawableStateChanged() {
+ super.drawableStateChanged();
+ final Drawable d = mMarginDrawable;
+ if (d != null && d.isStateful()) {
+ d.setState(getDrawableState());
+ }
+ }
+
+ // We want the duration of the page snap animation to be influenced by the distance that
+ // the screen has to travel, however, we don't want this duration to be effected in a
+ // purely linear fashion. Instead, we use this method to moderate the effect that the distance
+ // of travel has on the overall snap duration.
+ @SuppressWarnings("static-method")
+ float distanceInfluenceForSnapDuration(float f) {
+ float fEx = f;
+ fEx -= 0.5f; // center the values about 0.
+ fEx *= 0.3f * Math.PI / 2.0f;
+ return (float) Math.sin(fEx);
+ }
+
+ /**
+ * Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
+ *
+ * @param x the number of pixels to scroll by on the X axis
+ * @param y the number of pixels to scroll by on the Y axis
+ */
+ void smoothScrollTo(int x, int y) {
+ smoothScrollTo(x, y, 0);
+ }
+
+ /**
+ * Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
+ *
+ * @param x the number of pixels to scroll by on the X axis
+ * @param y the number of pixels to scroll by on the Y axis
+ * @param velocity the velocity associated with a fling, if applicable. (0 otherwise)
+ */
+ void smoothScrollTo(int x, int y, int velocity) {
+
+ if (getChildCount() == 0) {
+ // Nothing to do.
+ setScrollingCacheEnabled(false);
+ return;
+ }
+ int sx = getScrollX();
+ int sy = getScrollY();
+ int dx = x - sx;
+ int dy = y - sy;
+ if (dx == 0 && dy == 0) {
+ completeScroll(false);
+ populate();
+ setScrollState(SCROLL_STATE_IDLE);
+ return;
+ }
+
+ setScrollingCacheEnabled(true);
+ setScrollState(SCROLL_STATE_SETTLING);
+
+ final int width = getClientWidth();
+ final int halfWidth = width / 2;
+ final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width);
+ final float distance = halfWidth + halfWidth *
+ distanceInfluenceForSnapDuration(distanceRatio);
+
+ int duration = 0;
+ int velocityEx = Math.abs(velocity);
+ if (velocityEx > 0) {
+ duration = 4 * Math.round(1000 * Math.abs(distance / velocityEx));
+ } else {
+ final float pageWidth = width * mAdapter.getPageWidth(mCurItem);
+ final float pageDelta = Math.abs(dx) / (pageWidth + mPageMargin);
+ duration = (int) ((pageDelta + 1) * 100);
+ }
+ duration = Math.min(duration, MAX_SETTLE_DURATION);
+
+ mScroller.startScroll(sx, sy, dx, dy, duration);
+ ViewCompat.postInvalidateOnAnimation(this);
+ }
+
+ ItemInfo addNewItem(int position, int index) {
+ ItemInfo ii = new ItemInfo();
+ ii.position = position;
+ ii.object = mAdapter.instantiateItem(this, position);
+ ii.widthFactor = mAdapter.getPageWidth(position);
+ if (index < 0 || index >= mItems.size()) {
+ mItems.add(ii);
+ } else {
+ mItems.add(index, ii);
+ }
+ return ii;
+ }
+
+ void dataSetChanged() {
+ // This method only gets called if our observer is attached, so mAdapter is non-null.
+
+ final int adapterCount = mAdapter.getCount();
+ mExpectedAdapterCount = adapterCount;
+ boolean needPopulate = mItems.size() < mOffscreenPageLimit * 2 + 1 &&
+ mItems.size() < adapterCount;
+ int newCurrItem = mCurItem;
+
+ boolean isUpdating = false;
+ for (int i = 0; i < mItems.size(); i++) {
+ final ItemInfo ii = mItems.get(i);
+ final int newPos = mAdapter.getItemPosition(ii.object);
+
+ if (newPos == PagerAdapter.POSITION_UNCHANGED) {
+ continue;
+ }
+
+ if (newPos == PagerAdapter.POSITION_NONE) {
+ mItems.remove(i);
+ i--;
+
+ if (!isUpdating) {
+ mAdapter.startUpdate(this);
+ isUpdating = true;
+ }
+
+ mAdapter.destroyItem(this, ii.position, ii.object);
+ needPopulate = true;
+
+ if (mCurItem == ii.position) {
+ // Keep the current item in the valid range
+ newCurrItem = Math.max(0, Math.min(mCurItem, adapterCount - 1));
+ needPopulate = true;
+ }
+ continue;
+ }
+
+ if (ii.position != newPos) {
+ if (ii.position == mCurItem) {
+ // Our current item changed position. Follow it.
+ newCurrItem = newPos;
+ }
+
+ ii.position = newPos;
+ needPopulate = true;
+ }
+ }
+
+ if (isUpdating) {
+ mAdapter.finishUpdate(this);
+ }
+
+ Collections.sort(mItems, COMPARATOR);
+
+ if (needPopulate) {
+ // Reset our known page widths; populate will recompute them.
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (!lp.isDecor) {
+ lp.widthFactor = 0.f;
+ }
+ }
+
+ setCurrentItemInternal(newCurrItem, false, true);
+ requestLayout();
+ }
+ }
+
+ void populate() {
+ populate(mCurItem);
+ }
+
+ void populate(int newCurrentItem) {
+ ItemInfo oldCurInfo = null;
+ int focusDirection = View.FOCUS_FORWARD;
+ if (mCurItem != newCurrentItem) {
+ focusDirection = mCurItem < newCurrentItem ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
+ oldCurInfo = infoForPosition(mCurItem);
+ mCurItem = newCurrentItem;
+ }
+
+ if (mAdapter == null) {
+ sortChildDrawingOrder();
+ return;
+ }
+
+ // Bail now if we are waiting to populate. This is to hold off
+ // on creating views from the time the user releases their finger to
+ // fling to a new position until we have finished the scroll to
+ // that position, avoiding glitches from happening at that point.
+ if (mPopulatePending) {
+ if (DEBUG) Log.i(TAG, "populate is pending, skipping for now...");
+ sortChildDrawingOrder();
+ return;
+ }
+
+ // Also, don't populate until we are attached to a window. This is to
+ // avoid trying to populate before we have restored our view hierarchy
+ // state and conflicting with what is restored.
+ if (getWindowToken() == null) {
+ return;
+ }
+
+ mAdapter.startUpdate(this);
+
+ final int pageLimit = mOffscreenPageLimit;
+ final int startPos = Math.max(0, mCurItem - pageLimit);
+ final int N = mAdapter.getCount();
+ final int endPos = Math.min(N-1, mCurItem + pageLimit);
+
+ if (N != mExpectedAdapterCount) {
+ String resName;
+ try {
+ resName = getResources().getResourceName(getId());
+ } catch (Resources.NotFoundException e) {
+ resName = Integer.toHexString(getId());
+ }
+ throw new IllegalStateException("The application's PagerAdapter changed the adapter's" +
+ " contents without calling PagerAdapter#notifyDataSetChanged!" +
+ " Expected adapter item count: " + mExpectedAdapterCount + ", found: " + N +
+ " Pager id: " + resName +
+ " Pager class: " + getClass() +
+ " Problematic adapter: " + mAdapter.getClass());
+ }
+
+ // Locate the currently focused item or add it if needed.
+ int curIndex = -1;
+ ItemInfo curItem = null;
+ for (curIndex = 0; curIndex < mItems.size(); curIndex++) {
+ final ItemInfo ii = mItems.get(curIndex);
+ if (ii.position >= mCurItem) {
+ if (ii.position == mCurItem) curItem = ii;
+ break;
+ }
+ }
+
+ if (curItem == null && N > 0) {
+ curItem = addNewItem(mCurItem, curIndex);
+ }
+
+ // Fill 3x the available width or up to the number of offscreen
+ // pages requested to either side, whichever is larger.
+ // If we have no current item we have no work to do.
+ if (curItem != null) {
+ float extraWidthLeft = 0.f;
+ int itemIndex = curIndex - 1;
+ ItemInfo ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
+ final int clientWidth = getClientWidth();
+ final float leftWidthNeeded = clientWidth <= 0 ? 0 :
+ 2.f - curItem.widthFactor + (float) getPaddingLeft() / (float) clientWidth;
+ for (int pos = mCurItem - 1; pos >= 0; pos--) {
+ if (extraWidthLeft >= leftWidthNeeded && pos < startPos) {
+ if (ii == null) {
+ break;
+ }
+ if (pos == ii.position && !ii.scrolling) {
+ mItems.remove(itemIndex);
+ mAdapter.destroyItem(this, pos, ii.object);
+ if (DEBUG) {
+ Log.i(TAG, "populate() - destroyItem() with pos: " + pos +
+ " view: " + ii.object);
+ }
+ itemIndex--;
+ curIndex--;
+ ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
+ }
+ } else if (ii != null && pos == ii.position) {
+ extraWidthLeft += ii.widthFactor;
+ itemIndex--;
+ ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
+ } else {
+ ii = addNewItem(pos, itemIndex + 1);
+ extraWidthLeft += ii.widthFactor;
+ curIndex++;
+ ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
+ }
+ }
+
+ float extraWidthRight = curItem.widthFactor;
+ itemIndex = curIndex + 1;
+ if (extraWidthRight < 2.f) {
+ ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
+ final float rightWidthNeeded = clientWidth <= 0 ? 0 :
+ (float) getPaddingRight() / (float) clientWidth + 2.f;
+ for (int pos = mCurItem + 1; pos < N; pos++) {
+ if (extraWidthRight >= rightWidthNeeded && pos > endPos) {
+ if (ii == null) {
+ break;
+ }
+ if (pos == ii.position && !ii.scrolling) {
+ mItems.remove(itemIndex);
+ mAdapter.destroyItem(this, pos, ii.object);
+ if (DEBUG) {
+ Log.i(TAG, "populate() - destroyItem() with pos: " + pos +
+ " view: " + ii.object);
+ }
+ ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
+ }
+ } else if (ii != null && pos == ii.position) {
+ extraWidthRight += ii.widthFactor;
+ itemIndex++;
+ ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
+ } else {
+ ii = addNewItem(pos, itemIndex);
+ itemIndex++;
+ extraWidthRight += ii.widthFactor;
+ ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
+ }
+ }
+ }
+
+ calculatePageOffsets(curItem, curIndex, oldCurInfo);
+ }
+
+ if (DEBUG) {
+ Log.i(TAG, "Current page list:");
+ for (int i=0; i<mItems.size(); i++) {
+ Log.i(TAG, "#" + i + ": page " + mItems.get(i).position);
+ }
+ }
+
+ mAdapter.setPrimaryItem(this, mCurItem, curItem != null ? curItem.object : null);
+
+ mAdapter.finishUpdate(this);
+
+ // Check width measurement of current pages and drawing sort order.
+ // Update LayoutParams as needed.
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ lp.childIndex = i;
+ if (!lp.isDecor && lp.widthFactor == 0.f) {
+ // 0 means requery the adapter for this, it doesn't have a valid width.
+ final ItemInfo ii = infoForChild(child);
+ if (ii != null) {
+ lp.widthFactor = ii.widthFactor;
+ lp.position = ii.position;
+ }
+ }
+ }
+ sortChildDrawingOrder();
+
+ if (hasFocus()) {
+ View currentFocused = findFocus();
+ ItemInfo ii = currentFocused != null ? infoForAnyChild(currentFocused) : null;
+ if (ii == null || ii.position != mCurItem) {
+ for (int i=0; i<getChildCount(); i++) {
+ View child = getChildAt(i);
+ ii = infoForChild(child);
+ if (ii != null && ii.position == mCurItem) {
+ if (child.requestFocus(focusDirection)) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void sortChildDrawingOrder() {
+ if (mDrawingOrder != DRAW_ORDER_DEFAULT) {
+ if (mDrawingOrderedChildren == null) {
+ mDrawingOrderedChildren = new ArrayList<View>();
+ } else {
+ mDrawingOrderedChildren.clear();
+ }
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ mDrawingOrderedChildren.add(child);
+ }
+ Collections.sort(mDrawingOrderedChildren, sPositionComparator);
+ }
+ }
+
+ private void calculatePageOffsets(ItemInfo curItem, int curIndex, ItemInfo oldCurInfo) {
+ final int N = mAdapter.getCount();
+ final int width = getClientWidth();
+ final float marginOffset = width > 0 ? (float) mPageMargin / width : 0;
+ // Fix up offsets for later layout.
+ if (oldCurInfo != null) {
+ final int oldCurPosition = oldCurInfo.position;
+ // Base offsets off of oldCurInfo.
+ if (oldCurPosition < curItem.position) {
+ int itemIndex = 0;
+ ItemInfo ii = null;
+ float offset = oldCurInfo.offset + oldCurInfo.widthFactor + marginOffset;
+ for (int pos = oldCurPosition + 1;
+ pos <= curItem.position && itemIndex < mItems.size(); pos++) {
+ ii = mItems.get(itemIndex);
+ while (pos > ii.position && itemIndex < mItems.size() - 1) {
+ itemIndex++;
+ ii = mItems.get(itemIndex);
+ }
+ while (pos < ii.position) {
+ // We don't have an item populated for this,
+ // ask the adapter for an offset.
+ offset += mAdapter.getPageWidth(pos) + marginOffset;
+ pos++;
+ }
+ ii.offset = offset;
+ offset += ii.widthFactor + marginOffset;
+ }
+ } else if (oldCurPosition > curItem.position) {
+ int itemIndex = mItems.size() - 1;
+ ItemInfo ii = null;
+ float offset = oldCurInfo.offset;
+ for (int pos = oldCurPosition - 1;
+ pos >= curItem.position && itemIndex >= 0; pos--) {
+ ii = mItems.get(itemIndex);
+ while (pos < ii.position && itemIndex > 0) {
+ itemIndex--;
+ ii = mItems.get(itemIndex);
+ }
+ while (pos > ii.position) {
+ // We don't have an item populated for this,
+ // ask the adapter for an offset.
+ offset -= mAdapter.getPageWidth(pos) + marginOffset;
+ pos--;
+ }
+ offset -= ii.widthFactor + marginOffset;
+ ii.offset = offset;
+ }
+ }
+ }
+
+ // Base all offsets off of curItem.
+ final int itemCount = mItems.size();
+ float offset = curItem.offset;
+ int pos = curItem.position - 1;
+ mFirstOffset = curItem.position == 0 ? curItem.offset : -Float.MAX_VALUE;
+ mLastOffset = curItem.position == N - 1 ?
+ curItem.offset + curItem.widthFactor - 1 : Float.MAX_VALUE;
+ // Previous pages
+ for (int i = curIndex - 1; i >= 0; i--, pos--) {
+ final ItemInfo ii = mItems.get(i);
+ while (pos > ii.position) {
+ offset -= mAdapter.getPageWidth(pos--) + marginOffset;
+ }
+ offset -= ii.widthFactor + marginOffset;
+ ii.offset = offset;
+ if (ii.position == 0) mFirstOffset = offset;
+ }
+ offset = curItem.offset + curItem.widthFactor + marginOffset;
+ pos = curItem.position + 1;
+ // Next pages
+ for (int i = curIndex + 1; i < itemCount; i++, pos++) {
+ final ItemInfo ii = mItems.get(i);
+ while (pos < ii.position) {
+ offset += mAdapter.getPageWidth(pos++) + marginOffset;
+ }
+ if (ii.position == N - 1) {
+ mLastOffset = offset + ii.widthFactor - 1;
+ }
+ ii.offset = offset;
+ offset += ii.widthFactor + marginOffset;
+ }
+ }
+
+ /**
+ * This is the persistent state that is saved by ViewPager. Only needed
+ * if you are creating a sublass of ViewPager that must save its own
+ * state, in which case it should implement a subclass of this which
+ * contains that state.
+ */
+ public static class SavedState extends BaseSavedState {
+ int position;
+ Parcelable adapterState;
+ ClassLoader loader;
+
+ public SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeInt(position);
+ out.writeParcelable(adapterState, flags);
+ }
+
+ @Override
+ public String toString() {
+ return "FragmentPager.SavedState{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " position=" + position + "}";
+ }
+
+ @SuppressWarnings("hiding")
+ public static final Parcelable.Creator<SavedState> CREATOR
+ = ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks<SavedState>() {
+ @Override
+ public SavedState createFromParcel(Parcel in, ClassLoader loader) {
+ return new SavedState(in, loader);
+ }
+ @Override
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ });
+
+ SavedState(Parcel in, ClassLoader loader) {
+ super(in);
+ ClassLoader loaderEx = loader;
+ if (loaderEx == null) {
+ loaderEx = getClass().getClassLoader();
+ }
+ position = in.readInt();
+ adapterState = in.readParcelable(loaderEx);
+ this.loader = loaderEx;
+ }
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+ SavedState ss = new SavedState(superState);
+ ss.position = mCurItem;
+ if (mAdapter != null) {
+ ss.adapterState = mAdapter.saveState();
+ }
+ return ss;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ if (!(state instanceof SavedState)) {
+ super.onRestoreInstanceState(state);
+ return;
+ }
+
+ SavedState ss = (SavedState)state;
+ super.onRestoreInstanceState(ss.getSuperState());
+
+ if (mAdapter != null) {
+ mAdapter.restoreState(ss.adapterState, ss.loader);
+ setCurrentItemInternal(ss.position, false, true);
+ } else {
+ mRestoredCurItem = ss.position;
+ mRestoredAdapterState = ss.adapterState;
+ mRestoredClassLoader = ss.loader;
+ }
+ }
+
+ @Override
+ public void addView(View child, int index, ViewGroup.LayoutParams params) {
+ ViewGroup.LayoutParams paramsEx = params;
+ if (!checkLayoutParams(paramsEx)) {
+ paramsEx = generateLayoutParams(paramsEx);
+ }
+ final LayoutParams lp = (LayoutParams) paramsEx;
+ lp.isDecor |= child instanceof Decor;
+ if (mInLayout) {
+ if (lp.isDecor) {
+ throw new IllegalStateException("Cannot add pager decor view during layout");
+ }
+ lp.needsMeasure = true;
+ addViewInLayout(child, index, paramsEx);
+ } else {
+ super.addView(child, index, paramsEx);
+ }
+
+ if (USE_CACHE) {
+ if (child.getVisibility() != GONE) {
+ child.setDrawingCacheEnabled(mScrollingCacheEnabled);
+ } else {
+ child.setDrawingCacheEnabled(false);
+ }
+ }
+ }
+
+ @Override
+ public void removeView(View view) {
+ if (mInLayout) {
+ removeViewInLayout(view);
+ } else {
+ super.removeView(view);
+ }
+ }
+
+ ItemInfo infoForChild(View child) {
+ for (int i=0; i<mItems.size(); i++) {
+ ItemInfo ii = mItems.get(i);
+ if (mAdapter.isViewFromObject(child, ii.object)) {
+ return ii;
+ }
+ }
+ return null;
+ }
+
+ ItemInfo infoForAnyChild(View child) {
+ View childEx = child;
+ ViewParent parent;
+ while ((parent = childEx.getParent()) != this) {
+ if (parent == null || !(parent instanceof View)) {
+ return null;
+ }
+ childEx = (View)parent;
+ }
+ return infoForChild(childEx);
+ }
+
+ ItemInfo infoForPosition(int position) {
+ for (int i = 0; i < mItems.size(); i++) {
+ ItemInfo ii = mItems.get(i);
+ if (ii.position == position) {
+ return ii;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mFirstLayout = true;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // For simple implementation, our internal size is always 0.
+ // We depend on the container to specify the layout size of
+ // our view. We can't really know what it is since we will be
+ // adding and removing different arbitrary views and do not
+ // want the layout to change as this happens.
+ setMeasuredDimension(getDefaultSize(0, widthMeasureSpec),
+ getDefaultSize(0, heightMeasureSpec));
+
+ final int measuredWidth = getMeasuredWidth();
+ final int maxGutterSize = measuredWidth / 10;
+ mGutterSize = Math.min(maxGutterSize, mDefaultGutterSize);
+
+ // Children are just made to fill our space.
+ int childWidthSize = measuredWidth - getPaddingLeft() - getPaddingRight();
+ int childHeightSize = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
+
+ /*
+ * Make sure all children have been properly measured. Decor views first.
+ * Right now we cheat and make this less complicated by assuming decor
+ * views won't intersect. We will pin to edges based on gravity.
+ */
+ int size = getChildCount();
+ for (int i = 0; i < size; ++i) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (lp != null && lp.isDecor) {
+ final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+ final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
+ int widthMode = MeasureSpec.AT_MOST;
+ int heightMode = MeasureSpec.AT_MOST;
+ boolean consumeVertical = vgrav == Gravity.TOP || vgrav == Gravity.BOTTOM;
+ boolean consumeHorizontal = hgrav == Gravity.LEFT || hgrav == Gravity.RIGHT;
+
+ if (consumeVertical) {
+ widthMode = MeasureSpec.EXACTLY;
+ } else if (consumeHorizontal) {
+ heightMode = MeasureSpec.EXACTLY;
+ }
+
+ int widthSize = childWidthSize;
+ int heightSize = childHeightSize;
+ if (lp.width != ViewGroup.LayoutParams.WRAP_CONTENT) {
+ widthMode = MeasureSpec.EXACTLY;
+ if (lp.width != ViewGroup.LayoutParams.MATCH_PARENT) {
+ widthSize = lp.width;
+ }
+ }
+ if (lp.height != ViewGroup.LayoutParams.WRAP_CONTENT) {
+ heightMode = MeasureSpec.EXACTLY;
+ if (lp.height != ViewGroup.LayoutParams.MATCH_PARENT) {
+ heightSize = lp.height;
+ }
+ }
+ final int widthSpec = MeasureSpec.makeMeasureSpec(widthSize, widthMode);
+ final int heightSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
+ child.measure(widthSpec, heightSpec);
+
+ if (consumeVertical) {
+ childHeightSize -= child.getMeasuredHeight();
+ } else if (consumeHorizontal) {
+ childWidthSize -= child.getMeasuredWidth();
+ }
+ }
+ }
+ }
+
+ mChildWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY);
+ mChildHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeightSize, MeasureSpec.EXACTLY);
+
+ // Make sure we have created all fragments that we need to have shown.
+ mInLayout = true;
+ populate();
+ mInLayout = false;
+
+ // Page views next.
+ size = getChildCount();
+ for (int i = 0; i < size; ++i) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ if (DEBUG) Log.v(TAG, "Measuring #" + i + " " + child
+ + ": " + mChildWidthMeasureSpec);
+
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (lp == null || !lp.isDecor) {
+ final int widthSpec = MeasureSpec.makeMeasureSpec(
+ (int) (childWidthSize * (lp == null ? 0.f : lp.widthFactor)),
+ MeasureSpec.EXACTLY);
+ child.measure(widthSpec, mChildHeightMeasureSpec);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ // Make sure scroll position is set correctly.
+ if (w != oldw) {
+ recomputeScrollPosition(w, oldw, mPageMargin, mPageMargin);
+ }
+ }
+
+ private void recomputeScrollPosition(int width, int oldWidth, int margin, int oldMargin) {
+ if (oldWidth > 0 && !mItems.isEmpty()) {
+ final int widthWithMargin = width - getPaddingLeft() - getPaddingRight() + margin;
+ final int oldWidthWithMargin = oldWidth - getPaddingLeft() - getPaddingRight()
+ + oldMargin;
+ final int xpos = getScrollX();
+ final float pageOffset = (float) xpos / oldWidthWithMargin;
+ final int newOffsetPixels = (int) (pageOffset * widthWithMargin);
+
+ scrollTo(newOffsetPixels, getScrollY());
+ if (!mScroller.isFinished()) {
+ // We now return to your regularly scheduled scroll, already in progress.
+ final int newDuration = mScroller.getDuration() - mScroller.timePassed();
+ ItemInfo targetInfo = infoForPosition(mCurItem);
+ mScroller.startScroll(newOffsetPixels, 0,
+ (int) (targetInfo.offset * width), 0, newDuration);
+ }
+ } else {
+ final ItemInfo ii = infoForPosition(mCurItem);
+ final float scrollOffset = ii != null ? Math.min(ii.offset, mLastOffset) : 0;
+ final int scrollPos = (int) (scrollOffset *
+ (width - getPaddingLeft() - getPaddingRight()));
+ if (scrollPos != getScrollX()) {
+ completeScroll(false);
+ scrollTo(scrollPos, getScrollY());
+ }
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ final int count = getChildCount();
+ int width = r - l;
+ int height = b - t;
+ int paddingLeft = getPaddingLeft();
+ int paddingTop = getPaddingTop();
+ int paddingRight = getPaddingRight();
+ int paddingBottom = getPaddingBottom();
+ final int scrollX = getScrollX();
+
+ int decorCount = 0;
+
+ // First pass - decor views. We need to do this in two passes so that
+ // we have the proper offsets for non-decor views later.
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ int childLeft = 0;
+ int childTop = 0;
+ if (lp.isDecor) {
+ final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+ final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
+ switch (hgrav) {
+ default:
+ childLeft = paddingLeft;
+ break;
+ case Gravity.LEFT:
+ childLeft = paddingLeft;
+ paddingLeft += child.getMeasuredWidth();
+ break;
+ case Gravity.CENTER_HORIZONTAL:
+ childLeft = Math.max((width - child.getMeasuredWidth()) / 2,
+ paddingLeft);
+ break;
+ case Gravity.RIGHT:
+ childLeft = width - paddingRight - child.getMeasuredWidth();
+ paddingRight += child.getMeasuredWidth();
+ break;
+ }
+ switch (vgrav) {
+ default:
+ childTop = paddingTop;
+ break;
+ case Gravity.TOP:
+ childTop = paddingTop;
+ paddingTop += child.getMeasuredHeight();
+ break;
+ case Gravity.CENTER_VERTICAL:
+ childTop = Math.max((height - child.getMeasuredHeight()) / 2,
+ paddingTop);
+ break;
+ case Gravity.BOTTOM:
+ childTop = height - paddingBottom - child.getMeasuredHeight();
+ paddingBottom += child.getMeasuredHeight();
+ break;
+ }
+ childLeft += scrollX;
+ child.layout(childLeft, childTop,
+ childLeft + child.getMeasuredWidth(),
+ childTop + child.getMeasuredHeight());
+ decorCount++;
+ }
+ }
+ }
+
+ final int childWidth = width - paddingLeft - paddingRight;
+ // Page views. Do this once we have the right padding offsets from above.
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ ItemInfo ii;
+ if (!lp.isDecor && (ii = infoForChild(child)) != null) {
+ int loff = (int) (childWidth * ii.offset);
+ int childLeft = paddingLeft + loff;
+ int childTop = paddingTop;
+ if (lp.needsMeasure) {
+ // This was added during layout and needs measurement.
+ // Do it now that we know what we're working with.
+ lp.needsMeasure = false;
+ final int widthSpec = MeasureSpec.makeMeasureSpec(
+ (int) (childWidth * lp.widthFactor),
+ MeasureSpec.EXACTLY);
+ final int heightSpec = MeasureSpec.makeMeasureSpec(
+ height - paddingTop - paddingBottom,
+ MeasureSpec.EXACTLY);
+ child.measure(widthSpec, heightSpec);
+ }
+ if (DEBUG) Log.v(TAG, "Positioning #" + i + " " + child + " f=" + ii.object
+ + ":" + childLeft + "," + childTop + " " + child.getMeasuredWidth()
+ + "x" + child.getMeasuredHeight());
+ child.layout(childLeft, childTop,
+ childLeft + child.getMeasuredWidth(),
+ childTop + child.getMeasuredHeight());
+ }
+ }
+ }
+ mTopPageBounds = paddingTop;
+ mBottomPageBounds = height - paddingBottom;
+ mDecorChildCount = decorCount;
+
+ if (mFirstLayout) {
+ scrollToItem(mCurItem, false, 0, false);
+ }
+ mFirstLayout = false;
+ }
+
+ @Override
+ public void computeScroll() {
+ if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {
+ int oldX = getScrollX();
+ int oldY = getScrollY();
+ int x = mScroller.getCurrX();
+ int y = mScroller.getCurrY();
+
+ if (oldX != x || oldY != y) {
+ scrollTo(x, y);
+ if (!pageScrolled(x)) {
+ mScroller.abortAnimation();
+ scrollTo(0, y);
+ }
+ }
+
+ // Keep on drawing until the animation has finished.
+ ViewCompat.postInvalidateOnAnimation(this);
+ return;
+ }
+
+ // Done with scroll, clean up state.
+ completeScroll(true);
+ }
+
+ private boolean pageScrolled(int xpos) {
+ if (mItems.size() == 0) {
+ mCalledSuper = false;
+ onPageScrolled(0, 0, 0);
+ if (!mCalledSuper) {
+ throw new IllegalStateException(
+ "onPageScrolled did not call superclass implementation");
+ }
+ return false;
+ }
+ final ItemInfo ii = infoForCurrentScrollPosition();
+ final int width = getClientWidth();
+ final int widthWithMargin = width + mPageMargin;
+ final float marginOffset = (float) mPageMargin / width;
+ final int currentPage = ii.position;
+ final float pageOffset = (((float) xpos / width) - ii.offset) /
+ (ii.widthFactor + marginOffset);
+ final int offsetPixels = (int) (pageOffset * widthWithMargin);
+
+ mCalledSuper = false;
+ onPageScrolled(currentPage, pageOffset, offsetPixels);
+ if (!mCalledSuper) {
+ throw new IllegalStateException(
+ "onPageScrolled did not call superclass implementation");
+ }
+ return true;
+ }
+
+ /**
+ * This method will be invoked when the current page is scrolled, either as part
+ * of a programmatically initiated smooth scroll or a user initiated touch scroll.
+ * If you override this method you must call through to the superclass implementation
+ * (e.g. super.onPageScrolled(position, offset, offsetPixels)) before onPageScrolled
+ * returns.
+ *
+ * @param position Position index of the first page currently being displayed.
+ * Page position+1 will be visible if positionOffset is nonzero.
+ * @param offset Value from [0, 1) indicating the offset from the page at position.
+ * @param offsetPixels Value in pixels indicating the offset from position.
+ */
+ protected void onPageScrolled(int position, float offset, int offsetPixels) {
+ // Offset any decor views if needed - keep them on-screen at all times.
+ if (mDecorChildCount > 0) {
+ final int scrollX = getScrollX();
+ int paddingLeft = getPaddingLeft();
+ int paddingRight = getPaddingRight();
+ final int width = getWidth();
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (!lp.isDecor) continue;
+
+ final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+ int childLeft = 0;
+ switch (hgrav) {
+ default:
+ childLeft = paddingLeft;
+ break;
+ case Gravity.LEFT:
+ childLeft = paddingLeft;
+ paddingLeft += child.getWidth();
+ break;
+ case Gravity.CENTER_HORIZONTAL:
+ childLeft = Math.max((width - child.getMeasuredWidth()) / 2,
+ paddingLeft);
+ break;
+ case Gravity.RIGHT:
+ childLeft = width - paddingRight - child.getMeasuredWidth();
+ paddingRight += child.getMeasuredWidth();
+ break;
+ }
+ childLeft += scrollX;
+
+ final int childOffset = childLeft - child.getLeft();
+ if (childOffset != 0) {
+ child.offsetLeftAndRight(childOffset);
+ }
+ }
+ }
+
+ if (mOnPageChangeListener != null) {
+ mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels);
+ }
+ if (mInternalPageChangeListener != null) {
+ mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels);
+ }
+
+ if (mPageTransformer != null) {
+ final int scrollX = getScrollX();
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ if (lp.isDecor) continue;
+
+ final float transformPos = (float) (child.getLeft() - scrollX) / getClientWidth();
+ mPageTransformer.transformPage(child, transformPos);
+ }
+ }
+
+ mCalledSuper = true;
+ }
+
+ private void completeScroll(boolean postEvents) {
+ boolean needPopulate = mScrollState == SCROLL_STATE_SETTLING;
+ 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);
+ }
+ }
+ mPopulatePending = false;
+ for (int i=0; i<mItems.size(); i++) {
+ ItemInfo ii = mItems.get(i);
+ if (ii.scrolling) {
+ needPopulate = true;
+ ii.scrolling = false;
+ }
+ }
+ if (needPopulate) {
+ if (postEvents) {
+ ViewCompat.postOnAnimation(this, mEndScrollRunnable);
+ } else {
+ mEndScrollRunnable.run();
+ }
+ }
+ }
+
+ private boolean isGutterDrag(float x, float dx) {
+ return (x < mGutterSize && dx > 0) || (x > getWidth() - mGutterSize && dx < 0);
+ }
+
+ private void enableLayers(boolean enable) {
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final int layerType = enable ?
+ ViewCompat.LAYER_TYPE_HARDWARE : ViewCompat.LAYER_TYPE_NONE;
+ ViewCompat.setLayerType(getChildAt(i), layerType, null);
+ }
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ /*
+ * This method JUST determines whether we want to intercept the motion.
+ * If we return true, onMotionEvent will be called and we do the actual
+ * scrolling there.
+ */
+
+ final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
+
+ // Always take care of the touch gesture being complete.
+ 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;
+ }
+ return false;
+ }
+
+ // Nothing more to do here if we have decided whether or not we
+ // are dragging.
+ if (action != MotionEvent.ACTION_DOWN) {
+ if (mIsBeingDragged) {
+ if (DEBUG) Log.v(TAG, "Intercept returning true!");
+ return true;
+ }
+ if (mIsUnableToDrag) {
+ if (DEBUG) Log.v(TAG, "Intercept returning false!");
+ return false;
+ }
+ }
+
+ switch (action) {
+ case MotionEvent.ACTION_MOVE: {
+ /*
+ * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
+ * whether the user has moved far enough from his original down touch.
+ */
+
+ /*
+ * Locally do absolute value. mLastMotionY is set to the y value
+ * of the down event.
+ */
+ final int activePointerId = mActivePointerId;
+ if (activePointerId == INVALID_POINTER) {
+ // If we don't have a valid id, the touch down wasn't on content.
+ break;
+ }
+
+ final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId);
+ final float x = MotionEventCompat.getX(ev, pointerIndex);
+ final float dx = x - mLastMotionX;
+ final float xDiff = Math.abs(dx);
+ final float y = MotionEventCompat.getY(ev, pointerIndex);
+ final float yDiff = Math.abs(y - mInitialMotionY);
+ if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);
+
+ if (dx != 0 && !isGutterDrag(mLastMotionX, dx) &&
+ canScroll(this, false, (int) dx, (int) x, (int) y)) {
+ // Nested view has scrollable area under this point. Let it be handled there.
+ mLastMotionX = x;
+ mLastMotionY = y;
+ mIsUnableToDrag = true;
+ return false;
+ }
+ if (xDiff > mTouchSlop && xDiff * 0.5f > yDiff) {
+ if (DEBUG) Log.v(TAG, "Starting drag!");
+ mIsBeingDragged = true;
+ requestParentDisallowInterceptTouchEvent(true);
+ setScrollState(SCROLL_STATE_DRAGGING);
+ mLastMotionX = dx > 0 ? mInitialMotionX + mTouchSlop :
+ mInitialMotionX - mTouchSlop;
+ mLastMotionY = y;
+ setScrollingCacheEnabled(true);
+ } else if (yDiff > mTouchSlop) {
+ // The finger has moved enough in the vertical
+ // direction to be counted as a drag... abort
+ // any attempt to drag horizontally, to work correctly
+ // with children that have scrolling containers.
+ if (DEBUG) Log.v(TAG, "Starting unable to drag!");
+ mIsUnableToDrag = true;
+ }
+ if (mIsBeingDragged) {
+ // Scroll to follow the motion event
+ if (performDrag(x)) {
+ ViewCompat.postInvalidateOnAnimation(this);
+ }
+ }
+ break;
+ }
+
+ case MotionEvent.ACTION_DOWN: {
+ /*
+ * Remember location of down touch.
+ * ACTION_DOWN always refers to pointer index 0.
+ */
+ mLastMotionX = mInitialMotionX = ev.getX();
+ mLastMotionY = mInitialMotionY = ev.getY();
+ mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
+ mIsUnableToDrag = false;
+
+ mScroller.computeScrollOffset();
+ if (mScrollState == SCROLL_STATE_SETTLING &&
+ Math.abs(mScroller.getFinalX() - mScroller.getCurrX()) > mCloseEnough) {
+ // Let the user 'catch' the pager as it animates.
+ mScroller.abortAnimation();
+ mPopulatePending = false;
+ populate();
+ mIsBeingDragged = true;
+ requestParentDisallowInterceptTouchEvent(true);
+ setScrollState(SCROLL_STATE_DRAGGING);
+ } else {
+ completeScroll(false);
+ mIsBeingDragged = false;
+ }
+
+ if (DEBUG) Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY
+ + " mIsBeingDragged=" + mIsBeingDragged
+ + "mIsUnableToDrag=" + mIsUnableToDrag);
+ break;
+ }
+
+ case MotionEventCompat.ACTION_POINTER_UP:
+ onSecondaryPointerUp(ev);
+ break;
+ }
+
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(ev);
+
+ /*
+ * The only time we want to intercept motion events is if we are in the
+ * drag mode.
+ */
+ return mIsBeingDragged;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (mFakeDragging) {
+ // A fake drag is in progress already, ignore this real one
+ // but still eat the touch events.
+ // (It is likely that the user is multi-touching the screen.)
+ return true;
+ }
+
+ if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
+ // Don't handle edge touches immediately -- they may actually belong to one of our
+ // descendants.
+ return false;
+ }
+
+ if (mAdapter == null || mAdapter.getCount() == 0) {
+ // Nothing to present or scroll; nothing to touch.
+ return false;
+ }
+
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(ev);
+
+ final int action = ev.getAction();
+ boolean needsInvalidate = false;
+
+ switch (action & MotionEventCompat.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN: {
+ mScroller.abortAnimation();
+ mPopulatePending = false;
+ populate();
+
+ // Remember where the motion event started
+ mLastMotionX = mInitialMotionX = ev.getX();
+ mLastMotionY = mInitialMotionY = ev.getY();
+ mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
+ break;
+ }
+ case MotionEvent.ACTION_MOVE:
+ if (!mIsBeingDragged) {
+ final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
+ final float x = MotionEventCompat.getX(ev, pointerIndex);
+ final float xDiff = Math.abs(x - mLastMotionX);
+ final float y = MotionEventCompat.getY(ev, pointerIndex);
+ final float yDiff = Math.abs(y - mLastMotionY);
+ if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);
+ if (xDiff > mTouchSlop && xDiff > yDiff) {
+ if (DEBUG) Log.v(TAG, "Starting drag!");
+ mIsBeingDragged = true;
+ requestParentDisallowInterceptTouchEvent(true);
+ mLastMotionX = x - mInitialMotionX > 0 ? mInitialMotionX + mTouchSlop :
+ mInitialMotionX - mTouchSlop;
+ mLastMotionY = y;
+ setScrollState(SCROLL_STATE_DRAGGING);
+ setScrollingCacheEnabled(true);
+
+ // Disallow Parent Intercept, just in case
+ ViewParent parent = getParent();
+ if (parent != null) {
+ parent.requestDisallowInterceptTouchEvent(true);
+ }
+ }
+ }
+ // Not else! Note that mIsBeingDragged can be set above.
+ if (mIsBeingDragged) {
+ // Scroll to follow the motion event
+ final int activePointerIndex = MotionEventCompat.findPointerIndex(
+ ev, mActivePointerId);
+ final float x = MotionEventCompat.getX(ev, activePointerIndex);
+ needsInvalidate |= performDrag(x);
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (mIsBeingDragged) {
+ final VelocityTracker velocityTracker = mVelocityTracker;
+ velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+ int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
+ velocityTracker, mActivePointerId);
+ mPopulatePending = true;
+ final int width = getClientWidth();
+ final int scrollX = getScrollX();
+ final ItemInfo ii = infoForCurrentScrollPosition();
+ final int currentPage = ii.position;
+ final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor;
+ final int activePointerIndex =
+ MotionEventCompat.findPointerIndex(ev, mActivePointerId);
+ final float x = MotionEventCompat.getX(ev, activePointerIndex);
+ final int totalDelta = (int) (x - mInitialMotionX);
+ int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
+ totalDelta);
+ setCurrentItemInternal(nextPage, true, true, initialVelocity);
+
+ mActivePointerId = INVALID_POINTER;
+ endDrag();
+ needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();
+ }
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ if (mIsBeingDragged) {
+ scrollToItem(mCurItem, true, 0, false);
+ mActivePointerId = INVALID_POINTER;
+ endDrag();
+ needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();
+ }
+ break;
+ case MotionEventCompat.ACTION_POINTER_DOWN: {
+ final int index = MotionEventCompat.getActionIndex(ev);
+ final float x = MotionEventCompat.getX(ev, index);
+ mLastMotionX = x;
+ mActivePointerId = MotionEventCompat.getPointerId(ev, index);
+ break;
+ }
+ case MotionEventCompat.ACTION_POINTER_UP:
+ onSecondaryPointerUp(ev);
+ mLastMotionX = MotionEventCompat.getX(ev,
+ MotionEventCompat.findPointerIndex(ev, mActivePointerId));
+ break;
+ }
+ if (needsInvalidate) {
+ ViewCompat.postInvalidateOnAnimation(this);
+ }
+ return true;
+ }
+
+ private void requestParentDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ final ViewParent parent = getParent();
+ if (parent != null) {
+ parent.requestDisallowInterceptTouchEvent(disallowIntercept);
+ }
+ }
+
+ private boolean performDrag(float x) {
+ boolean needsInvalidate = false;
+
+ final float deltaX = mLastMotionX - x;
+ mLastMotionX = x;
+
+ float oldScrollX = getScrollX();
+ float scrollX = oldScrollX + deltaX;
+ final int width = getClientWidth();
+
+ float leftBound = width * mFirstOffset;
+ float rightBound = width * mLastOffset;
+ boolean leftAbsolute = true;
+ boolean rightAbsolute = true;
+
+ final ItemInfo firstItem = mItems.get(0);
+ final ItemInfo lastItem = mItems.get(mItems.size() - 1);
+ if (firstItem.position != 0) {
+ leftAbsolute = false;
+ leftBound = firstItem.offset * width;
+ }
+ if (lastItem.position != mAdapter.getCount() - 1) {
+ rightAbsolute = false;
+ rightBound = lastItem.offset * width;
+ }
+
+ if (scrollX < leftBound) {
+ if (leftAbsolute) {
+ float over = leftBound - scrollX;
+ needsInvalidate = mLeftEdge.onPull(Math.abs(over) / width);
+ }
+ scrollX = leftBound;
+ } else if (scrollX > rightBound) {
+ if (rightAbsolute) {
+ float over = scrollX - rightBound;
+ needsInvalidate = mRightEdge.onPull(Math.abs(over) / width);
+ }
+ scrollX = rightBound;
+ }
+ // Don't lose the rounded component
+ mLastMotionX += scrollX - (int) scrollX;
+ scrollTo((int) scrollX, getScrollY());
+ pageScrolled((int) scrollX);
+
+ return needsInvalidate;
+ }
+
+ /**
+ * @return Info about the page at the current scroll position.
+ * This can be synthetic for a missing middle page; the 'object' field can be null.
+ */
+ private ItemInfo infoForCurrentScrollPosition() {
+ final int width = getClientWidth();
+ final float scrollOffset = width > 0 ? (float) getScrollX() / width : 0;
+ final float marginOffset = width > 0 ? (float) mPageMargin / width : 0;
+ int lastPos = -1;
+ float lastOffset = 0.f;
+ float lastWidth = 0.f;
+ boolean first = true;
+
+ ItemInfo lastItem = null;
+ for (int i = 0; i < mItems.size(); i++) {
+ ItemInfo ii = mItems.get(i);
+ float offset;
+ if (!first && ii.position != lastPos + 1) {
+ // Create a synthetic item for a missing page.
+ ii = mTempItem;
+ ii.offset = lastOffset + lastWidth + marginOffset;
+ ii.position = lastPos + 1;
+ ii.widthFactor = mAdapter.getPageWidth(ii.position);
+ i--;
+ }
+ offset = ii.offset;
+
+ final float leftBound = offset;
+ final float rightBound = offset + ii.widthFactor + marginOffset;
+ if (first || scrollOffset >= leftBound) {
+ if (scrollOffset < rightBound || i == mItems.size() - 1) {
+ return ii;
+ }
+ } else {
+ return lastItem;
+ }
+ first = false;
+ lastPos = ii.position;
+ lastOffset = offset;
+ lastWidth = ii.widthFactor;
+ lastItem = ii;
+ }
+
+ return lastItem;
+ }
+
+ private int determineTargetPage(int currentPage, float pageOffset, int velocity, int deltaX) {
+ int targetPage;
+ if (Math.abs(deltaX) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) {
+ targetPage = velocity > 0 ? currentPage : currentPage + 1;
+ } else {
+ final float truncator = currentPage >= mCurItem ? 0.4f : 0.6f;
+ targetPage = (int) (currentPage + pageOffset + truncator);
+ }
+
+ if (mItems.size() > 0) {
+ final ItemInfo firstItem = mItems.get(0);
+ final ItemInfo lastItem = mItems.get(mItems.size() - 1);
+
+ // Only let the user target pages we have items for
+ targetPage = Math.max(firstItem.position, Math.min(targetPage, lastItem.position));
+ }
+
+ return targetPage;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ super.draw(canvas);
+ boolean needsInvalidate = false;
+
+ final int overScrollMode = ViewCompat.getOverScrollMode(this);
+ if (overScrollMode == ViewCompat.OVER_SCROLL_ALWAYS ||
+ (overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS &&
+ mAdapter != null && mAdapter.getCount() > 1)) {
+ if (!mLeftEdge.isFinished()) {
+ final int restoreCount = canvas.save();
+ final int height = getHeight() - getPaddingTop() - getPaddingBottom();
+ final int width = getWidth();
+
+ canvas.rotate(270);
+ canvas.translate(-height + getPaddingTop(), mFirstOffset * width);
+ mLeftEdge.setSize(height, width);
+ needsInvalidate |= mLeftEdge.draw(canvas);
+ canvas.restoreToCount(restoreCount);
+ }
+ if (!mRightEdge.isFinished()) {
+ final int restoreCount = canvas.save();
+ final int width = getWidth();
+ final int height = getHeight() - getPaddingTop() - getPaddingBottom();
+
+ canvas.rotate(90);
+ canvas.translate(-getPaddingTop(), -(mLastOffset + 1) * width);
+ mRightEdge.setSize(height, width);
+ needsInvalidate |= mRightEdge.draw(canvas);
+ canvas.restoreToCount(restoreCount);
+ }
+ } else {
+ mLeftEdge.finish();
+ mRightEdge.finish();
+ }
+
+ if (needsInvalidate) {
+ // Keep animating
+ ViewCompat.postInvalidateOnAnimation(this);
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ // Draw the margin drawable between pages if needed.
+ if (mPageMargin > 0 && mMarginDrawable != null && mItems.size() > 0 && mAdapter != null) {
+ final int scrollX = getScrollX();
+ final int width = getWidth();
+
+ final float marginOffset = (float) mPageMargin / width;
+ int itemIndex = 0;
+ ItemInfo ii = mItems.get(0);
+ float offset = ii.offset;
+ final int itemCount = mItems.size();
+ final int firstPos = ii.position;
+ final int lastPos = mItems.get(itemCount - 1).position;
+ for (int pos = firstPos; pos < lastPos; pos++) {
+ while (pos > ii.position && itemIndex < itemCount) {
+ ii = mItems.get(++itemIndex);
+ }
+
+ float drawAt;
+ if (pos == ii.position) {
+ drawAt = (ii.offset + ii.widthFactor) * width;
+ offset = ii.offset + ii.widthFactor + marginOffset;
+ } else {
+ float widthFactor = mAdapter.getPageWidth(pos);
+ drawAt = (offset + widthFactor) * width;
+ offset += widthFactor + marginOffset;
+ }
+
+ if (drawAt + mPageMargin > scrollX) {
+ mMarginDrawable.setBounds((int) drawAt, mTopPageBounds,
+ (int) (drawAt + mPageMargin + 0.5f), mBottomPageBounds);
+ mMarginDrawable.draw(canvas);
+ }
+
+ if (drawAt > scrollX + width) {
+ break; // No more visible, no sense in continuing
+ }
+ }
+ }
+ }
+
+ /**
+ * Start a fake drag of the pager.
+ *
+ * <p>A fake drag can be useful if you want to synchronize the motion of the ViewPager
+ * with the touch scrolling of another view, while still letting the ViewPager
+ * control the snapping motion and fling behavior. (e.g. parallax-scrolling tabs.)
+ * Call {@link #fakeDragBy(float)} to simulate the actual drag motion. Call
+ * {@link #endFakeDrag()} to complete the fake drag and fling as necessary.
+ *
+ * <p>During a fake drag the ViewPager will ignore all touch events. If a real drag
+ * is already in progress, this method will return false.
+ *
+ * @return true if the fake drag began successfully, false if it could not be started.
+ *
+ * @see #fakeDragBy(float)
+ * @see #endFakeDrag()
+ */
+ public boolean beginFakeDrag() {
+ if (mIsBeingDragged) {
+ return false;
+ }
+ mFakeDragging = true;
+ setScrollState(SCROLL_STATE_DRAGGING);
+ mInitialMotionX = mLastMotionX = 0;
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ } else {
+ mVelocityTracker.clear();
+ }
+ final long time = SystemClock.uptimeMillis();
+ final MotionEvent ev = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 0, 0, 0);
+ mVelocityTracker.addMovement(ev);
+ ev.recycle();
+ mFakeDragBeginTime = time;
+ return true;
+ }
+
+ /**
+ * End a fake drag of the pager.
+ *
+ * @see #beginFakeDrag()
+ * @see #fakeDragBy(float)
+ */
+ public void endFakeDrag() {
+ if (!mFakeDragging) {
+ throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");
+ }
+
+ final VelocityTracker velocityTracker = mVelocityTracker;
+ velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+ int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
+ velocityTracker, mActivePointerId);
+ mPopulatePending = true;
+ final int width = getClientWidth();
+ final int scrollX = getScrollX();
+ final ItemInfo ii = infoForCurrentScrollPosition();
+ final int currentPage = ii.position;
+ final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor;
+ final int totalDelta = (int) (mLastMotionX - mInitialMotionX);
+ int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
+ totalDelta);
+ setCurrentItemInternal(nextPage, true, true, initialVelocity);
+ endDrag();
+
+ mFakeDragging = false;
+ }
+
+ /**
+ * Fake drag by an offset in pixels. You must have called {@link #beginFakeDrag()} first.
+ *
+ * @param xOffset Offset in pixels to drag by.
+ * @see #beginFakeDrag()
+ * @see #endFakeDrag()
+ */
+ public void fakeDragBy(float xOffset) {
+ if (!mFakeDragging) {
+ throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");
+ }
+
+ mLastMotionX += xOffset;
+
+ float oldScrollX = getScrollX();
+ float scrollX = oldScrollX - xOffset;
+ final int width = getClientWidth();
+
+ float leftBound = width * mFirstOffset;
+ float rightBound = width * mLastOffset;
+
+ final ItemInfo firstItem = mItems.get(0);
+ final ItemInfo lastItem = mItems.get(mItems.size() - 1);
+ if (firstItem.position != 0) {
+ leftBound = firstItem.offset * width;
+ }
+ if (lastItem.position != mAdapter.getCount() - 1) {
+ rightBound = lastItem.offset * width;
+ }
+
+ if (scrollX < leftBound) {
+ scrollX = leftBound;
+ } else if (scrollX > rightBound) {
+ scrollX = rightBound;
+ }
+ // Don't lose the rounded component
+ mLastMotionX += scrollX - (int) scrollX;
+ scrollTo((int) scrollX, getScrollY());
+ pageScrolled((int) scrollX);
+
+ // Synthesize an event for the VelocityTracker.
+ final long time = SystemClock.uptimeMillis();
+ final MotionEvent ev = MotionEvent.obtain(mFakeDragBeginTime, time, MotionEvent.ACTION_MOVE,
+ mLastMotionX, 0, 0);
+ mVelocityTracker.addMovement(ev);
+ ev.recycle();
+ }
+
+ /**
+ * Returns true if a fake drag is in progress.
+ *
+ * @return true if currently in a fake drag, false otherwise.
+ *
+ * @see #beginFakeDrag()
+ * @see #fakeDragBy(float)
+ * @see #endFakeDrag()
+ */
+ public boolean isFakeDragging() {
+ return mFakeDragging;
+ }
+
+ private void onSecondaryPointerUp(MotionEvent ev) {
+ final int pointerIndex = MotionEventCompat.getActionIndex(ev);
+ final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
+ if (pointerId == mActivePointerId) {
+ // This was our active pointer going up. Choose a new
+ // active pointer and adjust accordingly.
+ final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+ mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex);
+ mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
+ if (mVelocityTracker != null) {
+ mVelocityTracker.clear();
+ }
+ }
+ }
+
+ private void endDrag() {
+ mIsBeingDragged = false;
+ mIsUnableToDrag = false;
+
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ }
+
+ private void setScrollingCacheEnabled(boolean enabled) {
+ if (mScrollingCacheEnabled != enabled) {
+ mScrollingCacheEnabled = enabled;
+ if (USE_CACHE) {
+ final int size = getChildCount();
+ for (int i = 0; i < size; ++i) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ child.setDrawingCacheEnabled(enabled);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean canScrollHorizontally(int direction) {
+ if (mAdapter == null) {
+ return false;
+ }
+
+ final int width = getClientWidth();
+ final int scrollX = getScrollX();
+ if (direction < 0) {
+ return (scrollX > (int) (width * mFirstOffset));
+ } else if (direction > 0) {
+ return (scrollX < (int) (width * mLastOffset));
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Tests scrollability within child views of v given a delta of dx.
+ *
+ * @param v View to test for horizontal scrollability
+ * @param checkV Whether the view v passed should itself be checked for scrollability (true),
+ * or just its children (false).
+ * @param dx Delta scrolled in pixels
+ * @param x X coordinate of the active touch point
+ * @param y Y coordinate of the active touch point
+ * @return true if child views of v can be scrolled by delta of dx.
+ */
+ protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
+ if (v instanceof ViewGroup) {
+ final ViewGroup group = (ViewGroup) v;
+ final int scrollX = v.getScrollX();
+ final int scrollY = v.getScrollY();
+ final int count = group.getChildCount();
+ // Count backwards - let topmost views consume scroll distance first.
+ for (int i = count - 1; i >= 0; i--) {
+ // TODO: Add versioned support here for transformed views.
+ // This will not work for transformed views in Honeycomb+
+ final View child = group.getChildAt(i);
+ if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&
+ y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&
+ canScroll(child, true, dx, x + scrollX - child.getLeft(),
+ y + scrollY - child.getTop())) {
+ return true;
+ }
+ }
+ }
+
+ return checkV && ViewCompat.canScrollHorizontally(v, -dx);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ // Let the focused view and/or our descendants get the key first
+ return super.dispatchKeyEvent(event) || executeKeyEvent(event);
+ }
+
+ /**
+ * You can call this function yourself to have the scroll view perform
+ * scrolling from a key event, just as if the event had been dispatched to
+ * it by the view hierarchy.
+ *
+ * @param event The key event to execute.
+ * @return Return true if the event was handled, else false.
+ */
+ public boolean executeKeyEvent(KeyEvent event) {
+ boolean handled = false;
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ handled = arrowScroll(FOCUS_LEFT);
+ break;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ handled = arrowScroll(FOCUS_RIGHT);
+ break;
+ case KeyEvent.KEYCODE_TAB:
+ if (Build.VERSION.SDK_INT >= 11) {
+ // The focus finder had a bug handling FOCUS_FORWARD and FOCUS_BACKWARD
+ // before Android 3.0. Ignore the tab key on those devices.
+ if (KeyEventCompat.hasNoModifiers(event)) {
+ handled = arrowScroll(FOCUS_FORWARD);
+ } else if (KeyEventCompat.hasModifiers(event, KeyEvent.META_SHIFT_ON)) {
+ handled = arrowScroll(FOCUS_BACKWARD);
+ }
+ }
+ break;
+ }
+ }
+ return handled;
+ }
+
+ public boolean arrowScroll(int direction) {
+ View currentFocused = findFocus();
+ if (currentFocused == this) {
+ currentFocused = null;
+ } else if (currentFocused != null) {
+ boolean isChild = false;
+ for (ViewParent parent = currentFocused.getParent(); parent instanceof ViewGroup;
+ parent = parent.getParent()) {
+ if (parent == this) {
+ isChild = true;
+ break;
+ }
+ }
+ if (!isChild) {
+ // This would cause the focus search down below to fail in fun ways.
+ final StringBuilder sb = new StringBuilder();
+ sb.append(currentFocused.getClass().getSimpleName());
+ for (ViewParent parent = currentFocused.getParent(); parent instanceof ViewGroup;
+ parent = parent.getParent()) {
+ sb.append(" => ").append(parent.getClass().getSimpleName());
+ }
+ Log.e(TAG, "arrowScroll tried to find focus based on non-child " +
+ "current focused view " + sb.toString());
+ currentFocused = null;
+ }
+ }
+
+ boolean handled = false;
+
+ View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused,
+ direction);
+ if (nextFocused != null && nextFocused != currentFocused) {
+ if (direction == View.FOCUS_LEFT) {
+ // If there is nothing to the left, or this is causing us to
+ // jump to the right, then what we really want to do is page left.
+ final int nextLeft = getChildRectInPagerCoordinates(mTempRect, nextFocused).left;
+ final int currLeft = getChildRectInPagerCoordinates(mTempRect, currentFocused).left;
+ if (currentFocused != null && nextLeft >= currLeft) {
+ handled = pageLeft();
+ } else {
+ handled = nextFocused.requestFocus();
+ }
+ } else if (direction == View.FOCUS_RIGHT) {
+ // If there is nothing to the right, or this is causing us to
+ // jump to the left, then what we really want to do is page right.
+ final int nextLeft = getChildRectInPagerCoordinates(mTempRect, nextFocused).left;
+ final int currLeft = getChildRectInPagerCoordinates(mTempRect, currentFocused).left;
+ if (currentFocused != null && nextLeft <= currLeft) {
+ handled = pageRight();
+ } else {
+ handled = nextFocused.requestFocus();
+ }
+ }
+ } else if (direction == FOCUS_LEFT || direction == FOCUS_BACKWARD) {
+ // Trying to move left and nothing there; try to page.
+ handled = pageLeft();
+ } else if (direction == FOCUS_RIGHT || direction == FOCUS_FORWARD) {
+ // Trying to move right and nothing there; try to page.
+ handled = pageRight();
+ }
+ if (handled) {
+ playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));
+ }
+ return handled;
+ }
+
+ private Rect getChildRectInPagerCoordinates(Rect outRect, View child) {
+ Rect outRectEx = outRect;
+ if (outRectEx == null) {
+ outRectEx = new Rect();
+ }
+ if (child == null) {
+ outRectEx.set(0, 0, 0, 0);
+ return outRectEx;
+ }
+ outRectEx.left = child.getLeft();
+ outRectEx.right = child.getRight();
+ outRectEx.top = child.getTop();
+ outRectEx.bottom = child.getBottom();
+
+ ViewParent parent = child.getParent();
+ while (parent instanceof ViewGroup && parent != this) {
+ final ViewGroup group = (ViewGroup) parent;
+ outRectEx.left += group.getLeft();
+ outRectEx.right += group.getRight();
+ outRectEx.top += group.getTop();
+ outRectEx.bottom += group.getBottom();
+
+ parent = group.getParent();
+ }
+ return outRectEx;
+ }
+
+ boolean pageLeft() {
+ if (mCurItem > 0) {
+ setCurrentItem(mCurItem-1, true);
+ return true;
+ }
+ return false;
+ }
+
+ boolean pageRight() {
+ if (mAdapter != null && mCurItem < (mAdapter.getCount()-1)) {
+ setCurrentItem(mCurItem+1, true);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * We only want the current page that is being shown to be focusable.
+ */
+ @Override
+ public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
+ final int focusableCount = views.size();
+
+ final int descendantFocusability = getDescendantFocusability();
+
+ if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
+ for (int i = 0; i < getChildCount(); i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() == VISIBLE) {
+ ItemInfo ii = infoForChild(child);
+ if (ii != null && ii.position == mCurItem) {
+ child.addFocusables(views, direction, focusableMode);
+ }
+ }
+ }
+ }
+
+ // we add ourselves (if focusable) in all cases except for when we are
+ // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is
+ // to avoid the focus search finding layouts when a more precise search
+ // among the focusable children would be more interesting.
+ if (
+ descendantFocusability != FOCUS_AFTER_DESCENDANTS ||
+ // No focusable descendants
+ (focusableCount == views.size())) {
+ // Note that we can't call the superclass here, because it will
+ // add all views in. So we need to do the same thing View does.
+ if (!isFocusable()) {
+ return;
+ }
+ if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE &&
+ isInTouchMode() && !isFocusableInTouchMode()) {
+ return;
+ }
+ views.add(this);
+ }
+ }
+
+ /**
+ * We only want the current page that is being shown to be touchable.
+ */
+ @Override
+ public void addTouchables(ArrayList<View> views) {
+ // Note that we don't call super.addTouchables(), which means that
+ // we don't call View.addTouchables(). This is okay because a ViewPager
+ // is itself not touchable.
+ for (int i = 0; i < getChildCount(); i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() == VISIBLE) {
+ ItemInfo ii = infoForChild(child);
+ if (ii != null && ii.position == mCurItem) {
+ child.addTouchables(views);
+ }
+ }
+ }
+ }
+
+ /**
+ * We only want the current page that is being shown to be focusable.
+ */
+ @Override
+ protected boolean onRequestFocusInDescendants(int direction,
+ Rect previouslyFocusedRect) {
+ int index;
+ int increment;
+ int end;
+ int count = getChildCount();
+ if ((direction & FOCUS_FORWARD) != 0) {
+ index = 0;
+ increment = 1;
+ end = count;
+ } else {
+ index = count - 1;
+ increment = -1;
+ end = -1;
+ }
+ for (int i = index; i != end; i += increment) {
+ View child = getChildAt(i);
+ if (child.getVisibility() == VISIBLE) {
+ ItemInfo ii = infoForChild(child);
+ if (ii != null && ii.position == mCurItem) {
+ if (child.requestFocus(direction, previouslyFocusedRect)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ // Dispatch scroll events from this ViewPager.
+ if (event.getEventType() == AccessibilityEventCompat.TYPE_VIEW_SCROLLED) {
+ return super.dispatchPopulateAccessibilityEvent(event);
+ }
+
+ // Dispatch all other accessibility events from the current page.
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() == VISIBLE) {
+ final ItemInfo ii = infoForChild(child);
+ if (ii != null && ii.position == mCurItem &&
+ child.dispatchPopulateAccessibilityEvent(event)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
+ return new LayoutParams();
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+ return generateDefaultLayoutParams();
+ }
+
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof LayoutParams && super.checkLayoutParams(p);
+ }
+
+ @Override
+ public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new LayoutParams(getContext(), attrs);
+ }
+
+ class MyAccessibilityDelegate extends AccessibilityDelegateCompat {
+
+ @Override
+ @SuppressWarnings("synthetic-access")
+ public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(host, event);
+ event.setClassName(ViewPager.class.getName());
+ final AccessibilityRecordCompat recordCompat = AccessibilityRecordCompat.obtain();
+ recordCompat.setScrollable(canScroll());
+ if (event.getEventType() == AccessibilityEventCompat.TYPE_VIEW_SCROLLED
+ && mAdapter != null) {
+ recordCompat.setItemCount(mAdapter.getCount());
+ recordCompat.setFromIndex(mCurItem);
+ recordCompat.setToIndex(mCurItem);
+ }
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.setClassName(ViewPager.class.getName());
+ info.setScrollable(canScroll());
+ if (canScrollHorizontally(1)) {
+ info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
+ }
+ if (canScrollHorizontally(-1)) {
+ info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
+ }
+ }
+
+ @Override
+ @SuppressWarnings("synthetic-access")
+ public boolean performAccessibilityAction(View host, int action, Bundle args) {
+ if (super.performAccessibilityAction(host, action, args)) {
+ return true;
+ }
+ switch (action) {
+ case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: {
+ if (canScrollHorizontally(1)) {
+ setCurrentItem(mCurItem + 1);
+ return true;
+ }
+ } return false;
+ case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: {
+ if (canScrollHorizontally(-1)) {
+ setCurrentItem(mCurItem - 1);
+ return true;
+ }
+ } return false;
+ }
+ return false;
+ }
+
+ @SuppressWarnings("synthetic-access")
+ private boolean canScroll() {
+ return (mAdapter != null) && (mAdapter.getCount() > 1);
+ }
+ }
+
+ private class PagerObserver extends DataSetObserver {
+ @Override
+ public void onChanged() {
+ dataSetChanged();
+ }
+ @Override
+ public void onInvalidated() {
+ dataSetChanged();
+ }
+ }
+
+ /**
+ * Layout parameters that should be supplied for views added to a
+ * ViewPager.
+ */
+ public static class LayoutParams extends ViewGroup.LayoutParams {
+ /**
+ * true if this view is a decoration on the pager itself and not
+ * a view supplied by the adapter.
+ */
+ public boolean isDecor;
+
+ /**
+ * Gravity setting for use on decor views only:
+ * Where to position the view page within the overall ViewPager
+ * container; constants are defined in {@link android.view.Gravity}.
+ */
+ public int gravity;
+
+ /**
+ * Width as a 0-1 multiplier of the measured pager width
+ */
+ float widthFactor = 0.f;
+
+ /**
+ * true if this view was added during layout and needs to be measured
+ * before being positioned.
+ */
+ boolean needsMeasure;
+
+ /**
+ * Adapter position this view is for if !isDecor
+ */
+ int position;
+
+ /**
+ * Current child index within the ViewPager that this view occupies
+ */
+ int childIndex;
+
+ public LayoutParams() {
+ super(MATCH_PARENT, MATCH_PARENT);
+ }
+
+ @SuppressWarnings("synthetic-access")
+ public LayoutParams(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs, LAYOUT_ATTRS);
+ gravity = a.getInteger(0, Gravity.TOP);
+ a.recycle();
+ }
+ }
+
+ static class ViewPositionComparator implements Comparator<View> {
+ @Override
+ public int compare(View lhs, View rhs) {
+ final LayoutParams llp = (LayoutParams) lhs.getLayoutParams();
+ final LayoutParams rlp = (LayoutParams) rhs.getLayoutParams();
+ if (llp.isDecor != rlp.isDecor) {
+ return llp.isDecor ? 1 : -1;
+ }
+ return llp.position - rlp.position;
+ }
+ }
+}
diff --git a/src/android/support/v4/view/accessibility/AccessibilityEventCompat.java b/src/android/support/v4/view/accessibility/AccessibilityEventCompat.java
new file mode 100644
index 0000000..c7b17ea
--- /dev/null
+++ b/src/android/support/v4/view/accessibility/AccessibilityEventCompat.java
@@ -0,0 +1,238 @@
+/*
+ * 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.accessibility;
+
+import android.os.Build;
+import android.view.accessibility.AccessibilityEvent;
+
+/**
+ * Helper for accessing features in {@link AccessibilityEvent}
+ * introduced after API level 4 in a backwards compatible fashion.
+ */
+public class AccessibilityEventCompat {
+
+ static interface AccessibilityEventVersionImpl {
+ public int getRecordCount(AccessibilityEvent event);
+ public void appendRecord(AccessibilityEvent event, Object record);
+ public Object getRecord(AccessibilityEvent event, int index);
+ }
+
+ static class AccessibilityEventStubImpl implements AccessibilityEventVersionImpl {
+
+ @Override
+ public void appendRecord(AccessibilityEvent event, Object record) {
+ return;
+ }
+
+ @Override
+ public Object getRecord(AccessibilityEvent event, int index) {
+ return null;
+ }
+
+ @Override
+ public int getRecordCount(AccessibilityEvent event) {
+ return 0;
+ }
+ }
+
+ static class AccessibilityEventIcsImpl extends AccessibilityEventStubImpl {
+
+ @Override
+ public void appendRecord(AccessibilityEvent event, Object record) {
+ AccessibilityEventCompatIcs.appendRecord(event, record);
+ }
+
+ @Override
+ public Object getRecord(AccessibilityEvent event, int index) {
+ return AccessibilityEventCompatIcs.getRecord(event, index);
+ }
+
+ @Override
+ public int getRecordCount(AccessibilityEvent event) {
+ return AccessibilityEventCompatIcs.getRecordCount(event);
+ }
+ }
+
+ private final static AccessibilityEventVersionImpl IMPL;
+
+ static {
+ if (Build.VERSION.SDK_INT >= 14) { // ICS
+ IMPL = new AccessibilityEventIcsImpl();
+ } else {
+ IMPL = new AccessibilityEventStubImpl();
+ }
+ }
+
+ /**
+ * Represents the event of a hover enter over a {@link android.view.View}.
+ */
+ public static final int TYPE_VIEW_HOVER_ENTER = 0x00000080;
+
+ /**
+ * Represents the event of a hover exit over a {@link android.view.View}.
+ */
+ public static final int TYPE_VIEW_HOVER_EXIT = 0x00000100;
+
+ /**
+ * Represents the event of starting a touch exploration gesture.
+ */
+ public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 0x00000200;
+
+ /**
+ * Represents the event of ending a touch exploration gesture.
+ */
+ public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 0x00000400;
+
+ /**
+ * Represents the event of changing the content of a window.
+ */
+ public static final int TYPE_WINDOW_CONTENT_CHANGED = 0x00000800;
+
+ /**
+ * Represents the event of scrolling a view.
+ */
+ public static final int TYPE_VIEW_SCROLLED = 0x00001000;
+
+ /**
+ * Represents the event of changing the selection in an {@link android.widget.EditText}.
+ */
+ public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 0x00002000;
+
+ /**
+ * Represents the event of an application making an announcement.
+ */
+ public static final int TYPE_ANNOUNCEMENT = 0x00004000;
+
+ /**
+ * Represents the event of gaining accessibility focus.
+ */
+ public static final int TYPE_VIEW_ACCESSIBILITY_FOCUSED = 0x00008000;
+
+ /**
+ * Represents the event of clearing accessibility focus.
+ */
+ public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 0x00010000;
+
+ /**
+ * Represents the event of traversing the text of a view at a given movement granularity.
+ */
+ public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 0x00020000;
+
+ /**
+ * Represents the event of beginning gesture detection.
+ */
+ public static final int TYPE_GESTURE_DETECTION_START = 0x00040000;
+
+ /**
+ * Represents the event of ending gesture detection.
+ */
+ public static final int TYPE_GESTURE_DETECTION_END = 0x00080000;
+
+ /**
+ * Represents the event of the user starting to touch the screen.
+ */
+ public static final int TYPE_TOUCH_INTERACTION_START = 0x00100000;
+
+ /**
+ * Represents the event of the user ending to touch the screen.
+ */
+ public static final int TYPE_TOUCH_INTERACTION_END = 0x00200000;
+
+ /**
+ * Mask for {@link AccessibilityEvent} all types.
+ *
+ * @see AccessibilityEvent#TYPE_VIEW_CLICKED
+ * @see AccessibilityEvent#TYPE_VIEW_LONG_CLICKED
+ * @see AccessibilityEvent#TYPE_VIEW_SELECTED
+ * @see AccessibilityEvent#TYPE_VIEW_FOCUSED
+ * @see AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED
+ * @see AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED
+ * @see AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED
+ * @see #TYPE_VIEW_HOVER_ENTER
+ * @see #TYPE_VIEW_HOVER_EXIT
+ * @see #TYPE_TOUCH_EXPLORATION_GESTURE_START
+ * @see #TYPE_TOUCH_EXPLORATION_GESTURE_END
+ * @see #TYPE_WINDOW_CONTENT_CHANGED
+ * @see #TYPE_VIEW_SCROLLED
+ * @see #TYPE_VIEW_TEXT_SELECTION_CHANGED
+ * @see #TYPE_ANNOUNCEMENT
+ * @see #TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ * @see #TYPE_GESTURE_DETECTION_START
+ * @see #TYPE_GESTURE_DETECTION_END
+ * @see #TYPE_TOUCH_INTERACTION_START
+ * @see #TYPE_TOUCH_INTERACTION_END
+ */
+ public static final int TYPES_ALL_MASK = 0xFFFFFFFF;
+
+ /*
+ * Hide constructor from clients.
+ */
+ private AccessibilityEventCompat() {
+
+ }
+
+ /**
+ * Gets the number of records contained in the event.
+ *
+ * @return The number of records.
+ */
+ public static int getRecordCount(AccessibilityEvent event) {
+ return IMPL.getRecordCount(event);
+ }
+
+ /**
+ * Appends an {@link android.view.accessibility.AccessibilityRecord} to the end of
+ * event records.
+ *
+ * @param record The record to append.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ @SuppressWarnings("deprecation")
+ public static void appendRecord(AccessibilityEvent event, AccessibilityRecordCompat record) {
+ IMPL.appendRecord(event, record.getImpl());
+ }
+
+ /**
+ * Gets the record at a given index.
+ *
+ * @param index The index.
+ * @return The record at the specified index.
+ */
+ @SuppressWarnings("deprecation")
+ public static AccessibilityRecordCompat getRecord(AccessibilityEvent event, int index) {
+ return new AccessibilityRecordCompat(IMPL.getRecord(event, index));
+ }
+
+ /**
+ * Creates an {@link AccessibilityRecordCompat} from an {@link AccessibilityEvent}
+ * that can be used to manipulate the event properties defined in
+ * {@link android.view.accessibility.AccessibilityRecord}.
+ * <p>
+ * <strong>Note:</strong> Do not call {@link AccessibilityRecordCompat#recycle()} on the
+ * returned {@link AccessibilityRecordCompat}. Call {@link AccessibilityEvent#recycle()}
+ * in case you want to recycle the event.
+ * </p>
+ *
+ * @param event The from which to create a record.
+ * @return An {@link AccessibilityRecordCompat}.
+ */
+ @SuppressWarnings("deprecation")
+ public static AccessibilityRecordCompat asRecord(AccessibilityEvent event) {
+ return new AccessibilityRecordCompat(event);
+ }
+}
diff --git a/src/android/support/v4/view/accessibility/AccessibilityEventCompatIcs.java b/src/android/support/v4/view/accessibility/AccessibilityEventCompatIcs.java
new file mode 100644
index 0000000..53c7471
--- /dev/null
+++ b/src/android/support/v4/view/accessibility/AccessibilityEventCompatIcs.java
@@ -0,0 +1,38 @@
+/*
+ * 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.accessibility;
+
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityRecord;
+
+/**
+ * ICS specific AccessibilityEvent API implementation.
+ */
+class AccessibilityEventCompatIcs {
+
+ public static int getRecordCount(AccessibilityEvent event) {
+ return event.getRecordCount();
+ }
+
+ public static void appendRecord(AccessibilityEvent event, Object record) {
+ event.appendRecord((AccessibilityRecord) record);
+ }
+
+ public static Object getRecord(AccessibilityEvent event, int index) {
+ return event.getRecord(index);
+ }
+}
diff --git a/src/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java b/src/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java
new file mode 100644
index 0000000..a611d61
--- /dev/null
+++ b/src/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java
@@ -0,0 +1,1960 @@
+/*
+ * 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.accessibility;
+
+import android.graphics.Rect;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v4.accessibilityservice.AccessibilityServiceInfoCompat;
+import android.support.v4.view.ViewCompat;
+import android.view.View;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Helper for accessing {@link android.view.accessibility.AccessibilityNodeInfo}
+ * introduced after API level 4 in a backwards compatible fashion.
+ */
+public class AccessibilityNodeInfoCompat {
+
+ static interface AccessibilityNodeInfoImpl {
+ public Object obtain();
+ public Object obtain(View source);
+ public Object obtain(Object info);
+ public Object obtain(View root, int virtualDescendantId);
+ public void setSource(Object info, View source);
+ public void setSource(Object info, View root, int virtualDescendantId);
+ public Object findFocus(Object info, int focus);
+ public Object focusSearch(Object info, int direction);
+ public int getWindowId(Object info);
+ public int getChildCount(Object info);
+ public Object getChild(Object info, int index);
+ public void addChild(Object info, View child);
+ public void addChild(Object info, View child, int virtualDescendantId);
+ public int getActions(Object info);
+ public void addAction(Object info, int action);
+ public boolean performAction(Object info, int action);
+ public boolean performAction(Object info, int action, Bundle arguments);
+ public void setMovementGranularities(Object info, int granularities);
+ public int getMovementGranularities(Object info);
+ public List<Object> findAccessibilityNodeInfosByText(Object info, String text);
+ public Object getParent(Object info);
+ public void setParent(Object info, View root, int virtualDescendantId);
+ public void setParent(Object info, View parent);
+ public void getBoundsInParent(Object info, Rect outBounds);
+ public void setBoundsInParent(Object info, Rect bounds);
+ public void getBoundsInScreen(Object info, Rect outBounds);
+ public void setBoundsInScreen(Object info, Rect bounds);
+ public boolean isCheckable(Object info);
+ public void setCheckable(Object info, boolean checkable);
+ public boolean isChecked(Object info);
+ public void setChecked(Object info, boolean checked);
+ public boolean isFocusable(Object info);
+ public void setFocusable(Object info, boolean focusable);
+ public boolean isFocused(Object info);
+ public void setFocused(Object info, boolean focused);
+ public boolean isVisibleToUser(Object info);
+ public void setVisibleToUser(Object info, boolean visibleToUser);
+ public boolean isAccessibilityFocused(Object info);
+ public void setAccessibilityFocused(Object info, boolean focused);
+ public boolean isSelected(Object info);
+ public void setSelected(Object info, boolean selected);
+ public boolean isClickable(Object info);
+ public void setClickable(Object info, boolean clickable);
+ public boolean isLongClickable(Object info);
+ public void setLongClickable(Object info, boolean longClickable);
+ public boolean isEnabled(Object info);
+ public void setEnabled(Object info, boolean enabled);
+ public boolean isPassword(Object info);
+ public void setPassword(Object info, boolean password);
+ public boolean isScrollable(Object info);
+ public void setScrollable(Object info, boolean scrollable);
+ public CharSequence getPackageName(Object info);
+ public void setPackageName(Object info, CharSequence packageName);
+ public CharSequence getClassName(Object info);
+ public void setClassName(Object info, CharSequence className);
+ public CharSequence getText(Object info);
+ public void setText(Object info, CharSequence text);
+ public CharSequence getContentDescription(Object info);
+ public void setContentDescription(Object info, CharSequence contentDescription);
+ public void recycle(Object info);
+ public String getViewIdResourceName(Object info);
+ public void setViewIdResourceName(Object info, String viewId);
+ public int getLiveRegion(Object info);
+ public void setLiveRegion(Object info, int mode);
+ }
+
+ static class AccessibilityNodeInfoStubImpl implements AccessibilityNodeInfoImpl {
+ @Override
+ public Object obtain() {
+ return null;
+ }
+
+ @Override
+ public Object obtain(View source) {
+ return null;
+ }
+
+ @Override
+ public Object obtain(View root, int virtualDescendantId) {
+ return null;
+ }
+
+ @Override
+ public Object obtain(Object info) {
+ return null;
+ }
+
+ @Override
+ public void addAction(Object info, int action) {
+ return;
+ }
+
+ @Override
+ public void addChild(Object info, View child) {
+ return;
+ }
+
+ @Override
+ public void addChild(Object info, View child, int virtualDescendantId) {
+ return;
+ }
+
+ @Override
+ public List<Object> findAccessibilityNodeInfosByText(Object info, String text) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public int getActions(Object info) {
+ return 0;
+ }
+
+ @Override
+ public void getBoundsInParent(Object info, Rect outBounds) {
+ return;
+ }
+
+ @Override
+ public void getBoundsInScreen(Object info, Rect outBounds) {
+ return;
+ }
+
+ @Override
+ public Object getChild(Object info, int index) {
+ return null;
+ }
+
+ @Override
+ public int getChildCount(Object info) {
+ return 0;
+ }
+
+ @Override
+ public CharSequence getClassName(Object info) {
+ return null;
+ }
+
+ @Override
+ public CharSequence getContentDescription(Object info) {
+ return null;
+ }
+
+ @Override
+ public CharSequence getPackageName(Object info) {
+ return null;
+ }
+
+ @Override
+ public Object getParent(Object info) {
+ return null;
+ }
+
+ @Override
+ public CharSequence getText(Object info) {
+ return null;
+ }
+
+ @Override
+ public int getWindowId(Object info) {
+ return 0;
+ }
+
+ @Override
+ public boolean isCheckable(Object info) {
+ return false;
+ }
+
+ @Override
+ public boolean isChecked(Object info) {
+ return false;
+ }
+
+ @Override
+ public boolean isClickable(Object info) {
+ return false;
+ }
+
+ @Override
+ public boolean isEnabled(Object info) {
+ return false;
+ }
+
+ @Override
+ public boolean isFocusable(Object info) {
+ return false;
+ }
+
+ @Override
+ public boolean isFocused(Object info) {
+ return false;
+ }
+
+ @Override
+ public boolean isVisibleToUser(Object info) {
+ return false;
+ }
+
+ @Override
+ public boolean isAccessibilityFocused(Object info) {
+ return false;
+ }
+
+ @Override
+ public boolean isLongClickable(Object info) {
+ return false;
+ }
+
+ @Override
+ public boolean isPassword(Object info) {
+ return false;
+ }
+
+ @Override
+ public boolean isScrollable(Object info) {
+ return false;
+ }
+
+ @Override
+ public boolean isSelected(Object info) {
+ return false;
+ }
+
+ @Override
+ public boolean performAction(Object info, int action) {
+ return false;
+ }
+
+ @Override
+ public boolean performAction(Object info, int action, Bundle arguments) {
+ return false;
+ }
+
+ @Override
+ public void setMovementGranularities(Object info, int granularities) {
+ return;
+ }
+
+ @Override
+ public int getMovementGranularities(Object info) {
+ return 0;
+ }
+
+ @Override
+ public void setBoundsInParent(Object info, Rect bounds) {
+ return;
+ }
+
+ @Override
+ public void setBoundsInScreen(Object info, Rect bounds) {
+ return;
+ }
+
+ @Override
+ public void setCheckable(Object info, boolean checkable) {
+ return;
+ }
+
+ @Override
+ public void setChecked(Object info, boolean checked) {
+ return;
+ }
+
+ @Override
+ public void setClassName(Object info, CharSequence className) {
+ return;
+ }
+
+ @Override
+ public void setClickable(Object info, boolean clickable) {
+ return;
+ }
+
+ @Override
+ public void setContentDescription(Object info, CharSequence contentDescription) {
+ return;
+ }
+
+ @Override
+ public void setEnabled(Object info, boolean enabled) {
+ return;
+ }
+
+ @Override
+ public void setFocusable(Object info, boolean focusable) {
+ return;
+ }
+
+ @Override
+ public void setFocused(Object info, boolean focused) {
+ return;
+ }
+
+ @Override
+ public void setVisibleToUser(Object info, boolean visibleToUser) {
+ return;
+ }
+
+ @Override
+ public void setAccessibilityFocused(Object info, boolean focused) {
+ return;
+ }
+
+ @Override
+ public void setLongClickable(Object info, boolean longClickable) {
+ return;
+ }
+
+ @Override
+ public void setPackageName(Object info, CharSequence packageName) {
+ return;
+ }
+
+ @Override
+ public void setParent(Object info, View parent) {
+ return;
+ }
+
+ @Override
+ public void setPassword(Object info, boolean password) {
+ return;
+ }
+
+ @Override
+ public void setScrollable(Object info, boolean scrollable) {
+ return;
+ }
+
+ @Override
+ public void setSelected(Object info, boolean selected) {
+ return;
+ }
+
+ @Override
+ public void setSource(Object info, View source) {
+ return;
+ }
+
+ @Override
+ public void setSource(Object info, View root, int virtualDescendantId) {
+ return;
+ }
+
+ @Override
+ public Object findFocus(Object info, int focus) {
+ return null;
+ }
+
+ @Override
+ public Object focusSearch(Object info, int direction) {
+ return null;
+ }
+
+ @Override
+ public void setText(Object info, CharSequence text) {
+ return;
+ }
+
+ @Override
+ public void recycle(Object info) {
+ return;
+ }
+
+ @Override
+ public void setParent(Object info, View root, int virtualDescendantId) {
+ return;
+ }
+
+ @Override
+ public String getViewIdResourceName(Object info) {
+ return null;
+ }
+
+ @Override
+ public void setViewIdResourceName(Object info, String viewId) {
+ return;
+ }
+
+ @Override
+ public int getLiveRegion(Object info) {
+ return ViewCompat.ACCESSIBILITY_LIVE_REGION_NONE;
+ }
+
+ @Override
+ public void setLiveRegion(Object info, int mode) {
+ // No-op
+ }
+ }
+
+ static class AccessibilityNodeInfoIcsImpl extends AccessibilityNodeInfoStubImpl {
+ @Override
+ public Object obtain() {
+ return AccessibilityNodeInfoCompatIcs.obtain();
+ }
+
+ @Override
+ public Object obtain(View source) {
+ return AccessibilityNodeInfoCompatIcs.obtain(source);
+ }
+
+ @Override
+ public Object obtain(Object info) {
+ return AccessibilityNodeInfoCompatIcs.obtain(info);
+ }
+
+ @Override
+ public void addAction(Object info, int action) {
+ AccessibilityNodeInfoCompatIcs.addAction(info, action);
+ }
+
+ @Override
+ public void addChild(Object info, View child) {
+ AccessibilityNodeInfoCompatIcs.addChild(info, child);
+ }
+
+ @Override
+ public List<Object> findAccessibilityNodeInfosByText(Object info, String text) {
+ return AccessibilityNodeInfoCompatIcs.findAccessibilityNodeInfosByText(info, text);
+ }
+
+ @Override
+ public int getActions(Object info) {
+ return AccessibilityNodeInfoCompatIcs.getActions(info);
+ }
+
+ @Override
+ public void getBoundsInParent(Object info, Rect outBounds) {
+ AccessibilityNodeInfoCompatIcs.getBoundsInParent(info, outBounds);
+ }
+
+ @Override
+ public void getBoundsInScreen(Object info, Rect outBounds) {
+ AccessibilityNodeInfoCompatIcs.getBoundsInScreen(info, outBounds);
+ }
+
+ @Override
+ public Object getChild(Object info, int index) {
+ return AccessibilityNodeInfoCompatIcs.getChild(info, index);
+ }
+
+ @Override
+ public int getChildCount(Object info) {
+ return AccessibilityNodeInfoCompatIcs.getChildCount(info);
+ }
+
+ @Override
+ public CharSequence getClassName(Object info) {
+ return AccessibilityNodeInfoCompatIcs.getClassName(info);
+ }
+
+ @Override
+ public CharSequence getContentDescription(Object info) {
+ return AccessibilityNodeInfoCompatIcs.getContentDescription(info);
+ }
+
+ @Override
+ public CharSequence getPackageName(Object info) {
+ return AccessibilityNodeInfoCompatIcs.getPackageName(info);
+ }
+
+ @Override
+ public Object getParent(Object info) {
+ return AccessibilityNodeInfoCompatIcs.getParent(info);
+ }
+
+ @Override
+ public CharSequence getText(Object info) {
+ return AccessibilityNodeInfoCompatIcs.getText(info);
+ }
+
+ @Override
+ public int getWindowId(Object info) {
+ return AccessibilityNodeInfoCompatIcs.getWindowId(info);
+ }
+
+ @Override
+ public boolean isCheckable(Object info) {
+ return AccessibilityNodeInfoCompatIcs.isCheckable(info);
+ }
+
+ @Override
+ public boolean isChecked(Object info) {
+ return AccessibilityNodeInfoCompatIcs.isChecked(info);
+ }
+
+ @Override
+ public boolean isClickable(Object info) {
+ return AccessibilityNodeInfoCompatIcs.isClickable(info);
+ }
+
+ @Override
+ public boolean isEnabled(Object info) {
+ return AccessibilityNodeInfoCompatIcs.isEnabled(info);
+ }
+
+ @Override
+ public boolean isFocusable(Object info) {
+ return AccessibilityNodeInfoCompatIcs.isFocusable(info);
+ }
+
+ @Override
+ public boolean isFocused(Object info) {
+ return AccessibilityNodeInfoCompatIcs.isFocused(info);
+ }
+
+ @Override
+ public boolean isLongClickable(Object info) {
+ return AccessibilityNodeInfoCompatIcs.isLongClickable(info);
+ }
+
+ @Override
+ public boolean isPassword(Object info) {
+ return AccessibilityNodeInfoCompatIcs.isPassword(info);
+ }
+
+ @Override
+ public boolean isScrollable(Object info) {
+ return AccessibilityNodeInfoCompatIcs.isScrollable(info);
+ }
+
+ @Override
+ public boolean isSelected(Object info) {
+ return AccessibilityNodeInfoCompatIcs.isSelected(info);
+ }
+
+ @Override
+ public boolean performAction(Object info, int action) {
+ return AccessibilityNodeInfoCompatIcs.performAction(info, action);
+ }
+
+ @Override
+ public void setBoundsInParent(Object info, Rect bounds) {
+ AccessibilityNodeInfoCompatIcs.setBoundsInParent(info, bounds);
+ }
+
+ @Override
+ public void setBoundsInScreen(Object info, Rect bounds) {
+ AccessibilityNodeInfoCompatIcs.setBoundsInScreen(info, bounds);
+ }
+
+ @Override
+ public void setCheckable(Object info, boolean checkable) {
+ AccessibilityNodeInfoCompatIcs.setCheckable(info, checkable);
+ }
+
+ @Override
+ public void setChecked(Object info, boolean checked) {
+ AccessibilityNodeInfoCompatIcs.setChecked(info, checked);
+ }
+
+ @Override
+ public void setClassName(Object info, CharSequence className) {
+ AccessibilityNodeInfoCompatIcs.setClassName(info, className);
+ }
+
+ @Override
+ public void setClickable(Object info, boolean clickable) {
+ AccessibilityNodeInfoCompatIcs.setClickable(info, clickable);
+ }
+
+ @Override
+ public void setContentDescription(Object info, CharSequence contentDescription) {
+ AccessibilityNodeInfoCompatIcs.setContentDescription(info, contentDescription);
+ }
+
+ @Override
+ public void setEnabled(Object info, boolean enabled) {
+ AccessibilityNodeInfoCompatIcs.setEnabled(info, enabled);
+ }
+
+ @Override
+ public void setFocusable(Object info, boolean focusable) {
+ AccessibilityNodeInfoCompatIcs.setFocusable(info, focusable);
+ }
+
+ @Override
+ public void setFocused(Object info, boolean focused) {
+ AccessibilityNodeInfoCompatIcs.setFocused(info, focused);
+ }
+
+ @Override
+ public void setLongClickable(Object info, boolean longClickable) {
+ AccessibilityNodeInfoCompatIcs.setLongClickable(info, longClickable);
+ }
+
+ @Override
+ public void setPackageName(Object info, CharSequence packageName) {
+ AccessibilityNodeInfoCompatIcs.setPackageName(info, packageName);
+ }
+
+ @Override
+ public void setParent(Object info, View parent) {
+ AccessibilityNodeInfoCompatIcs.setParent(info, parent);
+ }
+
+ @Override
+ public void setPassword(Object info, boolean password) {
+ AccessibilityNodeInfoCompatIcs.setPassword(info, password);
+ }
+
+ @Override
+ public void setScrollable(Object info, boolean scrollable) {
+ AccessibilityNodeInfoCompatIcs.setScrollable(info, scrollable);
+ }
+
+ @Override
+ public void setSelected(Object info, boolean selected) {
+ AccessibilityNodeInfoCompatIcs.setSelected(info, selected);
+ }
+
+ @Override
+ public void setSource(Object info, View source) {
+ AccessibilityNodeInfoCompatIcs.setSource(info, source);
+ }
+
+ @Override
+ public void setText(Object info, CharSequence text) {
+ AccessibilityNodeInfoCompatIcs.setText(info, text);
+ }
+
+ @Override
+ public void recycle(Object info) {
+ AccessibilityNodeInfoCompatIcs.recycle(info);
+ }
+ }
+
+ static {
+ if (Build.VERSION.SDK_INT >= 14) { // ICS
+ IMPL = new AccessibilityNodeInfoIcsImpl();
+ } else {
+ IMPL = new AccessibilityNodeInfoStubImpl();
+ }
+ }
+
+ private static final AccessibilityNodeInfoImpl IMPL;
+
+ private final Object mInfo;
+
+ // Actions introduced in IceCreamSandwich
+
+ /**
+ * Action that focuses the node.
+ */
+ public static final int ACTION_FOCUS = 0x00000001;
+
+ /**
+ * Action that unfocuses the node.
+ */
+ public static final int ACTION_CLEAR_FOCUS = 0x00000002;
+
+ /**
+ * Action that selects the node.
+ */
+ public static final int ACTION_SELECT = 0x00000004;
+
+ /**
+ * Action that unselects the node.
+ */
+ public static final int ACTION_CLEAR_SELECTION = 0x00000008;
+
+ /**
+ * Action that clicks on the node info.
+ */
+ public static final int ACTION_CLICK = 0x00000010;
+
+ /**
+ * Action that long clicks on the node.
+ */
+ public static final int ACTION_LONG_CLICK = 0x00000020;
+
+ // Actions introduced in JellyBean
+
+ /**
+ * Action that gives accessibility focus to the node.
+ */
+ public static final int ACTION_ACCESSIBILITY_FOCUS = 0x00000040;
+
+ /**
+ * Action that clears accessibility focus of the node.
+ */
+ public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000080;
+
+ /**
+ * Action that requests to go to the next entity in this node's text
+ * at a given movement granularity. For example, move to the next character,
+ * word, etc.
+ * <p>
+ * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<,
+ * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
+ * <strong>Example:</strong> Move to the previous character and do not extend selection.
+ * <code><pre><p>
+ * Bundle arguments = new Bundle();
+ * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
+ * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+ * arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
+ * false);
+ * info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+ * </code></pre></p>
+ * </p>
+ *
+ * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
+ * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
+ *
+ * @see #setMovementGranularities(int)
+ * @see #getMovementGranularities()
+ *
+ * @see #MOVEMENT_GRANULARITY_CHARACTER
+ * @see #MOVEMENT_GRANULARITY_WORD
+ * @see #MOVEMENT_GRANULARITY_LINE
+ * @see #MOVEMENT_GRANULARITY_PARAGRAPH
+ * @see #MOVEMENT_GRANULARITY_PAGE
+ */
+ public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 0x00000100;
+
+ /**
+ * Action that requests to go to the previous entity in this node's text
+ * at a given movement granularity. For example, move to the next character,
+ * word, etc.
+ * <p>
+ * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<,
+ * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
+ * <strong>Example:</strong> Move to the next character and do not extend selection.
+ * <code><pre><p>
+ * Bundle arguments = new Bundle();
+ * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
+ * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+ * arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
+ * false);
+ * info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
+ * arguments);
+ * </code></pre></p>
+ * </p>
+ *
+ * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
+ * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
+ *
+ * @see #setMovementGranularities(int)
+ * @see #getMovementGranularities()
+ *
+ * @see #MOVEMENT_GRANULARITY_CHARACTER
+ * @see #MOVEMENT_GRANULARITY_WORD
+ * @see #MOVEMENT_GRANULARITY_LINE
+ * @see #MOVEMENT_GRANULARITY_PARAGRAPH
+ * @see #MOVEMENT_GRANULARITY_PAGE
+ */
+ public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 0x00000200;
+
+ /**
+ * Action to move to the next HTML element of a given type. For example, move
+ * to the BUTTON, INPUT, TABLE, etc.
+ * <p>
+ * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
+ * <strong>Example:</strong>
+ * <code><pre><p>
+ * Bundle arguments = new Bundle();
+ * arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
+ * info.performAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, arguments);
+ * </code></pre></p>
+ * </p>
+ */
+ public static final int ACTION_NEXT_HTML_ELEMENT = 0x00000400;
+
+ /**
+ * Action to move to the previous HTML element of a given type. For example, move
+ * to the BUTTON, INPUT, TABLE, etc.
+ * <p>
+ * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
+ * <strong>Example:</strong>
+ * <code><pre><p>
+ * Bundle arguments = new Bundle();
+ * arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
+ * info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, arguments);
+ * </code></pre></p>
+ * </p>
+ */
+ public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800;
+
+ /**
+ * Action to scroll the node content forward.
+ */
+ public static final int ACTION_SCROLL_FORWARD = 0x00001000;
+
+ /**
+ * Action to scroll the node content backward.
+ */
+ public static final int ACTION_SCROLL_BACKWARD = 0x00002000;
+
+ // Actions introduced in JellyBeanMr2
+
+ /**
+ * Action to copy the current selection to the clipboard.
+ */
+ public static final int ACTION_COPY = 0x00004000;
+
+ /**
+ * Action to paste the current clipboard content.
+ */
+ public static final int ACTION_PASTE = 0x00008000;
+
+ /**
+ * Action to cut the current selection and place it to the clipboard.
+ */
+ public static final int ACTION_CUT = 0x00010000;
+
+ /**
+ * Action to set the selection. Performing this action with no arguments
+ * clears the selection.
+ * <p>
+ * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_SELECTION_START_INT},
+ * {@link #ACTION_ARGUMENT_SELECTION_END_INT}<br>
+ * <strong>Example:</strong>
+ * <code><pre><p>
+ * Bundle arguments = new Bundle();
+ * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 1);
+ * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 2);
+ * info.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments);
+ * </code></pre></p>
+ * </p>
+ *
+ * @see #ACTION_ARGUMENT_SELECTION_START_INT
+ * @see #ACTION_ARGUMENT_SELECTION_END_INT
+ */
+ public static final int ACTION_SET_SELECTION = 0x00020000;
+
+ // Action arguments
+
+ /**
+ * Argument for which movement granularity to be used when traversing the node text.
+ * <p>
+ * <strong>Type:</strong> int<br>
+ * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
+ * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
+ * </p>
+ */
+ public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT =
+ "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
+
+ /**
+ * Argument for which HTML element to get moving to the next/previous HTML element.
+ * <p>
+ * <strong>Type:</strong> String<br>
+ * <strong>Actions:</strong> {@link #ACTION_NEXT_HTML_ELEMENT},
+ * {@link #ACTION_PREVIOUS_HTML_ELEMENT}
+ * </p>
+ */
+ public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING =
+ "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
+
+ /**
+ * Argument for whether when moving at granularity to extend the selection
+ * or to move it otherwise.
+ * <p>
+ * <strong>Type:</strong> boolean<br>
+ * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
+ * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
+ * </p>
+ *
+ * @see #ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ * @see #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ */
+ public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN =
+ "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
+
+ /**
+ * Argument for specifying the selection start.
+ * <p>
+ * <strong>Type:</strong> int<br>
+ * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION}
+ * </p>
+ *
+ * @see #ACTION_SET_SELECTION
+ */
+ public static final String ACTION_ARGUMENT_SELECTION_START_INT =
+ "ACTION_ARGUMENT_SELECTION_START_INT";
+
+ /**
+ * Argument for specifying the selection end.
+ * <p>
+ * <strong>Type:</strong> int<br>
+ * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION}
+ * </p>
+ *
+ * @see #ACTION_SET_SELECTION
+ */
+ public static final String ACTION_ARGUMENT_SELECTION_END_INT =
+ "ACTION_ARGUMENT_SELECTION_END_INT";
+
+ // Focus types
+
+ /**
+ * The input focus.
+ */
+ public static final int FOCUS_INPUT = 1;
+
+ /**
+ * The accessibility focus.
+ */
+ public static final int FOCUS_ACCESSIBILITY = 2;
+
+ // Movement granularities
+
+ /**
+ * Movement granularity bit for traversing the text of a node by character.
+ */
+ public static final int MOVEMENT_GRANULARITY_CHARACTER = 0x00000001;
+
+ /**
+ * Movement granularity bit for traversing the text of a node by word.
+ */
+ public static final int MOVEMENT_GRANULARITY_WORD = 0x00000002;
+
+ /**
+ * Movement granularity bit for traversing the text of a node by line.
+ */
+ public static final int MOVEMENT_GRANULARITY_LINE = 0x00000004;
+
+ /**
+ * Movement granularity bit for traversing the text of a node by paragraph.
+ */
+ public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 0x00000008;
+
+ /**
+ * Movement granularity bit for traversing the text of a node by page.
+ */
+ public static final int MOVEMENT_GRANULARITY_PAGE = 0x00000010;
+
+ /**
+ * Creates a wrapper for info implementation.
+ *
+ * @param object The info to wrap.
+ * @return A wrapper for if the object is not null, null otherwise.
+ */
+ static AccessibilityNodeInfoCompat wrapNonNullInstance(Object object) {
+ if (object != null) {
+ return new AccessibilityNodeInfoCompat(object);
+ }
+ return null;
+ }
+
+ /**
+ * Creates a new instance wrapping an
+ * {@link android.view.accessibility.AccessibilityNodeInfo}.
+ *
+ * @param info The info.
+ */
+ public AccessibilityNodeInfoCompat(Object info) {
+ mInfo = info;
+ }
+
+ /**
+ * @return The wrapped {@link android.view.accessibility.AccessibilityNodeInfo}.
+ */
+ public Object getInfo() {
+ return mInfo;
+ }
+
+ /**
+ * Returns a cached instance if such is available otherwise a new one and
+ * sets the source.
+ *
+ * @return An instance.
+ * @see #setSource(View)
+ */
+ public static AccessibilityNodeInfoCompat obtain(View source) {
+ return AccessibilityNodeInfoCompat.wrapNonNullInstance(IMPL.obtain(source));
+ }
+
+ /**
+ * Returns a cached instance if such is available otherwise a new one
+ * and sets the source.
+ *
+ * @param root The root of the virtual subtree.
+ * @param virtualDescendantId The id of the virtual descendant.
+ * @return An instance.
+ *
+ * @see #setSource(View, int)
+ */
+ public static AccessibilityNodeInfoCompat obtain(View root, int virtualDescendantId) {
+ return AccessibilityNodeInfoCompat.wrapNonNullInstance(
+ IMPL.obtain(root, virtualDescendantId));
+ }
+
+ /**
+ * Returns a cached instance if such is available otherwise a new one.
+ *
+ * @return An instance.
+ */
+ public static AccessibilityNodeInfoCompat obtain() {
+ return AccessibilityNodeInfoCompat.wrapNonNullInstance(IMPL.obtain());
+ }
+
+ /**
+ * Returns a cached instance if such is available or a new one is create.
+ * The returned instance is initialized from the given <code>info</code>.
+ *
+ * @param info The other info.
+ * @return An instance.
+ */
+ public static AccessibilityNodeInfoCompat obtain(AccessibilityNodeInfoCompat info) {
+ return AccessibilityNodeInfoCompat.wrapNonNullInstance(IMPL.obtain(info.mInfo));
+ }
+
+ /**
+ * Sets the source.
+ *
+ * @param source The info source.
+ */
+ public void setSource(View source) {
+ IMPL.setSource(mInfo, source);
+ }
+
+ /**
+ * Sets the source to be a virtual descendant of the given <code>root</code>.
+ * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
+ * is set as the source.
+ * <p>
+ * A virtual descendant is an imaginary View that is reported as a part of the view
+ * hierarchy for accessibility purposes. This enables custom views that draw complex
+ * content to report themselves as a tree of virtual views, thus conveying their
+ * logical structure.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param root The root of the virtual subtree.
+ * @param virtualDescendantId The id of the virtual descendant.
+ */
+ public void setSource(View root, int virtualDescendantId) {
+ IMPL.setSource(mInfo, root, virtualDescendantId);
+ }
+
+ /**
+ * Find the view that has the specified focus type. The search starts from
+ * the view represented by this node info.
+ *
+ * @param focus The focus to find. One of {@link #FOCUS_INPUT} or
+ * {@link #FOCUS_ACCESSIBILITY}.
+ * @return The node info of the focused view or null.
+ *
+ * @see #FOCUS_INPUT
+ * @see #FOCUS_ACCESSIBILITY
+ */
+ public AccessibilityNodeInfoCompat findFocus(int focus) {
+ return AccessibilityNodeInfoCompat.wrapNonNullInstance(IMPL.findFocus(mInfo, focus));
+ }
+
+ /**
+ * Searches for the nearest view in the specified direction that can take
+ * input focus.
+ *
+ * @param direction The direction. Can be one of:
+ * {@link View#FOCUS_DOWN},
+ * {@link View#FOCUS_UP},
+ * {@link View#FOCUS_LEFT},
+ * {@link View#FOCUS_RIGHT},
+ * {@link View#FOCUS_FORWARD},
+ * {@link View#FOCUS_BACKWARD}.
+ *
+ * @return The node info for the view that can take accessibility focus.
+ */
+ public AccessibilityNodeInfoCompat focusSearch(int direction) {
+ return AccessibilityNodeInfoCompat.wrapNonNullInstance(IMPL.focusSearch(mInfo, direction));
+ }
+
+ /**
+ * Gets the id of the window from which the info comes from.
+ *
+ * @return The window id.
+ */
+ public int getWindowId() {
+ return IMPL.getWindowId(mInfo);
+ }
+
+ /**
+ * Gets the number of children.
+ *
+ * @return The child count.
+ */
+ public int getChildCount() {
+ return IMPL.getChildCount(mInfo);
+ }
+
+ /**
+ * Get the child at given index.
+ * <p>
+ * <strong>Note:</strong> It is a client responsibility to recycle the
+ * received info by calling {@link AccessibilityNodeInfoCompat#recycle()} to
+ * avoid creating of multiple instances.
+ * </p>
+ *
+ * @param index The child index.
+ * @return The child node.
+ * @throws IllegalStateException If called outside of an
+ * AccessibilityService.
+ */
+ public AccessibilityNodeInfoCompat getChild(int index) {
+ return AccessibilityNodeInfoCompat.wrapNonNullInstance(IMPL.getChild(mInfo, index));
+ }
+
+ /**
+ * Adds a child.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}. This class is
+ * made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param child The child.
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void addChild(View child) {
+ IMPL.addChild(mInfo, child);
+ }
+
+ /**
+ * Adds a virtual child which is a descendant of the given <code>root</code>.
+ * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
+ * is added as a child.
+ * <p>
+ * A virtual descendant is an imaginary View that is reported as a part of the view
+ * hierarchy for accessibility purposes. This enables custom views that draw complex
+ * content to report them selves as a tree of virtual views, thus conveying their
+ * logical structure.
+ * </p>
+ *
+ * @param root The root of the virtual subtree.
+ * @param virtualDescendantId The id of the virtual child.
+ */
+ public void addChild(View root, int virtualDescendantId) {
+ IMPL.addChild(mInfo, root, virtualDescendantId);
+ }
+
+ /**
+ * Gets the actions that can be performed on the node.
+ *
+ * @return The bit mask of with actions.
+ * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_FOCUS
+ * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_CLEAR_FOCUS
+ * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_SELECT
+ * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_CLEAR_SELECTION
+ */
+ public int getActions() {
+ return IMPL.getActions(mInfo);
+ }
+
+ /**
+ * Adds an action that can be performed on the node.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}. This class is
+ * made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param action The action.
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void addAction(int action) {
+ IMPL.addAction(mInfo, action);
+ }
+
+ /**
+ * Performs an action on the node.
+ * <p>
+ * <strong>Note:</strong> An action can be performed only if the request is
+ * made from an {@link android.accessibilityservice.AccessibilityService}.
+ * </p>
+ *
+ * @param action The action to perform.
+ * @return True if the action was performed.
+ * @throws IllegalStateException If called outside of an
+ * AccessibilityService.
+ */
+ public boolean performAction(int action) {
+ return IMPL.performAction(mInfo, action);
+ }
+
+ /**
+ * Performs an action on the node.
+ * <p>
+ * <strong>Note:</strong> An action can be performed only if the request is made
+ * from an {@link android.accessibilityservice.AccessibilityService}.
+ * </p>
+ *
+ * @param action The action to perform.
+ * @param arguments A bundle with additional arguments.
+ * @return True if the action was performed.
+ *
+ * @throws IllegalStateException If called outside of an AccessibilityService.
+ */
+ public boolean performAction(int action, Bundle arguments) {
+ return IMPL.performAction(mInfo, action, arguments);
+ }
+
+ /**
+ * Sets the movement granularities for traversing the text of this node.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param granularities The bit mask with granularities.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setMovementGranularities(int granularities) {
+ IMPL.setMovementGranularities(mInfo, granularities);
+ }
+
+ /**
+ * Gets the movement granularities for traversing the text of this node.
+ *
+ * @return The bit mask with granularities.
+ */
+ public int getMovementGranularities() {
+ return IMPL.getMovementGranularities(mInfo);
+ }
+
+ /**
+ * Finds {@link android.view.accessibility.AccessibilityNodeInfo}s by text. The match
+ * is case insensitive containment. The search is relative to this info i.e. this
+ * info is the root of the traversed tree.
+ * <p>
+ * <strong>Note:</strong> It is a client responsibility to recycle the
+ * received info by calling {@link android.view.accessibility.AccessibilityNodeInfo#recycle()}
+ * to avoid creating of multiple instances.
+ * </p>
+ *
+ * @param text The searched text.
+ * @return A list of node info.
+ */
+ public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(String text) {
+ List<AccessibilityNodeInfoCompat> result = new ArrayList<AccessibilityNodeInfoCompat>();
+ List<Object> infos = IMPL.findAccessibilityNodeInfosByText(mInfo, text);
+ final int infoCount = infos.size();
+ for (int i = 0; i < infoCount; i++) {
+ Object info = infos.get(i);
+ result.add(new AccessibilityNodeInfoCompat(info));
+ }
+ return result;
+ }
+
+ /**
+ * Gets the parent.
+ * <p>
+ * <strong>Note:</strong> It is a client responsibility to recycle the
+ * received info by calling {@link android.view.accessibility.AccessibilityNodeInfo#recycle()}
+ * to avoid creating of multiple instances.
+ * </p>
+ *
+ * @return The parent.
+ */
+ public AccessibilityNodeInfoCompat getParent() {
+ return AccessibilityNodeInfoCompat.wrapNonNullInstance(IMPL.getParent(mInfo));
+ }
+
+ /**
+ * Sets the parent.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}. This class is
+ * made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param parent The parent.
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setParent(View parent) {
+ IMPL.setParent(mInfo, parent);
+ }
+
+ /**
+ * Sets the parent to be a virtual descendant of the given <code>root</code>.
+ * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root
+ * is set as the parent.
+ * <p>
+ * A virtual descendant is an imaginary View that is reported as a part of the view
+ * hierarchy for accessibility purposes. This enables custom views that draw complex
+ * content to report them selves as a tree of virtual views, thus conveying their
+ * logical structure.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param root The root of the virtual subtree.
+ * @param virtualDescendantId The id of the virtual descendant.
+ */
+ public void setParent(View root, int virtualDescendantId) {
+ IMPL.setParent(mInfo, root, virtualDescendantId);
+ }
+
+ /**
+ * Gets the node bounds in parent coordinates.
+ *
+ * @param outBounds The output node bounds.
+ */
+ public void getBoundsInParent(Rect outBounds) {
+ IMPL.getBoundsInParent(mInfo, outBounds);
+ }
+
+ /**
+ * Sets the node bounds in parent coordinates.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}. This class is
+ * made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param bounds The node bounds.
+ *@throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setBoundsInParent(Rect bounds) {
+ IMPL.setBoundsInParent(mInfo, bounds);
+ }
+
+ /**
+ * Gets the node bounds in screen coordinates.
+ *
+ * @param outBounds The output node bounds.
+ */
+ public void getBoundsInScreen(Rect outBounds) {
+ IMPL.getBoundsInScreen(mInfo, outBounds);
+ }
+
+ /**
+ * Sets the node bounds in screen coordinates.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}. This class is
+ * made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param bounds The node bounds.
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setBoundsInScreen(Rect bounds) {
+ IMPL.setBoundsInScreen(mInfo, bounds);
+ }
+
+ /**
+ * Gets whether this node is checkable.
+ *
+ * @return True if the node is checkable.
+ */
+ public boolean isCheckable() {
+ return IMPL.isCheckable(mInfo);
+ }
+
+ /**
+ * Sets whether this node is checkable.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}. This class is
+ * made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param checkable True if the node is checkable.
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setCheckable(boolean checkable) {
+ IMPL.setCheckable(mInfo, checkable);
+ }
+
+ /**
+ * Gets whether this node is checked.
+ *
+ * @return True if the node is checked.
+ */
+ public boolean isChecked() {
+ return IMPL.isChecked(mInfo);
+ }
+
+ /**
+ * Sets whether this node is checked.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}. This class is
+ * made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param checked True if the node is checked.
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setChecked(boolean checked) {
+ IMPL.setChecked(mInfo, checked);
+ }
+
+ /**
+ * Gets whether this node is focusable.
+ *
+ * @return True if the node is focusable.
+ */
+ public boolean isFocusable() {
+ return IMPL.isFocusable(mInfo);
+ }
+
+ /**
+ * Sets whether this node is focusable.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}. This class is
+ * made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param focusable True if the node is focusable.
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setFocusable(boolean focusable) {
+ IMPL.setFocusable(mInfo, focusable);
+ }
+
+ /**
+ * Gets whether this node is focused.
+ *
+ * @return True if the node is focused.
+ */
+ public boolean isFocused() {
+ return IMPL.isFocused(mInfo);
+ }
+
+ /**
+ * Sets whether this node is focused.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}. This class is
+ * made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param focused True if the node is focused.
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setFocused(boolean focused) {
+ IMPL.setFocused(mInfo, focused);
+ }
+
+ /**
+ * Sets whether this node is visible to the user.
+ *
+ * @return Whether the node is visible to the user.
+ */
+ public boolean isVisibleToUser() {
+ return IMPL.isVisibleToUser(mInfo);
+ }
+
+ /**
+ * Sets whether this node is visible to the user.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param visibleToUser Whether the node is visible to the user.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setVisibleToUser(boolean visibleToUser) {
+ IMPL.setVisibleToUser(mInfo, visibleToUser);
+ }
+
+ /**
+ * Gets whether this node is accessibility focused.
+ *
+ * @return True if the node is accessibility focused.
+ */
+ public boolean isAccessibilityFocused() {
+ return IMPL.isAccessibilityFocused(mInfo);
+ }
+
+ /**
+ * Sets whether this node is accessibility focused.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param focused True if the node is accessibility focused.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setAccessibilityFocused(boolean focused) {
+ IMPL.setAccessibilityFocused(mInfo, focused);
+ }
+
+ /**
+ * Gets whether this node is selected.
+ *
+ * @return True if the node is selected.
+ */
+ public boolean isSelected() {
+ return IMPL.isSelected(mInfo);
+ }
+
+ /**
+ * Sets whether this node is selected.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}. This class is
+ * made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param selected True if the node is selected.
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setSelected(boolean selected) {
+ IMPL.setSelected(mInfo, selected);
+ }
+
+ /**
+ * Gets whether this node is clickable.
+ *
+ * @return True if the node is clickable.
+ */
+ public boolean isClickable() {
+ return IMPL.isClickable(mInfo);
+ }
+
+ /**
+ * Sets whether this node is clickable.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}. This class is
+ * made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param clickable True if the node is clickable.
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setClickable(boolean clickable) {
+ IMPL.setClickable(mInfo, clickable);
+ }
+
+ /**
+ * Gets whether this node is long clickable.
+ *
+ * @return True if the node is long clickable.
+ */
+ public boolean isLongClickable() {
+ return IMPL.isLongClickable(mInfo);
+ }
+
+ /**
+ * Sets whether this node is long clickable.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}. This class is
+ * made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param longClickable True if the node is long clickable.
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setLongClickable(boolean longClickable) {
+ IMPL.setLongClickable(mInfo, longClickable);
+ }
+
+ /**
+ * Gets whether this node is enabled.
+ *
+ * @return True if the node is enabled.
+ */
+ public boolean isEnabled() {
+ return IMPL.isEnabled(mInfo);
+ }
+
+ /**
+ * Sets whether this node is enabled.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}. This class is
+ * made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param enabled True if the node is enabled.
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setEnabled(boolean enabled) {
+ IMPL.setEnabled(mInfo, enabled);
+ }
+
+ /**
+ * Gets whether this node is a password.
+ *
+ * @return True if the node is a password.
+ */
+ public boolean isPassword() {
+ return IMPL.isPassword(mInfo);
+ }
+
+ /**
+ * Sets whether this node is a password.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}. This class is
+ * made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param password True if the node is a password.
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setPassword(boolean password) {
+ IMPL.setPassword(mInfo, password);
+ }
+
+ /**
+ * Gets if the node is scrollable.
+ *
+ * @return True if the node is scrollable, false otherwise.
+ */
+ public boolean isScrollable() {
+ return IMPL.isScrollable(mInfo);
+ }
+
+ /**
+ * Sets if the node is scrollable.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}. This class is
+ * made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param scrollable True if the node is scrollable, false otherwise.
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setScrollable(boolean scrollable) {
+ IMPL.setScrollable(mInfo, scrollable);
+ }
+
+ /**
+ * Gets the package this node comes from.
+ *
+ * @return The package name.
+ */
+ public CharSequence getPackageName() {
+ return IMPL.getPackageName(mInfo);
+ }
+
+ /**
+ * Sets the package this node comes from.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}. This class is
+ * made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param packageName The package name.
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setPackageName(CharSequence packageName) {
+ IMPL.setPackageName(mInfo, packageName);
+ }
+
+ /**
+ * Gets the class this node comes from.
+ *
+ * @return The class name.
+ */
+ public CharSequence getClassName() {
+ return IMPL.getClassName(mInfo);
+ }
+
+ /**
+ * Sets the class this node comes from.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}. This class is
+ * made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param className The class name.
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setClassName(CharSequence className) {
+ IMPL.setClassName(mInfo, className);
+ }
+
+ /**
+ * Gets the text of this node.
+ *
+ * @return The text.
+ */
+ public CharSequence getText() {
+ return IMPL.getText(mInfo);
+ }
+
+ /**
+ * Sets the text of this node.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}. This class is
+ * made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param text The text.
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setText(CharSequence text) {
+ IMPL.setText(mInfo, text);
+ }
+
+ /**
+ * Gets the content description of this node.
+ *
+ * @return The content description.
+ */
+ public CharSequence getContentDescription() {
+ return IMPL.getContentDescription(mInfo);
+ }
+
+ /**
+ * Sets the content description of this node.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}. This class is
+ * made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param contentDescription The content description.
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setContentDescription(CharSequence contentDescription) {
+ IMPL.setContentDescription(mInfo, contentDescription);
+ }
+
+ /**
+ * Return an instance back to be reused.
+ * <p>
+ * <strong>Note:</strong> You must not touch the object after calling this function.
+ *
+ * @throws IllegalStateException If the info is already recycled.
+ */
+ public void recycle() {
+ IMPL.recycle(mInfo);
+ }
+
+ /**
+ * Sets the fully qualified resource name of the source view's id.
+ *
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param viewId The id resource name.
+ */
+ public void setViewIdResourceName(String viewId) {
+ IMPL.setViewIdResourceName(mInfo, viewId);
+ }
+
+ /**
+ * Gets the fully qualified resource name of the source view's id.
+ *
+ * <p>
+ * <strong>Note:</strong> The primary usage of this API is for UI test automation
+ * and in order to report the source view id of an {@link AccessibilityNodeInfoCompat}
+ * the client has to set the {@link AccessibilityServiceInfoCompat#FLAG_REPORT_VIEW_IDS}
+ * flag when configuring his {@link android.accessibilityservice.AccessibilityService}.
+ * </p>
+ *
+ * @return The id resource name.
+ */
+ public String getViewIdResourceName() {
+ return IMPL.getViewIdResourceName(mInfo);
+ }
+
+ /**
+ * Gets the node's live region mode.
+ * <p>
+ * A live region is a node that contains information that is important for
+ * the user and when it changes the user should be notified. For example,
+ * in a login screen with a TextView that displays an "incorrect password"
+ * notification, that view should be marked as a live region with mode
+ * {@link ViewCompat#ACCESSIBILITY_LIVE_REGION_POLITE}.
+ * <p>
+ * It is the responsibility of the accessibility service to monitor
+ * {@link AccessibilityEventCompat#TYPE_WINDOW_CONTENT_CHANGED} events
+ * indicating changes to live region nodes and their children.
+ *
+ * @return The live region mode, or
+ * {@link ViewCompat#ACCESSIBILITY_LIVE_REGION_NONE} if the view is
+ * not a live region.
+ * @see ViewCompat#getAccessibilityLiveRegion(View)
+ */
+ public int getLiveRegion() {
+ return IMPL.getLiveRegion(mInfo);
+ }
+
+ /**
+ * Sets the node's live region mode.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}. This class is
+ * made immutable before being delivered to an AccessibilityService.
+ *
+ * @param mode The live region mode, or
+ * {@link ViewCompat#ACCESSIBILITY_LIVE_REGION_NONE} if the view is
+ * not a live region.
+ * @see ViewCompat#setAccessibilityLiveRegion(View, int)
+ */
+ public void setLiveRegion(int mode) {
+ IMPL.setLiveRegion(mInfo, mode);
+ }
+
+ @Override
+ public int hashCode() {
+ return (mInfo == null) ? 0 : mInfo.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ AccessibilityNodeInfoCompat other = (AccessibilityNodeInfoCompat) obj;
+ if (mInfo == null) {
+ if (other.mInfo != null) {
+ return false;
+ }
+ } else if (!mInfo.equals(other.mInfo)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(super.toString());
+
+ Rect bounds = new Rect();
+
+ getBoundsInParent(bounds);
+ builder.append("; boundsInParent: " + bounds);
+
+ getBoundsInScreen(bounds);
+ builder.append("; boundsInScreen: " + bounds);
+
+ builder.append("; packageName: ").append(getPackageName());
+ builder.append("; className: ").append(getClassName());
+ builder.append("; text: ").append(getText());
+ builder.append("; contentDescription: ").append(getContentDescription());
+ builder.append("; viewId: ").append(getViewIdResourceName());
+
+ builder.append("; checkable: ").append(isCheckable());
+ builder.append("; checked: ").append(isChecked());
+ builder.append("; focusable: ").append(isFocusable());
+ builder.append("; focused: ").append(isFocused());
+ builder.append("; selected: ").append(isSelected());
+ builder.append("; clickable: ").append(isClickable());
+ builder.append("; longClickable: ").append(isLongClickable());
+ builder.append("; enabled: ").append(isEnabled());
+ builder.append("; password: ").append(isPassword());
+ builder.append("; scrollable: " + isScrollable());
+
+ builder.append("; [");
+ for (int actionBits = getActions(); actionBits != 0;) {
+ final int action = 1 << Integer.numberOfTrailingZeros(actionBits);
+ actionBits &= ~action;
+ builder.append(getActionSymbolicName(action));
+ if (actionBits != 0) {
+ builder.append(", ");
+ }
+ }
+ builder.append("]");
+
+ return builder.toString();
+ }
+
+ private static String getActionSymbolicName(int action) {
+ switch (action) {
+ case ACTION_FOCUS:
+ return "ACTION_FOCUS";
+ case ACTION_CLEAR_FOCUS:
+ return "ACTION_CLEAR_FOCUS";
+ case ACTION_SELECT:
+ return "ACTION_SELECT";
+ case ACTION_CLEAR_SELECTION:
+ return "ACTION_CLEAR_SELECTION";
+ case ACTION_CLICK:
+ return "ACTION_CLICK";
+ case ACTION_LONG_CLICK:
+ return "ACTION_LONG_CLICK";
+ case ACTION_ACCESSIBILITY_FOCUS:
+ return "ACTION_ACCESSIBILITY_FOCUS";
+ case ACTION_CLEAR_ACCESSIBILITY_FOCUS:
+ return "ACTION_CLEAR_ACCESSIBILITY_FOCUS";
+ case ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
+ return "ACTION_NEXT_AT_MOVEMENT_GRANULARITY";
+ case ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
+ return "ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY";
+ case ACTION_NEXT_HTML_ELEMENT:
+ return "ACTION_NEXT_HTML_ELEMENT";
+ case ACTION_PREVIOUS_HTML_ELEMENT:
+ return "ACTION_PREVIOUS_HTML_ELEMENT";
+ case ACTION_SCROLL_FORWARD:
+ return "ACTION_SCROLL_FORWARD";
+ case ACTION_SCROLL_BACKWARD:
+ return "ACTION_SCROLL_BACKWARD";
+ case ACTION_CUT:
+ return "ACTION_CUT";
+ case ACTION_COPY:
+ return "ACTION_COPY";
+ case ACTION_PASTE:
+ return "ACTION_PASTE";
+ case ACTION_SET_SELECTION:
+ return "ACTION_SET_SELECTION";
+ default:
+ return"ACTION_UNKNOWN";
+ }
+ }
+}
diff --git a/src/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatIcs.java b/src/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatIcs.java
new file mode 100644
index 0000000..367f995
--- /dev/null
+++ b/src/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatIcs.java
@@ -0,0 +1,219 @@
+/*
+ * 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.accessibility;
+
+import android.graphics.Rect;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import java.util.List;
+
+/**
+ * ICS specific AccessibilityNodeInfo API implementation.
+ */
+class AccessibilityNodeInfoCompatIcs {
+
+ public static Object obtain() {
+ return AccessibilityNodeInfo.obtain();
+ }
+
+ public static Object obtain(View source) {
+ return AccessibilityNodeInfo.obtain(source);
+ }
+
+ public static Object obtain(Object info) {
+ return AccessibilityNodeInfo.obtain((AccessibilityNodeInfo) info);
+ }
+
+ public static void addAction(Object info, int action) {
+ ((AccessibilityNodeInfo) info).addAction(action);
+ }
+
+ public static void addChild(Object info, View child) {
+ ((AccessibilityNodeInfo) info).addChild(child);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static List<Object> findAccessibilityNodeInfosByText(Object info, String text) {
+ Object result = ((AccessibilityNodeInfo) info).findAccessibilityNodeInfosByText(text);
+ return (List<Object>) result;
+ }
+
+ public static int getActions(Object info) {
+ return ((AccessibilityNodeInfo) info).getActions();
+ }
+
+ public static void getBoundsInParent(Object info, Rect outBounds) {
+ ((AccessibilityNodeInfo) info).getBoundsInParent(outBounds);
+ }
+
+ public static void getBoundsInScreen(Object info, Rect outBounds) {
+ ((AccessibilityNodeInfo) info).getBoundsInScreen(outBounds);
+ }
+
+ public static Object getChild(Object info, int index) {
+ return ((AccessibilityNodeInfo) info).getChild(index);
+ }
+
+ public static int getChildCount(Object info) {
+ return ((AccessibilityNodeInfo) info).getChildCount();
+ }
+
+ public static CharSequence getClassName(Object info) {
+ return ((AccessibilityNodeInfo) info).getClassName();
+ }
+
+ public static CharSequence getContentDescription(Object info) {
+ return ((AccessibilityNodeInfo) info).getContentDescription();
+ }
+
+ public static CharSequence getPackageName(Object info) {
+ return ((AccessibilityNodeInfo) info).getPackageName();
+ }
+
+ public static Object getParent(Object info) {
+ return ((AccessibilityNodeInfo) info).getParent();
+ }
+
+ public static CharSequence getText(Object info) {
+ return ((AccessibilityNodeInfo) info).getText();
+ }
+
+ public static int getWindowId(Object info) {
+ return ((AccessibilityNodeInfo) info).getWindowId();
+ }
+
+ public static boolean isCheckable(Object info) {
+ return ((AccessibilityNodeInfo) info).isCheckable();
+ }
+
+ public static boolean isChecked(Object info) {
+ return ((AccessibilityNodeInfo) info).isChecked();
+ }
+
+ public static boolean isClickable(Object info) {
+ return ((AccessibilityNodeInfo) info).isClickable();
+ }
+
+ public static boolean isEnabled(Object info) {
+ return ((AccessibilityNodeInfo) info).isEnabled();
+ }
+
+ public static boolean isFocusable(Object info) {
+ return ((AccessibilityNodeInfo) info).isFocusable();
+ }
+
+ public static boolean isFocused(Object info) {
+ return ((AccessibilityNodeInfo) info).isFocused();
+ }
+
+ public static boolean isLongClickable(Object info) {
+ return ((AccessibilityNodeInfo) info).isLongClickable();
+ }
+
+ public static boolean isPassword(Object info) {
+ return ((AccessibilityNodeInfo) info).isPassword();
+ }
+
+ public static boolean isScrollable(Object info) {
+ return ((AccessibilityNodeInfo) info).isScrollable();
+ }
+
+ public static boolean isSelected(Object info) {
+ return ((AccessibilityNodeInfo) info).isSelected();
+ }
+
+ public static boolean performAction(Object info, int action) {
+ return ((AccessibilityNodeInfo) info).performAction(action);
+ }
+
+ public static void setBoundsInParent(Object info, Rect bounds) {
+ ((AccessibilityNodeInfo) info).setBoundsInParent(bounds);
+ }
+
+ public static void setBoundsInScreen(Object info, Rect bounds) {
+ ((AccessibilityNodeInfo) info).setBoundsInScreen(bounds);
+ }
+
+ public static void setCheckable(Object info, boolean checkable) {
+ ((AccessibilityNodeInfo) info).setCheckable(checkable);
+ }
+
+ public static void setChecked(Object info, boolean checked) {
+ ((AccessibilityNodeInfo) info).setChecked(checked);
+ }
+
+ public static void setClassName(Object info, CharSequence className) {
+ ((AccessibilityNodeInfo) info).setClassName(className);
+ }
+
+ public static void setClickable(Object info, boolean clickable) {
+ ((AccessibilityNodeInfo) info).setClickable(clickable);
+ }
+
+ public static void setContentDescription(Object info, CharSequence contentDescription) {
+ ((AccessibilityNodeInfo) info).setContentDescription(contentDescription);
+ }
+
+ public static void setEnabled(Object info, boolean enabled) {
+ ((AccessibilityNodeInfo) info).setEnabled(enabled);
+ }
+
+ public static void setFocusable(Object info, boolean focusable) {
+ ((AccessibilityNodeInfo) info).setFocusable(focusable);
+ }
+
+ public static void setFocused(Object info, boolean focused) {
+ ((AccessibilityNodeInfo) info).setFocused(focused);
+ }
+
+ public static void setLongClickable(Object info, boolean longClickable) {
+ ((AccessibilityNodeInfo) info).setLongClickable(longClickable);
+ }
+
+ public static void setPackageName(Object info, CharSequence packageName) {
+ ((AccessibilityNodeInfo) info).setPackageName(packageName);
+ }
+
+ public static void setParent(Object info, View parent) {
+ ((AccessibilityNodeInfo) info).setParent(parent);
+ }
+
+ public static void setPassword(Object info, boolean password) {
+ ((AccessibilityNodeInfo) info).setPassword(password);
+ }
+
+ public static void setScrollable(Object info, boolean scrollable) {
+ ((AccessibilityNodeInfo) info).setScrollable(scrollable);
+ }
+
+ public static void setSelected(Object info, boolean selected) {
+ ((AccessibilityNodeInfo) info).setSelected(selected);
+ }
+
+ public static void setSource(Object info, View source) {
+ ((AccessibilityNodeInfo) info).setSource(source);
+ }
+
+ public static void setText(Object info, CharSequence text) {
+ ((AccessibilityNodeInfo) info).setText(text);
+ }
+
+ public static void recycle(Object info) {
+ ((AccessibilityNodeInfo) info).recycle();
+ }
+}
diff --git a/src/android/support/v4/view/accessibility/AccessibilityNodeProviderCompat.java b/src/android/support/v4/view/accessibility/AccessibilityNodeProviderCompat.java
new file mode 100644
index 0000000..a1db285
--- /dev/null
+++ b/src/android/support/v4/view/accessibility/AccessibilityNodeProviderCompat.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2012 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.accessibility;
+
+import android.os.Bundle;
+import android.view.View;
+
+import java.util.List;
+
+/**
+ * Helper for accessing {@link android.view.accessibility.AccessibilityNodeProvider}
+ * introduced after API level 4 in a backwards compatible fashion.
+ */
+public class AccessibilityNodeProviderCompat {
+
+ interface AccessibilityNodeProviderImpl {
+ public Object newAccessibilityNodeProviderBridge(AccessibilityNodeProviderCompat compat);
+ }
+
+ static class AccessibilityNodeProviderStubImpl implements AccessibilityNodeProviderImpl {
+ @Override
+ public Object newAccessibilityNodeProviderBridge(AccessibilityNodeProviderCompat compat) {
+ return null;
+ }
+ }
+
+ private static final AccessibilityNodeProviderImpl IMPL;
+
+ private final Object mProvider;
+
+ static {
+ IMPL = new AccessibilityNodeProviderStubImpl();
+ }
+
+ /**
+ * Creates a new instance.
+ */
+ public AccessibilityNodeProviderCompat() {
+ mProvider = IMPL.newAccessibilityNodeProviderBridge(this);
+ }
+
+ /**
+ * Creates a new instance wrapping an
+ * {@link android.view.accessibility.AccessibilityNodeProvider}.
+ *
+ * @param provider The provider.
+ */
+ public AccessibilityNodeProviderCompat(Object provider) {
+ mProvider = provider;
+ }
+
+ /**
+ * @return The wrapped {@link android.view.accessibility.AccessibilityNodeProvider}.
+ */
+ public Object getProvider() {
+ return mProvider;
+ }
+
+ /**
+ * Returns an {@link AccessibilityNodeInfoCompat} representing a virtual view,
+ * i.e. a descendant of the host View, with the given <code>virtualViewId</code>
+ * or the host View itself if <code>virtualViewId</code> equals to {@link View#NO_ID}.
+ * <p>
+ * A virtual descendant is an imaginary View that is reported as a part of the view
+ * hierarchy for accessibility purposes. This enables custom views that draw complex
+ * content to report them selves as a tree of virtual views, thus conveying their
+ * logical structure.
+ * </p>
+ * <p>
+ * The implementer is responsible for obtaining an accessibility node info from the
+ * pool of reusable instances and setting the desired properties of the node info
+ * before returning it.
+ * </p>
+ *
+ * @param virtualViewId A client defined virtual view id.
+ * @return A populated {@link AccessibilityNodeInfoCompat} for a virtual descendant
+ * or the host View.
+ *
+ * @see AccessibilityNodeInfoCompat
+ */
+ @SuppressWarnings("static-method")
+ public AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int virtualViewId) {
+ return null;
+ }
+
+ /**
+ * Performs an accessibility action on a virtual view, i.e. a descendant of the
+ * host View, with the given <code>virtualViewId</code> or the host View itself
+ * if <code>virtualViewId</code> equals to {@link View#NO_ID}.
+ *
+ * @param virtualViewId A client defined virtual view id.
+ * @param action The action to perform.
+ * @param arguments Optional arguments.
+ * @return True if the action was performed.
+ *
+ * @see #createAccessibilityNodeInfo(int)
+ * @see AccessibilityNodeInfoCompat
+ */
+ @SuppressWarnings("static-method")
+ public boolean performAction(int virtualViewId, int action, Bundle arguments) {
+ return false;
+ }
+
+ /**
+ * Finds {@link AccessibilityNodeInfoCompat}s by text. The match is case insensitive
+ * containment. The search is relative to the virtual view, i.e. a descendant of the
+ * host View, with the given <code>virtualViewId</code> or the host View itself
+ * <code>virtualViewId</code> equals to {@link View#NO_ID}.
+ *
+ * @param virtualViewId A client defined virtual view id which defined
+ * the root of the tree in which to perform the search.
+ * @param text The searched text.
+ * @return A list of node info.
+ *
+ * @see #createAccessibilityNodeInfo(int)
+ * @see AccessibilityNodeInfoCompat
+ */
+ @SuppressWarnings("static-method")
+ public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(String text,
+ int virtualViewId) {
+ return null;
+ }
+
+ /**
+ * Find the virtual view, i.e. a descendant of the host View, that has the
+ * specified focus type.
+ *
+ * @param focus The focus to find. One of
+ * {@link AccessibilityNodeInfoCompat#FOCUS_INPUT} or
+ * {@link AccessibilityNodeInfoCompat#FOCUS_ACCESSIBILITY}.
+ * @return The node info of the focused view or null.
+ * @see AccessibilityNodeInfoCompat#FOCUS_INPUT
+ * @see AccessibilityNodeInfoCompat#FOCUS_ACCESSIBILITY
+ */
+ @SuppressWarnings("static-method")
+ public AccessibilityNodeInfoCompat findFocus(int focus) {
+ return null;
+ }
+}
diff --git a/src/android/support/v4/view/accessibility/AccessibilityRecordCompat.java b/src/android/support/v4/view/accessibility/AccessibilityRecordCompat.java
new file mode 100644
index 0000000..ce73b6c
--- /dev/null
+++ b/src/android/support/v4/view/accessibility/AccessibilityRecordCompat.java
@@ -0,0 +1,1083 @@
+/*
+ * 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.accessibility;
+
+import android.os.Build;
+import android.os.Parcelable;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Helper for accessing {@link android.view.accessibility.AccessibilityRecord}
+ * introduced after API level 4 in a backwards compatible fashion.
+ */
+public class AccessibilityRecordCompat {
+
+ static interface AccessibilityRecordImpl {
+ public Object obtain();
+ public Object obtain(Object record);
+ public void setSource(Object record, View source);
+ public void setSource(Object record, View root, int virtualDescendantId);
+ public AccessibilityNodeInfoCompat getSource(Object record);
+ public int getWindowId(Object record);
+ public boolean isChecked(Object record);
+ public void setChecked(Object record, boolean isChecked);
+ public boolean isEnabled(Object record);
+ public void setEnabled(Object record, boolean isEnabled);
+ public boolean isPassword(Object record);
+ public void setPassword(Object record, boolean isPassword);
+ public boolean isFullScreen(Object record);
+ public void setFullScreen(Object record, boolean isFullScreen);
+ public boolean isScrollable(Object record);
+ public void setScrollable(Object record, boolean scrollable);
+ public int getItemCount(Object record);
+ public void setItemCount(Object record, int itemCount);
+ public int getCurrentItemIndex(Object record);
+ public void setCurrentItemIndex(Object record, int currentItemIndex);
+ public int getFromIndex(Object record);
+ public void setFromIndex(Object record, int fromIndex);
+ public int getToIndex(Object record);
+ public void setToIndex(Object record, int toIndex);
+ public int getScrollX(Object record);
+ public void setScrollX(Object record, int scrollX);
+ public int getScrollY(Object record);
+ public void setScrollY(Object record, int scrollY);
+ public int getMaxScrollX(Object record);
+ public void setMaxScrollX(Object record, int maxScrollX);
+ public int getMaxScrollY(Object record);
+ public void setMaxScrollY(Object record, int maxScrollY);
+ public int getAddedCount(Object record);
+ public void setAddedCount(Object record, int addedCount);
+ public int getRemovedCount(Object record);
+ public void setRemovedCount(Object record, int removedCount);
+ public CharSequence getClassName(Object record);
+ public void setClassName(Object record, CharSequence className);
+ public List<CharSequence> getText(Object record);
+ public CharSequence getBeforeText(Object record);
+ public void setBeforeText(Object record, CharSequence beforeText);
+ public CharSequence getContentDescription(Object record);
+ public void setContentDescription(Object record, CharSequence contentDescription);
+ public Parcelable getParcelableData(Object record);
+ public void setParcelableData(Object record, Parcelable parcelableData);
+ public void recycle(Object record);
+ }
+
+ static class AccessibilityRecordStubImpl implements AccessibilityRecordImpl {
+ @Override
+ public Object obtain() {
+ return null;
+ }
+
+ @Override
+ public Object obtain(Object record) {
+ return null;
+ }
+
+ @Override
+ public int getAddedCount(Object record) {
+ return 0;
+ }
+
+ @Override
+ public CharSequence getBeforeText(Object record) {
+ return null;
+ }
+
+ @Override
+ public CharSequence getClassName(Object record) {
+ return null;
+ }
+
+ @Override
+ public CharSequence getContentDescription(Object record) {
+ return null;
+ }
+
+ @Override
+ public int getCurrentItemIndex(Object record) {
+ return 0;
+ }
+
+ @Override
+ public int getFromIndex(Object record) {
+ return 0;
+ }
+
+ @Override
+ public int getItemCount(Object record) {
+ return 0;
+ }
+
+ @Override
+ public int getMaxScrollX(Object record) {
+ return 0;
+ }
+
+ @Override
+ public int getMaxScrollY(Object record) {
+ return 0;
+ }
+
+ @Override
+ public Parcelable getParcelableData(Object record) {
+ return null;
+ }
+
+ @Override
+ public int getRemovedCount(Object record) {
+ return 0;
+ }
+
+ @Override
+ public int getScrollX(Object record) {
+ return 0;
+ }
+
+ @Override
+ public int getScrollY(Object record) {
+ return 0;
+ }
+
+ @Override
+ public AccessibilityNodeInfoCompat getSource(Object record) {
+ return null;
+ }
+
+ @Override
+ public List<CharSequence> getText(Object record) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public int getToIndex(Object record) {
+ return 0;
+ }
+
+ @Override
+ public int getWindowId(Object record) {
+ return 0;
+ }
+
+ @Override
+ public boolean isChecked(Object record) {
+ return false;
+ }
+
+ @Override
+ public boolean isEnabled(Object record) {
+ return false;
+ }
+
+ @Override
+ public boolean isFullScreen(Object record) {
+ return false;
+ }
+
+ @Override
+ public boolean isPassword(Object record) {
+ return false;
+ }
+
+ @Override
+ public boolean isScrollable(Object record) {
+ return false;
+ }
+
+ @Override
+ public void recycle(Object record) {
+ return;
+ }
+
+ @Override
+ public void setAddedCount(Object record, int addedCount) {
+ return;
+ }
+
+ @Override
+ public void setBeforeText(Object record, CharSequence beforeText) {
+ return;
+ }
+
+ @Override
+ public void setChecked(Object record, boolean isChecked) {
+ return;
+ }
+
+ @Override
+ public void setClassName(Object record, CharSequence className) {
+ return;
+ }
+
+ @Override
+ public void setContentDescription(Object record, CharSequence contentDescription) {
+ return;
+ }
+
+ @Override
+ public void setCurrentItemIndex(Object record, int currentItemIndex) {
+ return;
+ }
+
+ @Override
+ public void setEnabled(Object record, boolean isEnabled) {
+ return;
+ }
+
+ @Override
+ public void setFromIndex(Object record, int fromIndex) {
+ return;
+ }
+
+ @Override
+ public void setFullScreen(Object record, boolean isFullScreen) {
+ return;
+ }
+
+ @Override
+ public void setItemCount(Object record, int itemCount) {
+ return;
+ }
+
+ @Override
+ public void setMaxScrollX(Object record, int maxScrollX) {
+ return;
+ }
+
+ @Override
+ public void setMaxScrollY(Object record, int maxScrollY) {
+ return;
+ }
+
+ @Override
+ public void setParcelableData(Object record, Parcelable parcelableData) {
+ return;
+ }
+
+ @Override
+ public void setPassword(Object record, boolean isPassword) {
+ return;
+ }
+
+ @Override
+ public void setRemovedCount(Object record, int removedCount) {
+ return;
+ }
+
+ @Override
+ public void setScrollX(Object record, int scrollX) {
+ return;
+ }
+
+ @Override
+ public void setScrollY(Object record, int scrollY) {
+ return;
+ }
+
+ @Override
+ public void setScrollable(Object record, boolean scrollable) {
+ return;
+ }
+
+ @Override
+ public void setSource(Object record, View source) {
+ return;
+ }
+
+ @Override
+ public void setSource(Object record, View root, int virtualDescendantId) {
+ return;
+ }
+
+ @Override
+ public void setToIndex(Object record, int toIndex) {
+ return;
+ }
+ }
+
+ static class AccessibilityRecordIcsImpl extends AccessibilityRecordStubImpl {
+ @Override
+ public Object obtain() {
+ return AccessibilityRecordCompatIcs.obtain();
+ }
+
+ @Override
+ public Object obtain(Object record) {
+ return AccessibilityRecordCompatIcs.obtain(record);
+ }
+
+ @Override
+ public int getAddedCount(Object record) {
+ return AccessibilityRecordCompatIcs.getAddedCount(record);
+ }
+
+ @Override
+ public CharSequence getBeforeText(Object record) {
+ return AccessibilityRecordCompatIcs.getBeforeText(record);
+ }
+
+ @Override
+ public CharSequence getClassName(Object record) {
+ return AccessibilityRecordCompatIcs.getClassName(record);
+ }
+
+ @Override
+ public CharSequence getContentDescription(Object record) {
+ return AccessibilityRecordCompatIcs.getContentDescription(record);
+ }
+
+ @Override
+ public int getCurrentItemIndex(Object record) {
+ return AccessibilityRecordCompatIcs.getCurrentItemIndex(record);
+ }
+
+ @Override
+ public int getFromIndex(Object record) {
+ return AccessibilityRecordCompatIcs.getFromIndex(record);
+ }
+
+ @Override
+ public int getItemCount(Object record) {
+ return AccessibilityRecordCompatIcs.getItemCount(record);
+ }
+
+ @Override
+ public Parcelable getParcelableData(Object record) {
+ return AccessibilityRecordCompatIcs.getParcelableData(record);
+ }
+
+ @Override
+ public int getRemovedCount(Object record) {
+ return AccessibilityRecordCompatIcs.getRemovedCount(record);
+ }
+
+ @Override
+ public int getScrollX(Object record) {
+ return AccessibilityRecordCompatIcs.getScrollX(record);
+ }
+
+ @Override
+ public int getScrollY(Object record) {
+ return AccessibilityRecordCompatIcs.getScrollY(record);
+ }
+
+ @Override
+ public AccessibilityNodeInfoCompat getSource(Object record) {
+ return AccessibilityNodeInfoCompat.wrapNonNullInstance(
+ AccessibilityRecordCompatIcs.getSource(record));
+ }
+
+ @Override
+ public List<CharSequence> getText(Object record) {
+ return AccessibilityRecordCompatIcs.getText(record);
+ }
+
+ @Override
+ public int getToIndex(Object record) {
+ return AccessibilityRecordCompatIcs.getToIndex(record);
+ }
+
+ @Override
+ public int getWindowId(Object record) {
+ return AccessibilityRecordCompatIcs.getWindowId(record);
+ }
+
+ @Override
+ public boolean isChecked(Object record) {
+ return AccessibilityRecordCompatIcs.isChecked(record);
+ }
+
+ @Override
+ public boolean isEnabled(Object record) {
+ return AccessibilityRecordCompatIcs.isEnabled(record);
+ }
+
+ @Override
+ public boolean isFullScreen(Object record) {
+ return AccessibilityRecordCompatIcs.isFullScreen(record);
+ }
+
+ @Override
+ public boolean isPassword(Object record) {
+ return AccessibilityRecordCompatIcs.isPassword(record);
+ }
+
+ @Override
+ public boolean isScrollable(Object record) {
+ return AccessibilityRecordCompatIcs.isScrollable(record);
+ }
+
+ @Override
+ public void recycle(Object record) {
+ AccessibilityRecordCompatIcs.recycle(record);
+ }
+
+ @Override
+ public void setAddedCount(Object record, int addedCount) {
+ AccessibilityRecordCompatIcs.setAddedCount(record, addedCount);
+ }
+
+ @Override
+ public void setBeforeText(Object record, CharSequence beforeText) {
+ AccessibilityRecordCompatIcs.setBeforeText(record, beforeText);
+ }
+
+ @Override
+ public void setChecked(Object record, boolean isChecked) {
+ AccessibilityRecordCompatIcs.setChecked(record, isChecked);
+ }
+
+ @Override
+ public void setClassName(Object record, CharSequence className) {
+ AccessibilityRecordCompatIcs.setClassName(record, className);
+ }
+
+ @Override
+ public void setContentDescription(Object record, CharSequence contentDescription) {
+ AccessibilityRecordCompatIcs.setContentDescription(record, contentDescription);
+ }
+
+ @Override
+ public void setCurrentItemIndex(Object record, int currentItemIndex) {
+ AccessibilityRecordCompatIcs.setCurrentItemIndex(record, currentItemIndex);
+ }
+
+ @Override
+ public void setEnabled(Object record, boolean isEnabled) {
+ AccessibilityRecordCompatIcs.setEnabled(record, isEnabled);
+ }
+
+ @Override
+ public void setFromIndex(Object record, int fromIndex) {
+ AccessibilityRecordCompatIcs.setFromIndex(record, fromIndex);
+ }
+
+ @Override
+ public void setFullScreen(Object record, boolean isFullScreen) {
+ AccessibilityRecordCompatIcs.setFullScreen(record, isFullScreen);
+ }
+
+ @Override
+ public void setItemCount(Object record, int itemCount) {
+ AccessibilityRecordCompatIcs.setItemCount(record, itemCount);
+ }
+
+ @Override
+ public void setParcelableData(Object record, Parcelable parcelableData) {
+ AccessibilityRecordCompatIcs.setParcelableData(record, parcelableData);
+ }
+
+ @Override
+ public void setPassword(Object record, boolean isPassword) {
+ AccessibilityRecordCompatIcs.setPassword(record, isPassword);
+ }
+
+ @Override
+ public void setRemovedCount(Object record, int removedCount) {
+ AccessibilityRecordCompatIcs.setRemovedCount(record, removedCount);
+ }
+
+ @Override
+ public void setScrollX(Object record, int scrollX) {
+ AccessibilityRecordCompatIcs.setScrollX(record, scrollX);
+ }
+
+ @Override
+ public void setScrollY(Object record, int scrollY) {
+ AccessibilityRecordCompatIcs.setScrollY(record, scrollY);
+ }
+
+ @Override
+ public void setScrollable(Object record, boolean scrollable) {
+ AccessibilityRecordCompatIcs.setScrollable(record, scrollable);
+ }
+
+ @Override
+ public void setSource(Object record, View source) {
+ AccessibilityRecordCompatIcs.setSource(record, source);
+ }
+
+ @Override
+ public void setToIndex(Object record, int toIndex) {
+ AccessibilityRecordCompatIcs.setToIndex(record, toIndex);
+ }
+ }
+
+ static class AccessibilityRecordIcsMr1Impl extends AccessibilityRecordIcsImpl {
+ @Override
+ public int getMaxScrollX(Object record) {
+ return AccessibilityRecordCompatIcsMr1.getMaxScrollX(record);
+ }
+
+ @Override
+ public int getMaxScrollY(Object record) {
+ return AccessibilityRecordCompatIcsMr1.getMaxScrollY(record);
+ }
+
+ @Override
+ public void setMaxScrollX(Object record, int maxScrollX) {
+ AccessibilityRecordCompatIcsMr1.setMaxScrollX(record, maxScrollX);
+ }
+
+ @Override
+ public void setMaxScrollY(Object record, int maxScrollY) {
+ AccessibilityRecordCompatIcsMr1.setMaxScrollY(record, maxScrollY);
+ }
+ }
+
+ static {
+ if (Build.VERSION.SDK_INT >= 15) { // ICS MR1
+ IMPL = new AccessibilityRecordIcsMr1Impl();
+ } else if (Build.VERSION.SDK_INT >= 14) { // ICS
+ IMPL = new AccessibilityRecordIcsImpl();
+ } else {
+ IMPL = new AccessibilityRecordStubImpl();
+ }
+ }
+
+ private static final AccessibilityRecordImpl IMPL;
+
+ private final Object mRecord;
+
+ /**
+ * @deprecated This is not type safe. If you want to modify an
+ * {@link AccessibilityEvent}'s properties defined in
+ * {@link android.view.accessibility.AccessibilityRecord} use
+ * {@link AccessibilityEventCompat#asRecord(AccessibilityEvent)}. This method will be removed
+ * in a subsequent release of the support library.
+ */
+ @Deprecated
+ public AccessibilityRecordCompat(Object record) {
+ mRecord = record;
+ }
+
+ /**
+ * @return The wrapped implementation.
+ *
+ * @deprecated This method will be removed in a subsequent release of
+ * the support library.
+ */
+ @Deprecated
+ public Object getImpl() {
+ return mRecord;
+ }
+
+ /**
+ * Returns a cached instance if such is available or a new one is
+ * instantiated. The instance is initialized with data from the
+ * given record.
+ *
+ * @return An instance.
+ */
+ public static AccessibilityRecordCompat obtain(AccessibilityRecordCompat record) {
+ return new AccessibilityRecordCompat(IMPL.obtain(record.mRecord));
+ }
+
+ /**
+ * Returns a cached instance if such is available or a new one is
+ * instantiated.
+ *
+ * @return An instance.
+ */
+ public static AccessibilityRecordCompat obtain() {
+ return new AccessibilityRecordCompat(IMPL.obtain());
+ }
+
+ /**
+ * Sets the event source.
+ *
+ * @param source The source.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setSource(View source) {
+ IMPL.setSource(mRecord, source);
+ }
+
+ /**
+ * Sets the source to be a virtual descendant of the given <code>root</code>.
+ * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root
+ * is set as the source.
+ * <p>
+ * A virtual descendant is an imaginary View that is reported as a part of the view
+ * hierarchy for accessibility purposes. This enables custom views that draw complex
+ * content to report them selves as a tree of virtual views, thus conveying their
+ * logical structure.
+ * </p>
+ *
+ * @param root The root of the virtual subtree.
+ * @param virtualDescendantId The id of the virtual descendant.
+ */
+ public void setSource(View root, int virtualDescendantId) {
+ IMPL.setSource(mRecord, root, virtualDescendantId);
+ }
+
+ /**
+ * Gets the {@link android.view.accessibility.AccessibilityNodeInfo} of
+ * the event source.
+ * <p>
+ * <strong>Note:</strong> It is a client responsibility to recycle the
+ * received info by calling
+ * {@link android.view.accessibility.AccessibilityNodeInfo#recycle()
+ * AccessibilityNodeInfo#recycle()} to avoid creating of multiple instances.
+ *</p>
+ *
+ * @return The info of the source.
+ */
+ public AccessibilityNodeInfoCompat getSource() {
+ return IMPL.getSource(mRecord);
+ }
+
+ /**
+ * Gets the id of the window from which the event comes from.
+ *
+ * @return The window id.
+ */
+ public int getWindowId() {
+ return IMPL.getWindowId(mRecord);
+ }
+
+ /**
+ * Gets if the source is checked.
+ *
+ * @return True if the view is checked, false otherwise.
+ */
+ public boolean isChecked() {
+ return IMPL.isChecked(mRecord);
+ }
+
+ /**
+ * Sets if the source is checked.
+ *
+ * @param isChecked True if the view is checked, false otherwise.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setChecked(boolean isChecked) {
+ IMPL.setChecked(mRecord, isChecked);
+ }
+
+ /**
+ * Gets if the source is enabled.
+ *
+ * @return True if the view is enabled, false otherwise.
+ */
+ public boolean isEnabled() {
+ return IMPL.isEnabled(mRecord);
+ }
+
+ /**
+ * Sets if the source is enabled.
+ *
+ * @param isEnabled True if the view is enabled, false otherwise.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setEnabled(boolean isEnabled) {
+ IMPL.setEnabled(mRecord, isEnabled);
+ }
+
+ /**
+ * Gets if the source is a password field.
+ *
+ * @return True if the view is a password field, false otherwise.
+ */
+ public boolean isPassword() {
+ return IMPL.isPassword(mRecord);
+ }
+
+ /**
+ * Sets if the source is a password field.
+ *
+ * @param isPassword True if the view is a password field, false otherwise.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setPassword(boolean isPassword) {
+ IMPL.setPassword(mRecord, isPassword);
+ }
+
+ /**
+ * Gets if the source is taking the entire screen.
+ *
+ * @return True if the source is full screen, false otherwise.
+ */
+ public boolean isFullScreen() {
+ return IMPL.isFullScreen(mRecord);
+ }
+
+ /**
+ * Sets if the source is taking the entire screen.
+ *
+ * @param isFullScreen True if the source is full screen, false otherwise.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setFullScreen(boolean isFullScreen) {
+ IMPL.setFullScreen(mRecord, isFullScreen);
+ }
+
+ /**
+ * Gets if the source is scrollable.
+ *
+ * @return True if the source is scrollable, false otherwise.
+ */
+ public boolean isScrollable() {
+ return IMPL.isScrollable(mRecord);
+ }
+
+ /**
+ * Sets if the source is scrollable.
+ *
+ * @param scrollable True if the source is scrollable, false otherwise.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setScrollable(boolean scrollable) {
+ IMPL.setScrollable(mRecord, scrollable);
+ }
+
+ /**
+ * Gets the number of items that can be visited.
+ *
+ * @return The number of items.
+ */
+ public int getItemCount() {
+ return IMPL.getItemCount(mRecord);
+ }
+
+ /**
+ * Sets the number of items that can be visited.
+ *
+ * @param itemCount The number of items.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setItemCount(int itemCount) {
+ IMPL.setItemCount(mRecord, itemCount);
+ }
+
+ /**
+ * Gets the index of the source in the list of items the can be visited.
+ *
+ * @return The current item index.
+ */
+ public int getCurrentItemIndex() {
+ return IMPL.getCurrentItemIndex(mRecord);
+ }
+
+ /**
+ * Sets the index of the source in the list of items that can be visited.
+ *
+ * @param currentItemIndex The current item index.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setCurrentItemIndex(int currentItemIndex) {
+ IMPL.setCurrentItemIndex(mRecord, currentItemIndex);
+ }
+
+ /**
+ * Gets the index of the first character of the changed sequence,
+ * or the beginning of a text selection or the index of the first
+ * visible item when scrolling.
+ *
+ * @return The index of the first character or selection
+ * start or the first visible item.
+ */
+ public int getFromIndex() {
+ return IMPL.getFromIndex(mRecord);
+ }
+
+ /**
+ * Sets the index of the first character of the changed sequence
+ * or the beginning of a text selection or the index of the first
+ * visible item when scrolling.
+ *
+ * @param fromIndex The index of the first character or selection
+ * start or the first visible item.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setFromIndex(int fromIndex) {
+ IMPL.setFromIndex(mRecord, fromIndex);
+ }
+
+ /**
+ * Gets the index of text selection end or the index of the last
+ * visible item when scrolling.
+ *
+ * @return The index of selection end or last item index.
+ */
+ public int getToIndex() {
+ return IMPL.getToIndex(mRecord);
+ }
+
+ /**
+ * Sets the index of text selection end or the index of the last
+ * visible item when scrolling.
+ *
+ * @param toIndex The index of selection end or last item index.
+ */
+ public void setToIndex(int toIndex) {
+ IMPL.setToIndex(mRecord, toIndex);
+ }
+
+ /**
+ * Gets the scroll offset of the source left edge in pixels.
+ *
+ * @return The scroll.
+ */
+ public int getScrollX() {
+ return IMPL.getScrollX(mRecord);
+ }
+
+ /**
+ * Sets the scroll offset of the source left edge in pixels.
+ *
+ * @param scrollX The scroll.
+ */
+ public void setScrollX(int scrollX) {
+ IMPL.setScrollX(mRecord, scrollX);
+ }
+
+ /**
+ * Gets the scroll offset of the source top edge in pixels.
+ *
+ * @return The scroll.
+ */
+ public int getScrollY() {
+ return IMPL.getScrollY(mRecord);
+ }
+
+ /**
+ * Sets the scroll offset of the source top edge in pixels.
+ *
+ * @param scrollY The scroll.
+ */
+ public void setScrollY(int scrollY) {
+ IMPL.setScrollY(mRecord, scrollY);
+ }
+
+ /**
+ * Gets the max scroll offset of the source left edge in pixels.
+ *
+ * @return The max scroll.
+ */
+ public int getMaxScrollX() {
+ return IMPL.getMaxScrollX(mRecord);
+ }
+ /**
+ * Sets the max scroll offset of the source left edge in pixels.
+ *
+ * @param maxScrollX The max scroll.
+ */
+ public void setMaxScrollX(int maxScrollX) {
+ IMPL.setMaxScrollX(mRecord, maxScrollX);
+ }
+
+ /**
+ * Gets the max scroll offset of the source top edge in pixels.
+ *
+ * @return The max scroll.
+ */
+ public int getMaxScrollY() {
+ return IMPL.getMaxScrollY(mRecord);
+ }
+
+ /**
+ * Sets the max scroll offset of the source top edge in pixels.
+ *
+ * @param maxScrollY The max scroll.
+ */
+ public void setMaxScrollY(int maxScrollY) {
+ IMPL.setMaxScrollY(mRecord, maxScrollY);
+ }
+
+ /**
+ * Gets the number of added characters.
+ *
+ * @return The number of added characters.
+ */
+ public int getAddedCount() {
+ return IMPL.getAddedCount(mRecord);
+ }
+
+ /**
+ * Sets the number of added characters.
+ *
+ * @param addedCount The number of added characters.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setAddedCount(int addedCount) {
+ IMPL.setAddedCount(mRecord, addedCount);
+ }
+
+ /**
+ * Gets the number of removed characters.
+ *
+ * @return The number of removed characters.
+ */
+ public int getRemovedCount() {
+ return IMPL.getRemovedCount(mRecord);
+ }
+
+ /**
+ * Sets the number of removed characters.
+ *
+ * @param removedCount The number of removed characters.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setRemovedCount(int removedCount) {
+ IMPL.setRemovedCount(mRecord, removedCount);
+ }
+
+ /**
+ * Gets the class name of the source.
+ *
+ * @return The class name.
+ */
+ public CharSequence getClassName() {
+ return IMPL.getClassName(mRecord);
+ }
+
+ /**
+ * Sets the class name of the source.
+ *
+ * @param className The lass name.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setClassName(CharSequence className) {
+ IMPL.setClassName(mRecord, className);
+ }
+
+ /**
+ * Gets the text of the event. The index in the list represents the priority
+ * of the text. Specifically, the lower the index the higher the priority.
+ *
+ * @return The text.
+ */
+ public List<CharSequence> getText() {
+ return IMPL.getText(mRecord);
+ }
+
+ /**
+ * Sets the text before a change.
+ *
+ * @return The text before the change.
+ */
+ public CharSequence getBeforeText() {
+ return IMPL.getBeforeText(mRecord);
+ }
+
+ /**
+ * Sets the text before a change.
+ *
+ * @param beforeText The text before the change.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setBeforeText(CharSequence beforeText) {
+ IMPL.setBeforeText(mRecord, beforeText);
+ }
+
+ /**
+ * Gets the description of the source.
+ *
+ * @return The description.
+ */
+ public CharSequence getContentDescription() {
+ return IMPL.getContentDescription(mRecord);
+ }
+
+ /**
+ * Sets the description of the source.
+ *
+ * @param contentDescription The description.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setContentDescription(CharSequence contentDescription) {
+ IMPL.setContentDescription(mRecord, contentDescription);
+ }
+
+ /**
+ * Gets the {@link Parcelable} data.
+ *
+ * @return The parcelable data.
+ */
+ public Parcelable getParcelableData() {
+ return IMPL.getParcelableData(mRecord);
+ }
+
+ /**
+ * Sets the {@link Parcelable} data of the event.
+ *
+ * @param parcelableData The parcelable data.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setParcelableData(Parcelable parcelableData) {
+ IMPL.setParcelableData(mRecord, parcelableData);
+ }
+
+ /**
+ * Return an instance back to be reused.
+ * <p>
+ * <strong>Note:</strong> You must not touch the object after calling this
+ * function.
+ * </p>
+ *
+ * @throws IllegalStateException If the record is already recycled.
+ */
+ public void recycle() {
+ IMPL.recycle(mRecord);
+ }
+
+ @Override
+ public int hashCode() {
+ return (mRecord == null) ? 0 : mRecord.hashCode();
+ }
+
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ AccessibilityRecordCompat other = (AccessibilityRecordCompat) obj;
+ if (mRecord == null) {
+ if (other.mRecord != null) {
+ return false;
+ }
+ } else if (!mRecord.equals(other.mRecord)) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/android/support/v4/view/accessibility/AccessibilityRecordCompatIcs.java b/src/android/support/v4/view/accessibility/AccessibilityRecordCompatIcs.java
new file mode 100644
index 0000000..0ebcc6a
--- /dev/null
+++ b/src/android/support/v4/view/accessibility/AccessibilityRecordCompatIcs.java
@@ -0,0 +1,193 @@
+/*
+ * 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.accessibility;
+
+import android.os.Parcelable;
+import android.view.View;
+import android.view.accessibility.AccessibilityRecord;
+
+import java.util.List;
+
+/**
+ * ICS specific AccessibilityRecord API implementation.
+ */
+class AccessibilityRecordCompatIcs {
+
+ public static Object obtain() {
+ return AccessibilityRecord.obtain();
+ }
+
+ public static Object obtain(Object record) {
+ return AccessibilityRecord.obtain((AccessibilityRecord) record);
+ }
+
+ public static int getAddedCount(Object record) {
+ return ((AccessibilityRecord) record).getAddedCount();
+ }
+
+ public static CharSequence getBeforeText(Object record) {
+ return ((AccessibilityRecord) record).getBeforeText();
+ }
+
+ public static CharSequence getClassName(Object record) {
+ return ((AccessibilityRecord) record).getClassName();
+ }
+
+ public static CharSequence getContentDescription(Object record) {
+ return ((AccessibilityRecord) record).getContentDescription();
+ }
+
+ public static int getCurrentItemIndex(Object record) {
+ return ((AccessibilityRecord) record).getCurrentItemIndex();
+ }
+
+ public static int getFromIndex(Object record) {
+ return ((AccessibilityRecord) record).getFromIndex();
+ }
+
+ public static int getItemCount(Object record) {
+ return ((AccessibilityRecord) record).getItemCount();
+ }
+
+ public static Parcelable getParcelableData(Object record) {
+ return ((AccessibilityRecord) record).getParcelableData();
+ }
+
+ public static int getRemovedCount(Object record) {
+ return ((AccessibilityRecord) record).getRemovedCount();
+ }
+
+ public static int getScrollX(Object record) {
+ return ((AccessibilityRecord) record).getScrollX();
+ }
+
+ public static int getScrollY(Object record) {
+ return ((AccessibilityRecord) record).getScrollY();
+ }
+
+ public static Object getSource(Object record) {
+ return ((AccessibilityRecord) record).getSource();
+ }
+
+ public static List<CharSequence> getText(Object record) {
+ return ((AccessibilityRecord) record).getText();
+ }
+
+ public static int getToIndex(Object record) {
+ return ((AccessibilityRecord) record).getToIndex();
+ }
+
+ public static int getWindowId(Object record) {
+ return ((AccessibilityRecord) record).getWindowId();
+ }
+
+ public static boolean isChecked(Object record) {
+ return ((AccessibilityRecord) record).isChecked();
+ }
+
+ public static boolean isEnabled(Object record) {
+ return ((AccessibilityRecord) record).isEnabled();
+ }
+
+ public static boolean isFullScreen(Object record) {
+ return ((AccessibilityRecord) record).isFullScreen();
+ }
+
+ public static boolean isPassword(Object record) {
+ return ((AccessibilityRecord) record).isPassword();
+ }
+
+ public static boolean isScrollable(Object record) {
+ return ((AccessibilityRecord) record).isScrollable();
+ }
+
+ public static void recycle(Object record) {
+ ((AccessibilityRecord) record).recycle();
+ }
+
+ public static void setAddedCount(Object record, int addedCount) {
+ ((AccessibilityRecord) record).setAddedCount(addedCount);
+ }
+
+ public static void setBeforeText(Object record, CharSequence beforeText) {
+ ((AccessibilityRecord) record).setBeforeText(beforeText);
+ }
+
+ public static void setChecked(Object record, boolean isChecked) {
+ ((AccessibilityRecord) record).setChecked(isChecked);
+ }
+
+ public static void setClassName(Object record, CharSequence className) {
+ ((AccessibilityRecord) record).setClassName(className);
+ }
+
+ public static void setContentDescription(Object record, CharSequence contentDescription) {
+ ((AccessibilityRecord) record).setContentDescription(contentDescription);
+ }
+
+ public static void setCurrentItemIndex(Object record, int currentItemIndex) {
+ ((AccessibilityRecord) record).setCurrentItemIndex(currentItemIndex);
+ }
+
+ public static void setEnabled(Object record, boolean isEnabled) {
+ ((AccessibilityRecord) record).setEnabled(isEnabled);
+ }
+
+ public static void setFromIndex(Object record, int fromIndex) {
+ ((AccessibilityRecord) record).setFromIndex(fromIndex);
+ }
+
+ public static void setFullScreen(Object record, boolean isFullScreen) {
+ ((AccessibilityRecord) record).setFullScreen(isFullScreen);
+ }
+
+ public static void setItemCount(Object record, int itemCount) {
+ ((AccessibilityRecord) record).setItemCount(itemCount);
+ }
+
+ public static void setParcelableData(Object record, Parcelable parcelableData) {
+ ((AccessibilityRecord) record).setParcelableData(parcelableData);
+ }
+
+ public static void setPassword(Object record, boolean isPassword) {
+ ((AccessibilityRecord) record).setPassword(isPassword);
+ }
+
+ public static void setRemovedCount(Object record, int removedCount) {
+ ((AccessibilityRecord) record).setRemovedCount(removedCount);
+ }
+
+ public static void setScrollX(Object record, int scrollX) {
+ ((AccessibilityRecord) record).setScrollX(scrollX);
+ }
+
+ public static void setScrollY(Object record, int scrollY) {
+ ((AccessibilityRecord) record).setScrollY(scrollY);
+ }
+
+ public static void setScrollable(Object record, boolean scrollable) {
+ ((AccessibilityRecord) record).setScrollable(scrollable);
+ }
+
+ public static void setSource(Object record, View source) {
+ ((AccessibilityRecord) record).setSource(source);
+ }
+
+ public static void setToIndex(Object record, int toIndex) {
+ ((AccessibilityRecord) record).setToIndex(toIndex);
+ }
+}
diff --git a/src/android/support/v4/view/accessibility/AccessibilityRecordCompatIcsMr1.java b/src/android/support/v4/view/accessibility/AccessibilityRecordCompatIcsMr1.java
new file mode 100644
index 0000000..94164d7
--- /dev/null
+++ b/src/android/support/v4/view/accessibility/AccessibilityRecordCompatIcsMr1.java
@@ -0,0 +1,40 @@
+/*
+ * 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.accessibility;
+
+import android.view.accessibility.AccessibilityRecord;
+
+/**
+ * ICS MR1 specific AccessibilityRecord API implementation.
+ */
+class AccessibilityRecordCompatIcsMr1 {
+
+ public static int getMaxScrollX(Object record) {
+ return ((AccessibilityRecord) record).getMaxScrollX();
+ }
+
+ public static int getMaxScrollY(Object record) {
+ return ((AccessibilityRecord) record).getMaxScrollY();
+ }
+ public static void setMaxScrollX(Object record, int maxScrollX) {
+ ((AccessibilityRecord) record).setMaxScrollX(maxScrollX);
+ }
+
+ public static void setMaxScrollY(Object record, int maxScrollY) {
+ ((AccessibilityRecord) record).setMaxScrollY(maxScrollY);
+ }
+}
diff --git a/src/android/support/v4/widget/EdgeEffectCompat.java b/src/android/support/v4/widget/EdgeEffectCompat.java
new file mode 100644
index 0000000..9436c23
--- /dev/null
+++ b/src/android/support/v4/widget/EdgeEffectCompat.java
@@ -0,0 +1,238 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.os.Build;
+
+/**
+ * Helper for accessing {@link android.widget.EdgeEffect} introduced after
+ * API level 4 in a backwards compatible fashion.
+ *
+ * This class is used to access {@link android.widget.EdgeEffect} on platform versions
+ * that support it. When running on older platforms it will result in no-ops. It should
+ * be used by views that wish to use the standard Android visual effects at the edges
+ * of scrolling containers.
+ */
+public class EdgeEffectCompat {
+ private Object mEdgeEffect;
+
+ private static final EdgeEffectImpl IMPL;
+
+ static {
+ if (Build.VERSION.SDK_INT >= 14) { // ICS
+ IMPL = new EdgeEffectIcsImpl();
+ } else {
+ IMPL = new BaseEdgeEffectImpl();
+ }
+ }
+
+ interface EdgeEffectImpl {
+ public Object newEdgeEffect(Context context);
+ public void setSize(Object edgeEffect, int width, int height);
+ public boolean isFinished(Object edgeEffect);
+ public void finish(Object edgeEffect);
+ public boolean onPull(Object edgeEffect, float deltaDistance);
+ public boolean onRelease(Object edgeEffect);
+ public boolean onAbsorb(Object edgeEffect, int velocity);
+ public boolean draw(Object edgeEffect, Canvas canvas);
+ }
+
+ /**
+ * Null implementation to use pre-ICS
+ */
+ static class BaseEdgeEffectImpl implements EdgeEffectImpl {
+ @Override
+ public Object newEdgeEffect(Context context) {
+ return null;
+ }
+
+ @Override
+ public void setSize(Object edgeEffect, int width, int height) {
+ return;
+ }
+
+ @Override
+ public boolean isFinished(Object edgeEffect) {
+ return true;
+ }
+
+ @Override
+ public void finish(Object edgeEffect) {
+ return;
+ }
+
+ @Override
+ public boolean onPull(Object edgeEffect, float deltaDistance) {
+ return false;
+ }
+
+ @Override
+ public boolean onRelease(Object edgeEffect) {
+ return false;
+ }
+
+ @Override
+ public boolean onAbsorb(Object edgeEffect, int velocity) {
+ return false;
+ }
+
+ @Override
+ public boolean draw(Object edgeEffect, Canvas canvas) {
+ return false;
+ }
+ }
+
+ static class EdgeEffectIcsImpl implements EdgeEffectImpl {
+ @Override
+ public Object newEdgeEffect(Context context) {
+ return EdgeEffectCompatIcs.newEdgeEffect(context);
+ }
+
+ @Override
+ public void setSize(Object edgeEffect, int width, int height) {
+ EdgeEffectCompatIcs.setSize(edgeEffect, width, height);
+ }
+
+ @Override
+ public boolean isFinished(Object edgeEffect) {
+ return EdgeEffectCompatIcs.isFinished(edgeEffect);
+ }
+
+ @Override
+ public void finish(Object edgeEffect) {
+ EdgeEffectCompatIcs.finish(edgeEffect);
+ }
+
+ @Override
+ public boolean onPull(Object edgeEffect, float deltaDistance) {
+ return EdgeEffectCompatIcs.onPull(edgeEffect, deltaDistance);
+ }
+
+ @Override
+ public boolean onRelease(Object edgeEffect) {
+ return EdgeEffectCompatIcs.onRelease(edgeEffect);
+ }
+
+ @Override
+ public boolean onAbsorb(Object edgeEffect, int velocity) {
+ return EdgeEffectCompatIcs.onAbsorb(edgeEffect, velocity);
+ }
+
+ @Override
+ public boolean draw(Object edgeEffect, Canvas canvas) {
+ return EdgeEffectCompatIcs.draw(edgeEffect, canvas);
+ }
+ }
+
+ /**
+ * Construct a new EdgeEffect themed using the given context.
+ *
+ * <p>Note: On platform versions that do not support EdgeEffect, all operations
+ * on the newly constructed object will be mocked/no-ops.</p>
+ *
+ * @param context Context to use for theming the effect
+ */
+ public EdgeEffectCompat(Context context) {
+ mEdgeEffect = IMPL.newEdgeEffect(context);
+ }
+
+ /**
+ * Set the size of this edge effect in pixels.
+ *
+ * @param width Effect width in pixels
+ * @param height Effect height in pixels
+ */
+ public void setSize(int width, int height) {
+ IMPL.setSize(mEdgeEffect, width, height);
+ }
+
+ /**
+ * Reports if this EdgeEffectCompat's animation is finished. If this method returns false
+ * after a call to {@link #draw(Canvas)} the host widget should schedule another
+ * drawing pass to continue the animation.
+ *
+ * @return true if animation is finished, false if drawing should continue on the next frame.
+ */
+ public boolean isFinished() {
+ return IMPL.isFinished(mEdgeEffect);
+ }
+
+ /**
+ * Immediately finish the current animation.
+ * After this call {@link #isFinished()} will return true.
+ */
+ public void finish() {
+ IMPL.finish(mEdgeEffect);
+ }
+
+ /**
+ * A view should call this when content is pulled away from an edge by the user.
+ * This will update the state of the current visual effect and its associated animation.
+ * The host view should always {@link android.view.View#invalidate()} if this method
+ * returns true and draw the results accordingly.
+ *
+ * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
+ * 1.f (full length of the view) or negative values to express change
+ * back toward the edge reached to initiate the effect.
+ * @return true if the host view should call invalidate, false if it should not.
+ */
+ public boolean onPull(float deltaDistance) {
+ return IMPL.onPull(mEdgeEffect, deltaDistance);
+ }
+
+ /**
+ * Call when the object is released after being pulled.
+ * This will begin the "decay" phase of the effect. After calling this method
+ * the host view should {@link android.view.View#invalidate()} if this method
+ * returns true and thereby draw the results accordingly.
+ *
+ * @return true if the host view should invalidate, false if it should not.
+ */
+ public boolean onRelease() {
+ return IMPL.onRelease(mEdgeEffect);
+ }
+
+ /**
+ * Call when the effect absorbs an impact at the given velocity.
+ * Used when a fling reaches the scroll boundary.
+ *
+ * <p>When using a {@link android.widget.Scroller} or {@link android.widget.OverScroller},
+ * the method <code>getCurrVelocity</code> will provide a reasonable approximation
+ * to use here.</p>
+ *
+ * @param velocity Velocity at impact in pixels per second.
+ * @return true if the host view should invalidate, false if it should not.
+ */
+ public boolean onAbsorb(int velocity) {
+ return IMPL.onAbsorb(mEdgeEffect, velocity);
+ }
+
+ /**
+ * Draw into the provided canvas. Assumes that the canvas has been rotated
+ * accordingly and the size has been set. The effect will be drawn the full
+ * width of X=0 to X=width, beginning from Y=0 and extending to some factor <
+ * 1.f of height.
+ *
+ * @param canvas Canvas to draw into
+ * @return true if drawing should continue beyond this frame to continue the
+ * animation
+ */
+ public boolean draw(Canvas canvas) {
+ return IMPL.draw(mEdgeEffect, canvas);
+ }
+}
diff --git a/src/android/support/v4/widget/EdgeEffectCompatIcs.java b/src/android/support/v4/widget/EdgeEffectCompatIcs.java
new file mode 100644
index 0000000..c02eeb4
--- /dev/null
+++ b/src/android/support/v4/widget/EdgeEffectCompatIcs.java
@@ -0,0 +1,64 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.widget.EdgeEffect;
+
+/**
+ * Stub implementation that contains a real EdgeEffect on ICS.
+ *
+ * This class is an implementation detail for EdgeEffectCompat
+ * and should not be used directly.
+ */
+class EdgeEffectCompatIcs {
+ public static Object newEdgeEffect(Context context) {
+ return new EdgeEffect(context);
+ }
+
+ public static void setSize(Object edgeEffect, int width, int height) {
+ ((EdgeEffect) edgeEffect).setSize(width, height);
+ }
+
+ public static boolean isFinished(Object edgeEffect) {
+ return ((EdgeEffect) edgeEffect).isFinished();
+ }
+
+ public static void finish(Object edgeEffect) {
+ ((EdgeEffect) edgeEffect).finish();
+ }
+
+ public static boolean onPull(Object edgeEffect, float deltaDistance) {
+ ((EdgeEffect) edgeEffect).onPull(deltaDistance);
+ return true;
+ }
+
+ public static boolean onRelease(Object edgeEffect) {
+ EdgeEffect eff = (EdgeEffect) edgeEffect;
+ eff.onRelease();
+ return eff.isFinished();
+ }
+
+ public static boolean onAbsorb(Object edgeEffect, int velocity) {
+ ((EdgeEffect) edgeEffect).onAbsorb(velocity);
+ return true;
+ }
+
+ public static boolean draw(Object edgeEffect, Canvas canvas) {
+ return ((EdgeEffect) edgeEffect).draw(canvas);
+ }
+} \ No newline at end of file
diff --git a/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseRenderer.java b/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseRenderer.java
index c4950a3..0fb489f 100644
--- a/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseRenderer.java
+++ b/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseRenderer.java
@@ -167,7 +167,7 @@ public class PhotoPhaseRenderer implements GLSurfaceView.Renderer {
// Select a new transition
mWorld.selectRandomTransition();
mLastRunningTransition = System.currentTimeMillis();
-
+
// Now force continuously render while transition is applied
mDispatcher.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
}
@@ -514,7 +514,6 @@ public class PhotoPhaseRenderer implements GLSurfaceView.Renderer {
* Recreate the world
*/
void recreateWorld() {
-System.out.println("recreateWorld(): " + mIsPaused);
if (mIsPaused) {
mRecreateWorld = true;
return;
diff --git a/src/org/cyanogenmod/wallpapers/photophase/adapters/DispositionAdapter.java b/src/org/cyanogenmod/wallpapers/photophase/adapters/DispositionAdapter.java
new file mode 100644
index 0000000..4fa4926
--- /dev/null
+++ b/src/org/cyanogenmod/wallpapers/photophase/adapters/DispositionAdapter.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.adapters;
+
+import android.content.Context;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.cyanogenmod.wallpapers.photophase.R;
+import org.cyanogenmod.wallpapers.photophase.model.Dispositions;
+import org.cyanogenmod.wallpapers.photophase.widgets.DispositionView;
+import org.cyanogenmod.wallpapers.photophase.widgets.DispositionView.OnFrameSelectedListener;
+import org.cyanogenmod.wallpapers.photophase.widgets.ResizeFrame;
+
+import java.util.List;
+
+/**
+ * An {@link PagerAdapter} implementation for display all current templates
+ */
+public class DispositionAdapter extends PagerAdapter {
+
+ final List<Dispositions> mDispositions;
+ private final ResizeFrame mResizeFrame;
+ private final OnFrameSelectedListener mCallback;
+
+ private final SparseArray<DispositionView> mCurrentViews;
+
+ private LayoutInflater mInflater;
+
+ boolean mFirstAnimation;
+
+ /**
+ * Constructor of <code>DispositionAdapter</code>.
+ *
+ * @param ctx The current context
+ * @param dispositions An array with all dispositions
+ * @param resizeFrame The resize frame
+ * @param callback The callback where return selection events
+ */
+ public DispositionAdapter(Context ctx, List<Dispositions> dispositions,
+ ResizeFrame resizeFrame, OnFrameSelectedListener callback) {
+ super();
+ mDispositions = dispositions;
+ mResizeFrame = resizeFrame;
+ mCallback = callback;
+ mFirstAnimation = true;
+ mInflater = (LayoutInflater)ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mCurrentViews = new SparseArray<DispositionView>();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getCount() {
+ return mDispositions.size();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object instantiateItem(ViewGroup container, final int position) {
+ final DispositionView view = (DispositionView)mInflater.inflate(
+ R.layout.disposition_view, null);
+ if (position == 0) {
+ view.setResizeFrame(mResizeFrame);
+ view.setOnFrameSelectedListener(mCallback);
+ }
+ view.post(new Runnable() {
+ @Override
+ public void run() {
+ view.setDispositions(mDispositions.get(position),
+ position == 0 && mFirstAnimation);
+ mFirstAnimation = false;
+ }
+ });
+ ((ViewPager)container).addView(view, 0);
+ mCurrentViews.put(position, view);
+ return view;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void destroyItem(ViewGroup container, int position, Object object) {
+ mCurrentViews.remove(position);
+ ((ViewPager)container).removeView((View)object);
+ }
+
+ /**
+ * Method that returns the current view
+ *
+ * @param position The position of the item to return
+ * @return DispositionView The view or null if is not instance
+ */
+ public DispositionView getView(int position) {
+ return mCurrentViews.get(position);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isViewFromObject(View view, Object object) {
+ return view == ((View)object) || view == mResizeFrame;
+ }
+
+}
diff --git a/src/org/cyanogenmod/wallpapers/photophase/model/Dispositions.java b/src/org/cyanogenmod/wallpapers/photophase/model/Dispositions.java
new file mode 100644
index 0000000..fc27853
--- /dev/null
+++ b/src/org/cyanogenmod/wallpapers/photophase/model/Dispositions.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An array of {@link Disposition} classes.
+ */
+public class Dispositions {
+
+ private final List<Disposition> mDispositions;
+ private final int mRows;
+ private final int mCols;
+
+ /**
+ * Constructor of <code>Dispositions</code>
+ *
+ * @param dispositions List of all dispositions
+ * @param rows The number of rows of the dispositions
+ * @param cols The number of columns of the dispositions
+ */
+ public Dispositions(List<Disposition> dispositions, int rows, int cols) {
+ super();
+ mDispositions = dispositions;
+ mRows = rows;
+ mCols = cols;
+ }
+
+ /**
+ * Method that returns an array of all the dispositions
+ *
+ * @return List<Disposition> All the dispositions
+ */
+ public List<Disposition> getDispositions() {
+ return new ArrayList<Disposition>(mDispositions);
+ }
+
+ /**
+ * Method that returns the number of rows of the dispositions
+ *
+ * @return int The number of rows of the dispositions
+ */
+ public int getRows() {
+ return mRows;
+ }
+
+ /**
+ * Method that returns the number of columns of the dispositions
+ *
+ * @return int The number of columns of the dispositions
+ */
+ public int getCols() {
+ return mCols;
+ }
+
+}
diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/DispositionFragment.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/DispositionFragment.java
index 7071e76..5040178 100644
--- a/src/org/cyanogenmod/wallpapers/photophase/preferences/DispositionFragment.java
+++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/DispositionFragment.java
@@ -20,41 +20,44 @@ import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.preference.PreferenceFragment;
+import android.support.v4.view.ViewPager;
+import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.TextView;
import org.cyanogenmod.wallpapers.photophase.R;
+import org.cyanogenmod.wallpapers.photophase.adapters.DispositionAdapter;
import org.cyanogenmod.wallpapers.photophase.model.Disposition;
+import org.cyanogenmod.wallpapers.photophase.model.Dispositions;
+import org.cyanogenmod.wallpapers.photophase.utils.DispositionUtil;
import org.cyanogenmod.wallpapers.photophase.widgets.DispositionView;
import org.cyanogenmod.wallpapers.photophase.widgets.DispositionView.OnFrameSelectedListener;
import org.cyanogenmod.wallpapers.photophase.widgets.ResizeFrame;
+import java.util.ArrayList;
import java.util.List;
/**
* An abstract fragment class that allow to choose the layout disposition of the wallpaper.
*/
-public abstract class DispositionFragment
- extends PreferenceFragment implements OnFrameSelectedListener {
-
- private Runnable mRedraw = new Runnable() {
- @Override
- public void run() {
- if (getActivity() == null) return;
- try {
- mDispositionView.setDispositions(getUserDispositions(), getCols(), getRows());
- } catch (Exception ex) {
- // Ignored
- }
- }
- };
+public abstract class DispositionFragment extends PreferenceFragment
+ implements OnFrameSelectedListener, OnPageChangeListener {
- DispositionView mDispositionView;
+ private ViewPager mPager;
+ private DispositionAdapter mAdapter;
+ private ResizeFrame mResizeFrame;
+ private TextView mAdvise;
+ private DispositionView mCurrentDispositionView;
+ private int mCurrentPage;
+ private int mNumberOfTemplates;
+
+ private MenuItem mRestoreMenu;
private MenuItem mDeleteMenu;
/**
@@ -79,6 +82,13 @@ public abstract class DispositionFragment
public abstract List<Disposition> getDefaultDispositions();
/**
+ * Method that returns the system-defined dispositions templates
+ *
+ * @return String[] The system-defined dispositions templates
+ */
+ public abstract String[] getDispositionsTemplates();
+
+ /**
* Method that request to save the dispositions
*
* @param dispositions The dispositions to save
@@ -93,9 +103,9 @@ public abstract class DispositionFragment
public abstract int getRows();
/**
- * Method that returns the number of cols to use
+ * Method that returns the number of columns to use
*
- * @return int The number of cols
+ * @return int The number of columns
*/
public abstract int getCols();
@@ -117,13 +127,24 @@ public abstract class DispositionFragment
* {@inheritDoc}
*/
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
// Inflate the layout for this fragment
- ViewGroup v = (ViewGroup)inflater.inflate(R.layout.choose_disposition_fragment, container, false);
- mDispositionView = (DispositionView)v.findViewById(R.id.disposition_view);
- mDispositionView.setResizeFrame((ResizeFrame)v.findViewById(R.id.resize_frame));
- mDispositionView.setOnFrameSelectedListener(this);
- mDispositionView.post(mRedraw);
+ ViewGroup v = (ViewGroup)inflater.inflate(R.layout.choose_disposition_fragment,
+ container, false);
+
+ mCurrentPage = 0;
+ mNumberOfTemplates = getDispositionsTemplates().length;
+
+ mAdvise = (TextView)v.findViewById(R.id.advise);
+ mResizeFrame = (ResizeFrame)v.findViewById(R.id.resize_frame);
+
+ mAdapter = new DispositionAdapter(getActivity(), getAllDispositions(), mResizeFrame, this);
+ mPager = (ViewPager)v.findViewById(R.id.dispositions_pager);
+ mPager.setAdapter(mAdapter);
+ mPager.setOnPageChangeListener(this);
+ mPager.setCurrentItem(0);
+
return v;
}
@@ -132,21 +153,28 @@ public abstract class DispositionFragment
*/
@Override
public void onDestroyView() {
- super.onDestroyView();
- if (mDispositionView != null) {
- mDispositionView.removeCallbacks(mRedraw);
- if (mDispositionView.isChanged()) {
- saveDispositions(mDispositionView.getDispositions());
- }
+ boolean saved = false;
+
+ if (mCurrentDispositionView == null) {
+ mCurrentDispositionView = mAdapter.getView(0);
}
- // Notify that the settings was changed
- Intent intent = new Intent(PreferencesProvider.ACTION_SETTINGS_CHANGED);
- if (mDispositionView.isChanged()) {
- intent.putExtra(PreferencesProvider.EXTRA_FLAG_REDRAW, Boolean.TRUE);
- intent.putExtra(PreferencesProvider.EXTRA_FLAG_RECREATE_WORLD, Boolean.TRUE);
+ if (mCurrentDispositionView != null) {
+ if (mCurrentPage != 0 || mCurrentDispositionView.isChanged()) {
+ saveDispositions(mCurrentDispositionView.getDispositions());
+ saved = true;
+ }
+
+ // Notify that the settings was changed
+ Intent intent = new Intent(PreferencesProvider.ACTION_SETTINGS_CHANGED);
+ if (saved) {
+ intent.putExtra(PreferencesProvider.EXTRA_FLAG_REDRAW, Boolean.TRUE);
+ intent.putExtra(PreferencesProvider.EXTRA_FLAG_RECREATE_WORLD, Boolean.TRUE);
+ }
+ getActivity().sendBroadcast(intent);
}
- getActivity().sendBroadcast(intent);
+
+ super.onDestroyView();
}
/**
@@ -155,10 +183,9 @@ public abstract class DispositionFragment
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.dispositions, menu);
+ mRestoreMenu = menu.findItem(R.id.mnu_restore);
mDeleteMenu = menu.findItem(R.id.mnu_delete);
- if (mDeleteMenu != null) {
- mDeleteMenu.setVisible(false);
- }
+ mDeleteMenu.setVisible(false);
}
/**
@@ -185,14 +212,51 @@ public abstract class DispositionFragment
* Method that restores the disposition view to the default state
*/
private void restoreData() {
- mDispositionView.setDispositions(getUserDispositions(), getCols(), getRows());
+ if (mCurrentDispositionView == null) {
+ mCurrentDispositionView = mAdapter.getView(0);
+ }
+ mCurrentDispositionView.setDispositions(getUserDispositions(), getCols(), getRows(), true);
}
/**
* Method that restores the disposition view to the default state
*/
private void deleteFrame() {
- mDispositionView.deleteCurrentFrame();
+ if (mCurrentDispositionView == null) {
+ mCurrentDispositionView = mAdapter.getView(0);
+ }
+ mCurrentDispositionView.deleteCurrentFrame();
+ }
+
+ /**
+ * Method that returns the system-defined dispositions templates
+ *
+ * @return List<Dispositions> All the system-defined dispositions templates
+ */
+ public List<Dispositions> getAllDispositions() {
+ final int rows = getRows();
+ final int cols = getCols();
+
+ List<Dispositions> allDispositions = new ArrayList<Dispositions>();
+ allDispositions.add(new Dispositions(getUserDispositions(), rows, cols));
+ allDispositions.addAll(getSystemDefinedDispositions(rows, cols));
+ return allDispositions;
+ }
+ /**
+ * Method that returns the system-defined dispositions templates
+ *
+ * @param rows The number of rows
+ * @param cols The number of columns
+ * @return List<Dispositions> All the system-defined dispositions templates
+ */
+ private List<Dispositions> getSystemDefinedDispositions(int rows, int cols) {
+ String[] templates = getDispositionsTemplates();
+ List<Dispositions> systemDispositions = new ArrayList<Dispositions>(templates.length);
+ for (String template : templates) {
+ systemDispositions.add(new Dispositions(
+ DispositionUtil.toDispositions(template), rows, cols));
+ }
+ return systemDispositions;
}
/**
@@ -214,4 +278,50 @@ public abstract class DispositionFragment
mDeleteMenu.setVisible(false);
}
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ // Ignored
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onPageSelected(int position) {
+ // Save state
+ mCurrentPage = position;
+ mCurrentDispositionView = mAdapter.getView(position);
+
+ // Enable/Disable menus
+ if (mRestoreMenu != null) {
+ mRestoreMenu.setVisible(position == 0);
+ }
+ if (mDeleteMenu != null) {
+ mDeleteMenu.setVisible(false);
+ }
+
+ // Set the title
+ if (position == 0) {
+ mAdvise.setText(getString(R.string.pref_disposition_description));
+ } else {
+ mAdvise.setText(getString(R.string.pref_disposition_template,
+ String.valueOf(position), String.valueOf(mNumberOfTemplates)));
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ if (mDeleteMenu != null) {
+ mDeleteMenu.setVisible(false);
+ }
+ mResizeFrame.setVisibility(View.GONE);
+ }
+
}
diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/LandscapeDispositionFragment.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/LandscapeDispositionFragment.java
index 77267e6..b83c55f 100644
--- a/src/org/cyanogenmod/wallpapers/photophase/preferences/LandscapeDispositionFragment.java
+++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/LandscapeDispositionFragment.java
@@ -19,6 +19,7 @@ package org.cyanogenmod.wallpapers.photophase.preferences;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
+import org.cyanogenmod.wallpapers.photophase.R;
import org.cyanogenmod.wallpapers.photophase.preferences.PreferencesProvider.Preferences;
import org.cyanogenmod.wallpapers.photophase.model.Disposition;
import org.cyanogenmod.wallpapers.photophase.utils.DispositionUtil;
@@ -68,6 +69,14 @@ public class LandscapeDispositionFragment extends DispositionFragment {
* {@inheritDoc}
*/
@Override
+ public String[] getDispositionsTemplates() {
+ return getActivity().getResources().getStringArray(R.array.landscape_disposition_templates);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public void saveDispositions(List<Disposition> dispositions) {
Preferences.Layout.setLandscapeDisposition(getActivity(), dispositions);
}
diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/PortraitDispositionFragment.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/PortraitDispositionFragment.java
index 8763e1e..a3a53d9 100644
--- a/src/org/cyanogenmod/wallpapers/photophase/preferences/PortraitDispositionFragment.java
+++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/PortraitDispositionFragment.java
@@ -19,6 +19,7 @@ package org.cyanogenmod.wallpapers.photophase.preferences;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
+import org.cyanogenmod.wallpapers.photophase.R;
import org.cyanogenmod.wallpapers.photophase.model.Disposition;
import org.cyanogenmod.wallpapers.photophase.preferences.PreferencesProvider.Preferences;
import org.cyanogenmod.wallpapers.photophase.utils.DispositionUtil;
@@ -68,6 +69,14 @@ public class PortraitDispositionFragment extends DispositionFragment {
* {@inheritDoc}
*/
@Override
+ public String[] getDispositionsTemplates() {
+ return getActivity().getResources().getStringArray(R.array.portrait_disposition_templates);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public void saveDispositions(List<Disposition> dispositions) {
Preferences.Layout.setPortraitDisposition(getActivity(), dispositions);
}
diff --git a/src/org/cyanogenmod/wallpapers/photophase/widgets/DispositionView.java b/src/org/cyanogenmod/wallpapers/photophase/widgets/DispositionView.java
index 0bc462f..87e3999 100644
--- a/src/org/cyanogenmod/wallpapers/photophase/widgets/DispositionView.java
+++ b/src/org/cyanogenmod/wallpapers/photophase/widgets/DispositionView.java
@@ -40,6 +40,7 @@ import android.widget.ImageView.ScaleType;
import org.cyanogenmod.wallpapers.photophase.R;
import org.cyanogenmod.wallpapers.photophase.animations.Evaluators;
import org.cyanogenmod.wallpapers.photophase.model.Disposition;
+import org.cyanogenmod.wallpapers.photophase.model.Dispositions;
import org.cyanogenmod.wallpapers.photophase.utils.DispositionUtil;
import org.cyanogenmod.wallpapers.photophase.utils.MERAlgorithm;
import org.cyanogenmod.wallpapers.photophase.widgets.ResizeFrame.OnResizeListener;
@@ -140,18 +141,32 @@ public class DispositionView extends RelativeLayout implements OnLongClickListen
* Method that sets the disposition to draw on this view
*
* @param dispositions The dispositions to draw
- * @param cols The number of cols
+ * @param animate If should animate the view
+ */
+ public void setDispositions(Dispositions dispositions, boolean animate) {
+ setDispositions(dispositions.getDispositions(), dispositions.getCols(),
+ dispositions.getRows(), animate);
+ }
+
+ /**
+ * Method that sets the disposition to draw on this view
+ *
+ * @param dispositions The dispositions to draw
+ * @param cols The number of columns
* @param rows The number of rows
+ * @param animate If should animate the view
*/
- public void setDispositions(
- List<Disposition> dispositions, int cols, int rows) {
+ public void setDispositions(List<Disposition> dispositions, int cols, int rows,
+ boolean animate) {
mDispositions = dispositions;
mCols = cols;
mRows = rows;
// Remove all the current views and add the new ones
- recreateDispositions(true);
- mResizeFrame.setVisibility(View.GONE);
+ recreateDispositions(animate);
+ if (mResizeFrame != null) {
+ mResizeFrame.setVisibility(View.GONE);
+ }
mChanged = false;
}
@@ -207,6 +222,7 @@ public class DispositionView extends RelativeLayout implements OnLongClickListen
@SuppressWarnings("boxing")
public void deleteCurrentFrame() {
if (mTarget == null) return;
+ if (mResizeFrame == null) return;
final Disposition targetDisposition = resizerToDisposition();
@@ -348,7 +364,13 @@ public class DispositionView extends RelativeLayout implements OnLongClickListen
final ImageView v = new ImageView(getContext());
v.setImageResource(R.drawable.ic_camera);
v.setScaleType(ScaleType.CENTER);
- v.setBackgroundColor(getResources().getColor(R.color.disposition_frame_bg_color));
+
+ // Is locked? Then change the background color
+ v.setBackgroundColor(getResources().getColor(
+ mResizeFrame == null
+ ? R.color.disposition_locked_frame_bg_color
+ : R.color.disposition_frame_bg_color));
+
RelativeLayout.LayoutParams params =
new RelativeLayout.LayoutParams(r.width() - padding, r.height() - padding);
v.setX(r.left + padding);
@@ -401,13 +423,15 @@ public class DispositionView extends RelativeLayout implements OnLongClickListen
*/
@Override
public boolean onLongClick(View v) {
- if (!selectTarget(v)) return false;
- mVibrator.vibrate(300);
+ if (mResizeFrame != null && selectTarget(v)) {
+ mVibrator.vibrate(300);
+ }
return true;
}
@Override
public void onStartResize(int mode) {
+ if (mResizeFrame == null) return;
mOldResizeFrameLocation = new Rect(
mResizeFrame.getLeft(),
mResizeFrame.getTop(),
@@ -418,6 +442,7 @@ public class DispositionView extends RelativeLayout implements OnLongClickListen
@Override
public void onResize(int mode, int delta) {
if (mTarget == null) return;
+ if (mResizeFrame == null) return;
int w = getMeasuredWidth() - (getPaddingLeft() + getPaddingRight());
int h = getMeasuredHeight() - (getPaddingTop() + getPaddingBottom());
@@ -472,6 +497,7 @@ public class DispositionView extends RelativeLayout implements OnLongClickListen
@Override
public void onEndResize(final int mode) {
if (mTarget == null) return;
+ if (mResizeFrame == null) return;
// Compute the removed dispositions
computeRemovedDispositions(mode);
@@ -555,8 +581,10 @@ public class DispositionView extends RelativeLayout implements OnLongClickListen
boolean selectTarget(View v) {
//Do not do long click if we do not have a target
if (mTarget != null && v.equals(mTarget)) return false;
+ if (mResizeFrame == null) return false;
// Show the resize frame view just in place of the current clicked view
+
mResizeFrame.hide();
FrameLayout.LayoutParams frameParams =
(FrameLayout.LayoutParams)mResizeFrame.getLayoutParams();
@@ -666,7 +694,8 @@ public class DispositionView extends RelativeLayout implements OnLongClickListen
for (Disposition d : mDispositions) {
if (d.compareTo(disposition) != 0) {
if ((d.x + d.w) == disposition.x &&
- (d.y >= disposition.y) && ((d.y + d.h) <= (disposition.y + disposition.h))) {
+ (d.y >= disposition.y) &&
+ ((d.y + d.h) <= (disposition.y + disposition.h))) {
dispositions.add(d);
}
}
@@ -686,7 +715,8 @@ public class DispositionView extends RelativeLayout implements OnLongClickListen
for (Disposition d : mDispositions) {
if (d.compareTo(disposition) != 0) {
if ((d.y + d.h) == disposition.y &&
- (d.x >= disposition.x) && ((d.x + d.w) <= (disposition.x + disposition.w))) {
+ (d.x >= disposition.x) &&
+ ((d.x + d.w) <= (disposition.x + disposition.w))) {
dispositions.add(d);
}
}
@@ -706,7 +736,8 @@ public class DispositionView extends RelativeLayout implements OnLongClickListen
for (Disposition d : mDispositions) {
if (d.compareTo(disposition) != 0) {
if ((d.x) == (disposition.x + disposition.w) &&
- (d.y >= disposition.y) && ((d.y + d.h) <= (disposition.y + disposition.h))) {
+ (d.y >= disposition.y) &&
+ ((d.y + d.h) <= (disposition.y + disposition.h))) {
dispositions.add(d);
}
}
@@ -726,7 +757,8 @@ public class DispositionView extends RelativeLayout implements OnLongClickListen
for (Disposition d : mDispositions) {
if (d.compareTo(disposition) != 0) {
if ((d.y) == (disposition.y + disposition.h) &&
- (d.x >= disposition.x) && ((d.x + d.w) <= (disposition.x + disposition.w))) {
+ (d.x >= disposition.x) &&
+ ((d.x + d.w) <= (disposition.x + disposition.w))) {
dispositions.add(d);
}
}