ViewDataBinding.java revision d6527ee28cc3aa05818799af8def9593346f91bc
13d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar/*
23d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar * Copyright (C) 2014 The Android Open Source Project
33d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar *
43d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
53d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar * you may not use this file except in compliance with the License.
63d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar * You may obtain a copy of the License at
73d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar *
83d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
93d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar *
103d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar * Unless required by applicable law or agreed to in writing, software
113d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
123d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar * See the License for the specific language governing permissions and
143d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar * limitations under the License.
153d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar */
163d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar
17fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountpackage android.databinding;
18fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount
19e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mountimport com.android.databinding.library.R;
20e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount
215914aa7da50a90a4c705b5be02a215499d0ad232George Mountimport android.annotation.TargetApi;
22e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mountimport android.databinding.CallbackRegistry.NotifierCallback;
234c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mountimport android.os.Build.VERSION;
244c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mountimport android.os.Build.VERSION_CODES;
253f73e3149732132c02f5d19aab7b5da429794058Yigit Boyarimport android.os.Handler;
263f73e3149732132c02f5d19aab7b5da429794058Yigit Boyarimport android.os.Looper;
2796e1c821dd446d1ed78f8ae61131550588f60a24George Mountimport android.text.TextUtils;
284c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mountimport android.util.SparseIntArray;
293f73e3149732132c02f5d19aab7b5da429794058Yigit Boyarimport android.view.Choreographer;
30085724fd84795ae1631747443c43f77c08e11a28Yigit Boyarimport android.view.View;
315914aa7da50a90a4c705b5be02a215499d0ad232George Mountimport android.view.View.OnAttachStateChangeListener;
3200da715547ee7d5d38a3b8576090ca427a94daa5George Mountimport android.view.ViewGroup;
33085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
34085724fd84795ae1631747443c43f77c08e11a28Yigit Boyarimport java.lang.ref.WeakReference;
35085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
364c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mountpublic abstract class ViewDataBinding {
374c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
384c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
394c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Instead of directly accessing Build.VERSION.SDK_INT, generated code uses this value so that
404c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * we can test API dependent behavior.
414c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
424c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    static int SDK_INT = VERSION.SDK_INT;
434c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
44e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    private static final int REBIND = 1;
45e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    private static final int HALTED = 2;
46e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    private static final int REBOUND = 3;
47e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
484c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
494c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Prefix for android:tag on Views with binding. The root View and include tags will not have
504c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * android:tag attributes and will use ids instead.
514c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
527ff60c24c6de7ba0c674fe65a82ad4a88dab2e5dGeorge Mount    public static final String BINDING_TAG_PREFIX = "binding_";
534c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
544c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    // The length of BINDING_TAG_PREFIX prevents calling length repeatedly.
5500da715547ee7d5d38a3b8576090ca427a94daa5George Mount    private static final int BINDING_NUMBER_START = BINDING_TAG_PREFIX.length();
5600da715547ee7d5d38a3b8576090ca427a94daa5George Mount
57e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount    // ICS (v 14) fixes a leak when using setTag(int, Object)
58e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount    private static final boolean USE_TAG_ID = DataBinderMapper.TARGET_MIN_SDK >= 14;
59e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount
603f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar    private static final boolean USE_CHOREOGRAPHER = SDK_INT >= 16;
613f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar
624c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
634c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Method object extracted out to attach a listener to a bound Observable object.
644c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
655cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
665cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
674c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
68722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
695cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
705cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    };
715cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
724c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
734c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Method object extracted out to attach a listener to a bound ObservableList object.
744c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
755cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    private static final CreateWeakListener CREATE_LIST_LISTENER = new CreateWeakListener() {
765cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
774c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
78722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            return new WeakListListener(viewDataBinding, localFieldId).getListener();
795cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
805cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    };
815cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
824c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
834c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Method object extracted out to attach a listener to a bound ObservableMap object.
844c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
855cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    private static final CreateWeakListener CREATE_MAP_LISTENER = new CreateWeakListener() {
865cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
874c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
88722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            return new WeakMapListener(viewDataBinding, localFieldId).getListener();
895cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
905cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    };
915cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
92e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    private static final CallbackRegistry.NotifierCallback<OnRebindCallback, ViewDataBinding, Void>
93e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        REBIND_NOTIFIER = new NotifierCallback<OnRebindCallback, ViewDataBinding, Void>() {
94e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        @Override
95e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        public void onNotifyCallback(OnRebindCallback callback, ViewDataBinding sender, int mode,
96e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                Void arg2) {
97e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            switch (mode) {
98e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                case REBIND:
99e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    if (!callback.onPreBind(sender)) {
100e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                        sender.mRebindHalted = true;
101e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    }
102e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    break;
103e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                case HALTED:
104e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    callback.onCanceled(sender);
105e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    break;
106e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                case REBOUND:
107e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    callback.onBound(sender);
108e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    break;
109e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            }
110e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        }
111e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    };
112e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
1135914aa7da50a90a4c705b5be02a215499d0ad232George Mount    private static final OnAttachStateChangeListener ROOT_REATTACHED_LISTENER;
1145914aa7da50a90a4c705b5be02a215499d0ad232George Mount
1155914aa7da50a90a4c705b5be02a215499d0ad232George Mount    static {
1165914aa7da50a90a4c705b5be02a215499d0ad232George Mount        if (VERSION.SDK_INT < VERSION_CODES.KITKAT) {
1175914aa7da50a90a4c705b5be02a215499d0ad232George Mount            ROOT_REATTACHED_LISTENER = null;
1185914aa7da50a90a4c705b5be02a215499d0ad232George Mount        } else {
1195914aa7da50a90a4c705b5be02a215499d0ad232George Mount            ROOT_REATTACHED_LISTENER = new OnAttachStateChangeListener() {
1205914aa7da50a90a4c705b5be02a215499d0ad232George Mount                @TargetApi(VERSION_CODES.KITKAT)
1215914aa7da50a90a4c705b5be02a215499d0ad232George Mount                @Override
1225914aa7da50a90a4c705b5be02a215499d0ad232George Mount                public void onViewAttachedToWindow(View v) {
1235914aa7da50a90a4c705b5be02a215499d0ad232George Mount                    // execute the pending bindings.
1244d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount                    final ViewDataBinding binding = getBinding(v);
1253f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar                    binding.mRebindRunnable.run();
1265914aa7da50a90a4c705b5be02a215499d0ad232George Mount                    v.removeOnAttachStateChangeListener(this);
1275914aa7da50a90a4c705b5be02a215499d0ad232George Mount                }
1285914aa7da50a90a4c705b5be02a215499d0ad232George Mount
1295914aa7da50a90a4c705b5be02a215499d0ad232George Mount                @Override
1305914aa7da50a90a4c705b5be02a215499d0ad232George Mount                public void onViewDetachedFromWindow(View v) {
1315914aa7da50a90a4c705b5be02a215499d0ad232George Mount                }
1325914aa7da50a90a4c705b5be02a215499d0ad232George Mount            };
1335914aa7da50a90a4c705b5be02a215499d0ad232George Mount        }
1345914aa7da50a90a4c705b5be02a215499d0ad232George Mount    }
1355914aa7da50a90a4c705b5be02a215499d0ad232George Mount
1364c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
1374c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Runnable executed on animation heartbeat to rebind the dirty Views.
1384c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
1393f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar    private final Runnable mRebindRunnable = new Runnable() {
140085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        @Override
141085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        public void run() {
142e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            synchronized (this) {
143e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                mPendingRebind = false;
144e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            }
145e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
146e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                // Nested so that we don't get a lint warning in IntelliJ
147e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                if (!mRoot.isAttachedToWindow()) {
148e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    // Don't execute the pending bindings until the View
149e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    // is attached again.
150e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    mRoot.removeOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
151e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    mRoot.addOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
152e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    return;
1535914aa7da50a90a4c705b5be02a215499d0ad232George Mount                }
1544c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount            }
155e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            executePendingBindings();
156085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
157085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    };
158085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
1594c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
1604c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Flag indicates that there are pending bindings that need to be reevaluated.
1614c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
1624c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    private boolean mPendingRebind = false;
1634c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
1644c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
165e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * Indicates that a onPreBind has stopped the executePendingBindings call.
166e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     */
167e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    private boolean mRebindHalted = false;
168e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
169e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    /**
1704c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * The observed expressions.
1714c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
1724c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    private WeakListener[] mLocalFieldObservers;
1734c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
1744c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
1754c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * The root View that this Binding is associated with.
1764c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
1774c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    private final View mRoot;
1784c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
179e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    /**
180e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * The collection of OnRebindCallbacks.
181e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     */
182e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    private CallbackRegistry<OnRebindCallback, ViewDataBinding, Void> mRebindCallbacks;
183e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
184e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    /**
185e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * Flag to prevent reentrant executePendingBinding calls.
186e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     */
187e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    private boolean mIsExecutingPendingBindings;
188e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
1893f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar    // null api < 16
1903f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar    private Choreographer mChoreographer;
1913f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar
1923f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar    private final Choreographer.FrameCallback mFrameCallback;
1933f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar
1943f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar    // null api >= 16
1953f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar    private Handler mUIThreadHandler;
1963f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar
1974c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    protected ViewDataBinding(View root, int localFieldCount) {
1985cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        mLocalFieldObservers = new WeakListener[localFieldCount];
1994c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        this.mRoot = root;
2003f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar        if (Looper.myLooper() == null) {
2013f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            throw new IllegalStateException("DataBinding must be created in view's UI Thread");
2023f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar        }
2033f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar        if (USE_CHOREOGRAPHER) {
2043f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            mChoreographer = Choreographer.getInstance();
2053f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            mFrameCallback = new Choreographer.FrameCallback() {
2063f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar                @Override
2073f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar                public void doFrame(long frameTimeNanos) {
2083f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar                    mRebindRunnable.run();
2093f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar                }
2103f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            };
2113f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar        } else {
2123f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            mFrameCallback = null;
2133f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            mUIThreadHandler = new Handler(Looper.myLooper());
2143f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar        }
21596e1c821dd446d1ed78f8ae61131550588f60a24George Mount    }
21696e1c821dd446d1ed78f8ae61131550588f60a24George Mount
21796e1c821dd446d1ed78f8ae61131550588f60a24George Mount    protected void setRootTag(View view) {
21896e1c821dd446d1ed78f8ae61131550588f60a24George Mount        if (USE_TAG_ID) {
21996e1c821dd446d1ed78f8ae61131550588f60a24George Mount            view.setTag(R.id.dataBinding, this);
22096e1c821dd446d1ed78f8ae61131550588f60a24George Mount        } else {
22196e1c821dd446d1ed78f8ae61131550588f60a24George Mount            view.setTag(this);
22296e1c821dd446d1ed78f8ae61131550588f60a24George Mount        }
22396e1c821dd446d1ed78f8ae61131550588f60a24George Mount    }
22496e1c821dd446d1ed78f8ae61131550588f60a24George Mount
22596e1c821dd446d1ed78f8ae61131550588f60a24George Mount    protected void setRootTag(View[] views) {
226e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount        if (USE_TAG_ID) {
22796e1c821dd446d1ed78f8ae61131550588f60a24George Mount            for (View view : views) {
22896e1c821dd446d1ed78f8ae61131550588f60a24George Mount                view.setTag(R.id.dataBinding, this);
22996e1c821dd446d1ed78f8ae61131550588f60a24George Mount            }
230e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount        } else {
23196e1c821dd446d1ed78f8ae61131550588f60a24George Mount            for (View view : views) {
23296e1c821dd446d1ed78f8ae61131550588f60a24George Mount                view.setTag(this);
23396e1c821dd446d1ed78f8ae61131550588f60a24George Mount            }
234e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount        }
2355cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
2365cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
2374c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    public static int getBuildSdkInt() {
2384c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        return SDK_INT;
2394c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    }
2404c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
2414c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
2424c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Called when an observed object changes. Sets the appropriate dirty flag if applicable.
2434c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * @param localFieldId The index into mLocalFieldObservers that this Object resides in.
2444c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * @param object The object that has changed.
245fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount     * @param fieldId The BR ID of the field being changed or _all if
2464c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     *                no specific field is being notified.
2474c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * @return true if this change should cause a change to the UI.
2484c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
2494c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    protected abstract boolean onFieldChange(int localFieldId, Object object, int fieldId);
2504c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
251e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    /**
252e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * Set a value value in the Binding class.
253e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * <p>
254e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * Typically, the developer will be able to call the subclass's set method directly. For
255e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * example, if there is a variable <code>x</code> in the Binding, a <code>setX</code> method
256e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * will be generated. However, there are times when the specific subclass of ViewDataBinding
257e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * is unknown, so the generated method cannot be discovered without reflection. The
258e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * setVariable call allows the values of variables to be set without reflection.
259e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     *
260e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * @param variableId the BR id of the variable to be set. For example, if the variable is
261e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     *                   <code>x</code>, then variableId will be <code>BR.x</code>.
262e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * @param value The new value of the variable to be set.
263e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * @return <code>true</code> if the variable exists in the binding or <code>false</code>
264e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * otherwise.
265e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     */
266e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    public abstract boolean setVariable(int variableId, Object value);
267e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
268e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    /**
269e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * Add a listener to be called when reevaluating dirty fields. This also allows automatic
270e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * updates to be halted, but does not stop explicit calls to {@link #executePendingBindings()}.
271e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     *
272e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * @param listener The listener to add.
273e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     */
274e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    public void addOnRebindCallback(OnRebindCallback listener) {
275e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        if (mRebindCallbacks == null) {
276e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            mRebindCallbacks = new CallbackRegistry<OnRebindCallback, ViewDataBinding, Void>(REBIND_NOTIFIER);
277e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        }
278e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        mRebindCallbacks.add(listener);
279e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    }
280e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
281e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    /**
282e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * Removes a listener that was added in {@link #addOnRebindCallback(OnRebindCallback)}.
283e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     *
284e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * @param listener The listener to remove.
285e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     */
286e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    public void removeOnRebindCallback(OnRebindCallback listener) {
287e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        if (mRebindCallbacks != null) {
288e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            mRebindCallbacks.remove(listener);
289e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        }
290e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    }
2914c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
2924c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
2934c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Evaluates the pending bindings, updating any Views that have expressions bound to
2944c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * modified variables. This <b>must</b> be run on the UI thread.
2954c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
296e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    public void executePendingBindings() {
297e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        if (mIsExecutingPendingBindings) {
298e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            requestRebind();
299e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            return;
300e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        }
301447971abca811b11b8c1d8e7bfaa294856d03c16George Mount        if (!hasPendingBindings()) {
302447971abca811b11b8c1d8e7bfaa294856d03c16George Mount            return;
303447971abca811b11b8c1d8e7bfaa294856d03c16George Mount        }
304e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        mIsExecutingPendingBindings = true;
305e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        mRebindHalted = false;
306e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        if (mRebindCallbacks != null) {
307e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            mRebindCallbacks.notifyCallbacks(this, REBIND, null);
308e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
309e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            // The onRebindListeners will change mPendingHalted
310e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            if (mRebindHalted) {
311e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                mRebindCallbacks.notifyCallbacks(this, HALTED, null);
312e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            }
313e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        }
314e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        if (!mRebindHalted) {
315e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            executeBindings();
316e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            if (mRebindCallbacks != null) {
317e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                mRebindCallbacks.notifyCallbacks(this, REBOUND, null);
318e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            }
319e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        }
320e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        mIsExecutingPendingBindings = false;
321e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    }
322e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
323e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    void forceExecuteBindings() {
324e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        executeBindings();
325e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    }
326e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
327e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    protected abstract void executeBindings();
3284c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
3294c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
3302f64c44e4fa296cf658ca986c095eab62f82a31dYigit Boyar     * Invalidates all binding expressions and requests a new rebind to refresh UI.
3314c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
3324c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    public abstract void invalidateAll();
3334c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
3344c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
3352f64c44e4fa296cf658ca986c095eab62f82a31dYigit Boyar     * Returns whether the UI needs to be refresh to represent the current data.
3362f64c44e4fa296cf658ca986c095eab62f82a31dYigit Boyar     *
337447971abca811b11b8c1d8e7bfaa294856d03c16George Mount     * @return true if any field has changed and the binding should be evaluated.
338447971abca811b11b8c1d8e7bfaa294856d03c16George Mount     */
339447971abca811b11b8c1d8e7bfaa294856d03c16George Mount    public abstract boolean hasPendingBindings();
340447971abca811b11b8c1d8e7bfaa294856d03c16George Mount
341447971abca811b11b8c1d8e7bfaa294856d03c16George Mount    /**
3424c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Removes binding listeners to expression variables.
3434c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
3444c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    public void unbind() {
3455cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        for (WeakListener weakListener : mLocalFieldObservers) {
3465cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (weakListener != null) {
3475cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                weakListener.unregister();
3485cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            }
3495cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
350085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
351085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
3524c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    @Override
3534c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    protected void finalize() throws Throwable {
3544c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        unbind();
3554c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    }
3564c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
357ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount    static ViewDataBinding getBinding(View v) {
3584d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount        if (v != null) {
3594d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount            if (USE_TAG_ID) {
3604d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount                return (ViewDataBinding) v.getTag(R.id.dataBinding);
3614d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount            } else {
3624d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount                final Object tag = v.getTag();
3634d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount                if (tag instanceof ViewDataBinding) {
3644d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount                    return (ViewDataBinding) tag;
3654d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount                }
366ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount            }
367ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount        }
368ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount        return null;
369ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount    }
370ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount
3714c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
3724c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Returns the outermost View in the layout file associated with the Binding.
3734c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * @return the outermost View in the layout file associated with the Binding.
3744c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
375085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    public View getRoot() {
376085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        return mRoot;
377085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
378085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
379085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
380085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        boolean result = onFieldChange(mLocalFieldId, object, fieldId);
381085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (result) {
382085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            requestRebind();
383085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
384085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
385085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
386085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    protected boolean unregisterFrom(int localFieldId) {
3875cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        WeakListener listener = mLocalFieldObservers[localFieldId];
388085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (listener != null) {
389085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return listener.unregister();
390085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
391085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        return false;
392085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
393085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
394085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    protected void requestRebind() {
395e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        synchronized (this) {
396e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            if (mPendingRebind) {
397e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                return;
398e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            }
399e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            mPendingRebind = true;
400085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
4013f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar        if (USE_CHOREOGRAPHER) {
4023f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            mChoreographer.postFrameCallback(mFrameCallback);
4034c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        } else {
4043f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            mUIThreadHandler.post(mRebindRunnable);
4054c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        }
4063f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar
407085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
408085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
409085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    protected Object getObservedField(int localFieldId) {
4105cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        WeakListener listener = mLocalFieldObservers[localFieldId];
411085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (listener == null) {
412085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return null;
413085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
414085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        return listener.getTarget();
415085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
416085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
4175cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    private boolean updateRegistration(int localFieldId, Object observable,
4185cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            CreateWeakListener listenerCreator) {
419085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (observable == null) {
420085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return unregisterFrom(localFieldId);
421085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
4225cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        WeakListener listener = mLocalFieldObservers[localFieldId];
423085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (listener == null) {
4245cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            registerTo(localFieldId, observable, listenerCreator);
425085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return true;
426085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
427085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (listener.getTarget() == observable) {
428085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return false;//nothing to do, same object
429085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
430085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        unregisterFrom(localFieldId);
4315cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        registerTo(localFieldId, observable, listenerCreator);
432085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        return true;
433085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
434085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
4355cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    protected boolean updateRegistration(int localFieldId, Observable observable) {
4365cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
4375cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
4385cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
4395cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    protected boolean updateRegistration(int localFieldId, ObservableList observable) {
4405cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        return updateRegistration(localFieldId, observable, CREATE_LIST_LISTENER);
4415cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
4425cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
4435cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    protected boolean updateRegistration(int localFieldId, ObservableMap observable) {
4445cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        return updateRegistration(localFieldId, observable, CREATE_MAP_LISTENER);
4455cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
4465cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
4475cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    protected void registerTo(int localFieldId, Object observable,
4485cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            CreateWeakListener listenerCreator) {
449085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (observable == null) {
450085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return;
451085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
4525cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        WeakListener listener = mLocalFieldObservers[localFieldId];
453085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (listener == null) {
4545cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            listener = listenerCreator.create(this, localFieldId);
455085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            mLocalFieldObservers[localFieldId] = listener;
456085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
457085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        listener.setTarget(observable);
458085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
459085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
460ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount    protected static ViewDataBinding bind(View view, int layoutId) {
461ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount        return DataBindingUtil.bind(view, layoutId);
462ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount    }
463ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount
4644c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
46596e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * Walks the view hierarchy under root and pulls out tagged Views, includes, and views with
46696e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * IDs into an Object[] that is returned. This is used to walk the view hierarchy once to find
46796e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * all bound and ID'd views.
46896e1c821dd446d1ed78f8ae61131550588f60a24George Mount     *
46996e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param root The root of the view hierarchy to walk.
47096e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param numBindings The total number of ID'd views, views with expressions, and includes
47196e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param includes The include layout information, indexed by their container's index.
47296e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param viewsWithIds Indexes of views that don't have tags, but have IDs.
47396e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @return An array of size numBindings containing all Views in the hierarchy that have IDs
47496e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * (with elements in viewsWithIds), are tagged containing expressions, or the bindings for
47596e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * included layouts.
47696e1c821dd446d1ed78f8ae61131550588f60a24George Mount     */
47796e1c821dd446d1ed78f8ae61131550588f60a24George Mount    protected static Object[] mapBindings(View root, int numBindings,
47896e1c821dd446d1ed78f8ae61131550588f60a24George Mount            IncludedLayoutIndex[][] includes, SparseIntArray viewsWithIds) {
47996e1c821dd446d1ed78f8ae61131550588f60a24George Mount        Object[] bindings = new Object[numBindings];
48096e1c821dd446d1ed78f8ae61131550588f60a24George Mount        mapBindings(root, bindings, includes, viewsWithIds, true);
48196e1c821dd446d1ed78f8ae61131550588f60a24George Mount        return bindings;
48296e1c821dd446d1ed78f8ae61131550588f60a24George Mount    }
48396e1c821dd446d1ed78f8ae61131550588f60a24George Mount
48496e1c821dd446d1ed78f8ae61131550588f60a24George Mount    /**
48596e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * Walks the view hierarchy under roots and pulls out tagged Views, includes, and views with
48696e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * IDs into an Object[] that is returned. This is used to walk the view hierarchy once to find
48796e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * all bound and ID'd views.
4884c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     *
48996e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param roots The root Views of the view hierarchy to walk. This is used with merge tags.
49096e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param numBindings The total number of ID'd views, views with expressions, and includes
49196e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param includes The include layout information, indexed by their container's index.
49296e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param viewsWithIds Indexes of views that don't have tags, but have IDs.
49396e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @return An array of size numBindings containing all Views in the hierarchy that have IDs
49496e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * (with elements in viewsWithIds), are tagged containing expressions, or the bindings for
49596e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * included layouts.
4964c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
49796e1c821dd446d1ed78f8ae61131550588f60a24George Mount    protected static Object[] mapBindings(View[] roots, int numBindings,
49896e1c821dd446d1ed78f8ae61131550588f60a24George Mount            IncludedLayoutIndex[][] includes, SparseIntArray viewsWithIds) {
49996e1c821dd446d1ed78f8ae61131550588f60a24George Mount        Object[] bindings = new Object[numBindings];
50096e1c821dd446d1ed78f8ae61131550588f60a24George Mount        for (int i = 0; i < roots.length; i++) {
50196e1c821dd446d1ed78f8ae61131550588f60a24George Mount            mapBindings(roots[i], bindings, includes, viewsWithIds, true);
50200da715547ee7d5d38a3b8576090ca427a94daa5George Mount        }
50396e1c821dd446d1ed78f8ae61131550588f60a24George Mount        return bindings;
50400da715547ee7d5d38a3b8576090ca427a94daa5George Mount    }
50500da715547ee7d5d38a3b8576090ca427a94daa5George Mount
50696e1c821dd446d1ed78f8ae61131550588f60a24George Mount    private static void mapBindings(View view, Object[] bindings,
50796e1c821dd446d1ed78f8ae61131550588f60a24George Mount            IncludedLayoutIndex[][] includes, SparseIntArray viewsWithIds, boolean isRoot) {
50896e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final IncludedLayoutIndex[] includedLayoutIndexes;
509d6527ee28cc3aa05818799af8def9593346f91bcGeorge Mount        final ViewDataBinding existingBinding = getBinding(view);
510d6527ee28cc3aa05818799af8def9593346f91bcGeorge Mount        if (existingBinding != null) {
5114d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount            return;
5124d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount        }
51396e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final String tag = (String) view.getTag();
51496e1c821dd446d1ed78f8ae61131550588f60a24George Mount        boolean isBound = false;
51596e1c821dd446d1ed78f8ae61131550588f60a24George Mount        if (isRoot && tag != null && tag.startsWith("layout")) {
51696e1c821dd446d1ed78f8ae61131550588f60a24George Mount            final int underscoreIndex = tag.lastIndexOf('_');
51796e1c821dd446d1ed78f8ae61131550588f60a24George Mount            if (underscoreIndex > 0 && isNumeric(tag, underscoreIndex + 1)) {
51896e1c821dd446d1ed78f8ae61131550588f60a24George Mount                final int index = parseTagInt(tag, underscoreIndex + 1);
51996e1c821dd446d1ed78f8ae61131550588f60a24George Mount                bindings[index] = view;
52096e1c821dd446d1ed78f8ae61131550588f60a24George Mount                includedLayoutIndexes = includes == null ? null : includes[index];
52196e1c821dd446d1ed78f8ae61131550588f60a24George Mount                isBound = true;
52296e1c821dd446d1ed78f8ae61131550588f60a24George Mount            } else {
52396e1c821dd446d1ed78f8ae61131550588f60a24George Mount                includedLayoutIndexes = null;
52496e1c821dd446d1ed78f8ae61131550588f60a24George Mount            }
52596e1c821dd446d1ed78f8ae61131550588f60a24George Mount        } else if (tag != null && tag.startsWith(BINDING_TAG_PREFIX)) {
52696e1c821dd446d1ed78f8ae61131550588f60a24George Mount            int tagIndex = parseTagInt(tag, BINDING_NUMBER_START);
52796e1c821dd446d1ed78f8ae61131550588f60a24George Mount            bindings[tagIndex] = view;
52896e1c821dd446d1ed78f8ae61131550588f60a24George Mount            isBound = true;
52996e1c821dd446d1ed78f8ae61131550588f60a24George Mount            includedLayoutIndexes = includes == null ? null : includes[tagIndex];
53095d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount        } else {
53196e1c821dd446d1ed78f8ae61131550588f60a24George Mount            // Not a bound view
53296e1c821dd446d1ed78f8ae61131550588f60a24George Mount            includedLayoutIndexes = null;
53396e1c821dd446d1ed78f8ae61131550588f60a24George Mount        }
53496e1c821dd446d1ed78f8ae61131550588f60a24George Mount        if (!isBound) {
53596e1c821dd446d1ed78f8ae61131550588f60a24George Mount            final int id = view.getId();
53696e1c821dd446d1ed78f8ae61131550588f60a24George Mount            if (id > 0) {
53796e1c821dd446d1ed78f8ae61131550588f60a24George Mount                int index;
53896e1c821dd446d1ed78f8ae61131550588f60a24George Mount                if (viewsWithIds != null && (index = viewsWithIds.get(id, -1)) >= 0) {
53996e1c821dd446d1ed78f8ae61131550588f60a24George Mount                    bindings[index] = view;
54096e1c821dd446d1ed78f8ae61131550588f60a24George Mount                }
54196e1c821dd446d1ed78f8ae61131550588f60a24George Mount            }
54296e1c821dd446d1ed78f8ae61131550588f60a24George Mount        }
54396e1c821dd446d1ed78f8ae61131550588f60a24George Mount
54496e1c821dd446d1ed78f8ae61131550588f60a24George Mount        if (view instanceof  ViewGroup) {
54596e1c821dd446d1ed78f8ae61131550588f60a24George Mount            final ViewGroup viewGroup = (ViewGroup) view;
54696e1c821dd446d1ed78f8ae61131550588f60a24George Mount            final int count = viewGroup.getChildCount();
54796e1c821dd446d1ed78f8ae61131550588f60a24George Mount            int minInclude = 0;
54896e1c821dd446d1ed78f8ae61131550588f60a24George Mount            for (int i = 0; i < count; i++) {
54996e1c821dd446d1ed78f8ae61131550588f60a24George Mount                final View child = viewGroup.getChildAt(i);
55096e1c821dd446d1ed78f8ae61131550588f60a24George Mount                boolean isInclude = false;
55196e1c821dd446d1ed78f8ae61131550588f60a24George Mount                if (includedLayoutIndexes != null) {
55296e1c821dd446d1ed78f8ae61131550588f60a24George Mount                    String childTag = (String) child.getTag();
55396e1c821dd446d1ed78f8ae61131550588f60a24George Mount                    if (childTag != null && childTag.endsWith("_0") &&
55496e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            childTag.startsWith("layout") && childTag.indexOf('/') > 0) {
55596e1c821dd446d1ed78f8ae61131550588f60a24George Mount                        // This *could* be an include. Test against the expected includes.
55696e1c821dd446d1ed78f8ae61131550588f60a24George Mount                        int includeIndex = findIncludeIndex(childTag, minInclude,
55796e1c821dd446d1ed78f8ae61131550588f60a24George Mount                                includedLayoutIndexes);
55896e1c821dd446d1ed78f8ae61131550588f60a24George Mount                        if (includeIndex >= 0) {
55996e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            isInclude = true;
56096e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            minInclude = includeIndex + 1;
56196e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            IncludedLayoutIndex include = includedLayoutIndexes[includeIndex];
56296e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            int lastMatchingIndex = findLastMatching(viewGroup, i);
56396e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            if (lastMatchingIndex == i) {
5649bdb2415487832e88a05c7bd19391b05440b468eGeorge Mount                                bindings[include.index] = DataBindingUtil.bind(child,
5659bdb2415487832e88a05c7bd19391b05440b468eGeorge Mount                                        include.layoutId);
56696e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            } else {
56796e1c821dd446d1ed78f8ae61131550588f60a24George Mount                                final int includeCount =  lastMatchingIndex - i + 1;
56896e1c821dd446d1ed78f8ae61131550588f60a24George Mount                                final View[] included = new View[includeCount];
56996e1c821dd446d1ed78f8ae61131550588f60a24George Mount                                for (int j = 0; j < includeCount; j++) {
57096e1c821dd446d1ed78f8ae61131550588f60a24George Mount                                    included[j] = viewGroup.getChildAt(i + j);
57196e1c821dd446d1ed78f8ae61131550588f60a24George Mount                                }
5729bdb2415487832e88a05c7bd19391b05440b468eGeorge Mount                                bindings[include.index] = DataBindingUtil.bind(included,
5739bdb2415487832e88a05c7bd19391b05440b468eGeorge Mount                                        include.layoutId);
57496e1c821dd446d1ed78f8ae61131550588f60a24George Mount                                i += includeCount - 1;
57596e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            }
57696e1c821dd446d1ed78f8ae61131550588f60a24George Mount                        }
57796e1c821dd446d1ed78f8ae61131550588f60a24George Mount                    }
57896e1c821dd446d1ed78f8ae61131550588f60a24George Mount                }
57996e1c821dd446d1ed78f8ae61131550588f60a24George Mount                if (!isInclude) {
58096e1c821dd446d1ed78f8ae61131550588f60a24George Mount                    mapBindings(child, bindings, includes, viewsWithIds, false);
58196e1c821dd446d1ed78f8ae61131550588f60a24George Mount                }
58296e1c821dd446d1ed78f8ae61131550588f60a24George Mount            }
5834c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        }
58496e1c821dd446d1ed78f8ae61131550588f60a24George Mount    }
58596e1c821dd446d1ed78f8ae61131550588f60a24George Mount
58696e1c821dd446d1ed78f8ae61131550588f60a24George Mount    private static int findIncludeIndex(String tag, int minInclude,
58796e1c821dd446d1ed78f8ae61131550588f60a24George Mount            IncludedLayoutIndex[] layoutIndexes) {
58896e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final int slashIndex = tag.indexOf('/');
58996e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final CharSequence layoutName = tag.subSequence(slashIndex + 1, tag.length() - 2);
59096e1c821dd446d1ed78f8ae61131550588f60a24George Mount
59196e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final int length = layoutIndexes.length;
59296e1c821dd446d1ed78f8ae61131550588f60a24George Mount        for (int i = minInclude; i < length; i++) {
59396e1c821dd446d1ed78f8ae61131550588f60a24George Mount            final IncludedLayoutIndex layoutIndex = layoutIndexes[i];
59496e1c821dd446d1ed78f8ae61131550588f60a24George Mount            if (TextUtils.equals(layoutName, layoutIndex.layout)) {
59596e1c821dd446d1ed78f8ae61131550588f60a24George Mount                return i;
59696e1c821dd446d1ed78f8ae61131550588f60a24George Mount            }
59700da715547ee7d5d38a3b8576090ca427a94daa5George Mount        }
59896e1c821dd446d1ed78f8ae61131550588f60a24George Mount        return -1;
59900da715547ee7d5d38a3b8576090ca427a94daa5George Mount    }
60000da715547ee7d5d38a3b8576090ca427a94daa5George Mount
60196e1c821dd446d1ed78f8ae61131550588f60a24George Mount    private static int findLastMatching(ViewGroup viewGroup, int firstIncludedIndex) {
60296e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final View firstView = viewGroup.getChildAt(firstIncludedIndex);
60396e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final String firstViewTag = (String) firstView.getTag();
60496e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final String tagBase = firstViewTag.substring(0, firstViewTag.length() - 1); // don't include the "0"
60596e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final int tagSequenceIndex = tagBase.length();
60696e1c821dd446d1ed78f8ae61131550588f60a24George Mount
60796e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final int count = viewGroup.getChildCount();
60896e1c821dd446d1ed78f8ae61131550588f60a24George Mount        int max = firstIncludedIndex;
60996e1c821dd446d1ed78f8ae61131550588f60a24George Mount        for (int i = firstIncludedIndex + 1; i < count; i++) {
61096e1c821dd446d1ed78f8ae61131550588f60a24George Mount            final View view = viewGroup.getChildAt(i);
61196e1c821dd446d1ed78f8ae61131550588f60a24George Mount            final String tag = (String) view.getTag();
61296e1c821dd446d1ed78f8ae61131550588f60a24George Mount            if (tag != null && tag.startsWith(tagBase)) {
61396e1c821dd446d1ed78f8ae61131550588f60a24George Mount                if (tag.length() == firstViewTag.length() && tag.charAt(tag.length() - 1) == '0') {
61496e1c821dd446d1ed78f8ae61131550588f60a24George Mount                    return max; // Found another instance of the include
61596e1c821dd446d1ed78f8ae61131550588f60a24George Mount                }
61696e1c821dd446d1ed78f8ae61131550588f60a24George Mount                if (isNumeric(tag, tagSequenceIndex)) {
61796e1c821dd446d1ed78f8ae61131550588f60a24George Mount                    max = i;
61896e1c821dd446d1ed78f8ae61131550588f60a24George Mount                }
61996e1c821dd446d1ed78f8ae61131550588f60a24George Mount            }
62096e1c821dd446d1ed78f8ae61131550588f60a24George Mount        }
62196e1c821dd446d1ed78f8ae61131550588f60a24George Mount        return max;
62234a18e6a231f3b64726bd93e7e097a0d5a75995dGeorge Mount    }
62334a18e6a231f3b64726bd93e7e097a0d5a75995dGeorge Mount
62496e1c821dd446d1ed78f8ae61131550588f60a24George Mount    private static boolean isNumeric(String tag, int startIndex) {
62596e1c821dd446d1ed78f8ae61131550588f60a24George Mount        int length = tag.length();
62696e1c821dd446d1ed78f8ae61131550588f60a24George Mount        if (length == startIndex) {
62796e1c821dd446d1ed78f8ae61131550588f60a24George Mount            return false; // no numerals
62896e1c821dd446d1ed78f8ae61131550588f60a24George Mount        }
62996e1c821dd446d1ed78f8ae61131550588f60a24George Mount        for (int i = startIndex; i < length; i++) {
63096e1c821dd446d1ed78f8ae61131550588f60a24George Mount            if (!Character.isDigit(tag.charAt(i))) {
63196e1c821dd446d1ed78f8ae61131550588f60a24George Mount                return false;
63295d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount            }
63395d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount        }
63496e1c821dd446d1ed78f8ae61131550588f60a24George Mount        return true;
63595d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount    }
63695d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount
63734a18e6a231f3b64726bd93e7e097a0d5a75995dGeorge Mount    /**
63800da715547ee7d5d38a3b8576090ca427a94daa5George Mount     * Parse the tag without creating a new String object. This is fast and assumes the
63900da715547ee7d5d38a3b8576090ca427a94daa5George Mount     * tag is in the correct format.
64000da715547ee7d5d38a3b8576090ca427a94daa5George Mount     * @param str The tag string.
64100da715547ee7d5d38a3b8576090ca427a94daa5George Mount     * @return The binding tag number parsed from the tag string.
64200da715547ee7d5d38a3b8576090ca427a94daa5George Mount     */
64396e1c821dd446d1ed78f8ae61131550588f60a24George Mount    private static int parseTagInt(String str, int startIndex) {
64400da715547ee7d5d38a3b8576090ca427a94daa5George Mount        final int end = str.length();
64500da715547ee7d5d38a3b8576090ca427a94daa5George Mount        int val = 0;
64696e1c821dd446d1ed78f8ae61131550588f60a24George Mount        for (int i = startIndex; i < end; i++) {
64700da715547ee7d5d38a3b8576090ca427a94daa5George Mount            val *= 10;
64800da715547ee7d5d38a3b8576090ca427a94daa5George Mount            char c = str.charAt(i);
64900da715547ee7d5d38a3b8576090ca427a94daa5George Mount            val += (c - '0');
65000da715547ee7d5d38a3b8576090ca427a94daa5George Mount        }
65100da715547ee7d5d38a3b8576090ca427a94daa5George Mount        return val;
65200da715547ee7d5d38a3b8576090ca427a94daa5George Mount    }
65300da715547ee7d5d38a3b8576090ca427a94daa5George Mount
654722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount    private interface ObservableReference<T> {
655722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        WeakListener<T> getListener();
656722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        void addListener(T target);
657722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        void removeListener(T target);
658722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount    }
659722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount
660722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount    private static class WeakListener<T> extends WeakReference<ViewDataBinding> {
661722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        private final ObservableReference<T> mObservable;
6625cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        protected final int mLocalFieldId;
6635cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        private T mTarget;
664085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
665722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public WeakListener(ViewDataBinding binder, int localFieldId,
666722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount                ObservableReference<T> observable) {
667722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            super(binder);
6685cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            mLocalFieldId = localFieldId;
669722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            mObservable = observable;
670085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
671085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
6725cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        public void setTarget(T object) {
6735cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            unregister();
6745cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            mTarget = object;
6755cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (mTarget != null) {
676722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount                mObservable.addListener(mTarget);
677085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            }
678085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
679085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
680085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        public boolean unregister() {
6815cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            boolean unregistered = false;
6825cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (mTarget != null) {
683722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount                mObservable.removeListener(mTarget);
6845cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                unregistered = true;
685085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            }
686085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            mTarget = null;
6875cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            return unregistered;
688085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
689085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
6905cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        public T getTarget() {
6915cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            return mTarget;
692085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
6935cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
6944c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        protected ViewDataBinding getBinder() {
695722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            ViewDataBinding binder = get();
6965cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (binder == null) {
6975cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                unregister(); // The binder is dead
6985cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            }
6995cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            return binder;
7005cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
701085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
702085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
703722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount    private static class WeakPropertyListener extends Observable.OnPropertyChangedCallback
704722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            implements ObservableReference<Observable> {
705722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        final WeakListener<Observable> mListener;
706722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount
7074c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {
708722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            mListener = new WeakListener<Observable>(binder, localFieldId, this);
7095cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7105cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7115cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
712722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public WeakListener<Observable> getListener() {
713722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            return mListener;
7145cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7155cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7165cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
717722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void addListener(Observable target) {
718722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            target.addOnPropertyChangedCallback(this);
719085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
720085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
721085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        @Override
722722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void removeListener(Observable target) {
723722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            target.removeOnPropertyChangedCallback(this);
724722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        }
725722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount
726722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        @Override
727722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void onPropertyChanged(Observable sender, int propertyId) {
728722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            ViewDataBinding binder = mListener.getBinder();
7295cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (binder == null) {
7305cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                return;
7315cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            }
732722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            Observable obj = mListener.getTarget();
7335cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (obj != sender) {
7345cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                return; // notification from the wrong object?
735085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            }
736722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId);
737085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
738085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
7395cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
740722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount    private static class WeakListListener extends ObservableList.OnListChangedCallback
741722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            implements ObservableReference<ObservableList> {
742722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        final WeakListener<ObservableList> mListener;
7435cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7444c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        public WeakListListener(ViewDataBinding binder, int localFieldId) {
745722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            mListener = new WeakListener<ObservableList>(binder, localFieldId, this);
7465cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7475cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7485cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
749722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public WeakListener<ObservableList> getListener() {
750722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            return mListener;
7515cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7525cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7535cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
754722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void addListener(ObservableList target) {
755722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            target.addOnListChangedCallback(this);
7565cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7575cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7585cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
759722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void removeListener(ObservableList target) {
760722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            target.removeOnListChangedCallback(this);
7615cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7625cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7635cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
764722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void onChanged(ObservableList sender) {
765722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            ViewDataBinding binder = mListener.getBinder();
766722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            if (binder == null) {
767722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount                return;
768722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            }
769722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            ObservableList target = mListener.getTarget();
770722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            if (target != sender) {
771722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount                return; // We expect notifications only from sender
772722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            }
773722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            binder.handleFieldChange(mListener.mLocalFieldId, target, 0);
7745cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7755cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7765cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
777722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void onItemRangeChanged(ObservableList sender, int positionStart, int itemCount) {
778722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            onChanged(sender);
7795cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7805cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7815cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
782722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void onItemRangeInserted(ObservableList sender, int positionStart, int itemCount) {
783722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            onChanged(sender);
7845cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7855cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7865cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
787722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void onItemRangeMoved(ObservableList sender, int fromPosition, int toPosition,
788722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount                int itemCount) {
789722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            onChanged(sender);
790722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        }
791722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount
792722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        @Override
793722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void onItemRangeRemoved(ObservableList sender, int positionStart, int itemCount) {
794722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            onChanged(sender);
7955cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7965cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
7975cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
798722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount    private static class WeakMapListener extends ObservableMap.OnMapChangedCallback
799722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            implements ObservableReference<ObservableMap> {
800722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        final WeakListener<ObservableMap> mListener;
801722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount
8024c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        public WeakMapListener(ViewDataBinding binder, int localFieldId) {
803722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            mListener = new WeakListener<ObservableMap>(binder, localFieldId, this);
804722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        }
805722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount
806722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        @Override
807722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public WeakListener<ObservableMap> getListener() {
808722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            return mListener;
8095cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
8105cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
8115cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
812722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void addListener(ObservableMap target) {
813722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            target.addOnMapChangedCallback(this);
8145cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
8155cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
8165cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
817722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void removeListener(ObservableMap target) {
818722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            target.removeOnMapChangedCallback(this);
8195cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
8205cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
8215cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
8222c86cdbaf189e2b1774af7f64a2974de9321673fGeorge Mount        public void onMapChanged(ObservableMap sender, Object key) {
823722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            ViewDataBinding binder = mListener.getBinder();
824722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            if (binder == null || sender != mListener.getTarget()) {
8255cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                return;
8265cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            }
827722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            binder.handleFieldChange(mListener.mLocalFieldId, sender, 0);
8285cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
8295cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
8305cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
8315cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    private interface CreateWeakListener {
8324c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        WeakListener create(ViewDataBinding viewDataBinding, int localFieldId);
8335cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
83496e1c821dd446d1ed78f8ae61131550588f60a24George Mount
83596e1c821dd446d1ed78f8ae61131550588f60a24George Mount    protected static class IncludedLayoutIndex {
83696e1c821dd446d1ed78f8ae61131550588f60a24George Mount        public final String layout;
83796e1c821dd446d1ed78f8ae61131550588f60a24George Mount        public final int index;
83896e1c821dd446d1ed78f8ae61131550588f60a24George Mount        public final int layoutId;
83996e1c821dd446d1ed78f8ae61131550588f60a24George Mount
84096e1c821dd446d1ed78f8ae61131550588f60a24George Mount        public IncludedLayoutIndex(String layout, int index, int layoutId) {
84196e1c821dd446d1ed78f8ae61131550588f60a24George Mount            this.layout = layout;
84296e1c821dd446d1ed78f8ae61131550588f60a24George Mount            this.index = index;
84396e1c821dd446d1ed78f8ae61131550588f60a24George Mount            this.layoutId = layoutId;
84496e1c821dd446d1ed78f8ae61131550588f60a24George Mount        }
84596e1c821dd446d1ed78f8ae61131550588f60a24George Mount    }
846085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar}
847