1/*
2 * Copyright (C) 2012 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 static android.provider.ContactsContract.CommonDataKinds.GroupMembership;
20import static android.provider.ContactsContract.CommonDataKinds.StructuredName;
21
22import static com.android.contacts.util.MaterialColorMapUtils.getDefaultPrimaryAndSecondaryColors;
23
24import android.content.Context;
25import android.content.res.Resources;
26import android.graphics.Bitmap;
27import android.graphics.BitmapFactory;
28import android.graphics.drawable.Drawable;
29import android.media.RingtoneManager;
30import android.net.Uri;
31import android.os.Build;
32import android.provider.ContactsContract.CommonDataKinds.Email;
33import android.provider.ContactsContract.CommonDataKinds.Event;
34import android.provider.ContactsContract.CommonDataKinds.Im;
35import android.provider.ContactsContract.CommonDataKinds.Note;
36import android.provider.ContactsContract.CommonDataKinds.Organization;
37import android.provider.ContactsContract.CommonDataKinds.Phone;
38import android.provider.ContactsContract.CommonDataKinds.Photo;
39import android.provider.ContactsContract.CommonDataKinds.Relation;
40import android.provider.ContactsContract.CommonDataKinds.SipAddress;
41import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
42import android.provider.ContactsContract.CommonDataKinds.Website;
43import android.support.v4.content.res.ResourcesCompat;
44import android.text.TextUtils;
45import android.widget.ImageView;
46
47import com.android.contacts.ContactPhotoManager;
48import com.android.contacts.ContactPhotoManager.DefaultImageProvider;
49import com.android.contacts.ContactPhotoManager.DefaultImageRequest;
50import com.android.contacts.ContactsUtils;
51import com.android.contacts.R;
52import com.android.contacts.model.ValuesDelta;
53import com.android.contacts.model.account.AccountDisplayInfo;
54import com.android.contacts.model.account.AccountInfo;
55import com.android.contacts.model.dataitem.DataKind;
56import com.android.contacts.util.ContactPhotoUtils;
57import com.android.contacts.util.MaterialColorMapUtils.MaterialPalette;
58import com.android.contacts.widget.QuickContactImageView;
59
60import com.google.common.collect.Maps;
61
62import java.io.FileNotFoundException;
63import java.util.HashMap;
64
65/**
66 * Utility methods for creating contact editor.
67 */
68public class EditorUiUtils {
69
70    // Maps DataKind.mimeType to editor view layouts.
71    private static final HashMap<String, Integer> mimetypeLayoutMap = Maps.newHashMap();
72    static {
73        // Generally there should be a layout mapped to each existing DataKind mimetype but lots of
74        // them use the default text_fields_editor_view which we return as default so they don't
75        // need to be mapped.
76        //
77        // Other possible mime mappings are:
78        // DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME
79        // Nickname.CONTENT_ITEM_TYPE
80        // Email.CONTENT_ITEM_TYPE
81        // StructuredPostal.CONTENT_ITEM_TYPE
82        // Im.CONTENT_ITEM_TYPE
83        // Note.CONTENT_ITEM_TYPE
84        // Organization.CONTENT_ITEM_TYPE
85        // Phone.CONTENT_ITEM_TYPE
86        // SipAddress.CONTENT_ITEM_TYPE
87        // Website.CONTENT_ITEM_TYPE
88        // Relation.CONTENT_ITEM_TYPE
89        //
90        // Un-supported mime types need to mapped with -1.
91        mimetypeLayoutMap.put(StructuredName.CONTENT_ITEM_TYPE,
92                R.layout.structured_name_editor_view);
93        mimetypeLayoutMap.put(GroupMembership.CONTENT_ITEM_TYPE, -1);
94        mimetypeLayoutMap.put(Photo.CONTENT_ITEM_TYPE, -1);
95        mimetypeLayoutMap.put(Event.CONTENT_ITEM_TYPE, R.layout.event_field_editor_view);
96    }
97
98    /**
99     * Fetches a layout for a given mimetype.
100     *
101     * @param mimetype The mime type (e.g. StructuredName.CONTENT_ITEM_TYPE)
102     * @return The layout resource id.
103     */
104    public static int getLayoutResourceId(String mimetype) {
105        final Integer id = mimetypeLayoutMap.get(mimetype);
106        if (id == null) {
107            return R.layout.text_fields_editor_view;
108        }
109        return id;
110    }
111
112
113    public static String getAccountHeaderLabelForMyProfile(Context context,
114            AccountInfo accountInfo) {
115        if (accountInfo.isDeviceAccount()) {
116            return context.getString(R.string.local_profile_title);
117        } else {
118            return context.getString(R.string.external_profile_title,
119                    accountInfo.getTypeLabel());
120        }
121    }
122
123    public static String getAccountTypeHeaderLabel(Context context, AccountDisplayInfo
124            displayableAccount)  {
125        if (displayableAccount.isDeviceAccount()) {
126            // Do nothing. Type label should be "Device"
127            return displayableAccount.getTypeLabel().toString();
128        } else if (displayableAccount.isGoogleAccount()) {
129            return context.getString(R.string.google_account_type_format,
130                    displayableAccount.getTypeLabel());
131        } else {
132            return context.getString(R.string.account_type_format,
133                    displayableAccount.getTypeLabel());
134        }
135    }
136
137    /**
138     * Returns a content description String for the container of the account information
139     * returned by {@link #getAccountTypeHeaderLabel(Context, AccountDisplayInfo)}.
140     */
141    public static String getAccountInfoContentDescription(CharSequence accountName,
142            CharSequence accountType) {
143        final StringBuilder builder = new StringBuilder();
144        if (!TextUtils.isEmpty(accountType)) {
145            builder.append(accountType).append('\n');
146        }
147        if (!TextUtils.isEmpty(accountName)) {
148            builder.append(accountName);
149        }
150        return builder.toString();
151    }
152
153    /**
154     * Return an icon that represents {@param mimeType}.
155     */
156    public static Drawable getMimeTypeDrawable(Context context, String mimeType) {
157        switch (mimeType) {
158            case StructuredName.CONTENT_ITEM_TYPE:
159                return ResourcesCompat.getDrawable(context.getResources(),
160                        R.drawable.quantum_ic_person_vd_theme_24, null);
161            case StructuredPostal.CONTENT_ITEM_TYPE:
162                return ResourcesCompat.getDrawable(context.getResources(),
163                        R.drawable.quantum_ic_place_vd_theme_24, null);
164            case SipAddress.CONTENT_ITEM_TYPE:
165                return ResourcesCompat.getDrawable(context.getResources(),
166                        R.drawable.quantum_ic_dialer_sip_vd_theme_24, null);
167            case Phone.CONTENT_ITEM_TYPE:
168                return ResourcesCompat.getDrawable(context.getResources(),
169                        R.drawable.quantum_ic_phone_vd_theme_24, null);
170            case Im.CONTENT_ITEM_TYPE:
171                return ResourcesCompat.getDrawable(context.getResources(),
172                        R.drawable.quantum_ic_message_vd_theme_24, null);
173            case Event.CONTENT_ITEM_TYPE:
174                return ResourcesCompat.getDrawable(context.getResources(),
175                        R.drawable.quantum_ic_event_vd_theme_24, null);
176            case Email.CONTENT_ITEM_TYPE:
177                return ResourcesCompat.getDrawable(context.getResources(),
178                        R.drawable.quantum_ic_email_vd_theme_24, null);
179            case Website.CONTENT_ITEM_TYPE:
180                return ResourcesCompat.getDrawable(context.getResources(),
181                        R.drawable.quantum_ic_public_vd_theme_24, null);
182            case Photo.CONTENT_ITEM_TYPE:
183                return ResourcesCompat.getDrawable(context.getResources(),
184                        R.drawable.quantum_ic_camera_alt_vd_theme_24, null);
185            case GroupMembership.CONTENT_ITEM_TYPE:
186                return ResourcesCompat.getDrawable(context.getResources(),
187                        R.drawable.quantum_ic_label_vd_theme_24, null);
188            case Organization.CONTENT_ITEM_TYPE:
189                return ResourcesCompat.getDrawable(context.getResources(),
190                        R.drawable.quantum_ic_business_vd_theme_24, null);
191            case Note.CONTENT_ITEM_TYPE:
192                return ResourcesCompat.getDrawable(context.getResources(),
193                        R.drawable.quantum_ic_insert_comment_vd_theme_24, null);
194            case Relation.CONTENT_ITEM_TYPE:
195                return ResourcesCompat.getDrawable(context.getResources(),
196                        R.drawable.quantum_ic_circles_ext_vd_theme_24, null);
197            default:
198                return null;
199        }
200    }
201
202    /**
203     * Returns a ringtone string based on the ringtone URI and version #.
204     */
205    public static String getRingtoneStringFromUri(Uri pickedUri, int currentVersion) {
206        if (isNewerThanM(currentVersion)) {
207            if (pickedUri == null) return ""; // silent ringtone
208            if (RingtoneManager.isDefault(pickedUri)) return null; // default ringtone
209        }
210        if (pickedUri == null || RingtoneManager.isDefault(pickedUri)) return null;
211        return pickedUri.toString();
212    }
213
214    /**
215     * Returns a ringtone URI, based on the string and version #.
216     */
217    public static Uri getRingtoneUriFromString(String str, int currentVersion) {
218        if (str != null) {
219            if (isNewerThanM(currentVersion) && TextUtils.isEmpty(str)) return null;
220            return Uri.parse(str);
221        }
222        return RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
223    }
224
225    private static boolean isNewerThanM(int currentVersion) {
226        return currentVersion > Build.VERSION_CODES.M;
227    }
228
229    /** Returns the {@link Photo#PHOTO_FILE_ID} from the given ValuesDelta. */
230    public static Long getPhotoFileId(ValuesDelta valuesDelta) {
231        if (valuesDelta == null) return null;
232        if (valuesDelta.getAfter() == null || valuesDelta.getAfter().get(Photo.PHOTO) == null) {
233            return valuesDelta.getAsLong(Photo.PHOTO_FILE_ID);
234        }
235        return null;
236    }
237
238    /** Binds the full resolution image at the given Uri to the provided ImageView. */
239    static void loadPhoto(ContactPhotoManager contactPhotoManager, ImageView imageView,
240            Uri photoUri) {
241        final DefaultImageProvider fallbackToPreviousImage = new DefaultImageProvider() {
242            @Override
243            public void applyDefaultImage(ImageView view, int extent, boolean darkTheme,
244                    DefaultImageRequest defaultImageRequest) {
245                // Before we finish setting the full sized image, don't change the current
246                // image that is set in any way.
247            }
248        };
249        contactPhotoManager.loadPhoto(imageView, photoUri, imageView.getWidth(),
250                /* darkTheme =*/ false, /* isCircular =*/ false,
251                /* defaultImageRequest =*/ null, fallbackToPreviousImage);
252    }
253
254    /** Decodes the Bitmap from the photo bytes from the given ValuesDelta. */
255    public static Bitmap getPhotoBitmap(ValuesDelta valuesDelta) {
256        if (valuesDelta == null) return null;
257        final byte[] bytes = valuesDelta.getAsByteArray(Photo.PHOTO);
258        if (bytes == null) return null;
259        return BitmapFactory.decodeByteArray(bytes, /* offset =*/ 0, bytes.length);
260    }
261
262    /** Binds the default avatar to the given ImageView and tints it to match QuickContacts. */
263    public static void setDefaultPhoto(ImageView imageView , Resources resources,
264            MaterialPalette materialPalette) {
265        // Use the default avatar drawable
266        imageView.setImageDrawable(ContactPhotoManager.getDefaultAvatarDrawableForContact(
267                resources, /* hires =*/ false, /* defaultImageRequest =*/ null));
268
269        // Tint it to match the quick contacts
270        if (imageView instanceof QuickContactImageView) {
271            ((QuickContactImageView) imageView).setTint(materialPalette == null
272                    ? getDefaultPrimaryAndSecondaryColors(resources).mPrimaryColor
273                    : materialPalette.mPrimaryColor);
274        }
275    }
276
277    /**  Returns compressed bitmap bytes from the given Uri, scaled to the thumbnail dimensions. */
278    public static byte[] getCompressedThumbnailBitmapBytes(Context context, Uri uri)
279            throws FileNotFoundException {
280        final Bitmap bitmap = ContactPhotoUtils.getBitmapFromUri(context, uri);
281        final int size = ContactsUtils.getThumbnailSize(context);
282        final Bitmap bitmapScaled = Bitmap.createScaledBitmap(
283                bitmap, size, size, /* filter =*/ false);
284        return ContactPhotoUtils.compressBitmap(bitmapScaled);
285    }
286
287}
288