blob: ebe1dea6e8d3d80a37145fc2a3be5ee60e0ff54e [file] [log] [blame]
Akshay Kannancdb6c142016-01-20 18:25:44 -08001/*
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 */
mariagpuyol58748352016-03-09 15:36:28 -080016package com.android.emergency.preferences;
Akshay Kannancdb6c142016-01-20 18:25:44 -080017
Akshay Kannan44ef39d2016-01-26 09:08:41 -080018import android.app.AlertDialog;
mariagpuyol27b306e2017-03-01 13:14:31 -080019import android.content.ActivityNotFoundException;
mariagpuyole0f631b2016-08-16 18:58:26 -070020import android.content.ComponentName;
Akshay Kannancdb6c142016-01-20 18:25:44 -080021import android.content.Context;
mariagpuyold19ace52016-02-12 10:37:26 -080022import android.content.DialogInterface;
Akshay Kannancdb6c142016-01-20 18:25:44 -080023import android.content.Intent;
mariagpuyole0f631b2016-08-16 18:58:26 -070024import android.content.pm.PackageManager;
25import android.content.pm.ResolveInfo;
mariagpuyol95dc0402016-02-17 11:12:46 -080026import android.graphics.drawable.Drawable;
Akshay Kannancdb6c142016-01-20 18:25:44 -080027import android.net.Uri;
mariagpuyol53745bf2016-03-23 14:43:55 -070028import android.os.Bundle;
29import android.os.Parcel;
30import android.os.Parcelable;
Akshay Kannancdb6c142016-01-20 18:25:44 -080031import android.preference.Preference;
mariagpuyolaee23782016-02-16 13:29:49 -080032import android.support.annotation.NonNull;
mariagpuyol16472ca2016-02-16 17:49:26 -080033import android.support.annotation.Nullable;
mariagpuyol9bc08182016-05-13 14:54:49 -070034import android.text.BidiFormatter;
35import android.text.TextDirectionHeuristics;
mariagpuyol27b306e2017-03-01 13:14:31 -080036import android.util.Log;
mariagpuyold19ace52016-02-12 10:37:26 -080037import android.view.View;
mariagpuyol27b306e2017-03-01 13:14:31 -080038import android.widget.Toast;
mariagpuyold19ace52016-02-12 10:37:26 -080039
mariagpuyolacef32a2016-12-12 11:33:20 -080040import com.android.emergency.CircleFramedDrawable;
mariagpuyol58748352016-03-09 15:36:28 -080041import com.android.emergency.EmergencyContactManager;
mariagpuyol58748352016-03-09 15:36:28 -080042import com.android.emergency.R;
mariagpuyol9e2eea12016-04-01 18:10:12 -070043import com.android.internal.annotations.VisibleForTesting;
Akshay Kannan44ef39d2016-01-26 09:08:41 -080044import com.android.internal.logging.MetricsLogger;
Tamas Berghammer2fdeb9a2016-06-22 15:28:50 +010045import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Akshay Kannan44ef39d2016-01-26 09:08:41 -080046
mariagpuyole0f631b2016-08-16 18:58:26 -070047import java.util.List;
48
Akshay Kannancdb6c142016-01-20 18:25:44 -080049
50/**
mariagpuyol5cf7f732016-03-08 09:48:15 -080051 * A {@link Preference} to display or call a contact using the specified URI string.
Akshay Kannancdb6c142016-01-20 18:25:44 -080052 */
53public class ContactPreference extends Preference {
54
mariagpuyol27b306e2017-03-01 13:14:31 -080055 private static final String TAG = "ContactPreference";
56
Juan Lang95113882017-05-30 15:20:50 -070057 private final ContactFactory mContactFactory;
mariagpuyol53745bf2016-03-23 14:43:55 -070058 private EmergencyContactManager.Contact mContact;
mariagpuyol609f68a2016-02-22 17:45:47 -080059 @Nullable private RemoveContactPreferenceListener mRemoveContactPreferenceListener;
mariagpuyol53745bf2016-03-23 14:43:55 -070060 @Nullable private AlertDialog mRemoveContactDialog;
mariagpuyold19ace52016-02-12 10:37:26 -080061
62 /**
mariagpuyol609f68a2016-02-22 17:45:47 -080063 * Listener for removing a contact.
mariagpuyold19ace52016-02-12 10:37:26 -080064 */
mariagpuyol609f68a2016-02-22 17:45:47 -080065 public interface RemoveContactPreferenceListener {
mariagpuyold19ace52016-02-12 10:37:26 -080066 /**
mariagpuyol609f68a2016-02-22 17:45:47 -080067 * Callback to remove a contact preference.
mariagpuyold19ace52016-02-12 10:37:26 -080068 */
mariagpuyol609f68a2016-02-22 17:45:47 -080069 void onRemoveContactPreference(ContactPreference preference);
mariagpuyold19ace52016-02-12 10:37:26 -080070 }
Akshay Kannancdb6c142016-01-20 18:25:44 -080071
72 /**
Juan Lang95113882017-05-30 15:20:50 -070073 * Interface for getting a contact for a phone number Uri.
74 */
75 public interface ContactFactory {
76 /**
77 * Gets a {@link EmergencyContactManager.Contact} for a phone {@link Uri}.
78 *
79 * @param context The context to use.
80 * @param phoneUri The phone uri.
81 * @return a contact for the given phone uri.
82 */
83 EmergencyContactManager.Contact getContact(Context context, Uri phoneUri);
84 }
85
86 /**
Akshay Kannancdb6c142016-01-20 18:25:44 -080087 * Instantiates a ContactPreference that displays an emergency contact, taking in a Context and
mariagpuyol5cf7f732016-03-08 09:48:15 -080088 * the Uri.
Akshay Kannancdb6c142016-01-20 18:25:44 -080089 */
mariagpuyol08e2e512017-03-08 10:00:15 -080090 public ContactPreference(Context context, @NonNull Uri phoneUri) {
Juan Lang95113882017-05-30 15:20:50 -070091 this(context, phoneUri, new ContactFactory() {
92 @Override
93 public EmergencyContactManager.Contact getContact(Context context, Uri phoneUri) {
94 return EmergencyContactManager.getContact(context, phoneUri);
95 }
96 });
97 }
98
99 @VisibleForTesting
100 public ContactPreference(Context context, @NonNull Uri phoneUri,
101 @NonNull ContactFactory contactFactory) {
Akshay Kannancdb6c142016-01-20 18:25:44 -0800102 super(context);
Juan Lang95113882017-05-30 15:20:50 -0700103 mContactFactory = contactFactory;
mariagpuyol609f68a2016-02-22 17:45:47 -0800104 setOrder(DEFAULT_ORDER);
mariagpuyol53745bf2016-03-23 14:43:55 -0700105
mariagpuyol08e2e512017-03-08 10:00:15 -0800106 setPhoneUri(phoneUri);
mariagpuyol53745bf2016-03-23 14:43:55 -0700107
108 setWidgetLayoutResource(R.layout.preference_user_delete_widget);
109 setPersistent(false);
110 }
111
mariagpuyol08e2e512017-03-08 10:00:15 -0800112 public void setPhoneUri(@NonNull Uri phoneUri) {
113 if (mContact != null && !phoneUri.equals(mContact.getPhoneUri()) &&
mariagpuyol53745bf2016-03-23 14:43:55 -0700114 mRemoveContactDialog != null) {
115 mRemoveContactDialog.dismiss();
116 }
Juan Lang95113882017-05-30 15:20:50 -0700117 mContact = mContactFactory.getContact(getContext(), phoneUri);
mariagpuyol53745bf2016-03-23 14:43:55 -0700118
mariagpuyol5cf7f732016-03-08 09:48:15 -0800119 setTitle(mContact.getName());
mariagpuyol08e2e512017-03-08 10:00:15 -0800120 setKey(mContact.getPhoneUri().toString());
mariagpuyolf9ab89a2016-03-10 09:19:07 -0800121 String summary = mContact.getPhoneType() == null ?
122 mContact.getPhoneNumber() :
123 String.format(
mariagpuyol53745bf2016-03-23 14:43:55 -0700124 getContext().getResources().getString(R.string.phone_type_and_phone_number),
mariagpuyolf9ab89a2016-03-10 09:19:07 -0800125 mContact.getPhoneType(),
mariagpuyol9bc08182016-05-13 14:54:49 -0700126 BidiFormatter.getInstance().unicodeWrap(mContact.getPhoneNumber(),
127 TextDirectionHeuristics.LTR));
mariagpuyolf9ab89a2016-03-10 09:19:07 -0800128 setSummary(summary);
mariagpuyol53745bf2016-03-23 14:43:55 -0700129
130 // Update the message to show the correct name.
131 if (mRemoveContactDialog != null) {
132 mRemoveContactDialog.setMessage(
133 String.format(getContext().getString(R.string.remove_contact),
134 mContact.getName()));
135 }
mariagpuyol95dc0402016-02-17 11:12:46 -0800136
137 //TODO: Consider doing the following in a non-UI thread.
mariagpuyol9be32652016-03-16 12:34:53 -0700138 Drawable icon;
139 if (mContact.getPhoto() != null) {
140 icon = new CircleFramedDrawable(mContact.getPhoto(),
mariagpuyol53745bf2016-03-23 14:43:55 -0700141 (int) getContext().getResources().getDimension(R.dimen.circle_avatar_size));
mariagpuyol9be32652016-03-16 12:34:53 -0700142 } else {
mariagpuyol53745bf2016-03-23 14:43:55 -0700143 icon = getContext().getResources().getDrawable(R.drawable.ic_person_black_24dp);
mariagpuyol9be32652016-03-16 12:34:53 -0700144 }
mariagpuyol95dc0402016-02-17 11:12:46 -0800145 setIcon(icon);
146 }
147
mariagpuyol609f68a2016-02-22 17:45:47 -0800148 /** Listener to be informed when a contact preference should be deleted. */
149 public void setRemoveContactPreferenceListener(
150 RemoveContactPreferenceListener removeContactListener) {
151 mRemoveContactPreferenceListener = removeContactListener;
mariagpuyol53745bf2016-03-23 14:43:55 -0700152 if (mRemoveContactPreferenceListener == null) {
153 mRemoveContactDialog = null;
154 return;
155 }
156 if (mRemoveContactDialog != null) {
157 return;
158 }
159 // Create the remove contact dialog
160 AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
161 builder.setNegativeButton(getContext().getString(R.string.cancel), null);
162 builder.setPositiveButton(getContext().getString(R.string.remove),
163 new DialogInterface.OnClickListener() {
164 @Override
165 public void onClick(DialogInterface dialogInterface,
166 int which) {
167 if (mRemoveContactPreferenceListener != null) {
168 mRemoveContactPreferenceListener
169 .onRemoveContactPreference(ContactPreference.this);
170 }
171 }
172 });
173 builder.setMessage(String.format(getContext().getString(R.string.remove_contact),
174 mContact.getName()));
175 mRemoveContactDialog = builder.create();
mariagpuyol609f68a2016-02-22 17:45:47 -0800176 }
177
mariagpuyold19ace52016-02-12 10:37:26 -0800178 @Override
179 protected void onBindView(View view) {
180 super.onBindView(view);
181 View deleteContactIcon = view.findViewById(R.id.delete_contact);
mariagpuyol609f68a2016-02-22 17:45:47 -0800182 if (mRemoveContactPreferenceListener == null) {
mariagpuyol16472ca2016-02-16 17:49:26 -0800183 deleteContactIcon.setVisibility(View.GONE);
184 } else {
mariagpuyol609f68a2016-02-22 17:45:47 -0800185 deleteContactIcon.setOnClickListener(new View.OnClickListener() {
186 @Override
187 public void onClick(View view) {
mariagpuyol53745bf2016-03-23 14:43:55 -0700188 showRemoveContactDialog(null);
mariagpuyol609f68a2016-02-22 17:45:47 -0800189 }
190 });
191
mariagpuyold19ace52016-02-12 10:37:26 -0800192 }
Akshay Kannancdb6c142016-01-20 18:25:44 -0800193 }
194
mariagpuyol08e2e512017-03-08 10:00:15 -0800195 public Uri getPhoneUri() {
196 return mContact.getPhoneUri();
mariagpuyol609f68a2016-02-22 17:45:47 -0800197 }
198
mariagpuyol9e2eea12016-04-01 18:10:12 -0700199 @VisibleForTesting
200 EmergencyContactManager.Contact getContact() {
201 return mContact;
202 }
203
204 @VisibleForTesting
205 AlertDialog getRemoveContactDialog() {
206 return mRemoveContactDialog;
207 }
208
Akshay Kannancdb6c142016-01-20 18:25:44 -0800209 /**
Akshay Kannan44ef39d2016-01-26 09:08:41 -0800210 * Calls the contact.
211 */
mariagpuyol5cf7f732016-03-08 09:48:15 -0800212 public void callContact() {
213 Intent callIntent =
214 new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + mContact.getPhoneNumber()));
mariagpuyole0f631b2016-08-16 18:58:26 -0700215 PackageManager packageManager = getContext().getPackageManager();
216 List<ResolveInfo> infos =
217 packageManager.queryIntentActivities(callIntent, PackageManager.MATCH_SYSTEM_ONLY);
218 if (infos == null || infos.isEmpty()) {
219 return;
220 }
221 callIntent.setComponent(new ComponentName(infos.get(0).activityInfo.packageName,
222 infos.get(0).activityInfo.name));
223
mariagpuyol5cf7f732016-03-08 09:48:15 -0800224 MetricsLogger.action(getContext(), MetricsEvent.ACTION_CALL_EMERGENCY_CONTACT);
225 getContext().startActivity(callIntent);
mariagpuyol0504a442016-02-17 16:22:57 -0800226 }
mariagpuyol36b3f9d2016-03-11 16:02:31 -0800227
228 /**
229 * Displays a contact card for the contact.
230 */
231 public void displayContact() {
mariagpuyol27b306e2017-03-01 13:14:31 -0800232 Intent displayIntent = new Intent(Intent.ACTION_VIEW);
233 displayIntent.setData(mContact.getContactLookupUri());
234 try {
235 getContext().startActivity(displayIntent);
236 } catch (ActivityNotFoundException e) {
237 Toast.makeText(getContext(),
238 getContext().getString(R.string.fail_display_contact),
239 Toast.LENGTH_LONG).show();
240 Log.w(TAG, "No contact app available to display the contact", e);
241 return;
242 }
243
mariagpuyol36b3f9d2016-03-11 16:02:31 -0800244 }
mariagpuyol53745bf2016-03-23 14:43:55 -0700245
246 /** Shows the dialog to remove the contact, restoring it from {@code state} if it's not null. */
247 private void showRemoveContactDialog(Bundle state) {
248 if (mRemoveContactDialog == null) {
249 return;
250 }
251 if (state != null) {
252 mRemoveContactDialog.onRestoreInstanceState(state);
253 }
254 mRemoveContactDialog.show();
255 }
256
257 @Override
258 protected Parcelable onSaveInstanceState() {
259 final Parcelable superState = super.onSaveInstanceState();
260 if (mRemoveContactDialog == null || !mRemoveContactDialog.isShowing()) {
261 return superState;
262 }
263 final SavedState myState = new SavedState(superState);
264 myState.isDialogShowing = true;
265 myState.dialogBundle = mRemoveContactDialog.onSaveInstanceState();
266 return myState;
267 }
268
269 @Override
270 protected void onRestoreInstanceState(Parcelable state) {
271 if (state == null || !state.getClass().equals(SavedState.class)) {
272 // Didn't save state for us in onSaveInstanceState
273 super.onRestoreInstanceState(state);
274 return;
275 }
276 SavedState myState = (SavedState) state;
277 super.onRestoreInstanceState(myState.getSuperState());
278 if (myState.isDialogShowing) {
279 showRemoveContactDialog(myState.dialogBundle);
280 }
281 }
282
283 private static class SavedState extends BaseSavedState {
284 boolean isDialogShowing;
285 Bundle dialogBundle;
286
287 public SavedState(Parcel source) {
288 super(source);
289 isDialogShowing = source.readInt() == 1;
290 dialogBundle = source.readBundle();
291 }
292
293 @Override
294 public void writeToParcel(Parcel dest, int flags) {
295 super.writeToParcel(dest, flags);
296 dest.writeInt(isDialogShowing ? 1 : 0);
297 dest.writeBundle(dialogBundle);
298 }
299
300 public SavedState(Parcelable superState) {
301 super(superState);
302 }
303
304 public static final Parcelable.Creator<SavedState> CREATOR =
305 new Parcelable.Creator<SavedState>() {
306 public SavedState createFromParcel(Parcel in) {
307 return new SavedState(in);
308 }
309
310 public SavedState[] newArray(int size) {
311 return new SavedState[size];
312 }
313 };
314 }
mariagpuyole0f631b2016-08-16 18:58:26 -0700315}