1d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng/*
2d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Copyright (C) 2010 The Android Open Source Project
3d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng *
4d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Licensed under the Apache License, Version 2.0 (the "License");
5d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * you may not use this file except in compliance with the License.
6d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * You may obtain a copy of the License at
7d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng *
8d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng *      http://www.apache.org/licenses/LICENSE-2.0
9d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng *
10d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Unless required by applicable law or agreed to in writing, software
11d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * distributed under the License is distributed on an "AS IS" BASIS,
12d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * See the License for the specific language governing permissions and
14d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * limitations under the License.
15d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */
16d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengpackage com.android.contacts.common.list;
17d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
18d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.content.ContentUris;
19d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.content.Context;
20d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.content.CursorLoader;
21d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.content.SharedPreferences;
22d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.database.Cursor;
23d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.net.Uri;
24d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.net.Uri.Builder;
25d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.preference.PreferenceManager;
26d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.provider.ContactsContract;
27d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.provider.ContactsContract.Contacts;
28d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.provider.ContactsContract.Directory;
29b321f58e96516c007297492bc0e9287c9150b39cYorke Leeimport android.provider.ContactsContract.SearchSnippets;
30d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.text.TextUtils;
31d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.view.View;
32d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
33d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport com.android.contacts.common.preference.ContactsPreferences;
34d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
35d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport java.util.ArrayList;
36d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport java.util.List;
37d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
38d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng/**
39d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * A cursor adapter for the {@link ContactsContract.Contacts#CONTENT_TYPE} content type.
40d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */
41d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengpublic class DefaultContactListAdapter extends ContactListAdapter {
42d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
438c0712579331e77a66b23966f4eb9d7e0ab1d1f6Yorke Lee    public static final char SNIPPET_START_MATCH = '[';
448c0712579331e77a66b23966f4eb9d7e0ab1d1f6Yorke Lee    public static final char SNIPPET_END_MATCH = ']';
45d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
46d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng    public DefaultContactListAdapter(Context context) {
47d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        super(context);
48d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng    }
49d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
50d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng    @Override
51d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng    public void configureLoader(CursorLoader loader, long directoryId) {
52d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        if (loader instanceof ProfileAndContactsLoader) {
53d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            ((ProfileAndContactsLoader) loader).setLoadProfile(shouldIncludeProfile());
54d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        }
55d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
56d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        ContactListFilter filter = getFilter();
57d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        if (isSearchMode()) {
58d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            String query = getQueryString();
59d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            if (query == null) {
60d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                query = "";
61d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            }
62d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            query = query.trim();
63d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            if (TextUtils.isEmpty(query)) {
64d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                // Regardless of the directory, we don't want anything returned,
65d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                // so let's just send a "nothing" query to the local directory.
66d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                loader.setUri(Contacts.CONTENT_URI);
67d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                loader.setProjection(getProjection(false));
68d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                loader.setSelection("0");
69d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            } else {
70d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                Builder builder = Contacts.CONTENT_FILTER_URI.buildUpon();
71d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                builder.appendPath(query);      // Builder will encode the query
72d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
73d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                        String.valueOf(directoryId));
74d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                if (directoryId != Directory.DEFAULT && directoryId != Directory.LOCAL_INVISIBLE) {
75d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                    builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
76ad582fa758c6a276c26c5cc52079613a653c81bfBen Gilad                            String.valueOf(getDirectoryResultLimit(getDirectoryById(directoryId))));
77d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                }
78b321f58e96516c007297492bc0e9287c9150b39cYorke Lee                builder.appendQueryParameter(SearchSnippets.DEFERRED_SNIPPETING_KEY,"1");
79d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                loader.setUri(builder.build());
80d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                loader.setProjection(getProjection(true));
81d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            }
82d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        } else {
83d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            configureUri(loader, directoryId, filter);
84d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            loader.setProjection(getProjection(false));
85d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            configureSelection(loader, directoryId, filter);
86d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        }
87d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
88d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        String sortOrder;
89495c732e471c1c68db91ca6e824440130fdb7146Yorke Lee        if (getSortOrder() == ContactsPreferences.SORT_ORDER_PRIMARY) {
90d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            sortOrder = Contacts.SORT_KEY_PRIMARY;
91d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        } else {
92d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            sortOrder = Contacts.SORT_KEY_ALTERNATIVE;
93d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        }
94d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
95d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        loader.setSortOrder(sortOrder);
96d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng    }
97d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
98d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng    protected void configureUri(CursorLoader loader, long directoryId, ContactListFilter filter) {
99d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        Uri uri = Contacts.CONTENT_URI;
100d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        if (filter != null && filter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
101d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            String lookupKey = getSelectedContactLookupKey();
102d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            if (lookupKey != null) {
103d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                uri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey);
104d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            } else {
105d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, getSelectedContactId());
106d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            }
107d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        }
108d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
109d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        if (directoryId == Directory.DEFAULT && isSectionHeaderDisplayEnabled()) {
110d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            uri = ContactListAdapter.buildSectionIndexerUri(uri);
111d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        }
112d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
113d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        // The "All accounts" filter is the same as the entire contents of Directory.DEFAULT
114d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        if (filter != null
115d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                && filter.filterType != ContactListFilter.FILTER_TYPE_CUSTOM
116d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                && filter.filterType != ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
117d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            final Uri.Builder builder = uri.buildUpon();
118d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            builder.appendQueryParameter(
119d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                    ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT));
120d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            if (filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) {
121d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                filter.addAccountQueryParameterToUrl(builder);
122d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            }
123d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            uri = builder.build();
124d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        }
125d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
126d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        loader.setUri(uri);
127d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng    }
128d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
129d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng    private void configureSelection(
130d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            CursorLoader loader, long directoryId, ContactListFilter filter) {
131d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        if (filter == null) {
132d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            return;
133d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        }
134d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
135d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        if (directoryId != Directory.DEFAULT) {
136d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            return;
137d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        }
138d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
139d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        StringBuilder selection = new StringBuilder();
140d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        List<String> selectionArgs = new ArrayList<String>();
141d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
142d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        switch (filter.filterType) {
143d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            case ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS: {
144d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                // We have already added directory=0 to the URI, which takes care of this
145d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                // filter
146d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                break;
147d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            }
148d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            case ContactListFilter.FILTER_TYPE_SINGLE_CONTACT: {
149d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                // We have already added the lookup key to the URI, which takes care of this
150d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                // filter
151d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                break;
152d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            }
153d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            case ContactListFilter.FILTER_TYPE_STARRED: {
154d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                selection.append(Contacts.STARRED + "!=0");
155d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                break;
156d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            }
157d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            case ContactListFilter.FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY: {
158d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                selection.append(Contacts.HAS_PHONE_NUMBER + "=1");
159d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                break;
160d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            }
161d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            case ContactListFilter.FILTER_TYPE_CUSTOM: {
162d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                selection.append(Contacts.IN_VISIBLE_GROUP + "=1");
163d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                if (isCustomFilterForPhoneNumbersOnly()) {
164d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                    selection.append(" AND " + Contacts.HAS_PHONE_NUMBER + "=1");
165d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                }
166d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                break;
167d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            }
168d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            case ContactListFilter.FILTER_TYPE_ACCOUNT: {
169d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                // We use query parameters for account filter, so no selection to add here.
170d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                break;
171d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            }
172d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        }
173d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        loader.setSelection(selection.toString());
174d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        loader.setSelectionArgs(selectionArgs.toArray(new String[0]));
175d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng    }
176d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
177d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng    @Override
178d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng    protected void bindView(View itemView, int partition, Cursor cursor, int position) {
179aca984b5bc95a5e3c82ab79319694cb60b443315Brian Attwell        super.bindView(itemView, partition, cursor, position);
180d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        final ContactListItemView view = (ContactListItemView)itemView;
181d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
182d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        view.setHighlightedPrefix(isSearchMode() ? getUpperCaseQueryString() : null);
183d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
184d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        if (isSelectionVisible()) {
185d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            view.setActivated(isSelectedContact(partition, cursor));
186d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        }
187d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
188d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        bindSectionHeaderAndDivider(view, position, cursor);
189d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
190d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        if (isQuickContactEnabled()) {
191d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            bindQuickContact(view, partition, cursor, ContactQuery.CONTACT_PHOTO_ID,
192d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                    ContactQuery.CONTACT_PHOTO_URI, ContactQuery.CONTACT_ID,
1936084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                    ContactQuery.CONTACT_LOOKUP_KEY, ContactQuery.CONTACT_DISPLAY_NAME);
194d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        } else {
195d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            if (getDisplayPhotos()) {
196d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                bindPhoto(view, partition, cursor);
197d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            }
198d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        }
199d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
2004cbafe71ec04d4a511fe894c46915bb69c91b804Brian Attwell        bindNameAndViewId(view, cursor);
201d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        bindPresenceAndStatusMessage(view, cursor);
202d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
203d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        if (isSearchMode()) {
204d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            bindSearchSnippet(view, cursor);
205d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        } else {
206d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng            view.setSnippet(null);
207d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        }
208d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng    }
209d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng
210d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng    private boolean isCustomFilterForPhoneNumbersOnly() {
211d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        // TODO: this flag should not be stored in shared prefs.  It needs to be in the db.
212d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
213d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng        return prefs.getBoolean(ContactsPreferences.PREF_DISPLAY_ONLY_PHONES,
214d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng                ContactsPreferences.PREF_DISPLAY_ONLY_PHONES_DEFAULT);
215d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng    }
216d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng}
217