ViewDataBinding.java revision 3f73e3149732132c02f5d19aab7b5da429794058
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) {
684c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount            return new WeakPropertyListener(viewDataBinding, localFieldId);
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) {
784c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount            return new WeakListListener(viewDataBinding, localFieldId);
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) {
884c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount            return new WeakMapListener(viewDataBinding, localFieldId);
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.
124e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount                    final ViewDataBinding binding;
125e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount                    if (USE_TAG_ID) {
126e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount                        binding = (ViewDataBinding) v.getTag(R.id.dataBinding);
127e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount                    } else {
128e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount                        binding = (ViewDataBinding) v.getTag();
129e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount                    }
1303f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar                    binding.mRebindRunnable.run();
1315914aa7da50a90a4c705b5be02a215499d0ad232George Mount                    v.removeOnAttachStateChangeListener(this);
1325914aa7da50a90a4c705b5be02a215499d0ad232George Mount                }
1335914aa7da50a90a4c705b5be02a215499d0ad232George Mount
1345914aa7da50a90a4c705b5be02a215499d0ad232George Mount                @Override
1355914aa7da50a90a4c705b5be02a215499d0ad232George Mount                public void onViewDetachedFromWindow(View v) {
1365914aa7da50a90a4c705b5be02a215499d0ad232George Mount                }
1375914aa7da50a90a4c705b5be02a215499d0ad232George Mount            };
1385914aa7da50a90a4c705b5be02a215499d0ad232George Mount        }
1395914aa7da50a90a4c705b5be02a215499d0ad232George Mount    }
1405914aa7da50a90a4c705b5be02a215499d0ad232George Mount
1414c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
1424c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Runnable executed on animation heartbeat to rebind the dirty Views.
1434c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
1443f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar    private final Runnable mRebindRunnable = new Runnable() {
145085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        @Override
146085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        public void run() {
147e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            synchronized (this) {
148e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                mPendingRebind = false;
149e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            }
150e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
151e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                // Nested so that we don't get a lint warning in IntelliJ
152e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                if (!mRoot.isAttachedToWindow()) {
153e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    // Don't execute the pending bindings until the View
154e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    // is attached again.
155e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    mRoot.removeOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
156e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    mRoot.addOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
157e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    return;
1585914aa7da50a90a4c705b5be02a215499d0ad232George Mount                }
1594c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount            }
160e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            executePendingBindings();
161085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
162085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    };
163085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
1644c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
1654c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Flag indicates that there are pending bindings that need to be reevaluated.
1664c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
1674c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    private boolean mPendingRebind = false;
1684c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
1694c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
170e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * Indicates that a onPreBind has stopped the executePendingBindings call.
171e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     */
172e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    private boolean mRebindHalted = false;
173e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
174e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    /**
1754c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * The observed expressions.
1764c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
1774c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    private WeakListener[] mLocalFieldObservers;
1784c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
1794c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
1804c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * The root View that this Binding is associated with.
1814c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
1824c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    private final View mRoot;
1834c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
184e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    /**
185e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * The collection of OnRebindCallbacks.
186e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     */
187e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    private CallbackRegistry<OnRebindCallback, ViewDataBinding, Void> mRebindCallbacks;
188e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
189e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    /**
190e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * Flag to prevent reentrant executePendingBinding calls.
191e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     */
192e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    private boolean mIsExecutingPendingBindings;
193e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
1943f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar    // null api < 16
1953f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar    private Choreographer mChoreographer;
1963f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar
1973f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar    private final Choreographer.FrameCallback mFrameCallback;
1983f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar
1993f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar    // null api >= 16
2003f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar    private Handler mUIThreadHandler;
2013f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar
2024c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    protected ViewDataBinding(View root, int localFieldCount) {
2035cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        mLocalFieldObservers = new WeakListener[localFieldCount];
2044c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        this.mRoot = root;
2053f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar        if (Looper.myLooper() == null) {
2063f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            throw new IllegalStateException("DataBinding must be created in view's UI Thread");
2073f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar        }
2083f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar        if (USE_CHOREOGRAPHER) {
2093f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            mChoreographer = Choreographer.getInstance();
2103f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            mFrameCallback = new Choreographer.FrameCallback() {
2113f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar                @Override
2123f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar                public void doFrame(long frameTimeNanos) {
2133f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar                    mRebindRunnable.run();
2143f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar                }
2153f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            };
2163f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar        } else {
2173f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            mFrameCallback = null;
2183f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            mUIThreadHandler = new Handler(Looper.myLooper());
2193f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar        }
2203f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar        requestRebind();
22196e1c821dd446d1ed78f8ae61131550588f60a24George Mount    }
22296e1c821dd446d1ed78f8ae61131550588f60a24George Mount
22396e1c821dd446d1ed78f8ae61131550588f60a24George Mount    protected void setRootTag(View view) {
22496e1c821dd446d1ed78f8ae61131550588f60a24George Mount        if (USE_TAG_ID) {
22596e1c821dd446d1ed78f8ae61131550588f60a24George Mount            view.setTag(R.id.dataBinding, this);
22696e1c821dd446d1ed78f8ae61131550588f60a24George Mount        } else {
22796e1c821dd446d1ed78f8ae61131550588f60a24George Mount            view.setTag(this);
22896e1c821dd446d1ed78f8ae61131550588f60a24George Mount        }
22996e1c821dd446d1ed78f8ae61131550588f60a24George Mount    }
23096e1c821dd446d1ed78f8ae61131550588f60a24George Mount
23196e1c821dd446d1ed78f8ae61131550588f60a24George Mount    protected void setRootTag(View[] views) {
232e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount        if (USE_TAG_ID) {
23396e1c821dd446d1ed78f8ae61131550588f60a24George Mount            for (View view : views) {
23496e1c821dd446d1ed78f8ae61131550588f60a24George Mount                view.setTag(R.id.dataBinding, this);
23596e1c821dd446d1ed78f8ae61131550588f60a24George Mount            }
236e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount        } else {
23796e1c821dd446d1ed78f8ae61131550588f60a24George Mount            for (View view : views) {
23896e1c821dd446d1ed78f8ae61131550588f60a24George Mount                view.setTag(this);
23996e1c821dd446d1ed78f8ae61131550588f60a24George Mount            }
240e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount        }
2415cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
2425cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
2434c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    public static int getBuildSdkInt() {
2444c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        return SDK_INT;
2454c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    }
2464c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
2474c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
2484c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Called when an observed object changes. Sets the appropriate dirty flag if applicable.
2494c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * @param localFieldId The index into mLocalFieldObservers that this Object resides in.
2504c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * @param object The object that has changed.
251fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount     * @param fieldId The BR ID of the field being changed or _all if
2524c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     *                no specific field is being notified.
2534c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * @return true if this change should cause a change to the UI.
2544c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
2554c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    protected abstract boolean onFieldChange(int localFieldId, Object object, int fieldId);
2564c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
257e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    /**
258e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * Set a value value in the Binding class.
259e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * <p>
260e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * Typically, the developer will be able to call the subclass's set method directly. For
261e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * example, if there is a variable <code>x</code> in the Binding, a <code>setX</code> method
262e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * will be generated. However, there are times when the specific subclass of ViewDataBinding
263e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * is unknown, so the generated method cannot be discovered without reflection. The
264e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * setVariable call allows the values of variables to be set without reflection.
265e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     *
266e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * @param variableId the BR id of the variable to be set. For example, if the variable is
267e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     *                   <code>x</code>, then variableId will be <code>BR.x</code>.
268e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * @param value The new value of the variable to be set.
269e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * @return <code>true</code> if the variable exists in the binding or <code>false</code>
270e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * otherwise.
271e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     */
272e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    public abstract boolean setVariable(int variableId, Object value);
273e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
274e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    /**
275e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * Add a listener to be called when reevaluating dirty fields. This also allows automatic
276e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * updates to be halted, but does not stop explicit calls to {@link #executePendingBindings()}.
277e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     *
278e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * @param listener The listener to add.
279e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     */
280e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    public void addOnRebindCallback(OnRebindCallback listener) {
281e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        if (mRebindCallbacks == null) {
282e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            mRebindCallbacks = new CallbackRegistry<OnRebindCallback, ViewDataBinding, Void>(REBIND_NOTIFIER);
283e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        }
284e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        mRebindCallbacks.add(listener);
285e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    }
286e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
287e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    /**
288e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * Removes a listener that was added in {@link #addOnRebindCallback(OnRebindCallback)}.
289e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     *
290e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * @param listener The listener to remove.
291e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     */
292e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    public void removeOnRebindCallback(OnRebindCallback listener) {
293e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        if (mRebindCallbacks != null) {
294e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            mRebindCallbacks.remove(listener);
295e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        }
296e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    }
2974c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
2984c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
2994c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Evaluates the pending bindings, updating any Views that have expressions bound to
3004c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * modified variables. This <b>must</b> be run on the UI thread.
3014c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
302e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    public void executePendingBindings() {
303e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        if (mIsExecutingPendingBindings) {
304e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            requestRebind();
305e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            return;
306e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        }
307447971abca811b11b8c1d8e7bfaa294856d03c16George Mount        if (!hasPendingBindings()) {
308447971abca811b11b8c1d8e7bfaa294856d03c16George Mount            return;
309447971abca811b11b8c1d8e7bfaa294856d03c16George Mount        }
310e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        mIsExecutingPendingBindings = true;
311e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        mRebindHalted = false;
312e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        if (mRebindCallbacks != null) {
313e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            mRebindCallbacks.notifyCallbacks(this, REBIND, null);
314e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
315e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            // The onRebindListeners will change mPendingHalted
316e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            if (mRebindHalted) {
317e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                mRebindCallbacks.notifyCallbacks(this, HALTED, null);
318e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            }
319e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        }
320e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        if (!mRebindHalted) {
321e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            executeBindings();
322e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            if (mRebindCallbacks != null) {
323e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                mRebindCallbacks.notifyCallbacks(this, REBOUND, null);
324e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            }
325e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        }
326e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        mIsExecutingPendingBindings = false;
327e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    }
328e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
329e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    void forceExecuteBindings() {
330e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        executeBindings();
331e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    }
332e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
333e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    protected abstract void executeBindings();
3344c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
3354c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
3364c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Used internally to invalidate flags of included layouts.
337447971abca811b11b8c1d8e7bfaa294856d03c16George Mount     * @hide
3384c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
3394c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    public abstract void invalidateAll();
3404c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
3414c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
342447971abca811b11b8c1d8e7bfaa294856d03c16George Mount     * @return true if any field has changed and the binding should be evaluated.
343447971abca811b11b8c1d8e7bfaa294856d03c16George Mount     * @hide
344447971abca811b11b8c1d8e7bfaa294856d03c16George Mount     */
345447971abca811b11b8c1d8e7bfaa294856d03c16George Mount    public abstract boolean hasPendingBindings();
346447971abca811b11b8c1d8e7bfaa294856d03c16George Mount
347447971abca811b11b8c1d8e7bfaa294856d03c16George Mount    /**
3484c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Removes binding listeners to expression variables.
3494c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
3504c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    public void unbind() {
3515cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        for (WeakListener weakListener : mLocalFieldObservers) {
3525cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (weakListener != null) {
3535cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                weakListener.unregister();
3545cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            }
3555cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
356085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
357085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
3584c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    @Override
3594c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    protected void finalize() throws Throwable {
3604c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        unbind();
3614c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    }
3624c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
363ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount    static ViewDataBinding getBinding(View v) {
364ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount        if (USE_TAG_ID) {
365ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount            return (ViewDataBinding) v.getTag(R.id.dataBinding);
366ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount        } else {
367ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount            final Object tag = v.getTag();
368ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount            if (tag instanceof ViewDataBinding) {
369ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount                return (ViewDataBinding) tag;
370ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount            }
371ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount        }
372ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount        return null;
373ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount    }
374ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount
3754c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
3764c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Returns the outermost View in the layout file associated with the Binding.
3774c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * @return the outermost View in the layout file associated with the Binding.
3784c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
379085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    public View getRoot() {
380085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        return mRoot;
381085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
382085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
383085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
384085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        boolean result = onFieldChange(mLocalFieldId, object, fieldId);
385085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (result) {
386085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            requestRebind();
387085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
388085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
389085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
390085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    protected boolean unregisterFrom(int localFieldId) {
3915cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        WeakListener listener = mLocalFieldObservers[localFieldId];
392085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (listener != null) {
393085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return listener.unregister();
394085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
395085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        return false;
396085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
397085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
398085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    protected void requestRebind() {
399e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        synchronized (this) {
400e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            if (mPendingRebind) {
401e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                return;
402e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            }
403e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            mPendingRebind = true;
404085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
4053f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar        if (USE_CHOREOGRAPHER) {
4063f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            mChoreographer.postFrameCallback(mFrameCallback);
4074c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        } else {
4083f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            mUIThreadHandler.post(mRebindRunnable);
4094c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        }
4103f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar
411085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
412085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
413085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    protected Object getObservedField(int localFieldId) {
4145cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        WeakListener listener = mLocalFieldObservers[localFieldId];
415085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (listener == null) {
416085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return null;
417085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
418085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        return listener.getTarget();
419085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
420085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
4215cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    private boolean updateRegistration(int localFieldId, Object observable,
4225cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            CreateWeakListener listenerCreator) {
423085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (observable == null) {
424085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return unregisterFrom(localFieldId);
425085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
4265cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        WeakListener listener = mLocalFieldObservers[localFieldId];
427085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (listener == null) {
4285cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            registerTo(localFieldId, observable, listenerCreator);
429085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return true;
430085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
431085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (listener.getTarget() == observable) {
432085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return false;//nothing to do, same object
433085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
434085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        unregisterFrom(localFieldId);
4355cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        registerTo(localFieldId, observable, listenerCreator);
436085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        return true;
437085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
438085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
4395cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    protected boolean updateRegistration(int localFieldId, Observable observable) {
4405cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
4415cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
4425cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
4435cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    protected boolean updateRegistration(int localFieldId, ObservableList observable) {
4445cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        return updateRegistration(localFieldId, observable, CREATE_LIST_LISTENER);
4455cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
4465cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
4475cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    protected boolean updateRegistration(int localFieldId, ObservableMap observable) {
4485cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        return updateRegistration(localFieldId, observable, CREATE_MAP_LISTENER);
4495cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
4505cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
4515cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    protected void registerTo(int localFieldId, Object observable,
4525cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            CreateWeakListener listenerCreator) {
453085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (observable == null) {
454085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return;
455085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
4565cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        WeakListener listener = mLocalFieldObservers[localFieldId];
457085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (listener == null) {
4585cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            listener = listenerCreator.create(this, localFieldId);
459085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            mLocalFieldObservers[localFieldId] = listener;
460085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
461085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        listener.setTarget(observable);
462085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
463085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
464ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount    protected static ViewDataBinding bind(View view, int layoutId) {
465ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount        return DataBindingUtil.bind(view, layoutId);
466ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount    }
467ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount
4684c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
46996e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * Walks the view hierarchy under root and pulls out tagged Views, includes, and views with
47096e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * IDs into an Object[] that is returned. This is used to walk the view hierarchy once to find
47196e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * all bound and ID'd views.
47296e1c821dd446d1ed78f8ae61131550588f60a24George Mount     *
47396e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param root The root of the view hierarchy to walk.
47496e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param numBindings The total number of ID'd views, views with expressions, and includes
47596e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param includes The include layout information, indexed by their container's index.
47696e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param viewsWithIds Indexes of views that don't have tags, but have IDs.
47796e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @return An array of size numBindings containing all Views in the hierarchy that have IDs
47896e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * (with elements in viewsWithIds), are tagged containing expressions, or the bindings for
47996e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * included layouts.
48096e1c821dd446d1ed78f8ae61131550588f60a24George Mount     */
48196e1c821dd446d1ed78f8ae61131550588f60a24George Mount    protected static Object[] mapBindings(View root, int numBindings,
48296e1c821dd446d1ed78f8ae61131550588f60a24George Mount            IncludedLayoutIndex[][] includes, SparseIntArray viewsWithIds) {
48396e1c821dd446d1ed78f8ae61131550588f60a24George Mount        Object[] bindings = new Object[numBindings];
48496e1c821dd446d1ed78f8ae61131550588f60a24George Mount        mapBindings(root, bindings, includes, viewsWithIds, true);
48596e1c821dd446d1ed78f8ae61131550588f60a24George Mount        return bindings;
48696e1c821dd446d1ed78f8ae61131550588f60a24George Mount    }
48796e1c821dd446d1ed78f8ae61131550588f60a24George Mount
48896e1c821dd446d1ed78f8ae61131550588f60a24George Mount    /**
48996e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * Walks the view hierarchy under roots and pulls out tagged Views, includes, and views with
49096e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * IDs into an Object[] that is returned. This is used to walk the view hierarchy once to find
49196e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * all bound and ID'd views.
4924c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     *
49396e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param roots The root Views of the view hierarchy to walk. This is used with merge tags.
49496e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param numBindings The total number of ID'd views, views with expressions, and includes
49596e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param includes The include layout information, indexed by their container's index.
49696e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param viewsWithIds Indexes of views that don't have tags, but have IDs.
49796e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @return An array of size numBindings containing all Views in the hierarchy that have IDs
49896e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * (with elements in viewsWithIds), are tagged containing expressions, or the bindings for
49996e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * included layouts.
5004c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
50196e1c821dd446d1ed78f8ae61131550588f60a24George Mount    protected static Object[] mapBindings(View[] roots, int numBindings,
50296e1c821dd446d1ed78f8ae61131550588f60a24George Mount            IncludedLayoutIndex[][] includes, SparseIntArray viewsWithIds) {
50396e1c821dd446d1ed78f8ae61131550588f60a24George Mount        Object[] bindings = new Object[numBindings];
50496e1c821dd446d1ed78f8ae61131550588f60a24George Mount        for (int i = 0; i < roots.length; i++) {
50596e1c821dd446d1ed78f8ae61131550588f60a24George Mount            mapBindings(roots[i], bindings, includes, viewsWithIds, true);
50600da715547ee7d5d38a3b8576090ca427a94daa5George Mount        }
50796e1c821dd446d1ed78f8ae61131550588f60a24George Mount        return bindings;
50800da715547ee7d5d38a3b8576090ca427a94daa5George Mount    }
50900da715547ee7d5d38a3b8576090ca427a94daa5George Mount
51096e1c821dd446d1ed78f8ae61131550588f60a24George Mount    private static void mapBindings(View view, Object[] bindings,
51196e1c821dd446d1ed78f8ae61131550588f60a24George Mount            IncludedLayoutIndex[][] includes, SparseIntArray viewsWithIds, boolean isRoot) {
51296e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final IncludedLayoutIndex[] includedLayoutIndexes;
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
6544c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    private static abstract class WeakListener<T> {
6554c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        private final WeakReference<ViewDataBinding> mBinder;
6565cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        protected final int mLocalFieldId;
6575cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        private T mTarget;
658085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
6594c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        public WeakListener(ViewDataBinding binder, int localFieldId) {
6604c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount            mBinder = new WeakReference<ViewDataBinding>(binder);
6615cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            mLocalFieldId = localFieldId;
662085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
663085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
6645cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        public void setTarget(T object) {
6655cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            unregister();
6665cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            mTarget = object;
6675cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (mTarget != null) {
6685cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                addListener(mTarget);
669085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            }
670085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
671085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
672085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        public boolean unregister() {
6735cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            boolean unregistered = false;
6745cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (mTarget != null) {
6755cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                removeListener(mTarget);
6765cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                unregistered = true;
677085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            }
678085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            mTarget = null;
6795cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            return unregistered;
680085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
681085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
6825cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        public T getTarget() {
6835cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            return mTarget;
684085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
6855cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
6864c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        protected ViewDataBinding getBinder() {
6874c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount            ViewDataBinding binder = mBinder.get();
6885cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (binder == null) {
6895cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                unregister(); // The binder is dead
6905cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            }
6915cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            return binder;
6925cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
6935cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
6945cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        protected abstract void addListener(T target);
6955cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        protected abstract void removeListener(T target);
696085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
697085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
6984c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    private static class WeakPropertyListener extends WeakListener<Observable>
6995cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            implements OnPropertyChangedListener {
7004c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {
7015cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            super(binder, localFieldId);
7025cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7035cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7045cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
7055cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        protected void addListener(Observable target) {
7065cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            target.addOnPropertyChangedListener(this);
7075cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7085cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7095cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
7105cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        protected void removeListener(Observable target) {
7115cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            target.removeOnPropertyChangedListener(this);
712085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
713085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
714085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        @Override
715293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        public void onPropertyChanged(Observable sender, int fieldId) {
7164c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount            ViewDataBinding binder = getBinder();
7175cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (binder == null) {
7185cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                return;
7195cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            }
720085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            Observable obj = getTarget();
7215cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (obj != sender) {
7225cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                return; // notification from the wrong object?
723085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            }
7245cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            binder.handleFieldChange(mLocalFieldId, sender, fieldId);
725085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
726085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
7275cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7284c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    private static class WeakListListener extends WeakListener<ObservableList>
7295cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            implements OnListChangedListener {
7305cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7314c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        public WeakListListener(ViewDataBinding binder, int localFieldId) {
7325cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            super(binder, localFieldId);
7335cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7345cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7355cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
7365cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        public void onChanged() {
7374c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount            ViewDataBinding binder = getBinder();
7385cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (binder == null) {
7395cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                return;
7405cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            }
7415cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            ObservableList target = getTarget();
7425cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (target == null) {
7435cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                return; // We don't expect any notifications from null targets
7445cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            }
7455cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            binder.handleFieldChange(mLocalFieldId, target, 0);
7465cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7475cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7485cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
7495cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        public void onItemRangeChanged(int positionStart, int itemCount) {
7505cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            onChanged();
7515cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7525cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7535cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
7545cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        public void onItemRangeInserted(int positionStart, int itemCount) {
7555cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            onChanged();
7565cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7575cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7585cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
7595cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
7605cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            onChanged();
7615cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7625cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7635cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
7645cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        public void onItemRangeRemoved(int positionStart, int itemCount) {
7655cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            onChanged();
7665cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7675cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7685cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
7695cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        protected void addListener(ObservableList target) {
7705cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            target.addOnListChangedListener(this);
7715cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7725cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7735cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
7745cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        protected void removeListener(ObservableList target) {
7755cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            target.removeOnListChangedListener(this);
7765cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7775cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
7785cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7794c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    private static class WeakMapListener extends WeakListener<ObservableMap>
7805cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            implements OnMapChangedListener {
7814c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        public WeakMapListener(ViewDataBinding binder, int localFieldId) {
7825cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            super(binder, localFieldId);
7835cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7845cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7855cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
7865cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        protected void addListener(ObservableMap target) {
7875cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            target.addOnMapChangedListener(this);
7885cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7895cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7905cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
7915cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        protected void removeListener(ObservableMap target) {
7925cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            target.removeOnMapChangedListener(this);
7935cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7945cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7955cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
7962c86cdbaf189e2b1774af7f64a2974de9321673fGeorge Mount        public void onMapChanged(ObservableMap sender, Object key) {
7974c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount            ViewDataBinding binder = getBinder();
7985cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (binder == null || sender != getTarget()) {
7995cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                return;
8005cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            }
8015cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            binder.handleFieldChange(mLocalFieldId, sender, 0);
8025cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
8035cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
8045cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
8055cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    private interface CreateWeakListener {
8064c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        WeakListener create(ViewDataBinding viewDataBinding, int localFieldId);
8075cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
80896e1c821dd446d1ed78f8ae61131550588f60a24George Mount
80996e1c821dd446d1ed78f8ae61131550588f60a24George Mount    protected static class IncludedLayoutIndex {
81096e1c821dd446d1ed78f8ae61131550588f60a24George Mount        public final String layout;
81196e1c821dd446d1ed78f8ae61131550588f60a24George Mount        public final int index;
81296e1c821dd446d1ed78f8ae61131550588f60a24George Mount        public final int layoutId;
81396e1c821dd446d1ed78f8ae61131550588f60a24George Mount
81496e1c821dd446d1ed78f8ae61131550588f60a24George Mount        public IncludedLayoutIndex(String layout, int index, int layoutId) {
81596e1c821dd446d1ed78f8ae61131550588f60a24George Mount            this.layout = layout;
81696e1c821dd446d1ed78f8ae61131550588f60a24George Mount            this.index = index;
81796e1c821dd446d1ed78f8ae61131550588f60a24George Mount            this.layoutId = layoutId;
81896e1c821dd446d1ed78f8ae61131550588f60a24George Mount        }
81996e1c821dd446d1ed78f8ae61131550588f60a24George Mount    }
820085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar}
821