ViewDataBinding.java revision 4d4979490e1fa374c0d7f3599fed0a9e83a579d0
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;
284d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mountimport android.util.Log;
294c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mountimport android.util.SparseIntArray;
303f73e3149732132c02f5d19aab7b5da429794058Yigit Boyarimport android.view.Choreographer;
31085724fd84795ae1631747443c43f77c08e11a28Yigit Boyarimport android.view.View;
325914aa7da50a90a4c705b5be02a215499d0ad232George Mountimport android.view.View.OnAttachStateChangeListener;
3300da715547ee7d5d38a3b8576090ca427a94daa5George Mountimport android.view.ViewGroup;
34085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
35085724fd84795ae1631747443c43f77c08e11a28Yigit Boyarimport java.lang.ref.WeakReference;
36085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
374c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mountpublic abstract class ViewDataBinding {
384d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount    private final static String TAG = "ViewDataBinding";
394c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
404c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
414c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Instead of directly accessing Build.VERSION.SDK_INT, generated code uses this value so that
424c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * we can test API dependent behavior.
434c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
444c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    static int SDK_INT = VERSION.SDK_INT;
454c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
46e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    private static final int REBIND = 1;
47e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    private static final int HALTED = 2;
48e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    private static final int REBOUND = 3;
49e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
504c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
514c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Prefix for android:tag on Views with binding. The root View and include tags will not have
524c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * android:tag attributes and will use ids instead.
534c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
547ff60c24c6de7ba0c674fe65a82ad4a88dab2e5dGeorge Mount    public static final String BINDING_TAG_PREFIX = "binding_";
554c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
564c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    // The length of BINDING_TAG_PREFIX prevents calling length repeatedly.
5700da715547ee7d5d38a3b8576090ca427a94daa5George Mount    private static final int BINDING_NUMBER_START = BINDING_TAG_PREFIX.length();
5800da715547ee7d5d38a3b8576090ca427a94daa5George Mount
59e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount    // ICS (v 14) fixes a leak when using setTag(int, Object)
60e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount    private static final boolean USE_TAG_ID = DataBinderMapper.TARGET_MIN_SDK >= 14;
61e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount
623f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar    private static final boolean USE_CHOREOGRAPHER = SDK_INT >= 16;
633f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar
644c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
654c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Method object extracted out to attach a listener to a bound Observable object.
664c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
675cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
685cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
694c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
70722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
715cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
725cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    };
735cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
744c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
754c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Method object extracted out to attach a listener to a bound ObservableList object.
764c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
775cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    private static final CreateWeakListener CREATE_LIST_LISTENER = new CreateWeakListener() {
785cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
794c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
80722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            return new WeakListListener(viewDataBinding, localFieldId).getListener();
815cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
825cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    };
835cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
844c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
854c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Method object extracted out to attach a listener to a bound ObservableMap object.
864c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
875cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    private static final CreateWeakListener CREATE_MAP_LISTENER = new CreateWeakListener() {
885cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
894c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
90722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            return new WeakMapListener(viewDataBinding, localFieldId).getListener();
915cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
925cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    };
935cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
94e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    private static final CallbackRegistry.NotifierCallback<OnRebindCallback, ViewDataBinding, Void>
95e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        REBIND_NOTIFIER = new NotifierCallback<OnRebindCallback, ViewDataBinding, Void>() {
96e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        @Override
97e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        public void onNotifyCallback(OnRebindCallback callback, ViewDataBinding sender, int mode,
98e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                Void arg2) {
99e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            switch (mode) {
100e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                case REBIND:
101e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    if (!callback.onPreBind(sender)) {
102e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                        sender.mRebindHalted = true;
103e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    }
104e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    break;
105e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                case HALTED:
106e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    callback.onCanceled(sender);
107e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    break;
108e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                case REBOUND:
109e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    callback.onBound(sender);
110e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    break;
111e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            }
112e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        }
113e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    };
114e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
1155914aa7da50a90a4c705b5be02a215499d0ad232George Mount    private static final OnAttachStateChangeListener ROOT_REATTACHED_LISTENER;
1165914aa7da50a90a4c705b5be02a215499d0ad232George Mount
1175914aa7da50a90a4c705b5be02a215499d0ad232George Mount    static {
1185914aa7da50a90a4c705b5be02a215499d0ad232George Mount        if (VERSION.SDK_INT < VERSION_CODES.KITKAT) {
1195914aa7da50a90a4c705b5be02a215499d0ad232George Mount            ROOT_REATTACHED_LISTENER = null;
1205914aa7da50a90a4c705b5be02a215499d0ad232George Mount        } else {
1215914aa7da50a90a4c705b5be02a215499d0ad232George Mount            ROOT_REATTACHED_LISTENER = new OnAttachStateChangeListener() {
1225914aa7da50a90a4c705b5be02a215499d0ad232George Mount                @TargetApi(VERSION_CODES.KITKAT)
1235914aa7da50a90a4c705b5be02a215499d0ad232George Mount                @Override
1245914aa7da50a90a4c705b5be02a215499d0ad232George Mount                public void onViewAttachedToWindow(View v) {
1255914aa7da50a90a4c705b5be02a215499d0ad232George Mount                    // execute the pending bindings.
1264d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount                    final ViewDataBinding binding = getBinding(v);
1273f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar                    binding.mRebindRunnable.run();
1285914aa7da50a90a4c705b5be02a215499d0ad232George Mount                    v.removeOnAttachStateChangeListener(this);
1295914aa7da50a90a4c705b5be02a215499d0ad232George Mount                }
1305914aa7da50a90a4c705b5be02a215499d0ad232George Mount
1315914aa7da50a90a4c705b5be02a215499d0ad232George Mount                @Override
1325914aa7da50a90a4c705b5be02a215499d0ad232George Mount                public void onViewDetachedFromWindow(View v) {
1335914aa7da50a90a4c705b5be02a215499d0ad232George Mount                }
1345914aa7da50a90a4c705b5be02a215499d0ad232George Mount            };
1355914aa7da50a90a4c705b5be02a215499d0ad232George Mount        }
1365914aa7da50a90a4c705b5be02a215499d0ad232George Mount    }
1375914aa7da50a90a4c705b5be02a215499d0ad232George Mount
1384c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
1394c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Runnable executed on animation heartbeat to rebind the dirty Views.
1404c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
1413f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar    private final Runnable mRebindRunnable = new Runnable() {
142085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        @Override
143085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        public void run() {
144e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            synchronized (this) {
145e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                mPendingRebind = false;
146e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            }
147e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
148e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                // Nested so that we don't get a lint warning in IntelliJ
149e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                if (!mRoot.isAttachedToWindow()) {
150e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    // Don't execute the pending bindings until the View
151e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    // is attached again.
152e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    mRoot.removeOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
153e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    mRoot.addOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
154e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                    return;
1555914aa7da50a90a4c705b5be02a215499d0ad232George Mount                }
1564c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount            }
157e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            executePendingBindings();
158085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
159085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    };
160085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
1614c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
1624c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Flag indicates that there are pending bindings that need to be reevaluated.
1634c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
1644c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    private boolean mPendingRebind = false;
1654c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
1664c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
167e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * Indicates that a onPreBind has stopped the executePendingBindings call.
168e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     */
169e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    private boolean mRebindHalted = false;
170e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
171e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    /**
1724c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * The observed expressions.
1734c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
1744c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    private WeakListener[] mLocalFieldObservers;
1754c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
1764c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
1774c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * The root View that this Binding is associated with.
1784c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
1794c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    private final View mRoot;
1804c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
181e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    /**
182e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * The collection of OnRebindCallbacks.
183e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     */
184e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    private CallbackRegistry<OnRebindCallback, ViewDataBinding, Void> mRebindCallbacks;
185e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
186e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    /**
187e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * Flag to prevent reentrant executePendingBinding calls.
188e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     */
189e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    private boolean mIsExecutingPendingBindings;
190e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
1913f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar    // null api < 16
1923f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar    private Choreographer mChoreographer;
1933f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar
1943f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar    private final Choreographer.FrameCallback mFrameCallback;
1953f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar
1963f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar    // null api >= 16
1973f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar    private Handler mUIThreadHandler;
1983f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar
1994c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    protected ViewDataBinding(View root, int localFieldCount) {
2005cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        mLocalFieldObservers = new WeakListener[localFieldCount];
2014c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        this.mRoot = root;
2023f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar        if (Looper.myLooper() == null) {
2033f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            throw new IllegalStateException("DataBinding must be created in view's UI Thread");
2043f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar        }
2053f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar        if (USE_CHOREOGRAPHER) {
2063f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            mChoreographer = Choreographer.getInstance();
2073f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            mFrameCallback = new Choreographer.FrameCallback() {
2083f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar                @Override
2093f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar                public void doFrame(long frameTimeNanos) {
2103f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar                    mRebindRunnable.run();
2113f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar                }
2123f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            };
2133f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar        } else {
2143f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            mFrameCallback = null;
2153f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            mUIThreadHandler = new Handler(Looper.myLooper());
2163f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar        }
2173f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar        requestRebind();
21896e1c821dd446d1ed78f8ae61131550588f60a24George Mount    }
21996e1c821dd446d1ed78f8ae61131550588f60a24George Mount
22096e1c821dd446d1ed78f8ae61131550588f60a24George Mount    protected void setRootTag(View view) {
22196e1c821dd446d1ed78f8ae61131550588f60a24George Mount        if (USE_TAG_ID) {
22296e1c821dd446d1ed78f8ae61131550588f60a24George Mount            view.setTag(R.id.dataBinding, this);
22396e1c821dd446d1ed78f8ae61131550588f60a24George Mount        } else {
22496e1c821dd446d1ed78f8ae61131550588f60a24George Mount            view.setTag(this);
22596e1c821dd446d1ed78f8ae61131550588f60a24George Mount        }
22696e1c821dd446d1ed78f8ae61131550588f60a24George Mount    }
22796e1c821dd446d1ed78f8ae61131550588f60a24George Mount
22896e1c821dd446d1ed78f8ae61131550588f60a24George Mount    protected void setRootTag(View[] views) {
229e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount        if (USE_TAG_ID) {
23096e1c821dd446d1ed78f8ae61131550588f60a24George Mount            for (View view : views) {
23196e1c821dd446d1ed78f8ae61131550588f60a24George Mount                view.setTag(R.id.dataBinding, this);
23296e1c821dd446d1ed78f8ae61131550588f60a24George Mount            }
233e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount        } else {
23496e1c821dd446d1ed78f8ae61131550588f60a24George Mount            for (View view : views) {
23596e1c821dd446d1ed78f8ae61131550588f60a24George Mount                view.setTag(this);
23696e1c821dd446d1ed78f8ae61131550588f60a24George Mount            }
237e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount        }
2385cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
2395cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
2404c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    public static int getBuildSdkInt() {
2414c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        return SDK_INT;
2424c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    }
2434c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
2444c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
2454c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Called when an observed object changes. Sets the appropriate dirty flag if applicable.
2464c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * @param localFieldId The index into mLocalFieldObservers that this Object resides in.
2474c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * @param object The object that has changed.
248fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount     * @param fieldId The BR ID of the field being changed or _all if
2494c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     *                no specific field is being notified.
2504c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * @return true if this change should cause a change to the UI.
2514c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
2524c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    protected abstract boolean onFieldChange(int localFieldId, Object object, int fieldId);
2534c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
254e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    /**
255e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * Set a value value in the Binding class.
256e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * <p>
257e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * Typically, the developer will be able to call the subclass's set method directly. For
258e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * example, if there is a variable <code>x</code> in the Binding, a <code>setX</code> method
259e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * will be generated. However, there are times when the specific subclass of ViewDataBinding
260e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * is unknown, so the generated method cannot be discovered without reflection. The
261e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * setVariable call allows the values of variables to be set without reflection.
262e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     *
263e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * @param variableId the BR id of the variable to be set. For example, if the variable is
264e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     *                   <code>x</code>, then variableId will be <code>BR.x</code>.
265e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * @param value The new value of the variable to be set.
266e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * @return <code>true</code> if the variable exists in the binding or <code>false</code>
267e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * otherwise.
268e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     */
269e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    public abstract boolean setVariable(int variableId, Object value);
270e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
271e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    /**
272e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * Add a listener to be called when reevaluating dirty fields. This also allows automatic
273e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * updates to be halted, but does not stop explicit calls to {@link #executePendingBindings()}.
274e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     *
275e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * @param listener The listener to add.
276e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     */
277e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    public void addOnRebindCallback(OnRebindCallback listener) {
278e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        if (mRebindCallbacks == null) {
279e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            mRebindCallbacks = new CallbackRegistry<OnRebindCallback, ViewDataBinding, Void>(REBIND_NOTIFIER);
280e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        }
281e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        mRebindCallbacks.add(listener);
282e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    }
283e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
284e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    /**
285e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * Removes a listener that was added in {@link #addOnRebindCallback(OnRebindCallback)}.
286e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     *
287e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     * @param listener The listener to remove.
288e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount     */
289e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    public void removeOnRebindCallback(OnRebindCallback listener) {
290e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        if (mRebindCallbacks != null) {
291e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            mRebindCallbacks.remove(listener);
292e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        }
293e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    }
2944c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
2954c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
2964c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Evaluates the pending bindings, updating any Views that have expressions bound to
2974c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * modified variables. This <b>must</b> be run on the UI thread.
2984c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
299e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    public void executePendingBindings() {
300e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        if (mIsExecutingPendingBindings) {
301e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            requestRebind();
302e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            return;
303e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        }
304447971abca811b11b8c1d8e7bfaa294856d03c16George Mount        if (!hasPendingBindings()) {
305447971abca811b11b8c1d8e7bfaa294856d03c16George Mount            return;
306447971abca811b11b8c1d8e7bfaa294856d03c16George Mount        }
307e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        mIsExecutingPendingBindings = true;
308e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        mRebindHalted = false;
309e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        if (mRebindCallbacks != null) {
310e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            mRebindCallbacks.notifyCallbacks(this, REBIND, null);
311e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
312e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            // The onRebindListeners will change mPendingHalted
313e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            if (mRebindHalted) {
314e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                mRebindCallbacks.notifyCallbacks(this, HALTED, null);
315e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            }
316e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        }
317e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        if (!mRebindHalted) {
318e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            executeBindings();
319e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            if (mRebindCallbacks != null) {
320e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                mRebindCallbacks.notifyCallbacks(this, REBOUND, null);
321e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            }
322e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        }
323e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        mIsExecutingPendingBindings = false;
324e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    }
325e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
326e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    void forceExecuteBindings() {
327e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        executeBindings();
328e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    }
329e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount
330e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount    protected abstract void executeBindings();
3314c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
3324c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
3334c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Used internally to invalidate flags of included layouts.
334447971abca811b11b8c1d8e7bfaa294856d03c16George Mount     * @hide
3354c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
3364c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    public abstract void invalidateAll();
3374c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
3384c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
339447971abca811b11b8c1d8e7bfaa294856d03c16George Mount     * @return true if any field has changed and the binding should be evaluated.
340447971abca811b11b8c1d8e7bfaa294856d03c16George Mount     * @hide
341447971abca811b11b8c1d8e7bfaa294856d03c16George Mount     */
342447971abca811b11b8c1d8e7bfaa294856d03c16George Mount    public abstract boolean hasPendingBindings();
343447971abca811b11b8c1d8e7bfaa294856d03c16George Mount
344447971abca811b11b8c1d8e7bfaa294856d03c16George Mount    /**
3454c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Removes binding listeners to expression variables.
3464c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
3474c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    public void unbind() {
3485cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        for (WeakListener weakListener : mLocalFieldObservers) {
3495cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (weakListener != null) {
3505cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                weakListener.unregister();
3515cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            }
3525cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
353085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
354085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
3554c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    @Override
3564c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    protected void finalize() throws Throwable {
3574c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        unbind();
3584c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    }
3594c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
360ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount    static ViewDataBinding getBinding(View v) {
3614d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount        if (v != null) {
3624d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount            if (USE_TAG_ID) {
3634d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount                return (ViewDataBinding) v.getTag(R.id.dataBinding);
3644d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount            } else {
3654d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount                final Object tag = v.getTag();
3664d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount                if (tag instanceof ViewDataBinding) {
3674d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount                    return (ViewDataBinding) tag;
3684d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount                }
369ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount            }
370ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount        }
371ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount        return null;
372ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount    }
373ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount
3744c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
3754c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Returns the outermost View in the layout file associated with the Binding.
3764c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * @return the outermost View in the layout file associated with the Binding.
3774c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
378085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    public View getRoot() {
379085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        return mRoot;
380085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
381085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
382085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
383085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        boolean result = onFieldChange(mLocalFieldId, object, fieldId);
384085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (result) {
385085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            requestRebind();
386085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
387085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
388085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
389085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    protected boolean unregisterFrom(int localFieldId) {
3905cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        WeakListener listener = mLocalFieldObservers[localFieldId];
391085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (listener != null) {
392085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return listener.unregister();
393085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
394085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        return false;
395085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
396085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
397085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    protected void requestRebind() {
398e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount        synchronized (this) {
399e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            if (mPendingRebind) {
400e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount                return;
401e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            }
402e725f0d81e1b07e88f819be9a82181eeeb680dbfGeorge Mount            mPendingRebind = true;
403085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
4043f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar        if (USE_CHOREOGRAPHER) {
4053f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            mChoreographer.postFrameCallback(mFrameCallback);
4064c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        } else {
4073f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar            mUIThreadHandler.post(mRebindRunnable);
4084c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        }
4093f73e3149732132c02f5d19aab7b5da429794058Yigit Boyar
410085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
411085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
412085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    protected Object getObservedField(int localFieldId) {
4135cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        WeakListener listener = mLocalFieldObservers[localFieldId];
414085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (listener == null) {
415085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return null;
416085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
417085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        return listener.getTarget();
418085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
419085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
4205cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    private boolean updateRegistration(int localFieldId, Object observable,
4215cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            CreateWeakListener listenerCreator) {
422085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (observable == null) {
423085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return unregisterFrom(localFieldId);
424085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
4255cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        WeakListener listener = mLocalFieldObservers[localFieldId];
426085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (listener == null) {
4275cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            registerTo(localFieldId, observable, listenerCreator);
428085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return true;
429085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
430085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (listener.getTarget() == observable) {
431085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return false;//nothing to do, same object
432085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
433085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        unregisterFrom(localFieldId);
4345cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        registerTo(localFieldId, observable, listenerCreator);
435085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        return true;
436085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
437085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
4385cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    protected boolean updateRegistration(int localFieldId, Observable observable) {
4395cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
4405cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
4415cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
4425cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    protected boolean updateRegistration(int localFieldId, ObservableList observable) {
4435cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        return updateRegistration(localFieldId, observable, CREATE_LIST_LISTENER);
4445cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
4455cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
4465cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    protected boolean updateRegistration(int localFieldId, ObservableMap observable) {
4475cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        return updateRegistration(localFieldId, observable, CREATE_MAP_LISTENER);
4485cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
4495cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
4505cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    protected void registerTo(int localFieldId, Object observable,
4515cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            CreateWeakListener listenerCreator) {
452085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (observable == null) {
453085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return;
454085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
4555cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        WeakListener listener = mLocalFieldObservers[localFieldId];
456085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (listener == null) {
4575cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            listener = listenerCreator.create(this, localFieldId);
458085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            mLocalFieldObservers[localFieldId] = listener;
459085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
460085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        listener.setTarget(observable);
461085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
462085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
463ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount    protected static ViewDataBinding bind(View view, int layoutId) {
464ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount        return DataBindingUtil.bind(view, layoutId);
465ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount    }
466ed6428586a939e00d9e66314d5cf1056ad48767eGeorge Mount
4674c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
46896e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * Walks the view hierarchy under root and pulls out tagged Views, includes, and views with
46996e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * IDs into an Object[] that is returned. This is used to walk the view hierarchy once to find
47096e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * all bound and ID'd views.
47196e1c821dd446d1ed78f8ae61131550588f60a24George Mount     *
47296e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param root The root of the view hierarchy to walk.
47396e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param numBindings The total number of ID'd views, views with expressions, and includes
47496e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param includes The include layout information, indexed by their container's index.
47596e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param viewsWithIds Indexes of views that don't have tags, but have IDs.
47696e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @return An array of size numBindings containing all Views in the hierarchy that have IDs
47796e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * (with elements in viewsWithIds), are tagged containing expressions, or the bindings for
47896e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * included layouts.
47996e1c821dd446d1ed78f8ae61131550588f60a24George Mount     */
48096e1c821dd446d1ed78f8ae61131550588f60a24George Mount    protected static Object[] mapBindings(View root, int numBindings,
48196e1c821dd446d1ed78f8ae61131550588f60a24George Mount            IncludedLayoutIndex[][] includes, SparseIntArray viewsWithIds) {
48296e1c821dd446d1ed78f8ae61131550588f60a24George Mount        Object[] bindings = new Object[numBindings];
48396e1c821dd446d1ed78f8ae61131550588f60a24George Mount        mapBindings(root, bindings, includes, viewsWithIds, true);
48496e1c821dd446d1ed78f8ae61131550588f60a24George Mount        return bindings;
48596e1c821dd446d1ed78f8ae61131550588f60a24George Mount    }
48696e1c821dd446d1ed78f8ae61131550588f60a24George Mount
48796e1c821dd446d1ed78f8ae61131550588f60a24George Mount    /**
48896e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * Walks the view hierarchy under roots and pulls out tagged Views, includes, and views with
48996e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * IDs into an Object[] that is returned. This is used to walk the view hierarchy once to find
49096e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * all bound and ID'd views.
4914c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     *
49296e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param roots The root Views of the view hierarchy to walk. This is used with merge tags.
49396e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param numBindings The total number of ID'd views, views with expressions, and includes
49496e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param includes The include layout information, indexed by their container's index.
49596e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param viewsWithIds Indexes of views that don't have tags, but have IDs.
49696e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @return An array of size numBindings containing all Views in the hierarchy that have IDs
49796e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * (with elements in viewsWithIds), are tagged containing expressions, or the bindings for
49896e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * included layouts.
4994c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
50096e1c821dd446d1ed78f8ae61131550588f60a24George Mount    protected static Object[] mapBindings(View[] roots, int numBindings,
50196e1c821dd446d1ed78f8ae61131550588f60a24George Mount            IncludedLayoutIndex[][] includes, SparseIntArray viewsWithIds) {
50296e1c821dd446d1ed78f8ae61131550588f60a24George Mount        Object[] bindings = new Object[numBindings];
50396e1c821dd446d1ed78f8ae61131550588f60a24George Mount        for (int i = 0; i < roots.length; i++) {
50496e1c821dd446d1ed78f8ae61131550588f60a24George Mount            mapBindings(roots[i], bindings, includes, viewsWithIds, true);
50500da715547ee7d5d38a3b8576090ca427a94daa5George Mount        }
50696e1c821dd446d1ed78f8ae61131550588f60a24George Mount        return bindings;
50700da715547ee7d5d38a3b8576090ca427a94daa5George Mount    }
50800da715547ee7d5d38a3b8576090ca427a94daa5George Mount
5094d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount    protected static void validateFragmentBinding(ViewDataBinding binding, String fieldName) {
5104d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount        if (binding == null) {
5114d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount            Log.e(TAG, "The fragment " + fieldName + " has not generated a bound view. " +
5124d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount                    "Use DataBindingUtil.inflate, the generated Binding class's inflate method, " +
5134d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount                    "or the generated Binding class's bind method to ensure a bound view from " +
5144d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount                    "the fragment's onCreateView method.");
5154d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount        }
5164d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount    }
5174d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount
51896e1c821dd446d1ed78f8ae61131550588f60a24George Mount    private static void mapBindings(View view, Object[] bindings,
51996e1c821dd446d1ed78f8ae61131550588f60a24George Mount            IncludedLayoutIndex[][] includes, SparseIntArray viewsWithIds, boolean isRoot) {
52096e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final IncludedLayoutIndex[] includedLayoutIndexes;
5214d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount        final ViewDataBinding fragmentBinding = getBinding(view);
5224d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount        if (fragmentBinding != null) {
5234d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount            // Must be a fragment
5244d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount            final int fragmentId = view.getId();
5254d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount            final int index = (viewsWithIds == null) ? 0 : viewsWithIds.get(fragmentId);
5264d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount            if (fragmentId > 0) {
5274d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount                bindings[index] = view;
5284d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount            } else {
5294d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount                // A fragment without an ID doesn't have any binding expressions
5304d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount                // nor can it be accessed. We can just ignore it.
5314d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount            }
5324d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount            return;
5334d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount        }
53496e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final String tag = (String) view.getTag();
53596e1c821dd446d1ed78f8ae61131550588f60a24George Mount        boolean isBound = false;
53696e1c821dd446d1ed78f8ae61131550588f60a24George Mount        if (isRoot && tag != null && tag.startsWith("layout")) {
53796e1c821dd446d1ed78f8ae61131550588f60a24George Mount            final int underscoreIndex = tag.lastIndexOf('_');
53896e1c821dd446d1ed78f8ae61131550588f60a24George Mount            if (underscoreIndex > 0 && isNumeric(tag, underscoreIndex + 1)) {
53996e1c821dd446d1ed78f8ae61131550588f60a24George Mount                final int index = parseTagInt(tag, underscoreIndex + 1);
54096e1c821dd446d1ed78f8ae61131550588f60a24George Mount                bindings[index] = view;
54196e1c821dd446d1ed78f8ae61131550588f60a24George Mount                includedLayoutIndexes = includes == null ? null : includes[index];
54296e1c821dd446d1ed78f8ae61131550588f60a24George Mount                isBound = true;
54396e1c821dd446d1ed78f8ae61131550588f60a24George Mount            } else {
54496e1c821dd446d1ed78f8ae61131550588f60a24George Mount                includedLayoutIndexes = null;
54596e1c821dd446d1ed78f8ae61131550588f60a24George Mount            }
54696e1c821dd446d1ed78f8ae61131550588f60a24George Mount        } else if (tag != null && tag.startsWith(BINDING_TAG_PREFIX)) {
54796e1c821dd446d1ed78f8ae61131550588f60a24George Mount            int tagIndex = parseTagInt(tag, BINDING_NUMBER_START);
54896e1c821dd446d1ed78f8ae61131550588f60a24George Mount            bindings[tagIndex] = view;
54996e1c821dd446d1ed78f8ae61131550588f60a24George Mount            isBound = true;
55096e1c821dd446d1ed78f8ae61131550588f60a24George Mount            includedLayoutIndexes = includes == null ? null : includes[tagIndex];
55195d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount        } else {
55296e1c821dd446d1ed78f8ae61131550588f60a24George Mount            // Not a bound view
55396e1c821dd446d1ed78f8ae61131550588f60a24George Mount            includedLayoutIndexes = null;
55496e1c821dd446d1ed78f8ae61131550588f60a24George Mount        }
55596e1c821dd446d1ed78f8ae61131550588f60a24George Mount        if (!isBound) {
55696e1c821dd446d1ed78f8ae61131550588f60a24George Mount            final int id = view.getId();
55796e1c821dd446d1ed78f8ae61131550588f60a24George Mount            if (id > 0) {
55896e1c821dd446d1ed78f8ae61131550588f60a24George Mount                int index;
55996e1c821dd446d1ed78f8ae61131550588f60a24George Mount                if (viewsWithIds != null && (index = viewsWithIds.get(id, -1)) >= 0) {
56096e1c821dd446d1ed78f8ae61131550588f60a24George Mount                    bindings[index] = view;
56196e1c821dd446d1ed78f8ae61131550588f60a24George Mount                }
56296e1c821dd446d1ed78f8ae61131550588f60a24George Mount            }
56396e1c821dd446d1ed78f8ae61131550588f60a24George Mount        }
56496e1c821dd446d1ed78f8ae61131550588f60a24George Mount
56596e1c821dd446d1ed78f8ae61131550588f60a24George Mount        if (view instanceof  ViewGroup) {
56696e1c821dd446d1ed78f8ae61131550588f60a24George Mount            final ViewGroup viewGroup = (ViewGroup) view;
56796e1c821dd446d1ed78f8ae61131550588f60a24George Mount            final int count = viewGroup.getChildCount();
56896e1c821dd446d1ed78f8ae61131550588f60a24George Mount            int minInclude = 0;
56996e1c821dd446d1ed78f8ae61131550588f60a24George Mount            for (int i = 0; i < count; i++) {
57096e1c821dd446d1ed78f8ae61131550588f60a24George Mount                final View child = viewGroup.getChildAt(i);
57196e1c821dd446d1ed78f8ae61131550588f60a24George Mount                boolean isInclude = false;
57296e1c821dd446d1ed78f8ae61131550588f60a24George Mount                if (includedLayoutIndexes != null) {
57396e1c821dd446d1ed78f8ae61131550588f60a24George Mount                    String childTag = (String) child.getTag();
57496e1c821dd446d1ed78f8ae61131550588f60a24George Mount                    if (childTag != null && childTag.endsWith("_0") &&
57596e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            childTag.startsWith("layout") && childTag.indexOf('/') > 0) {
57696e1c821dd446d1ed78f8ae61131550588f60a24George Mount                        // This *could* be an include. Test against the expected includes.
57796e1c821dd446d1ed78f8ae61131550588f60a24George Mount                        int includeIndex = findIncludeIndex(childTag, minInclude,
57896e1c821dd446d1ed78f8ae61131550588f60a24George Mount                                includedLayoutIndexes);
57996e1c821dd446d1ed78f8ae61131550588f60a24George Mount                        if (includeIndex >= 0) {
58096e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            isInclude = true;
58196e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            minInclude = includeIndex + 1;
58296e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            IncludedLayoutIndex include = includedLayoutIndexes[includeIndex];
58396e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            int lastMatchingIndex = findLastMatching(viewGroup, i);
58496e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            if (lastMatchingIndex == i) {
5859bdb2415487832e88a05c7bd19391b05440b468eGeorge Mount                                bindings[include.index] = DataBindingUtil.bind(child,
5869bdb2415487832e88a05c7bd19391b05440b468eGeorge Mount                                        include.layoutId);
58796e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            } else {
58896e1c821dd446d1ed78f8ae61131550588f60a24George Mount                                final int includeCount =  lastMatchingIndex - i + 1;
58996e1c821dd446d1ed78f8ae61131550588f60a24George Mount                                final View[] included = new View[includeCount];
59096e1c821dd446d1ed78f8ae61131550588f60a24George Mount                                for (int j = 0; j < includeCount; j++) {
59196e1c821dd446d1ed78f8ae61131550588f60a24George Mount                                    included[j] = viewGroup.getChildAt(i + j);
59296e1c821dd446d1ed78f8ae61131550588f60a24George Mount                                }
5939bdb2415487832e88a05c7bd19391b05440b468eGeorge Mount                                bindings[include.index] = DataBindingUtil.bind(included,
5949bdb2415487832e88a05c7bd19391b05440b468eGeorge Mount                                        include.layoutId);
59596e1c821dd446d1ed78f8ae61131550588f60a24George Mount                                i += includeCount - 1;
59696e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            }
59796e1c821dd446d1ed78f8ae61131550588f60a24George Mount                        }
59896e1c821dd446d1ed78f8ae61131550588f60a24George Mount                    }
59996e1c821dd446d1ed78f8ae61131550588f60a24George Mount                }
60096e1c821dd446d1ed78f8ae61131550588f60a24George Mount                if (!isInclude) {
60196e1c821dd446d1ed78f8ae61131550588f60a24George Mount                    mapBindings(child, bindings, includes, viewsWithIds, false);
60296e1c821dd446d1ed78f8ae61131550588f60a24George Mount                }
60396e1c821dd446d1ed78f8ae61131550588f60a24George Mount            }
6044c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        }
60596e1c821dd446d1ed78f8ae61131550588f60a24George Mount    }
60696e1c821dd446d1ed78f8ae61131550588f60a24George Mount
60796e1c821dd446d1ed78f8ae61131550588f60a24George Mount    private static int findIncludeIndex(String tag, int minInclude,
60896e1c821dd446d1ed78f8ae61131550588f60a24George Mount            IncludedLayoutIndex[] layoutIndexes) {
60996e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final int slashIndex = tag.indexOf('/');
61096e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final CharSequence layoutName = tag.subSequence(slashIndex + 1, tag.length() - 2);
61196e1c821dd446d1ed78f8ae61131550588f60a24George Mount
61296e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final int length = layoutIndexes.length;
61396e1c821dd446d1ed78f8ae61131550588f60a24George Mount        for (int i = minInclude; i < length; i++) {
61496e1c821dd446d1ed78f8ae61131550588f60a24George Mount            final IncludedLayoutIndex layoutIndex = layoutIndexes[i];
61596e1c821dd446d1ed78f8ae61131550588f60a24George Mount            if (TextUtils.equals(layoutName, layoutIndex.layout)) {
61696e1c821dd446d1ed78f8ae61131550588f60a24George Mount                return i;
61796e1c821dd446d1ed78f8ae61131550588f60a24George Mount            }
61800da715547ee7d5d38a3b8576090ca427a94daa5George Mount        }
61996e1c821dd446d1ed78f8ae61131550588f60a24George Mount        return -1;
62000da715547ee7d5d38a3b8576090ca427a94daa5George Mount    }
62100da715547ee7d5d38a3b8576090ca427a94daa5George Mount
62296e1c821dd446d1ed78f8ae61131550588f60a24George Mount    private static int findLastMatching(ViewGroup viewGroup, int firstIncludedIndex) {
62396e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final View firstView = viewGroup.getChildAt(firstIncludedIndex);
62496e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final String firstViewTag = (String) firstView.getTag();
62596e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final String tagBase = firstViewTag.substring(0, firstViewTag.length() - 1); // don't include the "0"
62696e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final int tagSequenceIndex = tagBase.length();
62796e1c821dd446d1ed78f8ae61131550588f60a24George Mount
62896e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final int count = viewGroup.getChildCount();
62996e1c821dd446d1ed78f8ae61131550588f60a24George Mount        int max = firstIncludedIndex;
63096e1c821dd446d1ed78f8ae61131550588f60a24George Mount        for (int i = firstIncludedIndex + 1; i < count; i++) {
63196e1c821dd446d1ed78f8ae61131550588f60a24George Mount            final View view = viewGroup.getChildAt(i);
63296e1c821dd446d1ed78f8ae61131550588f60a24George Mount            final String tag = (String) view.getTag();
63396e1c821dd446d1ed78f8ae61131550588f60a24George Mount            if (tag != null && tag.startsWith(tagBase)) {
63496e1c821dd446d1ed78f8ae61131550588f60a24George Mount                if (tag.length() == firstViewTag.length() && tag.charAt(tag.length() - 1) == '0') {
63596e1c821dd446d1ed78f8ae61131550588f60a24George Mount                    return max; // Found another instance of the include
63696e1c821dd446d1ed78f8ae61131550588f60a24George Mount                }
63796e1c821dd446d1ed78f8ae61131550588f60a24George Mount                if (isNumeric(tag, tagSequenceIndex)) {
63896e1c821dd446d1ed78f8ae61131550588f60a24George Mount                    max = i;
63996e1c821dd446d1ed78f8ae61131550588f60a24George Mount                }
64096e1c821dd446d1ed78f8ae61131550588f60a24George Mount            }
64196e1c821dd446d1ed78f8ae61131550588f60a24George Mount        }
64296e1c821dd446d1ed78f8ae61131550588f60a24George Mount        return max;
64334a18e6a231f3b64726bd93e7e097a0d5a75995dGeorge Mount    }
64434a18e6a231f3b64726bd93e7e097a0d5a75995dGeorge Mount
64596e1c821dd446d1ed78f8ae61131550588f60a24George Mount    private static boolean isNumeric(String tag, int startIndex) {
64696e1c821dd446d1ed78f8ae61131550588f60a24George Mount        int length = tag.length();
64796e1c821dd446d1ed78f8ae61131550588f60a24George Mount        if (length == startIndex) {
64896e1c821dd446d1ed78f8ae61131550588f60a24George Mount            return false; // no numerals
64996e1c821dd446d1ed78f8ae61131550588f60a24George Mount        }
65096e1c821dd446d1ed78f8ae61131550588f60a24George Mount        for (int i = startIndex; i < length; i++) {
65196e1c821dd446d1ed78f8ae61131550588f60a24George Mount            if (!Character.isDigit(tag.charAt(i))) {
65296e1c821dd446d1ed78f8ae61131550588f60a24George Mount                return false;
65395d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount            }
65495d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount        }
65596e1c821dd446d1ed78f8ae61131550588f60a24George Mount        return true;
65695d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount    }
65795d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount
65834a18e6a231f3b64726bd93e7e097a0d5a75995dGeorge Mount    /**
65900da715547ee7d5d38a3b8576090ca427a94daa5George Mount     * Parse the tag without creating a new String object. This is fast and assumes the
66000da715547ee7d5d38a3b8576090ca427a94daa5George Mount     * tag is in the correct format.
66100da715547ee7d5d38a3b8576090ca427a94daa5George Mount     * @param str The tag string.
66200da715547ee7d5d38a3b8576090ca427a94daa5George Mount     * @return The binding tag number parsed from the tag string.
66300da715547ee7d5d38a3b8576090ca427a94daa5George Mount     */
66496e1c821dd446d1ed78f8ae61131550588f60a24George Mount    private static int parseTagInt(String str, int startIndex) {
66500da715547ee7d5d38a3b8576090ca427a94daa5George Mount        final int end = str.length();
66600da715547ee7d5d38a3b8576090ca427a94daa5George Mount        int val = 0;
66796e1c821dd446d1ed78f8ae61131550588f60a24George Mount        for (int i = startIndex; i < end; i++) {
66800da715547ee7d5d38a3b8576090ca427a94daa5George Mount            val *= 10;
66900da715547ee7d5d38a3b8576090ca427a94daa5George Mount            char c = str.charAt(i);
67000da715547ee7d5d38a3b8576090ca427a94daa5George Mount            val += (c - '0');
67100da715547ee7d5d38a3b8576090ca427a94daa5George Mount        }
67200da715547ee7d5d38a3b8576090ca427a94daa5George Mount        return val;
67300da715547ee7d5d38a3b8576090ca427a94daa5George Mount    }
67400da715547ee7d5d38a3b8576090ca427a94daa5George Mount
675722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount    private interface ObservableReference<T> {
676722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        WeakListener<T> getListener();
677722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        void addListener(T target);
678722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        void removeListener(T target);
679722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount    }
680722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount
681722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount    private static class WeakListener<T> extends WeakReference<ViewDataBinding> {
682722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        private final ObservableReference<T> mObservable;
6835cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        protected final int mLocalFieldId;
6845cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        private T mTarget;
685085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
686722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public WeakListener(ViewDataBinding binder, int localFieldId,
687722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount                ObservableReference<T> observable) {
688722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            super(binder);
6895cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            mLocalFieldId = localFieldId;
690722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            mObservable = observable;
691085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
692085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
6935cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        public void setTarget(T object) {
6945cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            unregister();
6955cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            mTarget = object;
6965cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (mTarget != null) {
697722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount                mObservable.addListener(mTarget);
698085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            }
699085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
700085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
701085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        public boolean unregister() {
7025cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            boolean unregistered = false;
7035cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (mTarget != null) {
704722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount                mObservable.removeListener(mTarget);
7055cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                unregistered = true;
706085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            }
707085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            mTarget = null;
7085cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            return unregistered;
709085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
710085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
7115cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        public T getTarget() {
7125cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            return mTarget;
713085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
7145cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7154c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        protected ViewDataBinding getBinder() {
716722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            ViewDataBinding binder = get();
7175cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (binder == null) {
7185cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                unregister(); // The binder is dead
7195cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            }
7205cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            return binder;
7215cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
722085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
723085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
724722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount    private static class WeakPropertyListener extends Observable.OnPropertyChangedCallback
725722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            implements ObservableReference<Observable> {
726722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        final WeakListener<Observable> mListener;
727722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount
7284c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {
729722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            mListener = new WeakListener<Observable>(binder, localFieldId, this);
7305cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7315cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7325cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
733722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public WeakListener<Observable> getListener() {
734722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            return mListener;
7355cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7365cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7375cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
738722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void addListener(Observable target) {
739722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            target.addOnPropertyChangedCallback(this);
740085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
741085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
742085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        @Override
743722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void removeListener(Observable target) {
744722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            target.removeOnPropertyChangedCallback(this);
745722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        }
746722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount
747722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        @Override
748722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void onPropertyChanged(Observable sender, int propertyId) {
749722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            ViewDataBinding binder = mListener.getBinder();
7505cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (binder == null) {
7515cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                return;
7525cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            }
753722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            Observable obj = mListener.getTarget();
7545cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (obj != sender) {
7555cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                return; // notification from the wrong object?
756085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            }
757722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId);
758085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
759085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
7605cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
761722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount    private static class WeakListListener extends ObservableList.OnListChangedCallback
762722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            implements ObservableReference<ObservableList> {
763722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        final WeakListener<ObservableList> mListener;
7645cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7654c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        public WeakListListener(ViewDataBinding binder, int localFieldId) {
766722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            mListener = new WeakListener<ObservableList>(binder, localFieldId, this);
7675cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7685cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7695cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
770722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public WeakListener<ObservableList> getListener() {
771722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            return mListener;
7725cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7735cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7745cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
775722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void addListener(ObservableList target) {
776722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            target.addOnListChangedCallback(this);
7775cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7785cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7795cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
780722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void removeListener(ObservableList target) {
781722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            target.removeOnListChangedCallback(this);
7825cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7835cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7845cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
785722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void onChanged(ObservableList sender) {
786722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            ViewDataBinding binder = mListener.getBinder();
787722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            if (binder == null) {
788722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount                return;
789722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            }
790722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            ObservableList target = mListener.getTarget();
791722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            if (target != sender) {
792722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount                return; // We expect notifications only from sender
793722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            }
794722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            binder.handleFieldChange(mListener.mLocalFieldId, target, 0);
7955cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
7965cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
7975cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
798722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void onItemRangeChanged(ObservableList sender, int positionStart, int itemCount) {
799722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            onChanged(sender);
8005cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
8015cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
8025cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
803722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void onItemRangeInserted(ObservableList sender, int positionStart, int itemCount) {
804722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            onChanged(sender);
8055cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
8065cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
8075cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
808722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void onItemRangeMoved(ObservableList sender, int fromPosition, int toPosition,
809722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount                int itemCount) {
810722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            onChanged(sender);
811722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        }
812722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount
813722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        @Override
814722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void onItemRangeRemoved(ObservableList sender, int positionStart, int itemCount) {
815722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            onChanged(sender);
8165cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
8175cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
8185cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
819722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount    private static class WeakMapListener extends ObservableMap.OnMapChangedCallback
820722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            implements ObservableReference<ObservableMap> {
821722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        final WeakListener<ObservableMap> mListener;
822722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount
8234c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        public WeakMapListener(ViewDataBinding binder, int localFieldId) {
824722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            mListener = new WeakListener<ObservableMap>(binder, localFieldId, this);
825722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        }
826722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount
827722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        @Override
828722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public WeakListener<ObservableMap> getListener() {
829722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            return mListener;
8305cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
8315cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
8325cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
833722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void addListener(ObservableMap target) {
834722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            target.addOnMapChangedCallback(this);
8355cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
8365cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
8375cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
838722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount        public void removeListener(ObservableMap target) {
839722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            target.removeOnMapChangedCallback(this);
8405cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
8415cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
8425cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
8432c86cdbaf189e2b1774af7f64a2974de9321673fGeorge Mount        public void onMapChanged(ObservableMap sender, Object key) {
844722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            ViewDataBinding binder = mListener.getBinder();
845722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            if (binder == null || sender != mListener.getTarget()) {
8465cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                return;
8475cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            }
848722fe711207a37783dfa7142284b0ebe5bd503fbGeorge Mount            binder.handleFieldChange(mListener.mLocalFieldId, sender, 0);
8495cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
8505cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
8515cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
8525cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    private interface CreateWeakListener {
8534c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        WeakListener create(ViewDataBinding viewDataBinding, int localFieldId);
8545cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
85596e1c821dd446d1ed78f8ae61131550588f60a24George Mount
85696e1c821dd446d1ed78f8ae61131550588f60a24George Mount    protected static class IncludedLayoutIndex {
85796e1c821dd446d1ed78f8ae61131550588f60a24George Mount        public final String layout;
85896e1c821dd446d1ed78f8ae61131550588f60a24George Mount        public final int index;
85996e1c821dd446d1ed78f8ae61131550588f60a24George Mount        public final int layoutId;
86096e1c821dd446d1ed78f8ae61131550588f60a24George Mount
86196e1c821dd446d1ed78f8ae61131550588f60a24George Mount        public IncludedLayoutIndex(String layout, int index, int layoutId) {
86296e1c821dd446d1ed78f8ae61131550588f60a24George Mount            this.layout = layout;
86396e1c821dd446d1ed78f8ae61131550588f60a24George Mount            this.index = index;
86496e1c821dd446d1ed78f8ae61131550588f60a24George Mount            this.layoutId = layoutId;
86596e1c821dd446d1ed78f8ae61131550588f60a24George Mount        }
86696e1c821dd446d1ed78f8ae61131550588f60a24George Mount    }
867085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar}
868