EmergencyContactsPreference.java revision caf463dbe3f5c3490f99b073eda0ced3d11c5ab9
1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package com.android.emergency.preferences; 17 18import android.content.Context; 19import android.content.SharedPreferences; 20import android.content.res.TypedArray; 21import android.net.Uri; 22import android.preference.Preference; 23import android.preference.PreferenceCategory; 24import android.preference.PreferenceManager; 25import android.util.AttributeSet; 26 27import com.android.emergency.EmergencyContactManager; 28import com.android.emergency.ReloadablePreferenceInterface; 29import com.android.internal.annotations.VisibleForTesting; 30import com.android.internal.logging.MetricsLogger; 31import com.android.internal.logging.MetricsProto.MetricsEvent; 32 33import java.util.ArrayList; 34import java.util.Iterator; 35import java.util.List; 36import java.util.regex.Pattern; 37 38/** 39 * Custom {@link PreferenceCategory} that deals with contacts being deleted from the contacts app. 40 * 41 * <p>Contacts are stored internally using their ContactsContract.CommonDataKinds.Phone.CONTENT_URI. 42 */ 43public class EmergencyContactsPreference extends PreferenceCategory 44 implements ReloadablePreferenceInterface, 45 ContactPreference.RemoveContactPreferenceListener { 46 47 private static final String CONTACT_SEPARATOR = "|"; 48 private static final String QUOTE_CONTACT_SEPARATOR = Pattern.quote(CONTACT_SEPARATOR); 49 50 /** Stores the emergency contact's ContactsContract.CommonDataKinds.Phone.CONTENT_URI */ 51 private List<Uri> mEmergencyContacts = new ArrayList<Uri>(); 52 private boolean mEmergencyContactsSet = false; 53 54 public EmergencyContactsPreference(Context context, AttributeSet attrs) { 55 super(context, attrs); 56 } 57 58 @Override 59 protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) { 60 setEmergencyContacts(restorePersistedValue ? 61 getPersistedEmergencyContacts() : 62 deserializeAndFilter(getKey(), 63 getContext(), 64 (String) defaultValue)); 65 } 66 67 @Override 68 protected Object onGetDefaultValue(TypedArray a, int index) { 69 return a.getString(index); 70 } 71 72 @Override 73 public void reloadFromPreference() { 74 setEmergencyContacts(getPersistedEmergencyContacts()); 75 } 76 77 @Override 78 public boolean isNotSet() { 79 return mEmergencyContacts.isEmpty(); 80 } 81 82 @Override 83 public void onRemoveContactPreference(ContactPreference contactPreference) { 84 Uri newContact = contactPreference.getContactUri(); 85 if (mEmergencyContacts.contains(newContact)) { 86 List<Uri> updatedContacts = new ArrayList<Uri>(mEmergencyContacts); 87 if (updatedContacts.remove(newContact) && callChangeListener(updatedContacts)) { 88 MetricsLogger.action(getContext(), MetricsEvent.ACTION_DELETE_EMERGENCY_CONTACT); 89 setEmergencyContacts(updatedContacts); 90 } 91 } 92 } 93 94 /** 95 * Adds a new emergency contact. The {@code contactUri} is the 96 * ContactsContract.CommonDataKinds.Phone.CONTENT_URI corresponding to the 97 * contact's selected phone number. 98 */ 99 public void addNewEmergencyContact(Uri contactUri) { 100 if (!mEmergencyContacts.contains(contactUri)) { 101 List<Uri> updatedContacts = new ArrayList<Uri>(mEmergencyContacts); 102 if (updatedContacts.add(contactUri) && callChangeListener(updatedContacts)) { 103 MetricsLogger.action(getContext(), MetricsEvent.ACTION_ADD_EMERGENCY_CONTACT); 104 setEmergencyContacts(updatedContacts); 105 } 106 } 107 } 108 109 @VisibleForTesting 110 public List<Uri> getEmergencyContacts() { 111 return mEmergencyContacts; 112 } 113 114 public void setEmergencyContacts(List<Uri> emergencyContacts) { 115 final boolean changed = !mEmergencyContacts.equals(emergencyContacts); 116 if (changed || !mEmergencyContactsSet) { 117 mEmergencyContacts = emergencyContacts; 118 mEmergencyContactsSet = true; 119 persistString(serialize(emergencyContacts)); 120 if (changed) { 121 notifyChanged(); 122 } 123 } 124 125 while (getPreferenceCount() - emergencyContacts.size() > 0) { 126 removePreference(getPreference(0)); 127 } 128 129 // Reload the preferences or add new ones if necessary 130 Iterator<Uri> it = emergencyContacts.iterator(); 131 int i = 0; 132 while (it.hasNext()) { 133 if (i < getPreferenceCount()) { 134 ContactPreference contactPreference = (ContactPreference) getPreference(i); 135 contactPreference.setUri(it.next()); 136 } else { 137 addContactPreference(it.next()); 138 } 139 i++; 140 } 141 } 142 143 private void addContactPreference(Uri contactUri) { 144 final ContactPreference contactPreference = new ContactPreference(getContext(), contactUri); 145 onBindContactView(contactPreference); 146 addPreference(contactPreference); 147 } 148 149 /** 150 * Called when {@code contactPreference} has been added to this category. You may now set 151 * listeners. 152 */ 153 protected void onBindContactView(final ContactPreference contactPreference) { 154 contactPreference.setRemoveContactPreferenceListener(this); 155 contactPreference 156 .setOnPreferenceClickListener( 157 new Preference.OnPreferenceClickListener() { 158 @Override 159 public boolean onPreferenceClick(Preference preference) { 160 contactPreference.displayContact(); 161 return true; 162 } 163 } 164 ); 165 } 166 167 private List<Uri> getPersistedEmergencyContacts() { 168 return deserializeAndFilter(getKey(), getContext(), getPersistedString("")); 169 } 170 171 /** 172 * Converts the string representing the emergency contacts to a list of Uris and only keeps 173 * those corresponding to still existing contacts. It persists the contacts if at least one 174 * contact was does not exist anymore. 175 */ 176 public static List<Uri> deserializeAndFilter(String key, Context context, 177 String emergencyContactString) { 178 String[] emergencyContactsArray = 179 emergencyContactString.split(QUOTE_CONTACT_SEPARATOR); 180 List<Uri> filteredEmergencyContacts = new ArrayList<Uri>(emergencyContactsArray.length); 181 for (String emergencyContact : emergencyContactsArray) { 182 Uri contactUri = Uri.parse(emergencyContact); 183 if (EmergencyContactManager.isValidEmergencyContact(context, contactUri)) { 184 filteredEmergencyContacts.add(contactUri); 185 } 186 } 187 // If not all contacts were added, then we need to overwrite the emergency contacts stored 188 // in shared preferences. This deals with emergency contacts being deleted from contacts: 189 // currently we have no way to being notified when this happens. 190 if (filteredEmergencyContacts.size() != emergencyContactsArray.length) { 191 String emergencyContactStrings = serialize(filteredEmergencyContacts); 192 SharedPreferences sharedPreferences = 193 PreferenceManager.getDefaultSharedPreferences(context); 194 sharedPreferences.edit().putString(key, emergencyContactStrings).commit(); 195 } 196 return filteredEmergencyContacts; 197 } 198 199 /** Converts the Uris to a string representation. */ 200 public static String serialize(List<Uri> emergencyContacts) { 201 StringBuilder sb = new StringBuilder(); 202 for (int i = 0; i < emergencyContacts.size(); i++) { 203 sb.append(emergencyContacts.get(i).toString()); 204 sb.append(CONTACT_SEPARATOR); 205 } 206 207 if (sb.length() > 0) { 208 sb.setLength(sb.length() - 1); 209 } 210 return sb.toString(); 211 } 212} 213