ViewDataBinding.java revision 7ff60c24c6de7ba0c674fe65a82ad4a88dab2e5d
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;
22e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mountimport android.os.Build;
234c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mountimport android.os.Build.VERSION;
244c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mountimport android.os.Build.VERSION_CODES;
2596e1c821dd446d1ed78f8ae61131550588f60a24George Mountimport android.text.TextUtils;
26e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mountimport android.util.Log;
274c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mountimport android.util.SparseIntArray;
28085724fd84795ae1631747443c43f77c08e11a28Yigit Boyarimport android.view.View;
295914aa7da50a90a4c705b5be02a215499d0ad232George Mountimport android.view.View.OnAttachStateChangeListener;
3000da715547ee7d5d38a3b8576090ca427a94daa5George Mountimport android.view.ViewGroup;
31085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
32085724fd84795ae1631747443c43f77c08e11a28Yigit Boyarimport java.lang.ref.WeakReference;
33085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
344c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mountpublic abstract class ViewDataBinding {
354c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
364c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
374c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Instead of directly accessing Build.VERSION.SDK_INT, generated code uses this value so that
384c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * we can test API dependent behavior.
394c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
404c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    static int SDK_INT = VERSION.SDK_INT;
414c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
424c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
434c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Prefix for android:tag on Views with binding. The root View and include tags will not have
444c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * android:tag attributes and will use ids instead.
454c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
467ff60c24c6de7ba0c674fe65a82ad4a88dab2e5dGeorge Mount    public static final String BINDING_TAG_PREFIX = "binding_";
474c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
484c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    // The length of BINDING_TAG_PREFIX prevents calling length repeatedly.
4900da715547ee7d5d38a3b8576090ca427a94daa5George Mount    private static final int BINDING_NUMBER_START = BINDING_TAG_PREFIX.length();
5000da715547ee7d5d38a3b8576090ca427a94daa5George Mount
51e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount    // ICS (v 14) fixes a leak when using setTag(int, Object)
52e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount    private static final boolean USE_TAG_ID = DataBinderMapper.TARGET_MIN_SDK >= 14;
53e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount
544c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
554c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Method object extracted out to attach a listener to a bound Observable object.
564c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
575cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
585cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
594c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
604c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount            return new WeakPropertyListener(viewDataBinding, localFieldId);
615cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
625cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    };
635cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
644c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
654c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Method object extracted out to attach a listener to a bound ObservableList object.
664c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
675cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    private static final CreateWeakListener CREATE_LIST_LISTENER = new CreateWeakListener() {
685cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
694c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
704c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount            return new WeakListListener(viewDataBinding, localFieldId);
715cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
725cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    };
735cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
744c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
754c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Method object extracted out to attach a listener to a bound ObservableMap object.
764c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
775cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    private static final CreateWeakListener CREATE_MAP_LISTENER = new CreateWeakListener() {
785cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
794c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
804c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount            return new WeakMapListener(viewDataBinding, localFieldId);
815cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
825cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    };
835cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
845914aa7da50a90a4c705b5be02a215499d0ad232George Mount    private static final OnAttachStateChangeListener ROOT_REATTACHED_LISTENER;
855914aa7da50a90a4c705b5be02a215499d0ad232George Mount
865914aa7da50a90a4c705b5be02a215499d0ad232George Mount    static {
875914aa7da50a90a4c705b5be02a215499d0ad232George Mount        if (VERSION.SDK_INT < VERSION_CODES.KITKAT) {
885914aa7da50a90a4c705b5be02a215499d0ad232George Mount            ROOT_REATTACHED_LISTENER = null;
895914aa7da50a90a4c705b5be02a215499d0ad232George Mount        } else {
905914aa7da50a90a4c705b5be02a215499d0ad232George Mount            ROOT_REATTACHED_LISTENER = new OnAttachStateChangeListener() {
915914aa7da50a90a4c705b5be02a215499d0ad232George Mount                @TargetApi(VERSION_CODES.KITKAT)
925914aa7da50a90a4c705b5be02a215499d0ad232George Mount                @Override
935914aa7da50a90a4c705b5be02a215499d0ad232George Mount                public void onViewAttachedToWindow(View v) {
945914aa7da50a90a4c705b5be02a215499d0ad232George Mount                    // execute the pending bindings.
95e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount                    final ViewDataBinding binding;
96e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount                    if (USE_TAG_ID) {
97e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount                        binding = (ViewDataBinding) v.getTag(R.id.dataBinding);
98e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount                    } else {
99e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount                        binding = (ViewDataBinding) v.getTag();
100e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount                    }
1015914aa7da50a90a4c705b5be02a215499d0ad232George Mount                    v.post(binding.mRebindRunnable);
1025914aa7da50a90a4c705b5be02a215499d0ad232George Mount                    v.removeOnAttachStateChangeListener(this);
1035914aa7da50a90a4c705b5be02a215499d0ad232George Mount                }
1045914aa7da50a90a4c705b5be02a215499d0ad232George Mount
1055914aa7da50a90a4c705b5be02a215499d0ad232George Mount                @Override
1065914aa7da50a90a4c705b5be02a215499d0ad232George Mount                public void onViewDetachedFromWindow(View v) {
1075914aa7da50a90a4c705b5be02a215499d0ad232George Mount                }
1085914aa7da50a90a4c705b5be02a215499d0ad232George Mount            };
1095914aa7da50a90a4c705b5be02a215499d0ad232George Mount        }
1105914aa7da50a90a4c705b5be02a215499d0ad232George Mount    }
1115914aa7da50a90a4c705b5be02a215499d0ad232George Mount
1124c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
1134c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Runnable executed on animation heartbeat to rebind the dirty Views.
1144c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
115085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    private Runnable mRebindRunnable = new Runnable() {
116085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        @Override
117085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        public void run() {
1184c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount            if (mPendingRebind) {
1195914aa7da50a90a4c705b5be02a215499d0ad232George Mount                boolean rebind = true;
1205914aa7da50a90a4c705b5be02a215499d0ad232George Mount                if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
1215914aa7da50a90a4c705b5be02a215499d0ad232George Mount                    rebind = mRoot.isAttachedToWindow();
1225914aa7da50a90a4c705b5be02a215499d0ad232George Mount                    if (!rebind) {
1235914aa7da50a90a4c705b5be02a215499d0ad232George Mount                        // Don't execute the pending bindings until the View
1245914aa7da50a90a4c705b5be02a215499d0ad232George Mount                        // is attached again.
1255914aa7da50a90a4c705b5be02a215499d0ad232George Mount                        mRoot.addOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
1265914aa7da50a90a4c705b5be02a215499d0ad232George Mount                    }
1275914aa7da50a90a4c705b5be02a215499d0ad232George Mount                }
1285914aa7da50a90a4c705b5be02a215499d0ad232George Mount                if (rebind) {
1295914aa7da50a90a4c705b5be02a215499d0ad232George Mount                    mPendingRebind = false;
1305914aa7da50a90a4c705b5be02a215499d0ad232George Mount                    executePendingBindings();
1315914aa7da50a90a4c705b5be02a215499d0ad232George Mount                }
1324c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount            }
133085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
134085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    };
135085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
1364c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
1374c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Flag indicates that there are pending bindings that need to be reevaluated.
1384c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
1394c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    private boolean mPendingRebind = false;
1404c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
1414c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
1424c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * The observed expressions.
1434c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
1444c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    private WeakListener[] mLocalFieldObservers;
1454c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
1464c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
1474c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * The root View that this Binding is associated with.
1484c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
1494c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    private final View mRoot;
1504c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
1514c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    protected ViewDataBinding(View root, int localFieldCount) {
1525cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        mLocalFieldObservers = new WeakListener[localFieldCount];
1534c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        this.mRoot = root;
15496e1c821dd446d1ed78f8ae61131550588f60a24George Mount    }
15596e1c821dd446d1ed78f8ae61131550588f60a24George Mount
15696e1c821dd446d1ed78f8ae61131550588f60a24George Mount    protected void setRootTag(View view) {
15796e1c821dd446d1ed78f8ae61131550588f60a24George Mount        if (USE_TAG_ID) {
15896e1c821dd446d1ed78f8ae61131550588f60a24George Mount            view.setTag(R.id.dataBinding, this);
15996e1c821dd446d1ed78f8ae61131550588f60a24George Mount        } else {
16096e1c821dd446d1ed78f8ae61131550588f60a24George Mount            view.setTag(this);
16196e1c821dd446d1ed78f8ae61131550588f60a24George Mount        }
16296e1c821dd446d1ed78f8ae61131550588f60a24George Mount    }
16396e1c821dd446d1ed78f8ae61131550588f60a24George Mount
16496e1c821dd446d1ed78f8ae61131550588f60a24George Mount    protected void setRootTag(View[] views) {
165e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount        if (USE_TAG_ID) {
16696e1c821dd446d1ed78f8ae61131550588f60a24George Mount            for (View view : views) {
16796e1c821dd446d1ed78f8ae61131550588f60a24George Mount                view.setTag(R.id.dataBinding, this);
16896e1c821dd446d1ed78f8ae61131550588f60a24George Mount            }
169e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount        } else {
17096e1c821dd446d1ed78f8ae61131550588f60a24George Mount            for (View view : views) {
17196e1c821dd446d1ed78f8ae61131550588f60a24George Mount                view.setTag(this);
17296e1c821dd446d1ed78f8ae61131550588f60a24George Mount            }
173e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount        }
1745cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
1755cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
1764c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    public static int getBuildSdkInt() {
1774c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        return SDK_INT;
1784c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    }
1794c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
1804c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
1814c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Called when an observed object changes. Sets the appropriate dirty flag if applicable.
1824c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * @param localFieldId The index into mLocalFieldObservers that this Object resides in.
1834c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * @param object The object that has changed.
184fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount     * @param fieldId The BR ID of the field being changed or _all if
1854c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     *                no specific field is being notified.
1864c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * @return true if this change should cause a change to the UI.
1874c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
1884c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    protected abstract boolean onFieldChange(int localFieldId, Object object, int fieldId);
1894c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
1904c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    public abstract boolean setVariable(int variableId, Object variable);
1914c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
1924c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
1934c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Evaluates the pending bindings, updating any Views that have expressions bound to
1944c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * modified variables. This <b>must</b> be run on the UI thread.
1954c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
1964c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    public abstract void executePendingBindings();
1974c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
1984c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
1994c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Used internally to invalidate flags of included layouts.
2004c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
2014c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    public abstract void invalidateAll();
2024c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
2034c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
2044c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Removes binding listeners to expression variables.
2054c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
2064c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    public void unbind() {
2075cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        for (WeakListener weakListener : mLocalFieldObservers) {
2085cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (weakListener != null) {
2095cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                weakListener.unregister();
2105cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            }
2115cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
212085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
213085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
2144c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    @Override
2154c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    protected void finalize() throws Throwable {
2164c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        unbind();
2174c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    }
2184c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount
2194c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
2204c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * Returns the outermost View in the layout file associated with the Binding.
2214c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     * @return the outermost View in the layout file associated with the Binding.
2224c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
223085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    public View getRoot() {
224085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        return mRoot;
225085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
226085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
227085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
228085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        boolean result = onFieldChange(mLocalFieldId, object, fieldId);
229085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (result) {
230085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            requestRebind();
231085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
232085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
233085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
234085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    protected boolean unregisterFrom(int localFieldId) {
2355cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        WeakListener listener = mLocalFieldObservers[localFieldId];
236085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (listener != null) {
237085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return listener.unregister();
238085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
239085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        return false;
240085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
241085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
242085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    protected void requestRebind() {
243085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (mPendingRebind) {
244085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return;
245085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
246085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        mPendingRebind = true;
2474c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
2484c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount            mRoot.postOnAnimation(mRebindRunnable);
2494c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        } else {
2504c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount            mRoot.post(mRebindRunnable);
2514c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        }
252085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
253085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
254085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    protected Object getObservedField(int localFieldId) {
2555cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        WeakListener listener = mLocalFieldObservers[localFieldId];
256085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (listener == null) {
257085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return null;
258085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
259085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        return listener.getTarget();
260085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
261085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
2625cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    private boolean updateRegistration(int localFieldId, Object observable,
2635cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            CreateWeakListener listenerCreator) {
264085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (observable == null) {
265085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return unregisterFrom(localFieldId);
266085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
2675cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        WeakListener listener = mLocalFieldObservers[localFieldId];
268085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (listener == null) {
2695cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            registerTo(localFieldId, observable, listenerCreator);
270085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return true;
271085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
272085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (listener.getTarget() == observable) {
273085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return false;//nothing to do, same object
274085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
275085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        unregisterFrom(localFieldId);
2765cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        registerTo(localFieldId, observable, listenerCreator);
277085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        return true;
278085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
279085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
2805cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    protected boolean updateRegistration(int localFieldId, Observable observable) {
2815cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
2825cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
2835cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
2845cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    protected boolean updateRegistration(int localFieldId, ObservableList observable) {
2855cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        return updateRegistration(localFieldId, observable, CREATE_LIST_LISTENER);
2865cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
2875cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
2885cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    protected boolean updateRegistration(int localFieldId, ObservableMap observable) {
2895cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        return updateRegistration(localFieldId, observable, CREATE_MAP_LISTENER);
2905cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
2915cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
2925cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    protected void registerTo(int localFieldId, Object observable,
2935cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            CreateWeakListener listenerCreator) {
294085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (observable == null) {
295085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return;
296085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
2975cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        WeakListener listener = mLocalFieldObservers[localFieldId];
298085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (listener == null) {
2995cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            listener = listenerCreator.create(this, localFieldId);
300085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            mLocalFieldObservers[localFieldId] = listener;
301085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
302085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        listener.setTarget(observable);
303085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
304085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
3054c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    /**
30696e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * Walks the view hierarchy under root and pulls out tagged Views, includes, and views with
30796e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * IDs into an Object[] that is returned. This is used to walk the view hierarchy once to find
30896e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * all bound and ID'd views.
30996e1c821dd446d1ed78f8ae61131550588f60a24George Mount     *
31096e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param root The root of the view hierarchy to walk.
31196e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param numBindings The total number of ID'd views, views with expressions, and includes
31296e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param includes The include layout information, indexed by their container's index.
31396e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param viewsWithIds Indexes of views that don't have tags, but have IDs.
31496e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @return An array of size numBindings containing all Views in the hierarchy that have IDs
31596e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * (with elements in viewsWithIds), are tagged containing expressions, or the bindings for
31696e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * included layouts.
31796e1c821dd446d1ed78f8ae61131550588f60a24George Mount     */
31896e1c821dd446d1ed78f8ae61131550588f60a24George Mount    protected static Object[] mapBindings(View root, int numBindings,
31996e1c821dd446d1ed78f8ae61131550588f60a24George Mount            IncludedLayoutIndex[][] includes, SparseIntArray viewsWithIds) {
32096e1c821dd446d1ed78f8ae61131550588f60a24George Mount        Object[] bindings = new Object[numBindings];
32196e1c821dd446d1ed78f8ae61131550588f60a24George Mount        mapBindings(root, bindings, includes, viewsWithIds, true);
32296e1c821dd446d1ed78f8ae61131550588f60a24George Mount        return bindings;
32396e1c821dd446d1ed78f8ae61131550588f60a24George Mount    }
32496e1c821dd446d1ed78f8ae61131550588f60a24George Mount
32596e1c821dd446d1ed78f8ae61131550588f60a24George Mount    /**
32696e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * Walks the view hierarchy under roots and pulls out tagged Views, includes, and views with
32796e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * IDs into an Object[] that is returned. This is used to walk the view hierarchy once to find
32896e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * all bound and ID'd views.
3294c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     *
33096e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param roots The root Views of the view hierarchy to walk. This is used with merge tags.
33196e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param numBindings The total number of ID'd views, views with expressions, and includes
33296e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param includes The include layout information, indexed by their container's index.
33396e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @param viewsWithIds Indexes of views that don't have tags, but have IDs.
33496e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * @return An array of size numBindings containing all Views in the hierarchy that have IDs
33596e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * (with elements in viewsWithIds), are tagged containing expressions, or the bindings for
33696e1c821dd446d1ed78f8ae61131550588f60a24George Mount     * included layouts.
3374c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount     */
33896e1c821dd446d1ed78f8ae61131550588f60a24George Mount    protected static Object[] mapBindings(View[] roots, int numBindings,
33996e1c821dd446d1ed78f8ae61131550588f60a24George Mount            IncludedLayoutIndex[][] includes, SparseIntArray viewsWithIds) {
34096e1c821dd446d1ed78f8ae61131550588f60a24George Mount        Object[] bindings = new Object[numBindings];
34196e1c821dd446d1ed78f8ae61131550588f60a24George Mount        for (int i = 0; i < roots.length; i++) {
34296e1c821dd446d1ed78f8ae61131550588f60a24George Mount            mapBindings(roots[i], bindings, includes, viewsWithIds, true);
34300da715547ee7d5d38a3b8576090ca427a94daa5George Mount        }
34496e1c821dd446d1ed78f8ae61131550588f60a24George Mount        return bindings;
34500da715547ee7d5d38a3b8576090ca427a94daa5George Mount    }
34600da715547ee7d5d38a3b8576090ca427a94daa5George Mount
34796e1c821dd446d1ed78f8ae61131550588f60a24George Mount    private static void mapBindings(View view, Object[] bindings,
34896e1c821dd446d1ed78f8ae61131550588f60a24George Mount            IncludedLayoutIndex[][] includes, SparseIntArray viewsWithIds, boolean isRoot) {
34996e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final IncludedLayoutIndex[] includedLayoutIndexes;
35096e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final String tag = (String) view.getTag();
35196e1c821dd446d1ed78f8ae61131550588f60a24George Mount        boolean isBound = false;
35296e1c821dd446d1ed78f8ae61131550588f60a24George Mount        if (isRoot && tag != null && tag.startsWith("layout")) {
35396e1c821dd446d1ed78f8ae61131550588f60a24George Mount            final int underscoreIndex = tag.lastIndexOf('_');
35496e1c821dd446d1ed78f8ae61131550588f60a24George Mount            if (underscoreIndex > 0 && isNumeric(tag, underscoreIndex + 1)) {
35596e1c821dd446d1ed78f8ae61131550588f60a24George Mount                final int index = parseTagInt(tag, underscoreIndex + 1);
35696e1c821dd446d1ed78f8ae61131550588f60a24George Mount                bindings[index] = view;
35796e1c821dd446d1ed78f8ae61131550588f60a24George Mount                includedLayoutIndexes = includes == null ? null : includes[index];
35896e1c821dd446d1ed78f8ae61131550588f60a24George Mount                isBound = true;
35996e1c821dd446d1ed78f8ae61131550588f60a24George Mount            } else {
36096e1c821dd446d1ed78f8ae61131550588f60a24George Mount                includedLayoutIndexes = null;
36196e1c821dd446d1ed78f8ae61131550588f60a24George Mount            }
36296e1c821dd446d1ed78f8ae61131550588f60a24George Mount        } else if (tag != null && tag.startsWith(BINDING_TAG_PREFIX)) {
36396e1c821dd446d1ed78f8ae61131550588f60a24George Mount            int tagIndex = parseTagInt(tag, BINDING_NUMBER_START);
36496e1c821dd446d1ed78f8ae61131550588f60a24George Mount            bindings[tagIndex] = view;
36596e1c821dd446d1ed78f8ae61131550588f60a24George Mount            isBound = true;
36696e1c821dd446d1ed78f8ae61131550588f60a24George Mount            includedLayoutIndexes = includes == null ? null : includes[tagIndex];
36795d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount        } else {
36896e1c821dd446d1ed78f8ae61131550588f60a24George Mount            // Not a bound view
36996e1c821dd446d1ed78f8ae61131550588f60a24George Mount            includedLayoutIndexes = null;
37096e1c821dd446d1ed78f8ae61131550588f60a24George Mount        }
37196e1c821dd446d1ed78f8ae61131550588f60a24George Mount        if (!isBound) {
37296e1c821dd446d1ed78f8ae61131550588f60a24George Mount            final int id = view.getId();
37396e1c821dd446d1ed78f8ae61131550588f60a24George Mount            if (id > 0) {
37496e1c821dd446d1ed78f8ae61131550588f60a24George Mount                int index;
37596e1c821dd446d1ed78f8ae61131550588f60a24George Mount                if (viewsWithIds != null && (index = viewsWithIds.get(id, -1)) >= 0) {
37696e1c821dd446d1ed78f8ae61131550588f60a24George Mount                    bindings[index] = view;
37796e1c821dd446d1ed78f8ae61131550588f60a24George Mount                }
37896e1c821dd446d1ed78f8ae61131550588f60a24George Mount            }
37996e1c821dd446d1ed78f8ae61131550588f60a24George Mount        }
38096e1c821dd446d1ed78f8ae61131550588f60a24George Mount
38196e1c821dd446d1ed78f8ae61131550588f60a24George Mount        if (view instanceof  ViewGroup) {
38296e1c821dd446d1ed78f8ae61131550588f60a24George Mount            final ViewGroup viewGroup = (ViewGroup) view;
38396e1c821dd446d1ed78f8ae61131550588f60a24George Mount            final int count = viewGroup.getChildCount();
38496e1c821dd446d1ed78f8ae61131550588f60a24George Mount            int minInclude = 0;
38596e1c821dd446d1ed78f8ae61131550588f60a24George Mount            for (int i = 0; i < count; i++) {
38696e1c821dd446d1ed78f8ae61131550588f60a24George Mount                final View child = viewGroup.getChildAt(i);
38796e1c821dd446d1ed78f8ae61131550588f60a24George Mount                boolean isInclude = false;
38896e1c821dd446d1ed78f8ae61131550588f60a24George Mount                if (includedLayoutIndexes != null) {
38996e1c821dd446d1ed78f8ae61131550588f60a24George Mount                    String childTag = (String) child.getTag();
39096e1c821dd446d1ed78f8ae61131550588f60a24George Mount                    if (childTag != null && childTag.endsWith("_0") &&
39196e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            childTag.startsWith("layout") && childTag.indexOf('/') > 0) {
39296e1c821dd446d1ed78f8ae61131550588f60a24George Mount                        Log.d("ViewDataBinding", "Found potential include: " + childTag);
39396e1c821dd446d1ed78f8ae61131550588f60a24George Mount                        // This *could* be an include. Test against the expected includes.
39496e1c821dd446d1ed78f8ae61131550588f60a24George Mount                        int includeIndex = findIncludeIndex(childTag, minInclude,
39596e1c821dd446d1ed78f8ae61131550588f60a24George Mount                                includedLayoutIndexes);
39696e1c821dd446d1ed78f8ae61131550588f60a24George Mount                        Log.d("ViewDataBinding", "found index: " + includeIndex);
39796e1c821dd446d1ed78f8ae61131550588f60a24George Mount                        if (includeIndex >= 0) {
39896e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            isInclude = true;
39996e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            minInclude = includeIndex + 1;
40096e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            IncludedLayoutIndex include = includedLayoutIndexes[includeIndex];
40196e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            int lastMatchingIndex = findLastMatching(viewGroup, i);
40296e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            Log.d("ViewDataBinding", "include index: " + include.index + ", first match = " + i + ", last match = " + lastMatchingIndex);
40396e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            if (lastMatchingIndex == i) {
40496e1c821dd446d1ed78f8ae61131550588f60a24George Mount                                bindings[include.index] = DataBindingUtil.bindTo(child, include.layoutId);
40596e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            } else {
40696e1c821dd446d1ed78f8ae61131550588f60a24George Mount                                final int includeCount =  lastMatchingIndex - i + 1;
40796e1c821dd446d1ed78f8ae61131550588f60a24George Mount                                final View[] included = new View[includeCount];
40896e1c821dd446d1ed78f8ae61131550588f60a24George Mount                                for (int j = 0; j < includeCount; j++) {
40996e1c821dd446d1ed78f8ae61131550588f60a24George Mount                                    included[j] = viewGroup.getChildAt(i + j);
41096e1c821dd446d1ed78f8ae61131550588f60a24George Mount                                }
41196e1c821dd446d1ed78f8ae61131550588f60a24George Mount                                bindings[include.index] = DataBindingUtil.bindTo(included, include.layoutId);
41296e1c821dd446d1ed78f8ae61131550588f60a24George Mount                                i += includeCount - 1;
41396e1c821dd446d1ed78f8ae61131550588f60a24George Mount                            }
41496e1c821dd446d1ed78f8ae61131550588f60a24George Mount                        }
41596e1c821dd446d1ed78f8ae61131550588f60a24George Mount                    }
41696e1c821dd446d1ed78f8ae61131550588f60a24George Mount                }
41796e1c821dd446d1ed78f8ae61131550588f60a24George Mount                if (!isInclude) {
41896e1c821dd446d1ed78f8ae61131550588f60a24George Mount                    mapBindings(child, bindings, includes, viewsWithIds, false);
41996e1c821dd446d1ed78f8ae61131550588f60a24George Mount                }
42096e1c821dd446d1ed78f8ae61131550588f60a24George Mount            }
4214c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        }
42296e1c821dd446d1ed78f8ae61131550588f60a24George Mount    }
42396e1c821dd446d1ed78f8ae61131550588f60a24George Mount
42496e1c821dd446d1ed78f8ae61131550588f60a24George Mount    private static int findIncludeIndex(String tag, int minInclude,
42596e1c821dd446d1ed78f8ae61131550588f60a24George Mount            IncludedLayoutIndex[] layoutIndexes) {
42696e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final int slashIndex = tag.indexOf('/');
42796e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final CharSequence layoutName = tag.subSequence(slashIndex + 1, tag.length() - 2);
42896e1c821dd446d1ed78f8ae61131550588f60a24George Mount
42996e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final int length = layoutIndexes.length;
43096e1c821dd446d1ed78f8ae61131550588f60a24George Mount        for (int i = minInclude; i < length; i++) {
43196e1c821dd446d1ed78f8ae61131550588f60a24George Mount            final IncludedLayoutIndex layoutIndex = layoutIndexes[i];
43296e1c821dd446d1ed78f8ae61131550588f60a24George Mount            if (TextUtils.equals(layoutName, layoutIndex.layout)) {
43396e1c821dd446d1ed78f8ae61131550588f60a24George Mount                return i;
43496e1c821dd446d1ed78f8ae61131550588f60a24George Mount            }
43500da715547ee7d5d38a3b8576090ca427a94daa5George Mount        }
43696e1c821dd446d1ed78f8ae61131550588f60a24George Mount        return -1;
43700da715547ee7d5d38a3b8576090ca427a94daa5George Mount    }
43800da715547ee7d5d38a3b8576090ca427a94daa5George Mount
43996e1c821dd446d1ed78f8ae61131550588f60a24George Mount    private static int findLastMatching(ViewGroup viewGroup, int firstIncludedIndex) {
44096e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final View firstView = viewGroup.getChildAt(firstIncludedIndex);
44196e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final String firstViewTag = (String) firstView.getTag();
44296e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final String tagBase = firstViewTag.substring(0, firstViewTag.length() - 1); // don't include the "0"
44396e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final int tagSequenceIndex = tagBase.length();
44496e1c821dd446d1ed78f8ae61131550588f60a24George Mount
44596e1c821dd446d1ed78f8ae61131550588f60a24George Mount        final int count = viewGroup.getChildCount();
44696e1c821dd446d1ed78f8ae61131550588f60a24George Mount        int max = firstIncludedIndex;
44796e1c821dd446d1ed78f8ae61131550588f60a24George Mount        for (int i = firstIncludedIndex + 1; i < count; i++) {
44896e1c821dd446d1ed78f8ae61131550588f60a24George Mount            final View view = viewGroup.getChildAt(i);
44996e1c821dd446d1ed78f8ae61131550588f60a24George Mount            final String tag = (String) view.getTag();
45096e1c821dd446d1ed78f8ae61131550588f60a24George Mount            if (tag != null && tag.startsWith(tagBase)) {
45196e1c821dd446d1ed78f8ae61131550588f60a24George Mount                if (tag.length() == firstViewTag.length() && tag.charAt(tag.length() - 1) == '0') {
45296e1c821dd446d1ed78f8ae61131550588f60a24George Mount                    return max; // Found another instance of the include
45396e1c821dd446d1ed78f8ae61131550588f60a24George Mount                }
45496e1c821dd446d1ed78f8ae61131550588f60a24George Mount                if (isNumeric(tag, tagSequenceIndex)) {
45596e1c821dd446d1ed78f8ae61131550588f60a24George Mount                    max = i;
45696e1c821dd446d1ed78f8ae61131550588f60a24George Mount                }
45796e1c821dd446d1ed78f8ae61131550588f60a24George Mount            }
45896e1c821dd446d1ed78f8ae61131550588f60a24George Mount        }
45996e1c821dd446d1ed78f8ae61131550588f60a24George Mount        return max;
46034a18e6a231f3b64726bd93e7e097a0d5a75995dGeorge Mount    }
46134a18e6a231f3b64726bd93e7e097a0d5a75995dGeorge Mount
46296e1c821dd446d1ed78f8ae61131550588f60a24George Mount    private static boolean isNumeric(String tag, int startIndex) {
46396e1c821dd446d1ed78f8ae61131550588f60a24George Mount        int length = tag.length();
46496e1c821dd446d1ed78f8ae61131550588f60a24George Mount        if (length == startIndex) {
46596e1c821dd446d1ed78f8ae61131550588f60a24George Mount            return false; // no numerals
46696e1c821dd446d1ed78f8ae61131550588f60a24George Mount        }
46796e1c821dd446d1ed78f8ae61131550588f60a24George Mount        for (int i = startIndex; i < length; i++) {
46896e1c821dd446d1ed78f8ae61131550588f60a24George Mount            if (!Character.isDigit(tag.charAt(i))) {
46996e1c821dd446d1ed78f8ae61131550588f60a24George Mount                return false;
47095d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount            }
47195d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount        }
47296e1c821dd446d1ed78f8ae61131550588f60a24George Mount        return true;
47395d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount    }
47495d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount
47534a18e6a231f3b64726bd93e7e097a0d5a75995dGeorge Mount    /**
47600da715547ee7d5d38a3b8576090ca427a94daa5George Mount     * Parse the tag without creating a new String object. This is fast and assumes the
47700da715547ee7d5d38a3b8576090ca427a94daa5George Mount     * tag is in the correct format.
47800da715547ee7d5d38a3b8576090ca427a94daa5George Mount     * @param str The tag string.
47900da715547ee7d5d38a3b8576090ca427a94daa5George Mount     * @return The binding tag number parsed from the tag string.
48000da715547ee7d5d38a3b8576090ca427a94daa5George Mount     */
48196e1c821dd446d1ed78f8ae61131550588f60a24George Mount    private static int parseTagInt(String str, int startIndex) {
48200da715547ee7d5d38a3b8576090ca427a94daa5George Mount        final int end = str.length();
48300da715547ee7d5d38a3b8576090ca427a94daa5George Mount        int val = 0;
48496e1c821dd446d1ed78f8ae61131550588f60a24George Mount        for (int i = startIndex; i < end; i++) {
48500da715547ee7d5d38a3b8576090ca427a94daa5George Mount            val *= 10;
48600da715547ee7d5d38a3b8576090ca427a94daa5George Mount            char c = str.charAt(i);
48700da715547ee7d5d38a3b8576090ca427a94daa5George Mount            val += (c - '0');
48800da715547ee7d5d38a3b8576090ca427a94daa5George Mount        }
48900da715547ee7d5d38a3b8576090ca427a94daa5George Mount        return val;
49000da715547ee7d5d38a3b8576090ca427a94daa5George Mount    }
49100da715547ee7d5d38a3b8576090ca427a94daa5George Mount
4924c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    private static abstract class WeakListener<T> {
4934c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        private final WeakReference<ViewDataBinding> mBinder;
4945cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        protected final int mLocalFieldId;
4955cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        private T mTarget;
496085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
4974c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        public WeakListener(ViewDataBinding binder, int localFieldId) {
4984c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount            mBinder = new WeakReference<ViewDataBinding>(binder);
4995cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            mLocalFieldId = localFieldId;
500085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
501085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
5025cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        public void setTarget(T object) {
5035cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            unregister();
5045cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            mTarget = object;
5055cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (mTarget != null) {
5065cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                addListener(mTarget);
507085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            }
508085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
509085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
510085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        public boolean unregister() {
5115cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            boolean unregistered = false;
5125cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (mTarget != null) {
5135cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                removeListener(mTarget);
5145cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                unregistered = true;
515085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            }
516085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            mTarget = null;
5175cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            return unregistered;
518085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
519085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
5205cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        public T getTarget() {
5215cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            return mTarget;
522085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
5235cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
5244c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        protected ViewDataBinding getBinder() {
5254c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount            ViewDataBinding binder = mBinder.get();
5265cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (binder == null) {
5275cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                unregister(); // The binder is dead
5285cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            }
5295cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            return binder;
5305cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
5315cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
5325cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        protected abstract void addListener(T target);
5335cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        protected abstract void removeListener(T target);
534085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
535085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
5364c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    private static class WeakPropertyListener extends WeakListener<Observable>
5375cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            implements OnPropertyChangedListener {
5384c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {
5395cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            super(binder, localFieldId);
5405cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
5415cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
5425cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
5435cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        protected void addListener(Observable target) {
5445cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            target.addOnPropertyChangedListener(this);
5455cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
5465cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
5475cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
5485cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        protected void removeListener(Observable target) {
5495cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            target.removeOnPropertyChangedListener(this);
550085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
551085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
552085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        @Override
553293de28642305ce210e1d2a1cfe0abfa4f737d7aGeorge Mount        public void onPropertyChanged(Observable sender, int fieldId) {
5544c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount            ViewDataBinding binder = getBinder();
5555cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (binder == null) {
5565cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                return;
5575cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            }
558085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            Observable obj = getTarget();
5595cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (obj != sender) {
5605cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                return; // notification from the wrong object?
561085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            }
5625cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            binder.handleFieldChange(mLocalFieldId, sender, fieldId);
563085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
564085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
5655cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
5664c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    private static class WeakListListener extends WeakListener<ObservableList>
5675cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            implements OnListChangedListener {
5685cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
5694c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        public WeakListListener(ViewDataBinding binder, int localFieldId) {
5705cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            super(binder, localFieldId);
5715cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
5725cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
5735cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
5745cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        public void onChanged() {
5754c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount            ViewDataBinding binder = getBinder();
5765cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (binder == null) {
5775cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                return;
5785cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            }
5795cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            ObservableList target = getTarget();
5805cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (target == null) {
5815cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                return; // We don't expect any notifications from null targets
5825cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            }
5835cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            binder.handleFieldChange(mLocalFieldId, target, 0);
5845cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
5855cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
5865cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
5875cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        public void onItemRangeChanged(int positionStart, int itemCount) {
5885cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            onChanged();
5895cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
5905cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
5915cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
5925cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        public void onItemRangeInserted(int positionStart, int itemCount) {
5935cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            onChanged();
5945cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
5955cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
5965cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
5975cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
5985cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            onChanged();
5995cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
6005cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
6015cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
6025cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        public void onItemRangeRemoved(int positionStart, int itemCount) {
6035cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            onChanged();
6045cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
6055cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
6065cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
6075cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        protected void addListener(ObservableList target) {
6085cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            target.addOnListChangedListener(this);
6095cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
6105cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
6115cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
6125cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        protected void removeListener(ObservableList target) {
6135cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            target.removeOnListChangedListener(this);
6145cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
6155cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
6165cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
6174c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount    private static class WeakMapListener extends WeakListener<ObservableMap>
6185cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            implements OnMapChangedListener {
6194c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        public WeakMapListener(ViewDataBinding binder, int localFieldId) {
6205cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            super(binder, localFieldId);
6215cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
6225cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
6235cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
6245cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        protected void addListener(ObservableMap target) {
6255cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            target.addOnMapChangedListener(this);
6265cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
6275cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
6285cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
6295cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        protected void removeListener(ObservableMap target) {
6305cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            target.removeOnMapChangedListener(this);
6315cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
6325cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
6335cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        @Override
6342c86cdbaf189e2b1774af7f64a2974de9321673fGeorge Mount        public void onMapChanged(ObservableMap sender, Object key) {
6354c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount            ViewDataBinding binder = getBinder();
6365cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            if (binder == null || sender != getTarget()) {
6375cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                return;
6385cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            }
6395cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            binder.handleFieldChange(mLocalFieldId, sender, 0);
6405cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
6415cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
6425cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
6435cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    private interface CreateWeakListener {
6444c5cc009bcbcfb19e33fb19db5ec80f83f7b3326George Mount        WeakListener create(ViewDataBinding viewDataBinding, int localFieldId);
6455cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
64696e1c821dd446d1ed78f8ae61131550588f60a24George Mount
64796e1c821dd446d1ed78f8ae61131550588f60a24George Mount    protected static class IncludedLayoutIndex {
64896e1c821dd446d1ed78f8ae61131550588f60a24George Mount        public final String layout;
64996e1c821dd446d1ed78f8ae61131550588f60a24George Mount        public final int index;
65096e1c821dd446d1ed78f8ae61131550588f60a24George Mount        public final int layoutId;
65196e1c821dd446d1ed78f8ae61131550588f60a24George Mount
65296e1c821dd446d1ed78f8ae61131550588f60a24George Mount        public IncludedLayoutIndex(String layout, int index, int layoutId) {
65396e1c821dd446d1ed78f8ae61131550588f60a24George Mount            this.layout = layout;
65496e1c821dd446d1ed78f8ae61131550588f60a24George Mount            this.index = index;
65596e1c821dd446d1ed78f8ae61131550588f60a24George Mount            this.layoutId = layoutId;
65696e1c821dd446d1ed78f8ae61131550588f60a24George Mount        }
65796e1c821dd446d1ed78f8ae61131550588f60a24George Mount    }
658085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar}
659