12352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown/*
22352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown * Copyright (C) 2011 The Android Open Source Project
32352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown *
42352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown * Licensed under the Apache License, Version 2.0 (the "License");
52352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown * you may not use this file except in compliance with the License.
62352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown * You may obtain a copy of the License at
72352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown *
82352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown *      http://www.apache.org/licenses/LICENSE-2.0
92352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown *
102352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown * Unless required by applicable law or agreed to in writing, software
112352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown * distributed under the License is distributed on an "AS IS" BASIS,
122352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown * See the License for the specific language governing permissions and
142352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown * limitations under the License.
152352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown */
162352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
172352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brownpackage android.view;
182352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
192352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brownimport com.android.internal.util.XmlUtils;
202352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
212352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brownimport android.content.Context;
222352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brownimport android.content.res.Resources;
232352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brownimport android.content.res.TypedArray;
242352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brownimport android.content.res.XmlResourceParser;
252352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brownimport android.graphics.Bitmap;
262352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brownimport android.graphics.drawable.BitmapDrawable;
272352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brownimport android.graphics.drawable.Drawable;
282352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brownimport android.os.Parcel;
292352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brownimport android.os.Parcelable;
302352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brownimport android.util.Log;
312352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
322352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown/**
332352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown * Represents an icon that can be used as a mouse pointer.
342352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown * <p>
352352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown * Pointer icons can be provided either by the system using system styles,
362352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown * or by applications using bitmaps or application resources.
372352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown * </p>
382352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown *
392352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown * @hide
402352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown */
412352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brownpublic final class PointerIcon implements Parcelable {
422352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    private static final String TAG = "PointerIcon";
432352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
442352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    /** Style constant: Custom icon with a user-supplied bitmap. */
452352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    public static final int STYLE_CUSTOM = -1;
462352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
472352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    /** Style constant: Null icon.  It has no bitmap. */
482352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    public static final int STYLE_NULL = 0;
492352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
502352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    /** Style constant: Arrow icon.  (Default mouse pointer) */
512352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    public static final int STYLE_ARROW = 1000;
522352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
532352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    /** {@hide} Style constant: Spot hover icon for touchpads. */
542352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    public static final int STYLE_SPOT_HOVER = 2000;
552352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
562352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    /** {@hide} Style constant: Spot touch icon for touchpads. */
572352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    public static final int STYLE_SPOT_TOUCH = 2001;
582352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
592352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    /** {@hide} Style constant: Spot anchor icon for touchpads. */
602352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    public static final int STYLE_SPOT_ANCHOR = 2002;
612352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
622352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    // OEM private styles should be defined starting at this range to avoid
632352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    // conflicts with any system styles that may be defined in the future.
642352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    private static final int STYLE_OEM_FIRST = 10000;
652352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
662352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    // The default pointer icon.
672352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    private static final int STYLE_DEFAULT = STYLE_ARROW;
682352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
692352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    private static final PointerIcon gNullIcon = new PointerIcon(STYLE_NULL);
702352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
712352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    private final int mStyle;
722352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    private int mSystemIconResourceId;
732352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    private Bitmap mBitmap;
742352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    private float mHotSpotX;
752352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    private float mHotSpotY;
762352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
772352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    private PointerIcon(int style) {
782352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        mStyle = style;
792352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    }
802352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
812352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    /**
822352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * Gets a special pointer icon that has no bitmap.
832352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *
842352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @return The null pointer icon.
852352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *
862352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @see #STYLE_NULL
872352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     */
882352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    public static PointerIcon getNullIcon() {
892352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        return gNullIcon;
902352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    }
912352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
922352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    /**
932352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * Gets the default pointer icon.
942352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *
952352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @param context The context.
962352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @return The default pointer icon.
972352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *
982352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @throws IllegalArgumentException if context is null.
992352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     */
1002352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    public static PointerIcon getDefaultIcon(Context context) {
1012352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        return getSystemIcon(context, STYLE_DEFAULT);
1022352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    }
1032352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
1042352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    /**
1052352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * Gets a system pointer icon for the given style.
1062352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * If style is not recognized, returns the default pointer icon.
1072352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *
1082352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @param context The context.
1092352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @param style The pointer icon style.
1102352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @return The pointer icon.
1112352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *
1122352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @throws IllegalArgumentException if context is null.
1132352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     */
1142352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    public static PointerIcon getSystemIcon(Context context, int style) {
1152352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        if (context == null) {
1162352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            throw new IllegalArgumentException("context must not be null");
1172352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        }
1182352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
1192352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        if (style == STYLE_NULL) {
1202352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            return gNullIcon;
1212352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        }
1222352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
1232352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        int styleIndex = getSystemIconStyleIndex(style);
1242352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        if (styleIndex == 0) {
1252352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            styleIndex = getSystemIconStyleIndex(STYLE_DEFAULT);
1262352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        }
1272352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
1282352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        TypedArray a = context.obtainStyledAttributes(null,
1292352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown                com.android.internal.R.styleable.Pointer,
1302352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown                com.android.internal.R.attr.pointerStyle, 0);
1312352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        int resourceId = a.getResourceId(styleIndex, -1);
1322352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        a.recycle();
1332352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
1342352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        if (resourceId == -1) {
1352352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            Log.w(TAG, "Missing theme resources for pointer icon style " + style);
1362352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            return style == STYLE_DEFAULT ? gNullIcon : getSystemIcon(context, STYLE_DEFAULT);
1372352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        }
1382352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
1392352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        PointerIcon icon = new PointerIcon(style);
1402352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        if ((resourceId & 0xff000000) == 0x01000000) {
1412352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            icon.mSystemIconResourceId = resourceId;
1422352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        } else {
1432352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            icon.loadResource(context.getResources(), resourceId);
1442352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        }
1452352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        return icon;
1462352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    }
1472352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
1482352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    /**
1492352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * Creates a custom pointer from the given bitmap and hotspot information.
1502352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *
1512352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @param bitmap The bitmap for the icon.
1522352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @param hotspotX The X offset of the pointer icon hotspot in the bitmap.
1532352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *        Must be within the [0, bitmap.getWidth()) range.
1542352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @param hotspotY The Y offset of the pointer icon hotspot in the bitmap.
1552352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *        Must be within the [0, bitmap.getHeight()) range.
1562352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @return A pointer icon for this bitmap.
1572352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *
1582352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @throws IllegalArgumentException if bitmap is null, or if the x/y hotspot
1592352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *         parameters are invalid.
1602352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     */
1612352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    public static PointerIcon createCustomIcon(Bitmap bitmap, float hotSpotX, float hotSpotY) {
1622352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        if (bitmap == null) {
1632352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            throw new IllegalArgumentException("bitmap must not be null");
1642352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        }
1652352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        validateHotSpot(bitmap, hotSpotX, hotSpotY);
1662352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
1672352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        PointerIcon icon = new PointerIcon(STYLE_CUSTOM);
1682352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        icon.mBitmap = bitmap;
1692352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        icon.mHotSpotX = hotSpotX;
1702352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        icon.mHotSpotY = hotSpotY;
1712352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        return icon;
1722352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    }
1732352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
1742352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    /**
1752352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * Loads a custom pointer icon from an XML resource.
1762352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * <p>
1772352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * The XML resource should have the following form:
1782352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * <code>
1792352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * &lt;?xml version="1.0" encoding="utf-8"?&gt;
1802352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * &lt;pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
1812352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *   android:bitmap="@drawable/my_pointer_bitmap"
1822352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *   android:hotSpotX="24"
1832352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *   android:hotSpotY="24" /&gt;
1842352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * </code>
1852352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * </p>
1862352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *
1872352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @param resources The resources object.
1882352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @param resourceId The resource id.
1892352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @return The pointer icon.
1902352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *
1912352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @throws IllegalArgumentException if resources is null.
1922352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @throws Resources.NotFoundException if the resource was not found or the drawable
1932352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * linked in the resource was not found.
1942352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     */
1952352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    public static PointerIcon loadCustomIcon(Resources resources, int resourceId) {
1962352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        if (resources == null) {
1972352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            throw new IllegalArgumentException("resources must not be null");
1982352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        }
1992352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
2002352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        PointerIcon icon = new PointerIcon(STYLE_CUSTOM);
2012352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        icon.loadResource(resources, resourceId);
2022352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        return icon;
2032352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    }
2042352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
2052352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    /**
2062352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * Loads the bitmap and hotspot information for a pointer icon, if it is not already loaded.
2072352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * Returns a pointer icon (not necessarily the same instance) with the information filled in.
2082352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *
2092352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @param context The context.
2102352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @return The loaded pointer icon.
2112352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *
2122352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @throws IllegalArgumentException if context is null.
2132352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @see #isLoaded()
2142352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @hide
2152352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     */
2162352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    public PointerIcon load(Context context) {
2172352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        if (context == null) {
2182352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            throw new IllegalArgumentException("context must not be null");
2192352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        }
2202352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
2212352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        if (mSystemIconResourceId == 0 || mBitmap != null) {
2222352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            return this;
2232352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        }
2242352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
2252352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        PointerIcon result = new PointerIcon(mStyle);
2262352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        result.mSystemIconResourceId = mSystemIconResourceId;
2272352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        result.loadResource(context.getResources(), mSystemIconResourceId);
2282352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        return result;
2292352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    }
2302352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
2312352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    /**
2322352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * Returns true if the pointer icon style is {@link #STYLE_NULL}.
2332352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *
2342352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @return True if the pointer icon style is {@link #STYLE_NULL}.
2352352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     */
2362352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    public boolean isNullIcon() {
2372352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        return mStyle == STYLE_NULL;
2382352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    }
2392352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
2402352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    /**
2412352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * Returns true if the pointer icon has been loaded and its bitmap and hotspot
2422352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * information are available.
2432352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *
2442352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @return True if the pointer icon is loaded.
2452352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @see #load(Context)
2462352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     */
2472352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    public boolean isLoaded() {
2482352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        return mBitmap != null || mStyle == STYLE_NULL;
2492352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    }
2502352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
2512352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    /**
2522352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * Gets the style of the pointer icon.
2532352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *
2542352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @return The pointer icon style.
2552352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     */
2562352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    public int getStyle() {
2572352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        return mStyle;
2582352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    }
2592352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
2602352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    /**
2612352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * Gets the bitmap of the pointer icon.
2622352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *
2632352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @return The pointer icon bitmap, or null if the style is {@link #STYLE_NULL}.
2642352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *
2652352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @throws IllegalStateException if the bitmap is not loaded.
2662352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @see #isLoaded()
2672352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @see #load(Context)
2682352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     */
2692352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    public Bitmap getBitmap() {
2702352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        throwIfIconIsNotLoaded();
2712352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        return mBitmap;
2722352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    }
2732352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
2742352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    /**
2752352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * Gets the X offset of the pointer icon hotspot.
2762352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *
2772352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @return The hotspot X offset.
2782352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *
2792352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @throws IllegalStateException if the bitmap is not loaded.
2802352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @see #isLoaded()
2812352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @see #load(Context)
2822352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     */
2832352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    public float getHotSpotX() {
2842352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        throwIfIconIsNotLoaded();
2852352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        return mHotSpotX;
2862352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    }
2872352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
2882352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    /**
2892352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * Gets the Y offset of the pointer icon hotspot.
2902352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *
2912352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @return The hotspot Y offset.
2922352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     *
2932352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @throws IllegalStateException if the bitmap is not loaded.
2942352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @see #isLoaded()
2952352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     * @see #load(Context)
2962352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown     */
2972352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    public float getHotSpotY() {
2982352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        throwIfIconIsNotLoaded();
2992352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        return mHotSpotY;
3002352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    }
3012352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
3022352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    private void throwIfIconIsNotLoaded() {
3032352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        if (!isLoaded()) {
3042352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            throw new IllegalStateException("The icon is not loaded.");
3052352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        }
3062352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    }
3072352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
3082352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    public static final Parcelable.Creator<PointerIcon> CREATOR
3092352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            = new Parcelable.Creator<PointerIcon>() {
3102352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        public PointerIcon createFromParcel(Parcel in) {
3112352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            int style = in.readInt();
3122352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            if (style == STYLE_NULL) {
3132352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown                return getNullIcon();
3142352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            }
3152352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
3162352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            int systemIconResourceId = in.readInt();
3172352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            if (systemIconResourceId != 0) {
3182352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown                PointerIcon icon = new PointerIcon(style);
3192352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown                icon.mSystemIconResourceId = systemIconResourceId;
3202352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown                return icon;
3212352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            }
3222352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
3232352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            Bitmap bitmap = Bitmap.CREATOR.createFromParcel(in);
3242352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            float hotSpotX = in.readFloat();
3252352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            float hotSpotY = in.readFloat();
3262352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            return PointerIcon.createCustomIcon(bitmap, hotSpotX, hotSpotY);
3272352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        }
3282352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
3292352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        public PointerIcon[] newArray(int size) {
3302352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            return new PointerIcon[size];
3312352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        }
3322352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    };
3332352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
3342352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    public int describeContents() {
3352352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        return 0;
3362352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    }
3372352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
3382352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    public void writeToParcel(Parcel out, int flags) {
3392352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        out.writeInt(mStyle);
3402352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
3412352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        if (mStyle != STYLE_NULL) {
3422352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            out.writeInt(mSystemIconResourceId);
3432352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            if (mSystemIconResourceId == 0) {
3442352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown                mBitmap.writeToParcel(out, flags);
3452352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown                out.writeFloat(mHotSpotX);
3462352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown                out.writeFloat(mHotSpotY);
3472352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            }
3482352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        }
3492352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    }
3502352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
3512352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    @Override
3522352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    public boolean equals(Object other) {
3532352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        if (this == other) {
3542352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            return true;
3552352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        }
3562352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
3572352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        if (other == null || !(other instanceof PointerIcon)) {
3582352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            return false;
3592352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        }
3602352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
3612352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        PointerIcon otherIcon = (PointerIcon) other;
3622352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        if (mStyle != otherIcon.mStyle
3632352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown                || mSystemIconResourceId != otherIcon.mSystemIconResourceId) {
3642352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            return false;
3652352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        }
3662352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
3672352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        if (mSystemIconResourceId == 0 && (mBitmap != otherIcon.mBitmap
3682352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown                || mHotSpotX != otherIcon.mHotSpotX
3692352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown                || mHotSpotY != otherIcon.mHotSpotY)) {
3702352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            return false;
3712352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        }
3722352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
3732352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        return true;
3742352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    }
3752352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
3762352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    private void loadResource(Resources resources, int resourceId) {
3772352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        XmlResourceParser parser = resources.getXml(resourceId);
3782352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        final int bitmapRes;
3792352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        final float hotSpotX;
3802352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        final float hotSpotY;
3812352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        try {
3822352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            XmlUtils.beginDocument(parser, "pointer-icon");
3832352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
3842352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            TypedArray a = resources.obtainAttributes(
3852352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown                    parser, com.android.internal.R.styleable.PointerIcon);
3862352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            bitmapRes = a.getResourceId(com.android.internal.R.styleable.PointerIcon_bitmap, 0);
3872352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            hotSpotX = a.getFloat(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0);
3882352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            hotSpotY = a.getFloat(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0);
3892352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            a.recycle();
3902352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        } catch (Exception ex) {
3912352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            throw new IllegalArgumentException("Exception parsing pointer icon resource.", ex);
3922352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        } finally {
3932352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            parser.close();
3942352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        }
3952352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
3962352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        if (bitmapRes == 0) {
3972352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            throw new IllegalArgumentException("<pointer-icon> is missing bitmap attribute.");
3982352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        }
3992352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
4002352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        Drawable drawable = resources.getDrawable(bitmapRes);
4012352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        if (!(drawable instanceof BitmapDrawable)) {
4022352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            throw new IllegalArgumentException("<pointer-icon> bitmap attribute must "
4032352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown                    + "refer to a bitmap drawable.");
4042352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        }
4052352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
4062352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        // Set the properties now that we have successfully loaded the icon.
4072352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        mBitmap = ((BitmapDrawable)drawable).getBitmap();
4082352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        mHotSpotX = hotSpotX;
4092352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        mHotSpotY = hotSpotY;
4102352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    }
4112352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
4122352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    private static void validateHotSpot(Bitmap bitmap, float hotSpotX, float hotSpotY) {
4132352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        if (hotSpotX < 0 || hotSpotX >= bitmap.getWidth()) {
4142352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            throw new IllegalArgumentException("x hotspot lies outside of the bitmap area");
4152352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        }
4162352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        if (hotSpotY < 0 || hotSpotY >= bitmap.getHeight()) {
4172352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            throw new IllegalArgumentException("y hotspot lies outside of the bitmap area");
4182352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        }
4192352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    }
4202352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
4212352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    private static int getSystemIconStyleIndex(int style) {
4222352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        switch (style) {
4232352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            case STYLE_ARROW:
4242352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown                return com.android.internal.R.styleable.Pointer_pointerIconArrow;
4252352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            case STYLE_SPOT_HOVER:
4262352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown                return com.android.internal.R.styleable.Pointer_pointerIconSpotHover;
4272352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            case STYLE_SPOT_TOUCH:
4282352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown                return com.android.internal.R.styleable.Pointer_pointerIconSpotTouch;
4292352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            case STYLE_SPOT_ANCHOR:
4302352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown                return com.android.internal.R.styleable.Pointer_pointerIconSpotAnchor;
4312352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            default:
4322352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown                return 0;
4332352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        }
4342352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    }
4352352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown}
436