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.editor;
18
19import android.os.Bundle;
20import android.os.Parcel;
21import android.os.Parcelable;
22
23import com.android.contacts.common.model.RawContactDelta;
24import com.android.contacts.common.model.ValuesDelta;
25import com.android.contacts.common.model.dataitem.DataKind;
26
27/**
28 * A class that provides unique view ids for {@link ContentEditorView}, {@link KindSectionView},
29 * {@link LabeledEditorView} and {@link EditView} on {@link EditContactActivity}.
30 * It is used to assign a unique but consistent id to each view across {@link EditContactActivity}'s
31 * lifecycle, so that we can re-construct view state (e.g. focused view) when the screen rotates.
32 *
33 * <p>This class is not thread safe.
34 */
35public final class ViewIdGenerator implements Parcelable {
36    private static final int INVALID_VIEW_ID = 0;
37    private static final int INITIAL_VIEW_ID = 1;
38
39    public static final int NO_VIEW_INDEX = -1;
40
41    private int mNextId;
42
43    /**
44     * Used as a map from the "key" of the views to actual ids.  {@link #getId()} generates keys for
45     * the views.
46     */
47    private Bundle mIdMap = new Bundle();
48
49    private static final char KEY_SEPARATOR = '*';
50
51    private final static StringBuilder sWorkStringBuilder = new StringBuilder();
52
53    public ViewIdGenerator() {
54        mNextId = INITIAL_VIEW_ID;
55    }
56
57    /** {@inheritDoc} */
58    public int describeContents() {
59        return 0;
60    }
61
62    /**
63     * Returns an id for a view associated with specified contact field.
64     *
65     * @param entity {@link RawContactDelta} associated with the view
66     * @param kind {@link DataKind} associated with the view, or null if none exists.
67     * @param values {@link ValuesDelta} associated with the view, or null if none exists.
68     * @param viewIndex index of the view in the parent {@link Editor}, if it's a leave view.
69     *     Otherwise, pass {@link #NO_VIEW_INDEX}.
70     */
71    public int getId(RawContactDelta entity, DataKind kind, ValuesDelta values,
72            int viewIndex) {
73        final String k = getMapKey(entity, kind, values, viewIndex);
74
75        int id = mIdMap.getInt(k, INVALID_VIEW_ID);
76        if (id == INVALID_VIEW_ID) {
77            // Make sure the new id won't conflict with auto-generated ids by masking with 0xffff.
78            id = (mNextId++) & 0xFFFF;
79            mIdMap.putInt(k, id);
80        }
81        return id;
82    }
83
84    private static String getMapKey(RawContactDelta entity, DataKind kind, ValuesDelta values,
85            int viewIndex) {
86        sWorkStringBuilder.setLength(0);
87        if (entity != null) {
88            sWorkStringBuilder.append(entity.getValues().getId());
89
90            if (kind != null) {
91                sWorkStringBuilder.append(KEY_SEPARATOR);
92                sWorkStringBuilder.append(kind.mimeType);
93
94                if (values != null) {
95                    sWorkStringBuilder.append(KEY_SEPARATOR);
96                    sWorkStringBuilder.append(values.getId());
97
98                    if (viewIndex != NO_VIEW_INDEX) {
99                        sWorkStringBuilder.append(KEY_SEPARATOR);
100                        sWorkStringBuilder.append(viewIndex);
101                    }
102                }
103            }
104        }
105        return sWorkStringBuilder.toString();
106    }
107
108    /** {@Override} */
109    public void writeToParcel(Parcel dest, int flags) {
110        dest.writeInt(mNextId);
111        dest.writeBundle(mIdMap);
112    }
113
114    private void readFromParcel(Parcel src) {
115        mNextId = src.readInt();
116        mIdMap = src.readBundle();
117    }
118
119    public static final Parcelable.Creator<ViewIdGenerator> CREATOR =
120            new Parcelable.Creator<ViewIdGenerator>() {
121        public ViewIdGenerator createFromParcel(Parcel in) {
122            final ViewIdGenerator vig = new ViewIdGenerator();
123            vig.readFromParcel(in);
124            return vig;
125        }
126
127        public ViewIdGenerator[] newArray(int size) {
128            return new ViewIdGenerator[size];
129        }
130    };
131}
132