Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d90a3311 authored by Romain Guy's avatar Romain Guy
Browse files

Add new setTag(int, Object) API to allow applications to specify several tags.

parent 413cf5f4
Loading
Loading
Loading
Loading
+28 −0
Original line number Diff line number Diff line
@@ -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"
+105 −1
Original line number Diff line number Diff line
@@ -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;
@@ -1288,6 +1289,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
     */
    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,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<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}.
+114 −0
Original line number Diff line number Diff line
/*
 * 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);
    }
}