Home | History | Annotate | Download | only in impl
      1 /*
      2  * Copyright (C) 2017 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.dialer.simulator.impl;
     18 
     19 import android.content.ContentProviderOperation;
     20 import android.content.Context;
     21 import android.content.OperationApplicationException;
     22 import android.graphics.Bitmap;
     23 import android.graphics.Canvas;
     24 import android.graphics.Color;
     25 import android.graphics.Paint;
     26 import android.os.RemoteException;
     27 import android.provider.ContactsContract;
     28 import android.provider.ContactsContract.CommonDataKinds.Phone;
     29 import android.support.annotation.NonNull;
     30 import android.support.annotation.Nullable;
     31 import android.support.annotation.WorkerThread;
     32 import android.text.TextUtils;
     33 import com.android.dialer.common.Assert;
     34 import com.google.auto.value.AutoValue;
     35 import java.io.ByteArrayOutputStream;
     36 import java.util.ArrayList;
     37 import java.util.List;
     38 
     39 /** Populates the device database with contacts. */
     40 public final class SimulatorContacts {
     41   // Phone numbers from https://www.google.com/about/company/facts/locations/
     42   private static final Contact[] SIMPLE_CONTACTS = {
     43     // US, contact with e164 number.
     44     Contact.builder()
     45         .setName("Michelangelo")
     46         .addPhoneNumber(new PhoneNumber("+1-302-6365454", Phone.TYPE_MOBILE))
     47         .addEmail(new Email("m (at) example.com"))
     48         .setIsStarred(true)
     49         .setOrangePhoto()
     50         .build(),
     51     // US, contact with a non-e164 number.
     52     Contact.builder()
     53         .setName("Leonardo da Vinci")
     54         .addPhoneNumber(new PhoneNumber("(425) 739-5600", Phone.TYPE_MOBILE))
     55         .addEmail(new Email("l (at) example.com"))
     56         .setIsStarred(true)
     57         .setBluePhoto()
     58         .build(),
     59     // UK, number where the (0) should be dropped.
     60     Contact.builder()
     61         .setName("Raphael")
     62         .addPhoneNumber(new PhoneNumber("+44 (0) 20 7031 3000", Phone.TYPE_MOBILE))
     63         .addEmail(new Email("r (at) example.com"))
     64         .setIsStarred(true)
     65         .setRedPhoto()
     66         .build(),
     67     // US and Australia, contact with a long name and multiple phone numbers.
     68     Contact.builder()
     69         .setName("Donatello di Niccol di Betto Bardi")
     70         .addPhoneNumber(new PhoneNumber("+1-650-2530000", Phone.TYPE_HOME))
     71         .addPhoneNumber(new PhoneNumber("+1 404-487-9000", Phone.TYPE_WORK))
     72         .addPhoneNumber(new PhoneNumber("+61 2 9374 4001", Phone.TYPE_FAX_HOME))
     73         .setIsStarred(true)
     74         .setPurplePhoto()
     75         .build(),
     76     // US, phone number shared with another contact and 2nd phone number with wait and pause.
     77     Contact.builder()
     78         .setName("Splinter")
     79         .addPhoneNumber(new PhoneNumber("+1-650-2530000", Phone.TYPE_HOME))
     80         .addPhoneNumber(new PhoneNumber("+1 303-245-0086;123,456", Phone.TYPE_WORK))
     81         .build(),
     82     // France, number with Japanese name.
     83     Contact.builder()
     84         .setName("")
     85         .addPhoneNumber(new PhoneNumber("+33 (0)1 42 68 53 00", Phone.TYPE_MOBILE))
     86         .build(),
     87     // Israel, RTL name and non-e164 number.
     88     Contact.builder()
     89         .setName("  ")
     90         .addPhoneNumber(new PhoneNumber("+33 (0)1 42 68 53 00", Phone.TYPE_MOBILE))
     91         .build(),
     92     // UAE, RTL name.
     93     Contact.builder()
     94         .setName(" ")
     95         .addPhoneNumber(new PhoneNumber("+971 4 4509500", Phone.TYPE_MOBILE))
     96         .build(),
     97     // Brazil, contact with no name.
     98     Contact.builder()
     99         .addPhoneNumber(new PhoneNumber("+55-31-2128-6800", Phone.TYPE_MOBILE))
    100         .build(),
    101     // Short number, contact with no name.
    102     Contact.builder().addPhoneNumber(new PhoneNumber("611", Phone.TYPE_MOBILE)).build(),
    103     // US, number with an anonymous prefix.
    104     Contact.builder()
    105         .setName("Anonymous")
    106         .addPhoneNumber(new PhoneNumber("*86 512-343-5283", Phone.TYPE_MOBILE))
    107         .build(),
    108     // None, contact with no phone number.
    109     Contact.builder()
    110         .setName("No Phone Number")
    111         .addEmail(new Email("no (at) example.com"))
    112         .setIsStarred(true)
    113         .build(),
    114   };
    115 
    116   @WorkerThread
    117   public static void populateContacts(@NonNull Context context) {
    118     Assert.isWorkerThread();
    119     ArrayList<ContentProviderOperation> operations = new ArrayList<>();
    120     for (Contact contact : SIMPLE_CONTACTS) {
    121       addContact(contact, operations);
    122     }
    123     try {
    124       context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operations);
    125     } catch (RemoteException | OperationApplicationException e) {
    126       Assert.fail("error adding contacts: " + e);
    127     }
    128   }
    129 
    130   private static void addContact(Contact contact, List<ContentProviderOperation> operations) {
    131     int index = operations.size();
    132 
    133     operations.add(
    134         ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
    135             .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, contact.getAccountType())
    136             .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, contact.getAccountName())
    137             .withValue(ContactsContract.RawContacts.STARRED, contact.getIsStarred() ? 1 : 0)
    138             .withYieldAllowed(true)
    139             .build());
    140 
    141     if (!TextUtils.isEmpty(contact.getName())) {
    142       operations.add(
    143           ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
    144               .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, index)
    145               .withValue(
    146                   ContactsContract.Data.MIMETYPE,
    147                   ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
    148               .withValue(
    149                   ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, contact.getName())
    150               .build());
    151     }
    152 
    153     if (contact.getPhotoStream() != null) {
    154       operations.add(
    155           ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
    156               .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, index)
    157               .withValue(
    158                   ContactsContract.Data.MIMETYPE,
    159                   ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
    160               .withValue(
    161                   ContactsContract.CommonDataKinds.Photo.PHOTO,
    162                   contact.getPhotoStream().toByteArray())
    163               .build());
    164     }
    165 
    166     for (PhoneNumber phoneNumber : contact.getPhoneNumbers()) {
    167       operations.add(
    168           ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
    169               .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, index)
    170               .withValue(
    171                   ContactsContract.Data.MIMETYPE,
    172                   ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
    173               .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phoneNumber.value)
    174               .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneNumber.type)
    175               .withValue(ContactsContract.CommonDataKinds.Phone.LABEL, phoneNumber.label)
    176               .build());
    177     }
    178 
    179     for (Email email : contact.getEmails()) {
    180       operations.add(
    181           ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
    182               .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, index)
    183               .withValue(
    184                   ContactsContract.Data.MIMETYPE,
    185                   ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
    186               .withValue(ContactsContract.CommonDataKinds.Email.DATA, email.value)
    187               .withValue(ContactsContract.CommonDataKinds.Email.TYPE, email.type)
    188               .withValue(ContactsContract.CommonDataKinds.Email.LABEL, email.label)
    189               .build());
    190     }
    191   }
    192 
    193   @AutoValue
    194   abstract static class Contact {
    195     @NonNull
    196     abstract String getAccountType();
    197 
    198     @NonNull
    199     abstract String getAccountName();
    200 
    201     @Nullable
    202     abstract String getName();
    203 
    204     abstract boolean getIsStarred();
    205 
    206     @Nullable
    207     abstract ByteArrayOutputStream getPhotoStream();
    208 
    209     @NonNull
    210     abstract List<PhoneNumber> getPhoneNumbers();
    211 
    212     @NonNull
    213     abstract List<Email> getEmails();
    214 
    215     static Builder builder() {
    216       return new AutoValue_SimulatorContacts_Contact.Builder()
    217           .setAccountType("com.google")
    218           .setAccountName("foo@example")
    219           .setIsStarred(false)
    220           .setPhoneNumbers(new ArrayList<>())
    221           .setEmails(new ArrayList<>());
    222     }
    223 
    224     @AutoValue.Builder
    225     abstract static class Builder {
    226       @NonNull private final List<PhoneNumber> phoneNumbers = new ArrayList<>();
    227       @NonNull private final List<Email> emails = new ArrayList<>();
    228 
    229       abstract Builder setAccountType(@NonNull String accountType);
    230 
    231       abstract Builder setAccountName(@NonNull String accountName);
    232 
    233       abstract Builder setName(@NonNull String name);
    234 
    235       abstract Builder setIsStarred(boolean isStarred);
    236 
    237       abstract Builder setPhotoStream(ByteArrayOutputStream photoStream);
    238 
    239       abstract Builder setPhoneNumbers(@NonNull List<PhoneNumber> phoneNumbers);
    240 
    241       abstract Builder setEmails(@NonNull List<Email> emails);
    242 
    243       abstract Contact build();
    244 
    245       Builder addPhoneNumber(PhoneNumber phoneNumber) {
    246         phoneNumbers.add(phoneNumber);
    247         return setPhoneNumbers(phoneNumbers);
    248       }
    249 
    250       Builder addEmail(Email email) {
    251         emails.add(email);
    252         return setEmails(emails);
    253       }
    254 
    255       Builder setRedPhoto() {
    256         setPhotoStream(getPhotoStreamWithColor(Color.rgb(0xe3, 0x33, 0x1c)));
    257         return this;
    258       }
    259 
    260       Builder setBluePhoto() {
    261         setPhotoStream(getPhotoStreamWithColor(Color.rgb(0x00, 0xaa, 0xe6)));
    262         return this;
    263       }
    264 
    265       Builder setOrangePhoto() {
    266         setPhotoStream(getPhotoStreamWithColor(Color.rgb(0xea, 0x95, 0x00)));
    267         return this;
    268       }
    269 
    270       Builder setPurplePhoto() {
    271         setPhotoStream(getPhotoStreamWithColor(Color.rgb(0x99, 0x5a, 0xa0)));
    272         return this;
    273       }
    274 
    275       /** Creates a contact photo with a green background and a circle of the given color. */
    276       private static ByteArrayOutputStream getPhotoStreamWithColor(int color) {
    277         int width = 300;
    278         int height = 300;
    279         Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    280         Canvas canvas = new Canvas(bitmap);
    281         canvas.drawColor(Color.argb(0xff, 0x4c, 0x9c, 0x23));
    282         Paint paint = new Paint();
    283         paint.setColor(color);
    284         paint.setStyle(Paint.Style.FILL);
    285         canvas.drawCircle(width / 2, height / 2, width / 3, paint);
    286 
    287         ByteArrayOutputStream photoStream = new ByteArrayOutputStream();
    288         bitmap.compress(Bitmap.CompressFormat.PNG, 75, photoStream);
    289         return photoStream;
    290       }
    291     }
    292   }
    293 
    294   static class PhoneNumber {
    295     public final String value;
    296     public final int type;
    297     public final String label;
    298 
    299     PhoneNumber(String value, int type) {
    300       this.value = value;
    301       this.type = type;
    302       label = "simulator phone number";
    303     }
    304   }
    305 
    306   static class Email {
    307     public final String value;
    308     public final int type;
    309     public final String label;
    310 
    311     Email(String simpleEmail) {
    312       value = simpleEmail;
    313       type = ContactsContract.CommonDataKinds.Email.TYPE_WORK;
    314       label = "simulator email";
    315     }
    316   }
    317 
    318   private SimulatorContacts() {}
    319 }
    320