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