summaryrefslogtreecommitdiff
path: root/java/com/android/dialer/simulator
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/android/dialer/simulator')
-rw-r--r--java/com/android/dialer/simulator/Simulator.java27
-rw-r--r--java/com/android/dialer/simulator/impl/AndroidManifest.xml18
-rw-r--r--java/com/android/dialer/simulator/impl/AutoValue_SimulatorCallLog_CallEntry.java160
-rw-r--r--java/com/android/dialer/simulator/impl/AutoValue_SimulatorContacts_Contact.java231
-rw-r--r--java/com/android/dialer/simulator/impl/AutoValue_SimulatorVoicemail_Voicemail.java184
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorActionProvider.java88
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorCallLog.java139
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorConnection.java56
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorConnectionService.java87
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorContacts.java319
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorModule.java34
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java47
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorVoicemail.java154
13 files changed, 1544 insertions, 0 deletions
diff --git a/java/com/android/dialer/simulator/Simulator.java b/java/com/android/dialer/simulator/Simulator.java
new file mode 100644
index 000000000..78058a48f
--- /dev/null
+++ b/java/com/android/dialer/simulator/Simulator.java
@@ -0,0 +1,27 @@
+/*
+ * 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.simulator;
+
+import android.content.Context;
+import android.view.ActionProvider;
+
+/** Used to add menu items to the Dialer menu to test the app using simulated calls and data. */
+public interface Simulator {
+ boolean shouldShow();
+
+ ActionProvider getActionProvider(Context context);
+}
diff --git a/java/com/android/dialer/simulator/impl/AndroidManifest.xml b/java/com/android/dialer/simulator/impl/AndroidManifest.xml
new file mode 100644
index 000000000..a30504d3f
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.dialer.simulator.impl">
+
+ <application>
+
+ <service
+ android:exported="true"
+ android:name=".SimulatorConnectionService"
+ android:permission="android.permission.BIND_CONNECTION_SERVICE">
+ <intent-filter>
+ <action android:name="android.telecomm.ConnectionService"/>
+ </intent-filter>
+ </service>
+
+ </application>
+
+</manifest>
diff --git a/java/com/android/dialer/simulator/impl/AutoValue_SimulatorCallLog_CallEntry.java b/java/com/android/dialer/simulator/impl/AutoValue_SimulatorCallLog_CallEntry.java
new file mode 100644
index 000000000..591819819
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/AutoValue_SimulatorCallLog_CallEntry.java
@@ -0,0 +1,160 @@
+/*
+ * 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.simulator.impl;
+
+import android.support.annotation.NonNull;
+import javax.annotation.Generated;
+
+@Generated("com.google.auto.value.processor.AutoValueProcessor")
+ final class AutoValue_SimulatorCallLog_CallEntry extends SimulatorCallLog.CallEntry {
+
+ private final String number;
+ private final int type;
+ private final int presentation;
+ private final long timeMillis;
+
+ private AutoValue_SimulatorCallLog_CallEntry(
+ String number,
+ int type,
+ int presentation,
+ long timeMillis) {
+ this.number = number;
+ this.type = type;
+ this.presentation = presentation;
+ this.timeMillis = timeMillis;
+ }
+
+ @NonNull
+ @Override
+ String getNumber() {
+ return number;
+ }
+
+ @Override
+ int getType() {
+ return type;
+ }
+
+ @Override
+ int getPresentation() {
+ return presentation;
+ }
+
+ @Override
+ long getTimeMillis() {
+ return timeMillis;
+ }
+
+ @Override
+ public String toString() {
+ return "CallEntry{"
+ + "number=" + number + ", "
+ + "type=" + type + ", "
+ + "presentation=" + presentation + ", "
+ + "timeMillis=" + timeMillis
+ + "}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof SimulatorCallLog.CallEntry) {
+ SimulatorCallLog.CallEntry that = (SimulatorCallLog.CallEntry) o;
+ return (this.number.equals(that.getNumber()))
+ && (this.type == that.getType())
+ && (this.presentation == that.getPresentation())
+ && (this.timeMillis == that.getTimeMillis());
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int h = 1;
+ h *= 1000003;
+ h ^= this.number.hashCode();
+ h *= 1000003;
+ h ^= this.type;
+ h *= 1000003;
+ h ^= this.presentation;
+ h *= 1000003;
+ h ^= (this.timeMillis >>> 32) ^ this.timeMillis;
+ return h;
+ }
+
+ static final class Builder extends SimulatorCallLog.CallEntry.Builder {
+ private String number;
+ private Integer type;
+ private Integer presentation;
+ private Long timeMillis;
+ Builder() {
+ }
+ private Builder(SimulatorCallLog.CallEntry source) {
+ this.number = source.getNumber();
+ this.type = source.getType();
+ this.presentation = source.getPresentation();
+ this.timeMillis = source.getTimeMillis();
+ }
+ @Override
+ SimulatorCallLog.CallEntry.Builder setNumber(String number) {
+ this.number = number;
+ return this;
+ }
+ @Override
+ SimulatorCallLog.CallEntry.Builder setType(int type) {
+ this.type = type;
+ return this;
+ }
+ @Override
+ SimulatorCallLog.CallEntry.Builder setPresentation(int presentation) {
+ this.presentation = presentation;
+ return this;
+ }
+ @Override
+ SimulatorCallLog.CallEntry.Builder setTimeMillis(long timeMillis) {
+ this.timeMillis = timeMillis;
+ return this;
+ }
+ @Override
+ SimulatorCallLog.CallEntry build() {
+ String missing = "";
+ if (this.number == null) {
+ missing += " number";
+ }
+ if (this.type == null) {
+ missing += " type";
+ }
+ if (this.presentation == null) {
+ missing += " presentation";
+ }
+ if (this.timeMillis == null) {
+ missing += " timeMillis";
+ }
+ if (!missing.isEmpty()) {
+ throw new IllegalStateException("Missing required properties:" + missing);
+ }
+ return new AutoValue_SimulatorCallLog_CallEntry(
+ this.number,
+ this.type,
+ this.presentation,
+ this.timeMillis);
+ }
+ }
+
+}
diff --git a/java/com/android/dialer/simulator/impl/AutoValue_SimulatorContacts_Contact.java b/java/com/android/dialer/simulator/impl/AutoValue_SimulatorContacts_Contact.java
new file mode 100644
index 000000000..00295f359
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/AutoValue_SimulatorContacts_Contact.java
@@ -0,0 +1,231 @@
+/*
+ * 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.simulator.impl;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import java.io.ByteArrayOutputStream;
+import java.util.List;
+import javax.annotation.Generated;
+
+@Generated("com.google.auto.value.processor.AutoValueProcessor")
+ final class AutoValue_SimulatorContacts_Contact extends SimulatorContacts.Contact {
+
+ private final String accountType;
+ private final String accountName;
+ private final String name;
+ private final boolean isStarred;
+ private final ByteArrayOutputStream photoStream;
+ private final List<SimulatorContacts.PhoneNumber> phoneNumbers;
+ private final List<SimulatorContacts.Email> emails;
+
+ private AutoValue_SimulatorContacts_Contact(
+ String accountType,
+ String accountName,
+ @Nullable String name,
+ boolean isStarred,
+ @Nullable ByteArrayOutputStream photoStream,
+ List<SimulatorContacts.PhoneNumber> phoneNumbers,
+ List<SimulatorContacts.Email> emails) {
+ this.accountType = accountType;
+ this.accountName = accountName;
+ this.name = name;
+ this.isStarred = isStarred;
+ this.photoStream = photoStream;
+ this.phoneNumbers = phoneNumbers;
+ this.emails = emails;
+ }
+
+ @NonNull
+ @Override
+ String getAccountType() {
+ return accountType;
+ }
+
+ @NonNull
+ @Override
+ String getAccountName() {
+ return accountName;
+ }
+
+ @Nullable
+ @Override
+ String getName() {
+ return name;
+ }
+
+ @Override
+ boolean getIsStarred() {
+ return isStarred;
+ }
+
+ @Nullable
+ @Override
+ ByteArrayOutputStream getPhotoStream() {
+ return photoStream;
+ }
+
+ @NonNull
+ @Override
+ List<SimulatorContacts.PhoneNumber> getPhoneNumbers() {
+ return phoneNumbers;
+ }
+
+ @NonNull
+ @Override
+ List<SimulatorContacts.Email> getEmails() {
+ return emails;
+ }
+
+ @Override
+ public String toString() {
+ return "Contact{"
+ + "accountType=" + accountType + ", "
+ + "accountName=" + accountName + ", "
+ + "name=" + name + ", "
+ + "isStarred=" + isStarred + ", "
+ + "photoStream=" + photoStream + ", "
+ + "phoneNumbers=" + phoneNumbers + ", "
+ + "emails=" + emails
+ + "}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof SimulatorContacts.Contact) {
+ SimulatorContacts.Contact that = (SimulatorContacts.Contact) o;
+ return (this.accountType.equals(that.getAccountType()))
+ && (this.accountName.equals(that.getAccountName()))
+ && ((this.name == null) ? (that.getName() == null) : this.name.equals(that.getName()))
+ && (this.isStarred == that.getIsStarred())
+ && ((this.photoStream == null) ? (that.getPhotoStream() == null) : this.photoStream.equals(that.getPhotoStream()))
+ && (this.phoneNumbers.equals(that.getPhoneNumbers()))
+ && (this.emails.equals(that.getEmails()));
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int h = 1;
+ h *= 1000003;
+ h ^= this.accountType.hashCode();
+ h *= 1000003;
+ h ^= this.accountName.hashCode();
+ h *= 1000003;
+ h ^= (name == null) ? 0 : this.name.hashCode();
+ h *= 1000003;
+ h ^= this.isStarred ? 1231 : 1237;
+ h *= 1000003;
+ h ^= (photoStream == null) ? 0 : this.photoStream.hashCode();
+ h *= 1000003;
+ h ^= this.phoneNumbers.hashCode();
+ h *= 1000003;
+ h ^= this.emails.hashCode();
+ return h;
+ }
+
+ static final class Builder extends SimulatorContacts.Contact.Builder {
+ private String accountType;
+ private String accountName;
+ private String name;
+ private Boolean isStarred;
+ private ByteArrayOutputStream photoStream;
+ private List<SimulatorContacts.PhoneNumber> phoneNumbers;
+ private List<SimulatorContacts.Email> emails;
+ Builder() {
+ }
+ private Builder(SimulatorContacts.Contact source) {
+ this.accountType = source.getAccountType();
+ this.accountName = source.getAccountName();
+ this.name = source.getName();
+ this.isStarred = source.getIsStarred();
+ this.photoStream = source.getPhotoStream();
+ this.phoneNumbers = source.getPhoneNumbers();
+ this.emails = source.getEmails();
+ }
+ @Override
+ SimulatorContacts.Contact.Builder setAccountType(String accountType) {
+ this.accountType = accountType;
+ return this;
+ }
+ @Override
+ SimulatorContacts.Contact.Builder setAccountName(String accountName) {
+ this.accountName = accountName;
+ return this;
+ }
+ @Override
+ SimulatorContacts.Contact.Builder setName(@Nullable String name) {
+ this.name = name;
+ return this;
+ }
+ @Override
+ SimulatorContacts.Contact.Builder setIsStarred(boolean isStarred) {
+ this.isStarred = isStarred;
+ return this;
+ }
+ @Override
+ SimulatorContacts.Contact.Builder setPhotoStream(@Nullable ByteArrayOutputStream photoStream) {
+ this.photoStream = photoStream;
+ return this;
+ }
+ @Override
+ SimulatorContacts.Contact.Builder setPhoneNumbers(List<SimulatorContacts.PhoneNumber> phoneNumbers) {
+ this.phoneNumbers = phoneNumbers;
+ return this;
+ }
+ @Override
+ SimulatorContacts.Contact.Builder setEmails(List<SimulatorContacts.Email> emails) {
+ this.emails = emails;
+ return this;
+ }
+ @Override
+ SimulatorContacts.Contact build() {
+ String missing = "";
+ if (this.accountType == null) {
+ missing += " accountType";
+ }
+ if (this.accountName == null) {
+ missing += " accountName";
+ }
+ if (this.isStarred == null) {
+ missing += " isStarred";
+ }
+ if (this.phoneNumbers == null) {
+ missing += " phoneNumbers";
+ }
+ if (this.emails == null) {
+ missing += " emails";
+ }
+ if (!missing.isEmpty()) {
+ throw new IllegalStateException("Missing required properties:" + missing);
+ }
+ return new AutoValue_SimulatorContacts_Contact(
+ this.accountType,
+ this.accountName,
+ this.name,
+ this.isStarred,
+ this.photoStream,
+ this.phoneNumbers,
+ this.emails);
+ }
+ }
+
+}
diff --git a/java/com/android/dialer/simulator/impl/AutoValue_SimulatorVoicemail_Voicemail.java b/java/com/android/dialer/simulator/impl/AutoValue_SimulatorVoicemail_Voicemail.java
new file mode 100644
index 000000000..58934801c
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/AutoValue_SimulatorVoicemail_Voicemail.java
@@ -0,0 +1,184 @@
+/*
+ * 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.simulator.impl;
+
+import android.support.annotation.NonNull;
+import javax.annotation.Generated;
+
+@Generated("com.google.auto.value.processor.AutoValueProcessor")
+ final class AutoValue_SimulatorVoicemail_Voicemail extends SimulatorVoicemail.Voicemail {
+
+ private final String phoneNumber;
+ private final String transcription;
+ private final long durationSeconds;
+ private final long timeMillis;
+ private final boolean isRead;
+
+ private AutoValue_SimulatorVoicemail_Voicemail(
+ String phoneNumber,
+ String transcription,
+ long durationSeconds,
+ long timeMillis,
+ boolean isRead) {
+ this.phoneNumber = phoneNumber;
+ this.transcription = transcription;
+ this.durationSeconds = durationSeconds;
+ this.timeMillis = timeMillis;
+ this.isRead = isRead;
+ }
+
+ @NonNull
+ @Override
+ String getPhoneNumber() {
+ return phoneNumber;
+ }
+
+ @NonNull
+ @Override
+ String getTranscription() {
+ return transcription;
+ }
+
+ @Override
+ long getDurationSeconds() {
+ return durationSeconds;
+ }
+
+ @Override
+ long getTimeMillis() {
+ return timeMillis;
+ }
+
+ @Override
+ boolean getIsRead() {
+ return isRead;
+ }
+
+ @Override
+ public String toString() {
+ return "Voicemail{"
+ + "phoneNumber=" + phoneNumber + ", "
+ + "transcription=" + transcription + ", "
+ + "durationSeconds=" + durationSeconds + ", "
+ + "timeMillis=" + timeMillis + ", "
+ + "isRead=" + isRead
+ + "}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof SimulatorVoicemail.Voicemail) {
+ SimulatorVoicemail.Voicemail that = (SimulatorVoicemail.Voicemail) o;
+ return (this.phoneNumber.equals(that.getPhoneNumber()))
+ && (this.transcription.equals(that.getTranscription()))
+ && (this.durationSeconds == that.getDurationSeconds())
+ && (this.timeMillis == that.getTimeMillis())
+ && (this.isRead == that.getIsRead());
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int h = 1;
+ h *= 1000003;
+ h ^= this.phoneNumber.hashCode();
+ h *= 1000003;
+ h ^= this.transcription.hashCode();
+ h *= 1000003;
+ h ^= (this.durationSeconds >>> 32) ^ this.durationSeconds;
+ h *= 1000003;
+ h ^= (this.timeMillis >>> 32) ^ this.timeMillis;
+ h *= 1000003;
+ h ^= this.isRead ? 1231 : 1237;
+ return h;
+ }
+
+ static final class Builder extends SimulatorVoicemail.Voicemail.Builder {
+ private String phoneNumber;
+ private String transcription;
+ private Long durationSeconds;
+ private Long timeMillis;
+ private Boolean isRead;
+ Builder() {
+ }
+ private Builder(SimulatorVoicemail.Voicemail source) {
+ this.phoneNumber = source.getPhoneNumber();
+ this.transcription = source.getTranscription();
+ this.durationSeconds = source.getDurationSeconds();
+ this.timeMillis = source.getTimeMillis();
+ this.isRead = source.getIsRead();
+ }
+ @Override
+ SimulatorVoicemail.Voicemail.Builder setPhoneNumber(String phoneNumber) {
+ this.phoneNumber = phoneNumber;
+ return this;
+ }
+ @Override
+ SimulatorVoicemail.Voicemail.Builder setTranscription(String transcription) {
+ this.transcription = transcription;
+ return this;
+ }
+ @Override
+ SimulatorVoicemail.Voicemail.Builder setDurationSeconds(long durationSeconds) {
+ this.durationSeconds = durationSeconds;
+ return this;
+ }
+ @Override
+ SimulatorVoicemail.Voicemail.Builder setTimeMillis(long timeMillis) {
+ this.timeMillis = timeMillis;
+ return this;
+ }
+ @Override
+ SimulatorVoicemail.Voicemail.Builder setIsRead(boolean isRead) {
+ this.isRead = isRead;
+ return this;
+ }
+ @Override
+ SimulatorVoicemail.Voicemail build() {
+ String missing = "";
+ if (this.phoneNumber == null) {
+ missing += " phoneNumber";
+ }
+ if (this.transcription == null) {
+ missing += " transcription";
+ }
+ if (this.durationSeconds == null) {
+ missing += " durationSeconds";
+ }
+ if (this.timeMillis == null) {
+ missing += " timeMillis";
+ }
+ if (this.isRead == null) {
+ missing += " isRead";
+ }
+ if (!missing.isEmpty()) {
+ throw new IllegalStateException("Missing required properties:" + missing);
+ }
+ return new AutoValue_SimulatorVoicemail_Voicemail(
+ this.phoneNumber,
+ this.transcription,
+ this.durationSeconds,
+ this.timeMillis,
+ this.isRead);
+ }
+ }
+
+}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorActionProvider.java b/java/com/android/dialer/simulator/impl/SimulatorActionProvider.java
new file mode 100644
index 000000000..6cd573361
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/SimulatorActionProvider.java
@@ -0,0 +1,88 @@
+/*
+ * 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.simulator.impl;
+
+import android.content.Context;
+import android.os.AsyncTask;
+import android.support.annotation.NonNull;
+import android.view.ActionProvider;
+import android.view.MenuItem;
+import android.view.SubMenu;
+import android.view.View;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+
+/** Implements the simulator submenu. */
+final class SimulatorActionProvider extends ActionProvider {
+ @NonNull private final Context context;
+
+ public SimulatorActionProvider(@NonNull Context context) {
+ super(Assert.isNotNull(context));
+ this.context = context;
+ }
+
+ @Override
+ public View onCreateActionView() {
+ LogUtil.enterBlock("SimulatorActionProvider.onCreateActionView(null)");
+ return null;
+ }
+
+ @Override
+ public View onCreateActionView(MenuItem forItem) {
+ LogUtil.enterBlock("SimulatorActionProvider.onCreateActionView(MenuItem)");
+ return null;
+ }
+
+ @Override
+ public boolean hasSubMenu() {
+ LogUtil.enterBlock("SimulatorActionProvider.hasSubMenu");
+ return true;
+ }
+
+ @Override
+ public void onPrepareSubMenu(SubMenu subMenu) {
+ super.onPrepareSubMenu(subMenu);
+ LogUtil.enterBlock("SimulatorActionProvider.onPrepareSubMenu");
+ subMenu.clear();
+ subMenu
+ .add("Add call")
+ .setOnMenuItemClickListener(
+ (item) -> {
+ SimulatorVoiceCall.addNewIncomingCall(context);
+ return true;
+ });
+ subMenu
+ .add("Populate database")
+ .setOnMenuItemClickListener(
+ (item) -> {
+ populateDatabase();
+ return true;
+ });
+ }
+
+ private void populateDatabase() {
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ public Void doInBackground(Void... params) {
+ SimulatorContacts.populateContacts(context);
+ SimulatorCallLog.populateCallLog(context);
+ SimulatorVoicemail.populateVoicemail(context);
+ return null;
+ }
+ }.execute();
+ }
+}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorCallLog.java b/java/com/android/dialer/simulator/impl/SimulatorCallLog.java
new file mode 100644
index 000000000..9ace047d0
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/SimulatorCallLog.java
@@ -0,0 +1,139 @@
+/*
+ * 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.simulator.impl;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.OperationApplicationException;
+import android.os.RemoteException;
+import android.provider.CallLog;
+import android.provider.CallLog.Calls;
+import android.support.annotation.NonNull;
+import android.support.annotation.WorkerThread;
+import com.android.dialer.common.Assert;
+
+import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
+
+/** Populates the device database with call log entries. */
+final class SimulatorCallLog {
+ // Phone numbers from https://www.google.com/about/company/facts/locations/
+ private static final CallEntry.Builder[] SIMPLE_CALL_LOG = {
+ CallEntry.builder().setType(Calls.MISSED_TYPE).setNumber("+1-302-6365454"),
+ CallEntry.builder()
+ .setType(Calls.MISSED_TYPE)
+ .setNumber("")
+ .setPresentation(Calls.PRESENTATION_UNKNOWN),
+ CallEntry.builder().setType(Calls.REJECTED_TYPE).setNumber("+1-302-6365454"),
+ CallEntry.builder().setType(Calls.INCOMING_TYPE).setNumber("+1-302-6365454"),
+ CallEntry.builder()
+ .setType(Calls.MISSED_TYPE)
+ .setNumber("1234")
+ .setPresentation(Calls.PRESENTATION_RESTRICTED),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("+1-302-6365454"),
+ CallEntry.builder().setType(Calls.BLOCKED_TYPE).setNumber("+1-302-6365454"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("(425) 739-5600"),
+ CallEntry.builder().setType(Calls.ANSWERED_EXTERNALLY_TYPE).setNumber("(425) 739-5600"),
+ CallEntry.builder().setType(Calls.MISSED_TYPE).setNumber("+1 (425) 739-5600"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("739-5600"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("711"),
+ CallEntry.builder().setType(Calls.INCOMING_TYPE).setNumber("711"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("(425) 739-5600"),
+ CallEntry.builder().setType(Calls.MISSED_TYPE).setNumber("+44 (0) 20 7031 3000"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("+1-650-2530000"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("+1 303-245-0086;123,456"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("+1 303-245-0086"),
+ CallEntry.builder().setType(Calls.INCOMING_TYPE).setNumber("+1-650-2530000"),
+ CallEntry.builder().setType(Calls.MISSED_TYPE).setNumber("650-2530000"),
+ CallEntry.builder().setType(Calls.REJECTED_TYPE).setNumber("2530000"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("+1 404-487-9000"),
+ CallEntry.builder().setType(Calls.INCOMING_TYPE).setNumber("+61 2 9374 4001"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("+33 (0)1 42 68 53 00"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("972-74-746-6245"),
+ CallEntry.builder().setType(Calls.INCOMING_TYPE).setNumber("+971 4 4509500"),
+ CallEntry.builder().setType(Calls.INCOMING_TYPE).setNumber("+971 4 4509500"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("55-31-2128-6800"),
+ CallEntry.builder().setType(Calls.MISSED_TYPE).setNumber("611"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("*86 512-343-5283"),
+ };
+
+ @WorkerThread
+ public static void populateCallLog(@NonNull Context context) {
+ Assert.isWorkerThread();
+ ArrayList<ContentProviderOperation> operations = new ArrayList<>();
+ // Do this 4 times to make the call log 4 times bigger.
+ long timeMillis = System.currentTimeMillis();
+ for (int i = 0; i < 4; i++) {
+ for (CallEntry.Builder builder : SIMPLE_CALL_LOG) {
+ CallEntry callEntry = builder.setTimeMillis(timeMillis).build();
+ operations.add(
+ ContentProviderOperation.newInsert(Calls.CONTENT_URI)
+ .withValues(callEntry.getAsContentValues())
+ .withYieldAllowed(true)
+ .build());
+ timeMillis -= TimeUnit.HOURS.toMillis(1);
+ }
+ }
+ try {
+ context.getContentResolver().applyBatch(CallLog.AUTHORITY, operations);
+ } catch (RemoteException | OperationApplicationException e) {
+ Assert.fail("error adding call entries: " + e);
+ }
+ }
+
+
+ abstract static class CallEntry {
+ @NonNull
+ abstract String getNumber();
+
+ abstract int getType();
+
+ abstract int getPresentation();
+
+ abstract long getTimeMillis();
+
+ static Builder builder() {
+ return new AutoValue_SimulatorCallLog_CallEntry.Builder()
+ .setPresentation(Calls.PRESENTATION_ALLOWED);
+ }
+
+ ContentValues getAsContentValues() {
+ ContentValues values = new ContentValues();
+ values.put(Calls.TYPE, getType());
+ values.put(Calls.NUMBER, getNumber());
+ values.put(Calls.NUMBER_PRESENTATION, getPresentation());
+ values.put(Calls.DATE, getTimeMillis());
+ return values;
+ }
+
+
+ abstract static class Builder {
+ abstract Builder setNumber(@NonNull String number);
+
+ abstract Builder setType(int type);
+
+ abstract Builder setPresentation(int presentation);
+
+ abstract Builder setTimeMillis(long timeMillis);
+
+ abstract CallEntry build();
+ }
+ }
+
+ private SimulatorCallLog() {}
+}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorConnection.java b/java/com/android/dialer/simulator/impl/SimulatorConnection.java
new file mode 100644
index 000000000..12d095890
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/SimulatorConnection.java
@@ -0,0 +1,56 @@
+/*
+ * 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.simulator.impl;
+
+import android.telecom.Connection;
+import android.telecom.DisconnectCause;
+import com.android.dialer.common.LogUtil;
+
+/** Represents a single phone call on the device. */
+final class SimulatorConnection extends Connection {
+
+ @Override
+ public void onAnswer() {
+ LogUtil.enterBlock("SimulatorConnection.onAnswer");
+ setActive();
+ }
+
+ @Override
+ public void onReject() {
+ LogUtil.enterBlock("SimulatorConnection.onReject");
+ setDisconnected(new DisconnectCause(DisconnectCause.REJECTED));
+ }
+
+ @Override
+ public void onHold() {
+ LogUtil.enterBlock("SimulatorConnection.onHold");
+ setOnHold();
+ }
+
+ @Override
+ public void onUnhold() {
+ LogUtil.enterBlock("SimulatorConnection.onUnhold");
+ setActive();
+ }
+
+ @Override
+ public void onDisconnect() {
+ LogUtil.enterBlock("SimulatorConnection.onDisconnect");
+ setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
+ destroy();
+ }
+}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorConnectionService.java b/java/com/android/dialer/simulator/impl/SimulatorConnectionService.java
new file mode 100644
index 000000000..322360786
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/SimulatorConnectionService.java
@@ -0,0 +1,87 @@
+/*
+ * 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.simulator.impl;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.Uri;
+import android.telecom.Connection;
+import android.telecom.ConnectionRequest;
+import android.telecom.ConnectionService;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
+import com.android.dialer.common.LogUtil;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Simple connection provider to create an incoming call. This is useful for emulators. */
+public final class SimulatorConnectionService extends ConnectionService {
+
+ private static final String PHONE_ACCOUNT_ID = "SIMULATOR_ACCOUNT_ID";
+
+ public static void register(Context context) {
+ LogUtil.enterBlock("SimulatorConnectionService.register");
+ context.getSystemService(TelecomManager.class).registerPhoneAccount(buildPhoneAccount(context));
+ }
+
+ private static PhoneAccount buildPhoneAccount(Context context) {
+ PhoneAccount.Builder builder =
+ new PhoneAccount.Builder(
+ getConnectionServiceHandle(context), "Simulator connection service");
+ List<String> uriSchemes = new ArrayList<>();
+ uriSchemes.add(PhoneAccount.SCHEME_TEL);
+
+ return builder
+ .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
+ .setShortDescription("Simulator Connection Service")
+ .setSupportedUriSchemes(uriSchemes)
+ .build();
+ }
+
+ public static PhoneAccountHandle getConnectionServiceHandle(Context context) {
+ return new PhoneAccountHandle(
+ new ComponentName(context, SimulatorConnectionService.class), PHONE_ACCOUNT_ID);
+ }
+
+ private static Uri getPhoneNumber(ConnectionRequest request) {
+ String phoneNumber = request.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
+ return Uri.fromParts(PhoneAccount.SCHEME_TEL, phoneNumber, null);
+ }
+
+ @Override
+ public Connection onCreateOutgoingConnection(
+ PhoneAccountHandle phoneAccount, ConnectionRequest request) {
+ LogUtil.i(
+ "SimulatorConnectionService.onCreateOutgoingConnection",
+ "outgoing calls not supported yet");
+ return null;
+ }
+
+ @Override
+ public Connection onCreateIncomingConnection(
+ PhoneAccountHandle phoneAccount, ConnectionRequest request) {
+ LogUtil.enterBlock("SimulatorConnectionService.onCreateIncomingConnection");
+ SimulatorConnection connection = new SimulatorConnection();
+ connection.setRinging();
+ connection.setAddress(getPhoneNumber(request), TelecomManager.PRESENTATION_ALLOWED);
+ connection.setConnectionCapabilities(
+ Connection.CAPABILITY_MUTE | Connection.CAPABILITY_SUPPORT_HOLD);
+ return connection;
+ }
+}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorContacts.java b/java/com/android/dialer/simulator/impl/SimulatorContacts.java
new file mode 100644
index 000000000..89315094a
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/SimulatorContacts.java
@@ -0,0 +1,319 @@
+/*
+ * 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.simulator.impl;
+
+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.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.WorkerThread;
+import android.text.TextUtils;
+import com.android.dialer.common.Assert;
+
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Populates the device database with contacts. */
+final class SimulatorContacts {
+ // 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)
+ .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)
+ .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)
+ .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)
+ .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))
+ .build(),
+ // France, number with Japanese name.
+ Contact.builder()
+ .setName("スパイク・スピーゲル")
+ .addPhoneNumber(new PhoneNumber("+33 (0)1 42 68 53 00", Phone.TYPE_MOBILE))
+ .build(),
+ // Israel, RTL name and non-e164 number.
+ Contact.builder()
+ .setName("עקב אריה טברסק")
+ .addPhoneNumber(new PhoneNumber("+33 (0)1 42 68 53 00", Phone.TYPE_MOBILE))
+ .build(),
+ // UAE, RTL name.
+ Contact.builder()
+ .setName("سلام دنیا")
+ .addPhoneNumber(new PhoneNumber("+971 4 4509500", Phone.TYPE_MOBILE))
+ .build(),
+ // Brazil, contact with no name.
+ Contact.builder()
+ .addPhoneNumber(new PhoneNumber("+55-31-2128-6800", Phone.TYPE_MOBILE))
+ .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))
+ .build(),
+ // None, contact with no phone number.
+ Contact.builder()
+ .setName("No Phone Number")
+ .addEmail(new Email("no@example.com"))
+ .setIsStarred(true)
+ .build(),
+ };
+
+ @WorkerThread
+ static void populateContacts(@NonNull Context context) {
+ Assert.isWorkerThread();
+ ArrayList<ContentProviderOperation> operations = new ArrayList<>();
+ for (Contact contact : SIMPLE_CONTACTS) {
+ addContact(contact, operations);
+ }
+ try {
+ context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operations);
+ } catch (RemoteException | OperationApplicationException e) {
+ Assert.fail("error adding 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())
+ .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());
+ }
+ }
+
+
+ abstract static class Contact {
+ @NonNull
+ abstract String getAccountType();
+
+ @NonNull
+ abstract String getAccountName();
+
+ @Nullable
+ abstract String getName();
+
+ abstract boolean getIsStarred();
+
+ @Nullable
+ abstract ByteArrayOutputStream getPhotoStream();
+
+ @NonNull
+ abstract List<PhoneNumber> getPhoneNumbers();
+
+ @NonNull
+ abstract List<Email> getEmails();
+
+ static Builder builder() {
+ return new AutoValue_SimulatorContacts_Contact.Builder()
+ .setAccountType("com.google")
+ .setAccountName("foo@example")
+ .setIsStarred(false)
+ .setPhoneNumbers(new ArrayList<>())
+ .setEmails(new ArrayList<>());
+ }
+
+
+ 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 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 SimulatorContacts() {}
+}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorModule.java b/java/com/android/dialer/simulator/impl/SimulatorModule.java
new file mode 100644
index 000000000..0f8ad3954
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/SimulatorModule.java
@@ -0,0 +1,34 @@
+/*
+ * 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.simulator.impl;
+
+import android.content.Context;
+import android.view.ActionProvider;
+import com.android.dialer.simulator.Simulator;
+
+/** The entry point for the simulator module. */
+public final class SimulatorModule implements Simulator {
+ @Override
+ public boolean shouldShow() {
+ return true;
+ }
+
+ @Override
+ public ActionProvider getActionProvider(Context context) {
+ return new SimulatorActionProvider(context);
+ }
+}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java b/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java
new file mode 100644
index 000000000..39c1d02a5
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java
@@ -0,0 +1,47 @@
+/*
+ * 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.simulator.impl;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+
+/** Utilities to simulate phone calls. */
+final class SimulatorVoiceCall {
+ public static void addNewIncomingCall(@NonNull Context context) {
+ LogUtil.enterBlock("SimulatorVoiceCall.addNewIncomingCall");
+ SimulatorConnectionService.register(context);
+
+ Bundle bundle = new Bundle();
+ // Set the caller ID to the Google London office.
+ bundle.putString(TelephonyManager.EXTRA_INCOMING_NUMBER, "+44 (0) 20 7031 3000");
+ try {
+ context
+ .getSystemService(TelecomManager.class)
+ .addNewIncomingCall(
+ SimulatorConnectionService.getConnectionServiceHandle(context), bundle);
+ } catch (SecurityException e) {
+ Assert.fail("unable to add call: " + e);
+ }
+ }
+
+ private SimulatorVoiceCall() {}
+}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorVoicemail.java b/java/com/android/dialer/simulator/impl/SimulatorVoicemail.java
new file mode 100644
index 000000000..ffb9191dc
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/SimulatorVoicemail.java
@@ -0,0 +1,154 @@
+/*
+ * 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.simulator.impl;
+
+import android.content.ComponentName;
+import android.content.ContentValues;
+import android.content.Context;
+import android.provider.VoicemailContract.Status;
+import android.provider.VoicemailContract.Voicemails;
+import android.support.annotation.NonNull;
+import android.support.annotation.WorkerThread;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.TelephonyManager;
+import com.android.dialer.common.Assert;
+
+import java.util.concurrent.TimeUnit;
+
+/** Populates the device database with voicemail entries. */
+final class SimulatorVoicemail {
+ private static final String ACCOUNT_ID = "ACCOUNT_ID";
+
+ private static final Voicemail.Builder[] SIMPLE_VOICEMAILS = {
+ // Long transcription with an embedded phone number.
+ Voicemail.builder()
+ .setPhoneNumber("+1-302-6365454")
+ .setTranscription(
+ "Hi, this is a very long voicemail. Please call me back at 650 253 0000. "
+ + "I hope you listen to all of it. This is very important. "
+ + "Hi, this is a very long voicemail. "
+ + "I hope you listen to all of it. It's very important.")
+ .setDurationSeconds(10)
+ .setIsRead(false),
+ // RTL transcription.
+ Voicemail.builder()
+ .setPhoneNumber("+1-302-6365454")
+ .setTranscription("هزاران دوست کم اند و یک دشمن زیاد")
+ .setDurationSeconds(60)
+ .setIsRead(true),
+ // Empty number.
+ Voicemail.builder()
+ .setPhoneNumber("")
+ .setTranscription("")
+ .setDurationSeconds(60)
+ .setIsRead(true),
+ // No duration.
+ Voicemail.builder()
+ .setPhoneNumber("+1-302-6365454")
+ .setTranscription("")
+ .setDurationSeconds(0)
+ .setIsRead(true),
+ // Short number.
+ Voicemail.builder()
+ .setPhoneNumber("711")
+ .setTranscription("This is a short voicemail.")
+ .setDurationSeconds(12)
+ .setIsRead(true),
+ };
+
+ @WorkerThread
+ public static void populateVoicemail(@NonNull Context context) {
+ Assert.isWorkerThread();
+ enableVoicemail(context);
+
+ // Do this 4 times to make the voicemail database 4 times bigger.
+ long timeMillis = System.currentTimeMillis();
+ for (int i = 0; i < 4; i++) {
+ for (Voicemail.Builder builder : SIMPLE_VOICEMAILS) {
+ Voicemail voicemail = builder.setTimeMillis(timeMillis).build();
+ context
+ .getContentResolver()
+ .insert(
+ Voicemails.buildSourceUri(context.getPackageName()),
+ voicemail.getAsContentValues(context));
+ timeMillis -= TimeUnit.HOURS.toMillis(2);
+ }
+ }
+ }
+
+ private static void enableVoicemail(@NonNull Context context) {
+ PhoneAccountHandle handle =
+ new PhoneAccountHandle(new ComponentName(context, SimulatorVoicemail.class), ACCOUNT_ID);
+
+ ContentValues values = new ContentValues();
+ values.put(Status.SOURCE_PACKAGE, handle.getComponentName().getPackageName());
+ values.put(Status.SOURCE_TYPE, TelephonyManager.VVM_TYPE_OMTP);
+ values.put(Status.PHONE_ACCOUNT_COMPONENT_NAME, handle.getComponentName().flattenToString());
+ values.put(Status.PHONE_ACCOUNT_ID, handle.getId());
+ values.put(Status.CONFIGURATION_STATE, Status.CONFIGURATION_STATE_OK);
+ values.put(Status.DATA_CHANNEL_STATE, Status.DATA_CHANNEL_STATE_OK);
+ values.put(Status.NOTIFICATION_CHANNEL_STATE, Status.NOTIFICATION_CHANNEL_STATE_OK);
+ context.getContentResolver().insert(Status.buildSourceUri(context.getPackageName()), values);
+ }
+
+
+ abstract static class Voicemail {
+ @NonNull
+ abstract String getPhoneNumber();
+
+ @NonNull
+ abstract String getTranscription();
+
+ abstract long getDurationSeconds();
+
+ abstract long getTimeMillis();
+
+ abstract boolean getIsRead();
+
+ static Builder builder() {
+ return new AutoValue_SimulatorVoicemail_Voicemail.Builder();
+ }
+
+ ContentValues getAsContentValues(Context context) {
+ ContentValues values = new ContentValues();
+ values.put(Voicemails.DATE, getTimeMillis());
+ values.put(Voicemails.NUMBER, getPhoneNumber());
+ values.put(Voicemails.DURATION, getDurationSeconds());
+ values.put(Voicemails.SOURCE_PACKAGE, context.getPackageName());
+ values.put(Voicemails.IS_READ, getIsRead() ? 1 : 0);
+ values.put(Voicemails.TRANSCRIPTION, getTranscription());
+ return values;
+ }
+
+
+ abstract static class Builder {
+ abstract Builder setPhoneNumber(@NonNull String phoneNumber);
+
+ abstract Builder setTranscription(@NonNull String transcription);
+
+ abstract Builder setDurationSeconds(long durationSeconds);
+
+ abstract Builder setTimeMillis(long timeMillis);
+
+ abstract Builder setIsRead(boolean isRead);
+
+ abstract Voicemail build();
+ }
+ }
+
+ private SimulatorVoicemail() {}
+}