summaryrefslogtreecommitdiff
path: root/java/com/android/dialer/simulator
diff options
context:
space:
mode:
authorEric Erfanian <erfanian@google.com>2017-02-22 16:32:36 -0800
committerEric Erfanian <erfanian@google.com>2017-03-01 09:56:52 -0800
commitccca31529c07970e89419fb85a9e8153a5396838 (patch)
treea7034c0a01672b97728c13282a2672771cd28baa /java/com/android/dialer/simulator
parente7ae4624ba6f25cb8e648db74e0d64c0113a16ba (diff)
Update dialer sources.
Test: Built package and system image. This change clobbers the old source, and is an export from an internal Google repository. The internal repository was forked form Android in March, and this change includes modifications since then, to near the v8 release. Since the fork, we've moved code from monolithic to independent modules. In addition, we've switched to Blaze/Bazel as the build sysetm. This export, however, still uses make. New dependencies have been added: - Dagger - Auto-Value - Glide - Libshortcutbadger Going forward, development will still be in Google3, and the Gerrit release will become an automated export, with the next drop happening in ~ two weeks. Android.mk includes local modifications from ToT. Abridged changelog: Bug fixes ● Not able to mute, add a call when using Phone app in multiwindow mode ● Double tap on keypad triggering multiple key and tones ● Reported spam numbers not showing as spam in the call log ● Crash when user tries to block number while Phone app is not set as default ● Crash when user picks a number from search auto-complete list Visual Voicemail (VVM) improvements ● Share Voicemail audio via standard exporting mechanisms that support file attachment (email, MMS, etc.) ● Make phone number, email and web sites in VVM transcript clickable ● Set PIN before declining VVM Terms of Service {Carrier} ● Set client type for outbound visual voicemail SMS {Carrier} New incoming call and incall UI on older devices (Android M) ● Updated Phone app icon ● New incall UI (large buttons, button labels) ● New and animated Answer/Reject gestures Accessibility ● Add custom answer/decline call buttons on answer screen for touch exploration accessibility services ● Increase size of touch target ● Add verbal feedback when a Voicemail fails to load ● Fix pressing of Phone buttons while in a phone call using Switch Access ● Fix selecting and opening contacts in talkback mode ● Split focus for ‘Learn More’ link in caller id & spam to help distinguish similar text Other ● Backup & Restore for App Preferences ● Prompt user to enable Wi-Fi calling if the call ends due to out of service and Wi-Fi is connected ● Rename “Dialpad” to “Keypad” ● Show "Private number" for restricted calls ● Delete unused items (vcard, add contact, call history) from Phone menu Change-Id: I2a7e53532a24c21bf308bf0a6d178d7ddbca4958
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() {}
+}