diff options
-rw-r--r-- | api/current.xml | 28 | ||||
-rw-r--r-- | core/java/android/view/View.java | 106 | ||||
-rw-r--r-- | tests/FrameworkTest/tests/src/com/android/frameworktest/view/SetTagsTest.java | 114 |
3 files changed, 247 insertions, 1 deletions
diff --git a/api/current.xml b/api/current.xml index 509abe1..c222d42 100644 --- a/api/current.xml +++ b/api/current.xml @@ -135944,6 +135944,19 @@ visibility="public" > </method> +<method name="getTag" + return="java.lang.Object" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="key" type="int"> +</parameter> +</method> <method name="getTop" return="int" abstract="false" @@ -137843,6 +137856,21 @@ <parameter name="tag" type="java.lang.Object"> </parameter> </method> +<method name="setTag" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="key" type="int"> +</parameter> +<parameter name="tag" type="java.lang.Object"> +</parameter> +</method> <method name="setTouchDelegate" return="void" abstract="false" diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 07c56ee..d042f28 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -61,6 +61,7 @@ import com.android.internal.view.menu.MenuBuilder; import java.util.ArrayList; import java.util.Arrays; +import java.util.WeakHashMap; import java.lang.ref.SoftReference; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; @@ -1287,7 +1288,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * a Rect. :) */ static final ThreadLocal<Rect> sThreadLocal = new ThreadLocal<Rect>(); - + + /** + * Map used to store views' tags. + */ + private static WeakHashMap<View, SparseArray<Object>> sTags; + + /** + * Lock used to access sTags. + */ + private static final Object sTagsLock = new Object(); + /** * The animation currently associated with this view. * @hide @@ -7000,6 +7011,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * Returns this view's tag. * * @return the Object stored in this view as a tag + * + * @see #setTag(Object) + * @see #getTag(int) */ @ViewDebug.ExportedProperty public Object getTag() { @@ -7013,12 +7027,102 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * resorting to another data structure. * * @param tag an Object to tag the view with + * + * @see #getTag() + * @see #setTag(int, Object) */ public void setTag(final Object tag) { mTag = tag; } /** + * Returns the tag associated with this view and the specified key. + * + * @param key The key identifying the tag + * + * @return the Object stored in this view as a tag + * + * @see #setTag(int, Object) + * @see #getTag() + */ + public Object getTag(int key) { + SparseArray<Object> tags = null; + synchronized (sTagsLock) { + if (sTags != null) { + tags = sTags.get(this); + } + } + + if (tags != null) return tags.get(key); + return null; + } + + /** + * Sets a tag associated with this view and a key. A tag can be used + * to mark a view in its hierarchy and does not have to be unique within + * the hierarchy. Tags can also be used to store data within a view + * without resorting to another data structure. + * + * The specified key should be an id declared in the resources of the + * application to ensure it is unique. Keys identified as belonging to + * the Android framework or not associated with any package will cause + * an {@link IllegalArgumentException} to be thrown. + * + * @param key The key identifying the tag + * @param tag An Object to tag the view with + * + * @throws IllegalArgumentException If they specified key is not valid + * + * @see #setTag(Object) + * @see #getTag(int) + */ + public void setTag(int key, final Object tag) { + // If the package id is 0x00 or 0x01, it's either an undefined package + // or a framework id + if ((key >>> 24) < 2) { + throw new IllegalArgumentException("The key must be an application-specific " + + "resource id."); + } + + setTagInternal(this, key, tag); + } + + /** + * Variation of {@link #setTag(int, Object)} that enforces the key to be a + * framework id. + * + * @hide + */ + public void setTagInternal(int key, Object tag) { + if ((key >>> 24) != 0x1) { + throw new IllegalArgumentException("The key must be a framework-specific " + + "resource id."); + } + + setTagInternal(this, key, tag); + } + + private static void setTagInternal(View view, int key, Object tag) { + SparseArray<Object> tags = null; + synchronized (sTagsLock) { + if (sTags == null) { + sTags = new WeakHashMap<View, SparseArray<Object>>(); + } else { + tags = sTags.get(view); + } + } + + if (tags == null) { + tags = new SparseArray<Object>(2); + synchronized (sTagsLock) { + sTags.put(view, tags); + } + } + + tags.put(key, tag); + } + + /** * Prints information about this view in the log output, with the tag * {@link #VIEW_LOG_TAG}. * diff --git a/tests/FrameworkTest/tests/src/com/android/frameworktest/view/SetTagsTest.java b/tests/FrameworkTest/tests/src/com/android/frameworktest/view/SetTagsTest.java new file mode 100644 index 0000000..523eeaf --- /dev/null +++ b/tests/FrameworkTest/tests/src/com/android/frameworktest/view/SetTagsTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2007 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 com.android.frameworktest.view; + +import com.android.frameworktest.R; +import android.test.suitebuilder.annotation.MediumTest; + +import android.test.ActivityInstrumentationTestCase2; +import android.widget.Button; + +/** + * Exercises {@link android.view.View}'s tags property. + */ +public class SetTagsTest extends ActivityInstrumentationTestCase2<Disabled> { + private Button mView; + + public SetTagsTest() { + super("com.android.frameworktest", Disabled.class); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + + mView = (Button) getActivity().findViewById(R.id.disabledButton); + } + + @MediumTest + public void testSetUpConditions() throws Exception { + assertNotNull(mView); + } + + @MediumTest + public void testSetTag() throws Exception { + mView.setTag("1"); + } + + @MediumTest + public void testGetTag() throws Exception { + Object o = new Object(); + mView.setTag(o); + + final Object stored = mView.getTag(); + assertNotNull(stored); + assertSame("The stored tag is inccorect", o, stored); + } + + @MediumTest + public void testSetTagWithKey() throws Exception { + mView.setTag(R.id.a, "2"); + } + + @MediumTest + public void testGetTagWithKey() throws Exception { + Object o = new Object(); + mView.setTag(R.id.a, o); + + final Object stored = mView.getTag(R.id.a); + assertNotNull(stored); + assertSame("The stored tag is inccorect", o, stored); + } + + @MediumTest + public void testSetTagWithFrameworkId() throws Exception { + boolean result = false; + try { + mView.setTag(android.R.id.list, "2"); + } catch (IllegalArgumentException e) { + result = true; + } + assertTrue("Setting a tag with a framework id did not throw an exception", result); + } + + @MediumTest + public void testSetTagWithNoPackageId() throws Exception { + boolean result = false; + try { + mView.setTag(0x000000AA, "2"); + } catch (IllegalArgumentException e) { + result = true; + } + assertTrue("Setting a tag with an id with no package did not throw an exception", result); + } + + @MediumTest + public void testSetTagInternalWithFrameworkId() throws Exception { + mView.setTagInternal(android.R.id.list, "2"); + } + + @MediumTest + public void testSetTagInternalWithApplicationId() throws Exception { + boolean result = false; + try { + mView.setTagInternal(R.id.a, "2"); + } catch (IllegalArgumentException e) { + result = true; + } + assertTrue("Setting a tag with an id with app package did not throw an exception", result); + } +} |