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 */ 16 package com.android.emergency.preferences; 17 18 import android.app.AlertDialog; 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.content.DialogInterface; 22 import android.content.Intent; 23 import android.content.pm.PackageManager; 24 import android.content.pm.ResolveInfo; 25 import android.graphics.drawable.Drawable; 26 import android.net.Uri; 27 import android.os.Bundle; 28 import android.os.Parcel; 29 import android.os.Parcelable; 30 import android.preference.Preference; 31 import android.support.annotation.NonNull; 32 import android.support.annotation.Nullable; 33 import android.text.BidiFormatter; 34 import android.text.TextDirectionHeuristics; 35 import android.view.View; 36 37 import com.android.emergency.EmergencyContactManager; 38 import com.android.emergency.R; 39 import com.android.internal.annotations.VisibleForTesting; 40 import com.android.internal.logging.MetricsLogger; 41 import com.android.internal.logging.MetricsProto.MetricsEvent; 42 import com.android.settingslib.drawable.CircleFramedDrawable; 43 44 import java.util.List; 45 46 47 /** 48 * A {@link Preference} to display or call a contact using the specified URI string. 49 */ 50 public class ContactPreference extends Preference { 51 52 private EmergencyContactManager.Contact mContact; 53 @Nullable private RemoveContactPreferenceListener mRemoveContactPreferenceListener; 54 @Nullable private AlertDialog mRemoveContactDialog; 55 56 /** 57 * Listener for removing a contact. 58 */ 59 public interface RemoveContactPreferenceListener { 60 /** 61 * Callback to remove a contact preference. 62 */ onRemoveContactPreference(ContactPreference preference)63 void onRemoveContactPreference(ContactPreference preference); 64 } 65 66 /** 67 * Instantiates a ContactPreference that displays an emergency contact, taking in a Context and 68 * the Uri. 69 */ ContactPreference(Context context, @NonNull Uri contactUri)70 public ContactPreference(Context context, @NonNull Uri contactUri) { 71 super(context); 72 setOrder(DEFAULT_ORDER); 73 74 setUri(contactUri); 75 76 setWidgetLayoutResource(R.layout.preference_user_delete_widget); 77 setPersistent(false); 78 } 79 setUri(@onNull Uri contactUri)80 public void setUri(@NonNull Uri contactUri) { 81 if (mContact != null && !contactUri.equals(mContact.getContactUri()) && 82 mRemoveContactDialog != null) { 83 mRemoveContactDialog.dismiss(); 84 } 85 86 mContact = EmergencyContactManager.getContact(getContext(), contactUri); 87 88 setTitle(mContact.getName()); 89 setKey(mContact.getContactUri().toString()); 90 String summary = mContact.getPhoneType() == null ? 91 mContact.getPhoneNumber() : 92 String.format( 93 getContext().getResources().getString(R.string.phone_type_and_phone_number), 94 mContact.getPhoneType(), 95 BidiFormatter.getInstance().unicodeWrap(mContact.getPhoneNumber(), 96 TextDirectionHeuristics.LTR)); 97 setSummary(summary); 98 99 // Update the message to show the correct name. 100 if (mRemoveContactDialog != null) { 101 mRemoveContactDialog.setMessage( 102 String.format(getContext().getString(R.string.remove_contact), 103 mContact.getName())); 104 } 105 106 //TODO: Consider doing the following in a non-UI thread. 107 Drawable icon; 108 if (mContact.getPhoto() != null) { 109 icon = new CircleFramedDrawable(mContact.getPhoto(), 110 (int) getContext().getResources().getDimension(R.dimen.circle_avatar_size)); 111 } else { 112 icon = getContext().getResources().getDrawable(R.drawable.ic_person_black_24dp); 113 } 114 setIcon(icon); 115 } 116 117 /** Listener to be informed when a contact preference should be deleted. */ setRemoveContactPreferenceListener( RemoveContactPreferenceListener removeContactListener)118 public void setRemoveContactPreferenceListener( 119 RemoveContactPreferenceListener removeContactListener) { 120 mRemoveContactPreferenceListener = removeContactListener; 121 if (mRemoveContactPreferenceListener == null) { 122 mRemoveContactDialog = null; 123 return; 124 } 125 if (mRemoveContactDialog != null) { 126 return; 127 } 128 // Create the remove contact dialog 129 AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); 130 builder.setNegativeButton(getContext().getString(R.string.cancel), null); 131 builder.setPositiveButton(getContext().getString(R.string.remove), 132 new DialogInterface.OnClickListener() { 133 @Override 134 public void onClick(DialogInterface dialogInterface, 135 int which) { 136 if (mRemoveContactPreferenceListener != null) { 137 mRemoveContactPreferenceListener 138 .onRemoveContactPreference(ContactPreference.this); 139 } 140 } 141 }); 142 builder.setMessage(String.format(getContext().getString(R.string.remove_contact), 143 mContact.getName())); 144 mRemoveContactDialog = builder.create(); 145 } 146 147 @Override onBindView(View view)148 protected void onBindView(View view) { 149 super.onBindView(view); 150 View deleteContactIcon = view.findViewById(R.id.delete_contact); 151 if (mRemoveContactPreferenceListener == null) { 152 deleteContactIcon.setVisibility(View.GONE); 153 } else { 154 deleteContactIcon.setOnClickListener(new View.OnClickListener() { 155 @Override 156 public void onClick(View view) { 157 showRemoveContactDialog(null); 158 } 159 }); 160 161 } 162 } 163 getContactUri()164 public Uri getContactUri() { 165 return mContact.getContactUri(); 166 } 167 168 @VisibleForTesting getContact()169 EmergencyContactManager.Contact getContact() { 170 return mContact; 171 } 172 173 @VisibleForTesting getRemoveContactDialog()174 AlertDialog getRemoveContactDialog() { 175 return mRemoveContactDialog; 176 } 177 178 /** 179 * Calls the contact. 180 */ callContact()181 public void callContact() { 182 Intent callIntent = 183 new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + mContact.getPhoneNumber())); 184 PackageManager packageManager = getContext().getPackageManager(); 185 List<ResolveInfo> infos = 186 packageManager.queryIntentActivities(callIntent, PackageManager.MATCH_SYSTEM_ONLY); 187 if (infos == null || infos.isEmpty()) { 188 return; 189 } 190 callIntent.setComponent(new ComponentName(infos.get(0).activityInfo.packageName, 191 infos.get(0).activityInfo.name)); 192 193 MetricsLogger.action(getContext(), MetricsEvent.ACTION_CALL_EMERGENCY_CONTACT); 194 getContext().startActivity(callIntent); 195 } 196 197 /** 198 * Displays a contact card for the contact. 199 */ displayContact()200 public void displayContact() { 201 Intent contactIntent = new Intent(Intent.ACTION_VIEW); 202 contactIntent.setData(mContact.getContactLookupUri()); 203 getContext().startActivity(contactIntent); 204 } 205 206 /** Shows the dialog to remove the contact, restoring it from {@code state} if it's not null. */ showRemoveContactDialog(Bundle state)207 private void showRemoveContactDialog(Bundle state) { 208 if (mRemoveContactDialog == null) { 209 return; 210 } 211 if (state != null) { 212 mRemoveContactDialog.onRestoreInstanceState(state); 213 } 214 mRemoveContactDialog.show(); 215 } 216 217 @Override onSaveInstanceState()218 protected Parcelable onSaveInstanceState() { 219 final Parcelable superState = super.onSaveInstanceState(); 220 if (mRemoveContactDialog == null || !mRemoveContactDialog.isShowing()) { 221 return superState; 222 } 223 final SavedState myState = new SavedState(superState); 224 myState.isDialogShowing = true; 225 myState.dialogBundle = mRemoveContactDialog.onSaveInstanceState(); 226 return myState; 227 } 228 229 @Override onRestoreInstanceState(Parcelable state)230 protected void onRestoreInstanceState(Parcelable state) { 231 if (state == null || !state.getClass().equals(SavedState.class)) { 232 // Didn't save state for us in onSaveInstanceState 233 super.onRestoreInstanceState(state); 234 return; 235 } 236 SavedState myState = (SavedState) state; 237 super.onRestoreInstanceState(myState.getSuperState()); 238 if (myState.isDialogShowing) { 239 showRemoveContactDialog(myState.dialogBundle); 240 } 241 } 242 243 private static class SavedState extends BaseSavedState { 244 boolean isDialogShowing; 245 Bundle dialogBundle; 246 SavedState(Parcel source)247 public SavedState(Parcel source) { 248 super(source); 249 isDialogShowing = source.readInt() == 1; 250 dialogBundle = source.readBundle(); 251 } 252 253 @Override writeToParcel(Parcel dest, int flags)254 public void writeToParcel(Parcel dest, int flags) { 255 super.writeToParcel(dest, flags); 256 dest.writeInt(isDialogShowing ? 1 : 0); 257 dest.writeBundle(dialogBundle); 258 } 259 SavedState(Parcelable superState)260 public SavedState(Parcelable superState) { 261 super(superState); 262 } 263 264 public static final Parcelable.Creator<SavedState> CREATOR = 265 new Parcelable.Creator<SavedState>() { 266 public SavedState createFromParcel(Parcel in) { 267 return new SavedState(in); 268 } 269 270 public SavedState[] newArray(int size) { 271 return new SavedState[size]; 272 } 273 }; 274 } 275 } 276