blob: f22552db77e984c8ce9141b1ab984f14efa68d8f [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.dialer.databasepopulator;
import android.content.ContentProviderOperation;
import android.content.Context;
import android.content.OperationApplicationException;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.RawContacts;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import android.text.TextUtils;
import com.android.dialer.common.Assert;
import com.google.auto.value.AutoValue;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/** Populates the device database with contacts. */
public final class ContactsPopulator {
// Phone numbers from https://www.google.com/about/company/facts/locations/
private static final Contact[] SIMPLE_CONTACTS = {
// US, contact with e164 number.
Contact.builder()
.setName("Michelangelo")
.addPhoneNumber(new PhoneNumber("+1-302-6365454", Phone.TYPE_MOBILE))
.addEmail(new Email("m@example.com"))
.setIsStarred(true)
.setPinned(1)
.setOrangePhoto()
.build(),
// US, contact with a non-e164 number.
Contact.builder()
.setName("Leonardo da Vinci")
.addPhoneNumber(new PhoneNumber("(425) 739-5600", Phone.TYPE_MOBILE))
.addEmail(new Email("l@example.com"))
.setIsStarred(true)
.setPinned(2)
.setBluePhoto()
.build(),
// UK, number where the (0) should be dropped.
Contact.builder()
.setName("Raphael")
.addPhoneNumber(new PhoneNumber("+44 (0) 20 7031 3000", Phone.TYPE_MOBILE))
.addEmail(new Email("r@example.com"))
.setIsStarred(true)
.setPinned(3)
.setRedPhoto()
.build(),
// US and Australia, contact with a long name and multiple phone numbers.
Contact.builder()
.setName("Donatello di Niccolò di Betto Bardi")
.addPhoneNumber(new PhoneNumber("+1-650-2530000", Phone.TYPE_HOME))
.addPhoneNumber(new PhoneNumber("+1 404-487-9000", Phone.TYPE_WORK))
.addPhoneNumber(new PhoneNumber("+61 2 9374 4001", Phone.TYPE_FAX_HOME))
.setIsStarred(true)
.setPinned(4)
.setPurplePhoto()
.build(),
// US, phone number shared with another contact and 2nd phone number with wait and pause.
Contact.builder()
.setName("Splinter")
.addPhoneNumber(new PhoneNumber("+1-650-2530000", Phone.TYPE_HOME))
.addPhoneNumber(new PhoneNumber("+1 303-245-0086;123,456", Phone.TYPE_WORK))
.setBluePhoto()
.build(),
// France, number with Japanese name.
Contact.builder()
.setName("スパイク・スピーゲル")
.addPhoneNumber(new PhoneNumber("+33 (0)1 42 68 53 00", Phone.TYPE_MOBILE))
.setBluePhoto()
.build(),
// Israel, RTL name and non-e164 number.
Contact.builder()
.setName("עקב אריה טברסק")
.addPhoneNumber(new PhoneNumber("+33 (0)1 42 68 53 00", Phone.TYPE_MOBILE))
.setBluePhoto()
.build(),
// UAE, RTL name.
Contact.builder()
.setName("سلام دنیا")
.addPhoneNumber(new PhoneNumber("+971 4 4509500", Phone.TYPE_MOBILE))
.setBluePhoto()
.build(),
// Brazil, contact with no name.
Contact.builder()
.addPhoneNumber(new PhoneNumber("+55-31-2128-6800", Phone.TYPE_MOBILE))
.setBluePhoto()
.build(),
// Short number, contact with no name.
Contact.builder().addPhoneNumber(new PhoneNumber("611", Phone.TYPE_MOBILE)).build(),
// US, number with an anonymous prefix.
Contact.builder()
.setName("Anonymous")
.addPhoneNumber(new PhoneNumber("*86 512-343-5283", Phone.TYPE_MOBILE))
.setBluePhoto()
.build(),
// None, contact with no phone number.
Contact.builder()
.setName("No Phone Number")
.addEmail(new Email("no@example.com"))
.setIsStarred(true)
.setBluePhoto()
.build(),
};
@WorkerThread
public static void populateContacts(@NonNull Context context, boolean fastMode) {
Assert.isWorkerThread();
ArrayList<ContentProviderOperation> operations = new ArrayList<>();
List<Contact> contacts = new ArrayList<>();
if (fastMode) {
contacts.add(SIMPLE_CONTACTS[0]);
} else {
contacts = Arrays.asList(SIMPLE_CONTACTS);
}
for (Contact contact : contacts) {
addContact(contact, operations);
}
try {
context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operations);
} catch (RemoteException | OperationApplicationException e) {
Assert.fail("error adding contacts: " + e);
}
}
@WorkerThread
public static void populateSpeedDialTestContacts(@NonNull Context context) {
Assert.isWorkerThread();
ArrayList<ContentProviderOperation> operations = new ArrayList<>();
addContact(SIMPLE_CONTACTS[0], operations);
addContact(SIMPLE_CONTACTS[5], operations);
try {
context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operations);
} catch (RemoteException | OperationApplicationException e) {
Assert.fail("error adding contacts: " + e);
}
}
@WorkerThread
public static void populateContacts(@NonNull Context context) {
populateContacts(context, false);
}
@WorkerThread
public static void deleteAllContacts(@NonNull Context context) {
Assert.isWorkerThread();
try {
context
.getContentResolver()
.applyBatch(
ContactsContract.AUTHORITY,
new ArrayList<>(
Arrays.asList(
ContentProviderOperation.newDelete(RawContacts.CONTENT_URI).build())));
} catch (RemoteException | OperationApplicationException e) {
Assert.fail("failed to delete contacts: " + e);
}
}
private static void addContact(Contact contact, List<ContentProviderOperation> operations) {
int index = operations.size();
operations.add(
ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, contact.getAccountType())
.withValue(ContactsContract.RawContacts.ACCOUNT_NAME, contact.getAccountName())
.withValue(ContactsContract.RawContacts.STARRED, contact.getIsStarred() ? 1 : 0)
.withValue(
ContactsContract.RawContacts.PINNED,
contact.getIsStarred() ? contact.getPinned() : 0)
.withYieldAllowed(true)
.build());
if (!TextUtils.isEmpty(contact.getName())) {
operations.add(
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, index)
.withValue(
ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
.withValue(
ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, contact.getName())
.build());
}
if (contact.getPhotoStream() != null) {
operations.add(
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, index)
.withValue(
ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
.withValue(
ContactsContract.CommonDataKinds.Photo.PHOTO,
contact.getPhotoStream().toByteArray())
.build());
}
for (PhoneNumber phoneNumber : contact.getPhoneNumbers()) {
operations.add(
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, index)
.withValue(
ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phoneNumber.value)
.withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneNumber.type)
.withValue(ContactsContract.CommonDataKinds.Phone.LABEL, phoneNumber.label)
.build());
}
for (Email email : contact.getEmails()) {
operations.add(
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, index)
.withValue(
ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Email.DATA, email.value)
.withValue(ContactsContract.CommonDataKinds.Email.TYPE, email.type)
.withValue(ContactsContract.CommonDataKinds.Email.LABEL, email.label)
.build());
}
}
@AutoValue
abstract static class Contact {
@NonNull
abstract String getAccountType();
@NonNull
abstract String getAccountName();
@Nullable
abstract String getName();
abstract boolean getIsStarred();
abstract int getPinned();
@Nullable
abstract ByteArrayOutputStream getPhotoStream();
@NonNull
abstract List<PhoneNumber> getPhoneNumbers();
@NonNull
abstract List<Email> getEmails();
static Builder builder() {
return new AutoValue_ContactsPopulator_Contact.Builder()
.setAccountType("com.google")
.setAccountName("foo@example")
.setPinned(0)
.setIsStarred(false)
.setPhoneNumbers(new ArrayList<>())
.setEmails(new ArrayList<>());
}
@AutoValue.Builder
abstract static class Builder {
@NonNull private final List<PhoneNumber> phoneNumbers = new ArrayList<>();
@NonNull private final List<Email> emails = new ArrayList<>();
abstract Builder setAccountType(@NonNull String accountType);
abstract Builder setAccountName(@NonNull String accountName);
abstract Builder setName(@NonNull String name);
abstract Builder setIsStarred(boolean isStarred);
abstract Builder setPinned(int position);
abstract Builder setPhotoStream(ByteArrayOutputStream photoStream);
abstract Builder setPhoneNumbers(@NonNull List<PhoneNumber> phoneNumbers);
abstract Builder setEmails(@NonNull List<Email> emails);
abstract Contact build();
Builder addPhoneNumber(PhoneNumber phoneNumber) {
phoneNumbers.add(phoneNumber);
return setPhoneNumbers(phoneNumbers);
}
Builder addEmail(Email email) {
emails.add(email);
return setEmails(emails);
}
Builder setRedPhoto() {
setPhotoStream(getPhotoStreamWithColor(Color.rgb(0xe3, 0x33, 0x1c)));
return this;
}
Builder setBluePhoto() {
setPhotoStream(getPhotoStreamWithColor(Color.rgb(0x00, 0xaa, 0xe6)));
return this;
}
Builder setOrangePhoto() {
setPhotoStream(getPhotoStreamWithColor(Color.rgb(0xea, 0x95, 0x00)));
return this;
}
Builder setPurplePhoto() {
setPhotoStream(getPhotoStreamWithColor(Color.rgb(0x99, 0x5a, 0xa0)));
return this;
}
/** Creates a contact photo with a green background and a circle of the given color. */
private static ByteArrayOutputStream getPhotoStreamWithColor(int color) {
int width = 300;
int height = 300;
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawColor(Color.argb(0xff, 0x4c, 0x9c, 0x23));
Paint paint = new Paint();
paint.setColor(color);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(width / 2, height / 2, width / 3, paint);
ByteArrayOutputStream photoStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 75, photoStream);
return photoStream;
}
}
}
static class PhoneNumber {
public final String value;
public final int type;
public final String label;
PhoneNumber(String value, int type) {
this.value = value;
this.type = type;
label = "simulator phone number";
}
}
static class Email {
public final String value;
public final int type;
public final String label;
Email(String simpleEmail) {
value = simpleEmail;
type = ContactsContract.CommonDataKinds.Email.TYPE_WORK;
label = "simulator email";
}
}
private ContactsPopulator() {}
}