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 
17 package com.android.contacts.editor;
18 
19 import android.os.Bundle;
20 import android.os.Parcel;
21 import android.os.Parcelable;
22 
23 import com.android.contacts.common.model.RawContactDelta;
24 import com.android.contacts.common.model.ValuesDelta;
25 import 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  */
35 public 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 
ViewIdGenerator()53     public ViewIdGenerator() {
54         mNextId = INITIAL_VIEW_ID;
55     }
56 
57     /** {@inheritDoc} */
describeContents()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      */
getId(RawContactDelta entity, DataKind kind, ValuesDelta values, int viewIndex)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 
getMapKey(RawContactDelta entity, DataKind kind, ValuesDelta values, int viewIndex)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} */
writeToParcel(Parcel dest, int flags)109     public void writeToParcel(Parcel dest, int flags) {
110         dest.writeInt(mNextId);
111         dest.writeBundle(mIdMap);
112     }
113 
readFromParcel(Parcel src)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