From d90a33111b3f1d0b2fff7ba9bafabc63fdb67d3a Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Wed, 6 May 2009 14:54:28 -0700 Subject: Add new setTag(int, Object) API to allow applications to specify several tags. --- api/current.xml | 28 +++++ core/java/android/view/View.java | 106 ++++++++++++++++++- .../android/frameworktest/view/SetTagsTest.java | 114 +++++++++++++++++++++ 3 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 tests/FrameworkTest/tests/src/com/android/frameworktest/view/SetTagsTest.java diff --git a/api/current.xml b/api/current.xml index 509abe1b782c..c222d42d58e8 100644 --- a/api/current.xml +++ b/api/current.xml @@ -135944,6 +135944,19 @@ visibility="public" > + + + + + + + + + + sThreadLocal = new ThreadLocal(); - + + /** + * Map used to store views' tags. + */ + private static WeakHashMap> 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,11 +7027,101 @@ 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 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 tags = null; + synchronized (sTagsLock) { + if (sTags == null) { + sTags = new WeakHashMap>(); + } else { + tags = sTags.get(view); + } + } + + if (tags == null) { + tags = new SparseArray(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 000000000000..523eeaf96f48 --- /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 { + 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); + } +} -- cgit v1.2.3-59-g8ed1b