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 }