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