1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.contacts.ui;
18
19import com.android.contacts.model.EntityDelta;
20import com.android.contacts.model.ContactsSource.DataKind;
21import com.android.contacts.model.EntityDelta.ValuesDelta;
22import com.android.contacts.ui.widget.GenericEditorView;
23import com.android.contacts.ui.widget.KindSectionView;
24
25import android.os.Bundle;
26import android.os.Parcel;
27import android.os.Parcelable;
28
29/**
30 * A class that provides unique view ids for {@link ContentEditorView}, {@link KindSectionView},
31 * {@link GenericEditorView} and {@link EditView} on {@link EditContactActivity}.
32 * It is used to assign a unique but consistent id to each view across {@link EditContactActivity}'s
33 * lifecycle, so that we can re-construct view state (e.g. focused view) when the screen rotates.
34 *
35 * <p>This class is not thread safe.
36 */
37public final class ViewIdGenerator implements Parcelable {
38    private static final int INVALID_VIEW_ID = 0;
39    private static final int INITIAL_VIEW_ID = 1;
40
41    public static final int NO_VIEW_INDEX = -1;
42
43    private int mNextId;
44
45    /**
46     * Used as a map from the "key" of the views to actual ids.  {@link #getId()} generates keys for
47     * the views.
48     */
49    private Bundle mIdMap = new Bundle();
50
51    private static final char KEY_SEPARATOR = '*';
52
53    private final static StringBuilder sWorkStringBuilder = new StringBuilder();
54
55    public ViewIdGenerator() {
56        mNextId = INITIAL_VIEW_ID;
57    }
58
59    /** {@inheritDoc} */
60    public int describeContents() {
61        return 0;
62    }
63
64    /**
65     * Returns an id for a view associated with specified contact field.
66     *
67     * @param entity {@link EntityDelta} associated with the view
68     * @param kind {@link DataKind} associated with the view, or null if none exists.
69     * @param values {@link ValuesDelta} associated with the view, or null if none exists.
70     * @param viewIndex index of the view in the parent {@link Editor}, if it's a leave view.
71     *     Otherwise, pass {@link #NO_VIEW_INDEX}.
72     */
73    public int getId(EntityDelta entity, DataKind kind, ValuesDelta values,
74            int viewIndex) {
75        final String k = getMapKey(entity, kind, values, viewIndex);
76
77        int id = mIdMap.getInt(k, INVALID_VIEW_ID);
78        if (id == INVALID_VIEW_ID) {
79            // Make sure the new id won't conflict with auto-generated ids by masking with 0xffff.
80            id = (mNextId++) & 0xFFFF;
81            mIdMap.putInt(k, id);
82        }
83        return id;
84    }
85
86    private static String getMapKey(EntityDelta entity, DataKind kind, ValuesDelta values,
87            int viewIndex) {
88        sWorkStringBuilder.setLength(0);
89        if (entity != null) {
90            sWorkStringBuilder.append(entity.getValues().getId());
91
92            if (kind != null) {
93                sWorkStringBuilder.append(KEY_SEPARATOR);
94                sWorkStringBuilder.append(kind.mimeType);
95
96                if (values != null) {
97                    sWorkStringBuilder.append(KEY_SEPARATOR);
98                    sWorkStringBuilder.append(values.getId());
99
100                    if (viewIndex != NO_VIEW_INDEX) {
101                        sWorkStringBuilder.append(KEY_SEPARATOR);
102                        sWorkStringBuilder.append(viewIndex);
103                    }
104                }
105            }
106        }
107        return sWorkStringBuilder.toString();
108    }
109
110    /** {@Override} */
111    public void writeToParcel(Parcel dest, int flags) {
112        dest.writeInt(mNextId);
113        dest.writeBundle(mIdMap);
114    }
115
116    private void readFromParcel(Parcel src) {
117        mNextId = src.readInt();
118        mIdMap = src.readBundle();
119    }
120
121    public static final Parcelable.Creator<ViewIdGenerator> CREATOR =
122            new Parcelable.Creator<ViewIdGenerator>() {
123        public ViewIdGenerator createFromParcel(Parcel in) {
124            final ViewIdGenerator vig = new ViewIdGenerator();
125            vig.readFromParcel(in);
126            return vig;
127        }
128
129        public ViewIdGenerator[] newArray(int size) {
130            return new ViewIdGenerator[size];
131        }
132    };
133}
134