1293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount/*
2293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount * Copyright (C) 2014 The Android Open Source Project
3293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount *
4293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount * Licensed under the Apache License, Version 2.0 (the "License");
5293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount * you may not use this file except in compliance with the License.
6293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount * You may obtain a copy of the License at
7293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount *
8293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount *      http://www.apache.org/licenses/LICENSE-2.0
9293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount *
10293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount * Unless required by applicable law or agreed to in writing, software
11293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount * distributed under the License is distributed on an "AS IS" BASIS,
12293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount * See the License for the specific language governing permissions and
14293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount * limitations under the License.
15293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount */
16fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountpackage android.databinding;
17293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
18293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mountimport java.util.ArrayList;
19293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mountimport java.util.List;
20293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
21293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount/**
22c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount * A utility for storing and notifying callbacks. This class supports reentrant modification
23293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount * of the callbacks during notification without adversely disrupting notifications.
24293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount * A common pattern for callbacks is to receive a notification and then remove
25293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount * themselves. This class handles this behavior with constant memory under
26293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount * most circumstances.
27293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount *
282c86cdbaf189e2b1774af7f64a2974de9321673fGeorge Mount * <p>A subclass of {@link CallbackRegistry.NotifierCallback} must be passed to
29293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount * the constructor to define how notifications should be called. That implementation
30c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount * does the actual notification on the listener. It is typically a static instance
31c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount * that can be reused for all similar CallbackRegistries.</p>
32293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount *
33c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount * <p>This class supports only callbacks with at most three parameters.
34c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount * Typically, these are the notification originator and a parameter, with another to
35c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount * indicate which method to call, but these may be used as required. If more than
36c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount * three parameters are required or primitive types other than the single int provided
37293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount * must be used, <code>A</code> should be some kind of containing structure that
38293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount * the subclass may reuse between notifications.</p>
39293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount *
40293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount * @param <C> The callback type.
41293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount * @param <T> The notification sender type. Typically this is the containing class.
425cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount * @param <A> Opaque argument used to pass additional data beyond an int.
43293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount */
445cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mountpublic class CallbackRegistry<C, T, A> implements Cloneable {
45293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    private static final String TAG = "CallbackRegistry";
46293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
47293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    /** An ordered collection of listeners waiting to be notified. */
48293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    private List<C> mCallbacks = new ArrayList<C>();
49293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
50293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    /**
51293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * A bit flag for the first 64 listeners that are removed during notification.
52293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * The lowest significant bit corresponds to the 0th index into mCallbacks.
53293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * For a small number of callbacks, no additional array of objects needs to
54293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * be allocated.
55293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     */
56293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    private long mFirst64Removed = 0x0;
57293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
58293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    /**
59293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * Bit flags for the remaining callbacks that are removed during notification.
60293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * When there are more than 64 callbacks and one is marked for removal, a dynamic
61293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * array of bits are allocated for the callbacks.
62293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     */
63293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    private long[] mRemainderRemoved;
64293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
65293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    /** The recursion level of the notification */
66293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    private int mNotificationLevel;
67293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
68293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    /** The notification mechanism for notifying an event. */
695cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    private final NotifierCallback<C, T, A> mNotifier;
70293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
71293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    /**
72293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * Creates an EventRegistry that notifies the event with notifier.
73293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @param notifier The class to use to notify events.
74293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     */
755cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    public CallbackRegistry(NotifierCallback<C, T, A> notifier) {
76293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        mNotifier = notifier;
77293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    }
78293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
79293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    /**
80293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * Notify all callbacks.
81293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     *
82293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @param sender The originator. This is an opaque parameter passed to
83c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
84293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @param arg An opaque parameter passed to
85c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
865cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount     * @param arg2 An opaque parameter passed to
87c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
88293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     */
895cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    public synchronized void notifyCallbacks(T sender, int arg, A arg2) {
90293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        mNotificationLevel++;
915cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        notifyRecurse(sender, arg, arg2);
92293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        mNotificationLevel--;
93293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        if (mNotificationLevel == 0) {
94293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            if (mRemainderRemoved != null) {
95293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                for (int i = mRemainderRemoved.length - 1; i >= 0; i--) {
96293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                    final long removedBits = mRemainderRemoved[i];
97293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                    if (removedBits != 0) {
98293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                        removeRemovedCallbacks((i + 1) * Long.SIZE, removedBits);
99293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                        mRemainderRemoved[i] = 0;
100293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                    }
101293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                }
102293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            }
103293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            if (mFirst64Removed != 0) {
104293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                removeRemovedCallbacks(0, mFirst64Removed);
105293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                mFirst64Removed = 0;
106293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            }
107293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        }
108293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    }
109293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
110293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    /**
111293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * Notify up to the first Long.SIZE callbacks that don't have a bit set in <code>removed</code>.
112293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     *
113293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @param sender The originator. This is an opaque parameter passed to
114c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
115293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @param arg An opaque parameter passed to
116c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
1175cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount     * @param arg2 An opaque parameter passed to
118c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
119293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     */
1205cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    private void notifyFirst64(T sender, int arg, A arg2) {
121293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        final int maxNotified = Math.min(Long.SIZE, mCallbacks.size());
1225cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        notifyCallbacks(sender, arg, arg2, 0, maxNotified, mFirst64Removed);
123293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    }
124293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
125293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    /**
126293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * Notify all callbacks using a recursive algorithm to avoid allocating on the heap.
127293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * This part captures the callbacks beyond Long.SIZE that have no bits allocated for
1285cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount     * removal before it recurses into {@link #notifyRemainder(Object, int, A, int)}.
129293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     *
130293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * <p>Recursion is used to avoid allocating temporary state on the heap.</p>
131293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     *
132293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @param sender The originator. This is an opaque parameter passed to
133c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
134293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @param arg An opaque parameter passed to
135c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
1365cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount     * @param arg2 An opaque parameter passed to
137c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
138293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     */
1395cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    private void notifyRecurse(T sender, int arg, A arg2) {
140293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        final int callbackCount = mCallbacks.size();
141293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        final int remainderIndex = mRemainderRemoved == null ? -1 : mRemainderRemoved.length - 1;
142293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
143293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        // Now we've got all callbakcs that have no mRemainderRemoved value, so notify the
144293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        // others.
1455cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        notifyRemainder(sender, arg, arg2, remainderIndex);
146293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
147293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        // notifyRemainder notifies all at maxIndex, so we'd normally start at maxIndex + 1
148293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        // However, we must also keep track of those in mFirst64Removed, so we add 2 instead:
149293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        final int startCallbackIndex = (remainderIndex + 2) * Long.SIZE;
150293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
151293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        // The remaining have no bit set
1525cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        notifyCallbacks(sender, arg, arg2, startCallbackIndex, callbackCount, 0);
153293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    }
154293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
155293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    /**
156293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * Notify callbacks that have mRemainderRemoved bits set for remainderIndex. If
157293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * remainderIndex is -1, the first 64 will be notified instead.
158293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     *
159293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @param sender The originator. This is an opaque parameter passed to
160c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
161293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @param arg An opaque parameter passed to
162c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
1635cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount     * @param arg2 An opaque parameter passed to
164c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
165293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @param remainderIndex The index into mRemainderRemoved that should be notified.
166293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     */
1675cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    private void notifyRemainder(T sender, int arg, A arg2, int remainderIndex) {
168293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        if (remainderIndex < 0) {
1695cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            notifyFirst64(sender, arg, arg2);
170293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        } else {
171293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            final long bits = mRemainderRemoved[remainderIndex];
172293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            final int startIndex = (remainderIndex + 1) * Long.SIZE;
173293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            final int endIndex = Math.min(mCallbacks.size(), startIndex + Long.SIZE);
1745cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            notifyRemainder(sender, arg, arg2, remainderIndex - 1);
1755cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            notifyCallbacks(sender, arg, arg2, startIndex, endIndex, bits);
176293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        }
177293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    }
178293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
179293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    /**
180293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * Notify callbacks from startIndex to endIndex, using bits as the bit status
181293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * for whether they have been removed or not. bits should be from mRemainderRemoved or
182293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * mFirst64Removed. bits set to 0 indicates that all callbacks from startIndex to
183293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * endIndex should be notified.
184293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     *
185293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @param sender The originator. This is an opaque parameter passed to
186c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
187293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @param arg An opaque parameter passed to
188c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
1895cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount     * @param arg2 An opaque parameter passed to
190c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
191293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @param startIndex The index into the mCallbacks to start notifying.
192293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @param endIndex One past the last index into mCallbacks to notify.
193293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @param bits A bit field indicating which callbacks have been removed and shouldn't
194293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     *             be notified.
195293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     */
1965cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    private void notifyCallbacks(T sender, int arg, A arg2, final int startIndex,
1975cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            final int endIndex, final long bits) {
198293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        long bitMask = 1;
199293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        for (int i = startIndex; i < endIndex; i++) {
200293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            if ((bits & bitMask) == 0) {
2015cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2);
202293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            }
203293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            bitMask <<= 1;
204293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        }
205293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    }
206293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
207293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    /**
208293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * Add a callback to be notified. If the callback is already in the list, another won't
209293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * be added. This does not affect current notifications.
210293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @param callback The callback to add.
211293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     */
212293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    public synchronized void add(C callback) {
2132bf1d3b17e04fd03ff447d57f770c89f935f52aeGeorge Mount        if (callback == null) {
2142bf1d3b17e04fd03ff447d57f770c89f935f52aeGeorge Mount            throw new IllegalArgumentException("callback cannot be null");
2152bf1d3b17e04fd03ff447d57f770c89f935f52aeGeorge Mount        }
216293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        int index = mCallbacks.lastIndexOf(callback);
217293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        if (index < 0 || isRemoved(index)) {
218293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            mCallbacks.add(callback);
219293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        }
220293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    }
221293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
222293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    /**
223293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * Returns true if the callback at index has been marked for removal.
224293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     *
225293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @param index The index into mCallbacks to check.
226293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @return true if the callback at index has been marked for removal.
227293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     */
228293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    private boolean isRemoved(int index) {
229293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        if (index < Long.SIZE) {
230293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            // It is in the first 64 callbacks, just check the bit.
231293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            final long bitMask = 1L << index;
232293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            return (mFirst64Removed & bitMask) != 0;
233293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        } else if (mRemainderRemoved == null) {
234293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            // It is after the first 64 callbacks, but nothing else was marked for removal.
235293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            return false;
236293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        } else {
237293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            final int maskIndex = (index / Long.SIZE) - 1;
238293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            if (maskIndex >= mRemainderRemoved.length) {
239293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                // There are some items in mRemainderRemoved, but nothing at the given index.
240293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                return false;
241293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            } else {
242293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                // There is something marked for removal, so we have to check the bit.
243293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                final long bits = mRemainderRemoved[maskIndex];
244293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                final long bitMask = 1L << (index % Long.SIZE);
245293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                return (bits & bitMask) != 0;
246293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            }
247293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        }
248293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    }
249293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
250293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    /**
251293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * Removes callbacks from startIndex to startIndex + Long.SIZE, based
252293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * on the bits set in removed.
253c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     *
254293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @param startIndex The index into the mCallbacks to start removing callbacks.
255293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @param removed The bits indicating removal, where each bit is set for one callback
256293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     *                to be removed.
257293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     */
258293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    private void removeRemovedCallbacks(int startIndex, long removed) {
259293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        // The naive approach should be fine. There may be a better bit-twiddling approach.
260293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        final int endIndex = startIndex + Long.SIZE;
261293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
262293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        long bitMask = 1L << (Long.SIZE - 1);
263293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        for (int i = endIndex - 1; i >= startIndex; i--) {
264293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            if ((removed & bitMask) != 0) {
265293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                mCallbacks.remove(i);
266293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            }
267293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            bitMask >>>= 1;
268293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        }
269293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    }
270293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
271293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    /**
272293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * Remove a callback. This callback won't be notified after this call completes.
273c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     *
274293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @param callback The callback to remove.
275293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     */
276293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    public synchronized void remove(C callback) {
277293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        if (mNotificationLevel == 0) {
278293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            mCallbacks.remove(callback);
279293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        } else {
280293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            int index = mCallbacks.lastIndexOf(callback);
281293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            if (index >= 0) {
282293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                setRemovalBit(index);
283293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            }
284293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        }
285293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    }
286293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
287293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    private void setRemovalBit(int index) {
288293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        if (index < Long.SIZE) {
289293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            // It is in the first 64 callbacks, just check the bit.
290293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            final long bitMask = 1L << index;
291293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            mFirst64Removed |= bitMask;
292293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        } else {
293293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            final int remainderIndex = (index / Long.SIZE) - 1;
294293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            if (mRemainderRemoved == null) {
295293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                mRemainderRemoved = new long[mCallbacks.size() / Long.SIZE];
296293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            } else if (mRemainderRemoved.length < remainderIndex) {
297293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                // need to make it bigger
298293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                long[] newRemainders = new long[mCallbacks.size() / Long.SIZE];
299293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                System.arraycopy(mRemainderRemoved, 0, newRemainders, 0, mRemainderRemoved.length);
300293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                mRemainderRemoved = newRemainders;
301293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            }
302293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            final long bitMask = 1L << (index % Long.SIZE);
303293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            mRemainderRemoved[remainderIndex] |= bitMask;
304293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        }
305293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    }
306293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
307293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    /**
308293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * Makes a copy of the registered callbacks and returns it.
309293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     *
310293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @return a copy of the registered callbacks.
311293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     */
312c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount    public synchronized ArrayList<C> copyCallbacks() {
313293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        ArrayList<C> callbacks = new ArrayList<C>(mCallbacks.size());
314293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        int numListeners = mCallbacks.size();
315293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        for (int i = 0; i < numListeners; i++) {
316293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            if (!isRemoved(i)) {
317293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                callbacks.add(mCallbacks.get(i));
318293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            }
319293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        }
320293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        return callbacks;
321293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    }
322293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
323293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    /**
324c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     * Modifies <code>callbacks</code> to contain all callbacks in the CallbackRegistry.
325c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     *
326c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     * @param callbacks modified to contain all callbacks registered to receive events.
327c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     */
328c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount    public synchronized void copyCallbacks(List<C> callbacks) {
329c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount        callbacks.clear();
330c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount        int numListeners = mCallbacks.size();
331c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount        for (int i = 0; i < numListeners; i++) {
332c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount            if (!isRemoved(i)) {
333c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount                callbacks.add(mCallbacks.get(i));
334c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount            }
335c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount        }
336c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount    }
337c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount
338c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount    /**
339293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * Returns true if there are no registered callbacks or false otherwise.
340293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     *
341293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @return true if there are no registered callbacks or false otherwise.
342293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     */
343293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    public synchronized boolean isEmpty() {
344293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        if (mCallbacks.isEmpty()) {
345293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            return true;
346293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        } else if (mNotificationLevel == 0) {
347293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            return false;
348293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        } else {
349293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            int numListeners = mCallbacks.size();
350293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            for (int i = 0; i < numListeners; i++) {
351293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                if (!isRemoved(i)) {
352293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                    return false;
353293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                }
354293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            }
355293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            return true;
356293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        }
357293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    }
358293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
359293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    /**
360293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * Removes all callbacks from the list.
361293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     */
362293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    public synchronized void clear() {
363293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        if (mNotificationLevel == 0) {
364293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            mCallbacks.clear();
365293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        } else if (!mCallbacks.isEmpty()) {
366293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            for (int i = mCallbacks.size() - 1; i >= 0; i--) {
367293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                setRemovalBit(i);
368293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            }
369293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        }
370293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    }
371293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
372c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount    /**
373c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     * @return A copy of the CallbackRegistry with all callbacks listening to both instances.
374c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     */
375c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount    @SuppressWarnings("unchecked")
3765cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    public synchronized CallbackRegistry<C, T, A> clone() {
3775cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        CallbackRegistry<C, T, A> clone = null;
378293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        try {
3795cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            clone = (CallbackRegistry<C, T, A>) super.clone();
380293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            clone.mFirst64Removed = 0;
381293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            clone.mRemainderRemoved = null;
382293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            clone.mNotificationLevel = 0;
383293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            clone.mCallbacks = new ArrayList<C>();
384293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            final int numListeners = mCallbacks.size();
385293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            for (int i = 0; i < numListeners; i++) {
386293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                if (!isRemoved(i)) {
387293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                    clone.mCallbacks.add(mCallbacks.get(i));
388293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount                }
389293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount            }
390293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        } catch (CloneNotSupportedException e) {
3912c86cdbaf189e2b1774af7f64a2974de9321673fGeorge Mount            e.printStackTrace();
392293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        }
393293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        return clone;
394293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    }
395293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount
396293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    /**
397293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * Class used to notify events from CallbackRegistry.
398293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     *
399293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @param <C> The callback type.
400293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     * @param <T> The notification sender type. Typically this is the containing class.
4015cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount     * @param <A> An opaque argument to pass to the notifier
402293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount     */
4035cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    public abstract static class NotifierCallback<C, T, A> {
404293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        /**
405c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount         * Called by CallbackRegistry during
406c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount         * {@link CallbackRegistry#notifyCallbacks(Object, int, Object)}} to notify the callback.
407293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount         *
408293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount         * @param callback The callback to notify.
409293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount         * @param sender The opaque sender object.
410293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount         * @param arg The opaque notification parameter.
4115cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount         * @param arg2 An opaque argument passed in
4122c86cdbaf189e2b1774af7f64a2974de9321673fGeorge Mount         *        {@link CallbackRegistry#notifyCallbacks}
4132c86cdbaf189e2b1774af7f64a2974de9321673fGeorge Mount         * @see CallbackRegistry#CallbackRegistry(CallbackRegistry.NotifierCallback)
414293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount         */
4155cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        public abstract void onNotifyCallback(C callback, T sender, int arg, A arg2);
416293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount    }
417293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount}
418