• 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.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