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