1/*
2 * Copyright (C) 2015 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 */
16package com.android.messaging.datamodel.media;
17
18import android.content.Intent;
19import android.content.res.Resources;
20import android.content.res.Resources.NotFoundException;
21import android.net.Uri;
22import android.provider.ContactsContract.CommonDataKinds.Im;
23import android.provider.ContactsContract.CommonDataKinds.Organization;
24import android.provider.ContactsContract.CommonDataKinds.Phone;
25import android.support.v4.util.ArrayMap;
26import android.text.TextUtils;
27
28import com.android.messaging.Factory;
29import com.android.messaging.R;
30import com.android.messaging.datamodel.MediaScratchFileProvider;
31import com.android.messaging.datamodel.data.PersonItemData;
32import com.android.messaging.util.ContactUtil;
33import com.android.messaging.util.LogUtil;
34import com.android.messaging.util.SafeAsyncTask;
35import com.android.vcard.VCardEntry;
36import com.android.vcard.VCardEntry.EmailData;
37import com.android.vcard.VCardEntry.ImData;
38import com.android.vcard.VCardEntry.NoteData;
39import com.android.vcard.VCardEntry.OrganizationData;
40import com.android.vcard.VCardEntry.PhoneData;
41import com.android.vcard.VCardEntry.PostalData;
42import com.android.vcard.VCardEntry.WebsiteData;
43import com.android.vcard.VCardProperty;
44
45import java.io.UnsupportedEncodingException;
46import java.net.URLEncoder;
47import java.util.ArrayList;
48import java.util.List;
49
50/**
51 * Holds one entry item (i.e. a single contact) within a VCard resource. It is able to take
52 * a VCardEntry and extract relevant information from it.
53 */
54public class VCardResourceEntry {
55    public static final String PROPERTY_KIND = "KIND";
56
57    public static final String KIND_LOCATION = "location";
58
59    private final List<VCardResourceEntry.VCardResourceEntryDestinationItem> mContactInfo;
60    private final Uri mAvatarUri;
61    private final String mDisplayName;
62    private final CustomVCardEntry mVCard;
63
64    public VCardResourceEntry(final CustomVCardEntry vcard, final Uri avatarUri) {
65        mContactInfo = getContactInfoFromVCardEntry(vcard);
66        mDisplayName = getDisplayNameFromVCardEntry(vcard);
67        mAvatarUri = avatarUri;
68        mVCard = vcard;
69    }
70
71    void close() {
72        // If the avatar image was temporarily saved in the scratch folder, remove that.
73        if (MediaScratchFileProvider.isMediaScratchSpaceUri(mAvatarUri)) {
74            SafeAsyncTask.executeOnThreadPool(new Runnable() {
75                @Override
76                public void run() {
77                    Factory.get().getApplicationContext().getContentResolver().delete(
78                            mAvatarUri, null, null);
79                }
80            });
81        }
82    }
83
84    public String getKind() {
85        VCardProperty kindProperty = mVCard.getProperty(PROPERTY_KIND);
86        return kindProperty == null ? null : kindProperty.getRawValue();
87    }
88
89    public Uri getAvatarUri() {
90        return mAvatarUri;
91    }
92
93    public String getDisplayName() {
94        return mDisplayName;
95    }
96
97    public String getDisplayAddress() {
98        List<PostalData> postalList = mVCard.getPostalList();
99        if (postalList == null || postalList.size() < 1) {
100            return null;
101        }
102
103        return formatAddress(postalList.get(0));
104    }
105
106    public String getNotes() {
107        List<NoteData> notes = mVCard.getNotes();
108        if (notes == null || notes.size() == 0) {
109            return null;
110        }
111        StringBuilder noteBuilder = new StringBuilder();
112        for (NoteData note : notes) {
113            noteBuilder.append(note.getNote());
114        }
115        return noteBuilder.toString();
116    }
117
118    /**
119     * Returns a UI-facing representation that can be bound and consumed by the UI layer to display
120     * this VCard resource entry.
121     */
122    public PersonItemData getDisplayItem() {
123        return new PersonItemData() {
124            @Override
125            public Uri getAvatarUri() {
126                return VCardResourceEntry.this.getAvatarUri();
127            }
128
129            @Override
130            public String getDisplayName() {
131                return VCardResourceEntry.this.getDisplayName();
132            }
133
134            @Override
135            public String getDetails() {
136                return null;
137            }
138
139            @Override
140            public Intent getClickIntent() {
141                return null;
142            }
143
144            @Override
145            public long getContactId() {
146                return ContactUtil.INVALID_CONTACT_ID;
147            }
148
149            @Override
150            public String getLookupKey() {
151                return null;
152            }
153
154            @Override
155            public String getNormalizedDestination() {
156                return null;
157            }
158        };
159    }
160
161    public List<VCardResourceEntry.VCardResourceEntryDestinationItem> getContactInfo() {
162        return mContactInfo;
163    }
164
165    private static List<VCardResourceEntryDestinationItem> getContactInfoFromVCardEntry(
166            final VCardEntry vcard) {
167        final Resources resources = Factory.get().getApplicationContext().getResources();
168        final List<VCardResourceEntry.VCardResourceEntryDestinationItem> retList =
169                new ArrayList<VCardResourceEntry.VCardResourceEntryDestinationItem>();
170        if (vcard.getPhoneList() != null) {
171            for (final PhoneData phone : vcard.getPhoneList()) {
172                final Intent intent = new Intent(Intent.ACTION_DIAL);
173                intent.setData(Uri.parse("tel:" + phone.getNumber()));
174                retList.add(new VCardResourceEntryDestinationItem(phone.getNumber(),
175                        Phone.getTypeLabel(resources, phone.getType(), phone.getLabel()).toString(),
176                        intent));
177            }
178        }
179
180        if (vcard.getEmailList() != null) {
181            for (final EmailData email : vcard.getEmailList()) {
182                final Intent intent = new Intent(Intent.ACTION_SENDTO);
183                intent.setData(Uri.parse("mailto:"));
184                intent.putExtra(Intent.EXTRA_EMAIL, new String[] { email.getAddress() });
185                retList.add(new VCardResourceEntryDestinationItem(email.getAddress(),
186                        Phone.getTypeLabel(resources, email.getType(),
187                                email.getLabel()).toString(), intent));
188            }
189        }
190
191        if (vcard.getPostalList() != null) {
192            for (final PostalData postalData : vcard.getPostalList()) {
193                String type;
194                try {
195                    type = resources.
196                            getStringArray(android.R.array.postalAddressTypes)
197                            [postalData.getType() - 1];
198                } catch (final NotFoundException ex) {
199                    type = resources.getStringArray(android.R.array.postalAddressTypes)[2];
200                } catch (final Exception e) {
201                    LogUtil.e(LogUtil.BUGLE_TAG, "createContactItem postal Exception:" + e);
202                    type = resources.getStringArray(android.R.array.postalAddressTypes)[2];
203                }
204                Intent intent = new Intent(Intent.ACTION_VIEW);
205                final String address = formatAddress(postalData);
206                try {
207                    intent.setData(Uri.parse("geo:0,0?q=" + URLEncoder.encode(address, "UTF-8")));
208                } catch (UnsupportedEncodingException e) {
209                    intent = null;
210                }
211
212                retList.add(new VCardResourceEntryDestinationItem(address, type, intent));
213            }
214        }
215
216        if (vcard.getImList() != null) {
217            for (final ImData imData : vcard.getImList()) {
218                String type = null;
219                try {
220                    type = resources.
221                            getString(Im.getProtocolLabelResource(imData.getProtocol()));
222                } catch (final NotFoundException ex) {
223                    // Do nothing since this implies an empty label.
224                }
225                retList.add(new VCardResourceEntryDestinationItem(imData.getAddress(), type, null));
226            }
227        }
228
229        if (vcard.getOrganizationList() != null) {
230            for (final OrganizationData organtization : vcard.getOrganizationList()) {
231                String type = null;
232                try {
233                     type = resources.getString(Organization.getTypeLabelResource(
234                                    organtization.getType()));
235                } catch (final NotFoundException ex) {
236                    //set other kind as "other"
237                    type = resources.getStringArray(android.R.array.organizationTypes)[1];
238                } catch (final Exception e) {
239                    LogUtil.e(LogUtil.BUGLE_TAG, "createContactItem org Exception:" + e);
240                    type = resources.getStringArray(android.R.array.organizationTypes)[1];
241                }
242                retList.add(new VCardResourceEntryDestinationItem(
243                        organtization.getOrganizationName(), type, null));
244            }
245        }
246
247        if (vcard.getWebsiteList() != null) {
248            for (final WebsiteData web : vcard.getWebsiteList()) {
249                if (web != null && TextUtils.isGraphic(web.getWebsite())){
250                    String website = web.getWebsite();
251                    if (!website.startsWith("http://") && !website.startsWith("https://")) {
252                        // Prefix required for parsing to end up with a scheme and result in
253                        // navigation
254                        website = "http://" + website;
255                    }
256                    final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(website));
257                    retList.add(new VCardResourceEntryDestinationItem(web.getWebsite(), null,
258                            intent));
259                }
260            }
261        }
262
263        if (vcard.getBirthday() != null) {
264             final String birthday = vcard.getBirthday();
265             if (TextUtils.isGraphic(birthday)){
266                 retList.add(new VCardResourceEntryDestinationItem(birthday,
267                         resources.getString(R.string.vcard_detail_birthday_label), null));
268             }
269        }
270
271        if (vcard.getNotes() != null) {
272            for (final NoteData note : vcard.getNotes()) {
273                 final ArrayMap<String, String> curChildMap = new ArrayMap<String, String>();
274                 if (TextUtils.isGraphic(note.getNote())){
275                     retList.add(new VCardResourceEntryDestinationItem(note.getNote(),
276                             resources.getString(R.string.vcard_detail_notes_label), null));
277                 }
278             }
279        }
280        return retList;
281    }
282
283    private static String formatAddress(final PostalData postalData) {
284        final StringBuilder sb = new StringBuilder();
285        final String poBox = postalData.getPobox();
286        if (!TextUtils.isEmpty(poBox)) {
287            sb.append(poBox);
288            sb.append(" ");
289        }
290        final String extendedAddress = postalData.getExtendedAddress();
291        if (!TextUtils.isEmpty(extendedAddress)) {
292            sb.append(extendedAddress);
293            sb.append(" ");
294        }
295        final String street = postalData.getStreet();
296        if (!TextUtils.isEmpty(street)) {
297            sb.append(street);
298            sb.append(" ");
299        }
300        final String localty = postalData.getLocalty();
301        if (!TextUtils.isEmpty(localty)) {
302            sb.append(localty);
303            sb.append(" ");
304        }
305        final String region = postalData.getRegion();
306        if (!TextUtils.isEmpty(region)) {
307            sb.append(region);
308            sb.append(" ");
309        }
310        final String postalCode = postalData.getPostalCode();
311        if (!TextUtils.isEmpty(postalCode)) {
312            sb.append(postalCode);
313            sb.append(" ");
314        }
315        final String country = postalData.getCountry();
316        if (!TextUtils.isEmpty(country)) {
317            sb.append(country);
318        }
319        return sb.toString();
320    }
321
322    private static String getDisplayNameFromVCardEntry(final VCardEntry vcard) {
323        String name = vcard.getDisplayName();
324        if (name == null) {
325            vcard.consolidateFields();
326            name = vcard.getDisplayName();
327        }
328        return name;
329    }
330
331    /**
332     * Represents one entry line (e.g. phone number and phone label) for a single contact. Each
333     * VCardResourceEntry may hold one or more VCardResourceEntryDestinationItem's.
334     */
335    public static class VCardResourceEntryDestinationItem {
336        private final String mDisplayDestination;
337        private final String mDestinationType;
338        private final Intent mClickIntent;
339        public VCardResourceEntryDestinationItem(final String displayDestination,
340                final String destinationType, final Intent clickIntent) {
341            mDisplayDestination = displayDestination;
342            mDestinationType = destinationType;
343            mClickIntent = clickIntent;
344        }
345
346        /**
347         * Returns a UI-facing representation that can be bound and consumed by the UI layer to
348         * display this VCard resource destination entry.
349         */
350        public PersonItemData getDisplayItem() {
351            return new PersonItemData() {
352                @Override
353                public Uri getAvatarUri() {
354                    return null;
355                }
356
357                @Override
358                public String getDisplayName() {
359                    return mDisplayDestination;
360                }
361
362                @Override
363                public String getDetails() {
364                    return mDestinationType;
365                }
366
367                @Override
368                public Intent getClickIntent() {
369                    return mClickIntent;
370                }
371
372                @Override
373                public long getContactId() {
374                    return ContactUtil.INVALID_CONTACT_ID;
375                }
376
377                @Override
378                public String getLookupKey() {
379                    return null;
380                }
381
382                @Override
383                public String getNormalizedDestination() {
384                    return null;
385                }
386            };
387        }
388    }
389}
390