Add new class EmergencyContactManager
This refactor moves functionality related to reading, adding or removing
contacts to the list of emergency contacts to EmergencyContactManager.
Change-Id: I186fd48b30abd0f817ab73e4b19373761cb22508
diff --git a/Android.mk b/Android.mk
index 15ae845..e5aa05f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -24,27 +24,27 @@
LOCAL_MODULE_TAGS := eng
LOCAL_JAVA_LIBRARIES := framework
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v14-preference \
- android-support-v13 \
- android-support-v7-appcompat \
- android-support-v7-preference \
- android-support-v7-recyclerview \
- android-support-v4 \
- android-support-design
+ android-support-v13 \
+ android-support-v7-appcompat \
+ android-support-v7-preference \
+ android-support-v7-recyclerview \
+ android-support-v4 \
+ android-support-design
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_RESOURCE_DIR += $(LOCAL_PATH)/res \
- $(support_library_root_dir)/v7/appcompat/res \
- $(support_library_root_dir)/v7/preference/res \
- $(support_library_root_dir)/v7/recyclerview/res \
- $(support_library_root_dir)/v14/preference/res \
- $(support_library_root_dir)/design/res
+ $(support_library_root_dir)/v7/appcompat/res \
+ $(support_library_root_dir)/v7/preference/res \
+ $(support_library_root_dir)/v7/recyclerview/res \
+ $(support_library_root_dir)/v14/preference/res \
+ $(support_library_root_dir)/design/res
LOCAL_AAPT_FLAGS := --auto-add-overlay \
- --extra-packages android.support.v7.preference \
- --extra-packages android.support.v14.preference \
- --extra-packages android.support.v7.appcompat \
- --extra-packages android.support.v7.recyclerview \
- --extra-packages android.support.design
+ --extra-packages android.support.v7.preference \
+ --extra-packages android.support.v14.preference \
+ --extra-packages android.support.v7.appcompat \
+ --extra-packages android.support.v7.recyclerview \
+ --extra-packages android.support.design
LOCAL_SDK_VERSION := current
LOCAL_PACKAGE_NAME := EmergencyInfo
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 03ae85c..8ad3149 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -57,7 +57,6 @@
<string name="remove_contact">Remove %s from emergency contacts?</string>
<string name="remove">Remove</string>
<string name="cancel">Cancel</string>
- <string name="unknown_contact">Unknown contact</string>
<string name="description">This information can help first responders in an emergency. It is
stored on your device only, but anyone can read it from the emergency dialer without
diff --git a/src/com/android/emergency/ContactPreference.java b/src/com/android/emergency/ContactPreference.java
index 8fd15ba..b7d5e4b 100644
--- a/src/com/android/emergency/ContactPreference.java
+++ b/src/com/android/emergency/ContactPreference.java
@@ -17,18 +17,15 @@
import android.Manifest;
import android.app.AlertDialog;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.database.Cursor;
import android.net.Uri;
import android.preference.Preference;
-import android.provider.ContactsContract;
+import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.view.View;
-import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto.MetricsEvent;
@@ -41,6 +38,7 @@
private final Uri mUri;
private final DeleteContactListener mDeleteContactListener;
+ private final String mName;
/**
* Listener for deleting a contact.
@@ -49,20 +47,23 @@
/**
* Callback to delete a contact.
*/
- void onContactDelete(String contactUri);
+ void onContactDelete(Uri contactUri);
}
/**
* Instantiates a ContactPreference that displays an emergency contact, taking in a Context and
- * the Uri of the contact as a String.
+ * the Uri, name and phone number of the contact and a listener to be informed when clicking on
+ * the delete icon.
*/
- public ContactPreference(Context context, String uriString,
- DeleteContactListener deleteContactListener) {
+ public ContactPreference(Context context,
+ @NonNull Uri contactUri,
+ @NonNull String contactName,
+ @NonNull DeleteContactListener deleteContactListener) {
super(context);
- mUri = Uri.parse(uriString);
+ mUri = contactUri;
+ mName = contactName;
mDeleteContactListener = deleteContactListener;
- String name = getName();
- setTitle((name != null) ? name : getContext().getString(R.string.unknown_contact));
+ setTitle(mName);
setWidgetLayoutResource(R.layout.preference_user_delete_widget);
}
@@ -76,12 +77,12 @@
public void onClick(View view) {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setMessage(String.format(getContext()
- .getString(R.string.remove_contact), getName()));
+ .getString(R.string.remove_contact), mName));
builder.setPositiveButton(getContext().getString(R.string.remove),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int which) {
- mDeleteContactListener.onContactDelete(mUri.toString());
+ mDeleteContactListener.onContactDelete(mUri);
}
}).setNegativeButton(getContext().getString(R.string.cancel), null);
builder.create().show();
@@ -94,19 +95,16 @@
* Calls the contact.
*/
public void callContact() {
- Uri number = getNumber();
- if (number == null) {
- String errorMessage = getContext().getString(R.string.phone_number_error);
- Toast errorToast = Toast.makeText(getContext(), errorMessage, Toast.LENGTH_SHORT);
- // TODO: Get toast to display over lock screen
- errorToast.show();
- } else {
- Intent callIntent = new Intent(Intent.ACTION_CALL, number);
+ String phoneNumber = EmergencyContactManager.getNumber(getContext(), mUri);
+ if (phoneNumber != null) {
if (ActivityCompat.checkSelfPermission(getContext(),
Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
+ Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + phoneNumber));
MetricsLogger.action(getContext(), MetricsEvent.ACTION_CALL_EMERGENCY_CONTACT);
getContext().startActivity(callIntent);
}
+ } else {
+ // TODO: Show dialog saying that there is no number.
}
}
@@ -118,55 +116,4 @@
contactIntent.setData(mUri);
getContext().startActivity(contactIntent);
}
-
- /**
- * Returns the URI for the contact.
- */
- public Uri getUri() {
- return mUri;
- }
-
- private String getName() {
- Cursor cursor = getContext().getContentResolver().query(getUri(), null, null, null, null);
- try {
- if (cursor != null && cursor.moveToFirst()) {
- return cursor.getString(cursor.getColumnIndex(
- ContactsContract.Contacts.DISPLAY_NAME));
- }
- } finally {
- cursor.close();
- }
- return null;
- }
-
- private Uri getNumber() {
- // TODO: Investigate if this can be done in 1 query instead of 2.
- ContentResolver contentResolver = getContext().getContentResolver();
- Cursor contactCursor = contentResolver.query(getUri(), null, null, null, null);
- try {
- if (contactCursor != null && contactCursor.moveToFirst()) {
- String id = contactCursor.getString(
- contactCursor.getColumnIndex(ContactsContract.Contacts._ID));
- if (contactCursor.getInt(contactCursor.getColumnIndex(
- ContactsContract.Contacts.HAS_PHONE_NUMBER)) != 0) {
- Cursor phoneCursor = contentResolver.query(
- ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
- ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
- new String[]{id}, null);
- try {
- if (phoneCursor != null && phoneCursor.moveToFirst()) {
- return Uri.parse("tel:" + phoneCursor.getString(
- phoneCursor.getColumnIndex(
- ContactsContract.CommonDataKinds.Phone.NUMBER)));
- }
- } finally {
- phoneCursor.close();
- }
- }
- }
- } finally {
- contactCursor.close();
- }
- return null;
- }
}
diff --git a/src/com/android/emergency/EmergencyContactManager.java b/src/com/android/emergency/EmergencyContactManager.java
new file mode 100644
index 0000000..a295dc2
--- /dev/null
+++ b/src/com/android/emergency/EmergencyContactManager.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2016 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.emergency;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.util.ArraySet;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Manages emergency contacts of the user.
+ */
+public class EmergencyContactManager {
+ private final SharedPreferences mSharedPreferences;
+ private final String mKey;
+ private final Context mContext;
+
+ /**
+ * Creates a new instance initialized with context and the shared preferences used to store the
+ * emergency contacts under the specified key.
+ */
+ public EmergencyContactManager(Context context, SharedPreferences sharedPreferences,
+ String key) {
+ mContext = context;
+ mSharedPreferences = sharedPreferences;
+ mKey = key;
+ }
+
+ /**
+ * Adds a new contact to the emergency contacts. */
+ public void addContact(Uri contactUri) {
+ // TODO: Consider refactoring this to use always setContacts() rather than
+ // addContact()/removeContact()
+ Set<Uri> emergencyContacts = getEmergencyContacts();
+ if (emergencyContacts.add(contactUri)) {
+ setEmergencyContacts(emergencyContacts);
+ }
+ }
+
+ /** Removes the specified contact from the list of emergency contacts. */
+ public void removeContact(Uri contactUri) {
+ // TODO: Consider refactoring this to use always setContacts() rather than
+ // addContact()/removeContact()
+ Set<Uri> emergencyContacts = getEmergencyContacts();
+ if (emergencyContacts.remove(contactUri)) {
+ setEmergencyContacts(emergencyContacts);
+ }
+ }
+
+ public Set<Uri> getEmergencyContacts() {
+ Set<String> emergencyContactStrings = mSharedPreferences.getStringSet(mKey,
+ Collections.<String>emptySet());
+ Set<Uri> emergencyContacts = new ArraySet<Uri>(emergencyContactStrings.size());
+ for (String emergencyContact : emergencyContactStrings) {
+ Uri contactUri = Uri.parse(emergencyContact);
+ if (isValidEmergencyContact(contactUri)) {
+ emergencyContacts.add(contactUri);
+ }
+ }
+ // If not all contacts were added, then we need to overwrite the emergency contacts stored
+ // in shared preferences. This deals with emergency contacts being deleted from contacts:
+ // currently we have no way to being notified when this happens.
+ if (emergencyContacts.size() != emergencyContactStrings.size()) {
+ setEmergencyContacts(emergencyContacts);
+ }
+ return emergencyContacts;
+ }
+
+ /** Returns the display name of the contact. */
+ public static String getName(Context context, Uri contactUri) {
+ Cursor cursor = context.getContentResolver().query(contactUri, null, null, null, null);
+ try {
+ if (cursor != null && cursor.moveToFirst()) {
+ return cursor.getString(cursor.getColumnIndex(
+ ContactsContract.Contacts.DISPLAY_NAME));
+ }
+ } finally {
+ cursor.close();
+ }
+ return null;
+ }
+
+ private static boolean contactExists(Context context, Uri contactUri) {
+ Cursor cursor = context.getContentResolver().query(contactUri, null, null, null, null);
+ try {
+ if (cursor != null && cursor.moveToFirst()) {
+ return true;
+ }
+ } finally {
+ cursor.close();
+ }
+ return false;
+ }
+
+ /** Returns the phone number of the contact. */
+ public static String getNumber(Context context, Uri contactUri) {
+ // TODO: Investigate if this can be done in 1 query instead of 2.
+ ContentResolver contentResolver = context.getContentResolver();
+ Cursor contactCursor = contentResolver.query(contactUri, null, null, null, null);
+ try {
+ if (contactCursor != null && contactCursor.moveToFirst()) {
+ String id = contactCursor.getString(
+ contactCursor.getColumnIndex(ContactsContract.Contacts._ID));
+ if (contactCursor.getInt(contactCursor.getColumnIndex(
+ ContactsContract.Contacts.HAS_PHONE_NUMBER)) != 0) {
+ Cursor phoneCursor = contentResolver.query(
+ ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
+ ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
+ new String[]{id}, null);
+ try {
+ if (phoneCursor != null && phoneCursor.moveToFirst()) {
+ return phoneCursor.getString(
+ phoneCursor.getColumnIndex(
+ ContactsContract.CommonDataKinds.Phone.NUMBER));
+ }
+ } finally {
+ phoneCursor.close();
+ }
+ }
+ }
+ } finally {
+ contactCursor.close();
+ }
+ return null;
+ }
+
+ /** Returns whether the contact uri is not null and corresponds to an existing contact. */
+ private boolean isValidEmergencyContact(Uri contactUri) {
+ return contactUri != null && contactExists(mContext, contactUri);
+ }
+
+ private void setEmergencyContacts(Set<Uri> emergencyContacts) {
+ Set<String> emergencyContactStrings = new ArraySet<String>(emergencyContacts.size());
+ for (Uri contactUri : emergencyContacts) {
+ emergencyContactStrings.add(contactUri.toString());
+ }
+ mSharedPreferences.edit().putStringSet(mKey, emergencyContactStrings).commit();
+ }
+}
diff --git a/src/com/android/emergency/EmergencyInfoFragment.java b/src/com/android/emergency/EmergencyInfoFragment.java
index 08dec6b..bb60664 100644
--- a/src/com/android/emergency/EmergencyInfoFragment.java
+++ b/src/com/android/emergency/EmergencyInfoFragment.java
@@ -31,7 +31,6 @@
import android.provider.ContactsContract;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.content.ContextCompat;
-import android.util.ArraySet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -39,7 +38,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto.MetricsEvent;
-import java.util.Collections;
import java.util.Set;
/**
@@ -77,6 +75,9 @@
/** SharedPreferences- initialized in onCreate */
private SharedPreferences mSharedPreferences = null;
+ /** Emergency contact manager that handles adding an removing emergency contacts. */
+ private EmergencyContactManager mEmergencyContactManager;
+
/** Reference to the preferenceScreen controlled by this fragment */
private PreferenceScreen mPreferenceScreen;
@@ -121,6 +122,8 @@
mReadOnly = getArguments().getBoolean(READ_ONLY_KEY);
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
+ mEmergencyContactManager = new EmergencyContactManager(getContext(), mSharedPreferences,
+ EMERGENCY_CONTACTS_KEY);
mPreferenceScreen = getPreferenceScreen();
for (String preferenceKey : PREFERENCE_KEYS) {
@@ -144,11 +147,14 @@
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CONTACT_PICKER_RESULT && resultCode == Activity.RESULT_OK) {
- // TODO: If there are no phone numbers, prevent the user from adding the contact.
- // TODO: If there are multiple phone numbers, ask the user to pick one.
Uri uri = data.getData();
- addContact(uri.toString());
+ mEmergencyContactManager.addContact(uri);
+ MetricsLogger.action(getContext(), MetricsEvent.ACTION_ADD_EMERGENCY_CONTACT);
populateEmergencyContacts();
+ // TODO: If there are multiple phone numbers, ask the user to pick one.
+ if (EmergencyContactManager.getNumber(getContext(), uri) == null) {
+ // TODO: show warning dialog: no phone number
+ }
}
}
@@ -165,8 +171,9 @@
}
@Override
- public void onContactDelete(String contactUri) {
- deleteContact(contactUri);
+ public void onContactDelete(Uri contactUri) {
+ mEmergencyContactManager.removeContact(contactUri);
+ MetricsLogger.action(getContext(), MetricsEvent.ACTION_DELETE_EMERGENCY_CONTACT);
populateEmergencyContacts();
}
@@ -214,8 +221,9 @@
private void populateEmergencyContacts() {
PreferenceCategory emergencyContactsCategory =
(PreferenceCategory) findPreference(EMERGENCY_CONTACTS_KEY);
+ // TODO: Use a list adapter instead of removing all each time.
emergencyContactsCategory.removeAll();
- Set<String> emergencyContacts = getEmergencyContacts();
+ Set<Uri> emergencyContacts = mEmergencyContactManager.getEmergencyContacts();
if (!emergencyContacts.isEmpty()) {
// Get permission if necessary, else populate emergency contacts list
@@ -225,12 +233,15 @@
Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED;
if (!hasContactsPermission || !hasCallPermission) {
requestPermissions(new String[]{
- Manifest.permission.READ_CONTACTS,
- Manifest.permission.CALL_PHONE}, PERMISSION_REQUEST);
+ Manifest.permission.READ_CONTACTS,
+ Manifest.permission.CALL_PHONE}, PERMISSION_REQUEST);
} else {
- for (String contactUri : emergencyContacts) {
+ for (Uri contactUri : emergencyContacts) {
final ContactPreference contactPreference =
- new ContactPreference(getContext(), contactUri, this);
+ new ContactPreference(getContext(),
+ contactUri,
+ EmergencyContactManager.getName(getContext(), contactUri),
+ this);
contactPreference.setOnPreferenceClickListener(
createContactPreferenceClickListener(contactPreference));
emergencyContactsCategory.addPreference(contactPreference);
@@ -241,39 +252,12 @@
if (!mReadOnly) {
// If in edit mode, add a button to create a new emergency contact.
emergencyContactsCategory.addPreference(createAddEmergencyContactPreference());
- } else if (emergencyContacts.isEmpty()) {
+ } else if (emergencyContactsCategory.getPreferenceCount() == 0) {
// If in view mode and there are no contacts, remove the section entirely.
mPreferenceScreen.removePreference(emergencyContactsCategory);
}
}
- private void addContact(String contactUri) {
- Set<String> oldContacts = getEmergencyContacts();
- if (!oldContacts.contains(contactUri)) {
- // Manipulate a copy of emergency contacts rather than editing directly- see
- // getEmergencyContacts for why this is necessary.
- ArraySet<String> newContacts = new ArraySet<String>(oldContacts.size() + 1);
- newContacts.addAll(oldContacts);
- newContacts.add(contactUri);
- setEmergencyContacts(newContacts);
- MetricsLogger.action(getContext(), MetricsEvent.ACTION_ADD_EMERGENCY_CONTACT);
- }
- }
-
- private void deleteContact(String contactUri) {
- Set<String> oldContacts = getEmergencyContacts();
- if (oldContacts.contains(contactUri)) {
- // Manipulate a copy of emergency contacts rather than editing directly- see
- // getEmergencyContacts for why this is necessary.
- ArraySet<String> newContacts = new ArraySet<String>(oldContacts.size());
- newContacts.addAll(oldContacts);
- newContacts.remove(contactUri);
- setEmergencyContacts(newContacts);
- MetricsLogger.action(getContext(),
- MetricsEvent.ACTION_DELETE_EMERGENCY_CONTACT);
- }
- }
-
private Preference.OnPreferenceClickListener createContactPreferenceClickListener(
final ContactPreference contactPreference) {
return new Preference.OnPreferenceClickListener() {
@@ -307,21 +291,4 @@
});
return addEmergencyContact;
}
-
-
- /**
- * Returns a Set of stored emergency contacts. If editing, make a copy of the set as
- * described by {@link SharedPreferences#getStringSet(String, Set<String>)}, then call
- * {@link #setEmergencyContacts(Set)} to store the new contact information.
- */
- private Set<String> getEmergencyContacts() {
- Set<String> emergencyContacts = mSharedPreferences
- .getStringSet(EMERGENCY_CONTACTS_KEY, Collections.<String>emptySet());
- return emergencyContacts;
- }
-
- private void setEmergencyContacts(Set<String> emergencyContacts) {
- mSharedPreferences.edit().putStringSet(EMERGENCY_CONTACTS_KEY, emergencyContacts)
- .commit();
- }
}