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;
17 
18 import android.content.ContentUris;
19 import android.content.Context;
20 import android.database.Cursor;
21 import android.graphics.Bitmap;
22 import android.graphics.BitmapFactory;
23 import com.android.internal.logging.MetricsLogger;
24 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
25 import android.net.Uri;
26 import android.provider.ContactsContract;
27 import android.util.Log;
28 
29 import java.io.ByteArrayInputStream;
30 
31 /**
32  * Provides methods to read name, phone number, photo, etc. from contacts.
33  */
34 public class EmergencyContactManager {
35     private static final String TAG = "EmergencyContactManager";
36 
37     /**
38      * Returns a {@link Contact} that contains all the relevant information of the contact indexed
39      * by {@code @phoneUri}.
40      */
getContact(Context context, Uri phoneUri)41     public static Contact getContact(Context context, Uri phoneUri) {
42         String phoneNumber = null;
43         String phoneType = null;
44         String name = null;
45         Bitmap photo = null;
46         final Uri contactLookupUri =
47                 ContactsContract.Contacts.getLookupUri(context.getContentResolver(),
48                         phoneUri);
49         Cursor cursor = context.getContentResolver().query(
50                 phoneUri,
51                 new String[]{ContactsContract.Contacts.DISPLAY_NAME,
52                         ContactsContract.CommonDataKinds.Phone.NUMBER,
53                         ContactsContract.CommonDataKinds.Phone.TYPE,
54                         ContactsContract.CommonDataKinds.Phone.LABEL,
55                         ContactsContract.CommonDataKinds.Photo.PHOTO_ID},
56                 null, null, null);
57         try {
58             if (cursor.moveToNext()) {
59                 name = cursor.getString(0);
60                 phoneNumber = cursor.getString(1);
61                 phoneType = ContactsContract.CommonDataKinds.Phone.getTypeLabel(
62                         context.getResources(),
63                         cursor.getInt(2),
64                         cursor.getString(3)).toString();
65                 Long photoId = cursor.getLong(4);
66                 if (photoId != null && photoId > 0) {
67                     Uri photoUri = ContentUris.withAppendedId(ContactsContract.Data.CONTENT_URI,
68                             photoId);
69                     Cursor cursor2 = context.getContentResolver().query(
70                             photoUri,
71                             new String[]{ContactsContract.Contacts.Photo.PHOTO},
72                             null, null, null);
73                     try {
74                         if (cursor2.moveToNext()) {
75                             byte[] data = cursor2.getBlob(0);
76                             if (data != null) {
77                                 photo = BitmapFactory.decodeStream(new ByteArrayInputStream(data));
78                             }
79                         }
80                     } finally {
81                         if (cursor2 != null) {
82                             cursor2.close();
83                         }
84                     }
85                 }
86             }
87         } finally {
88             if (cursor != null) {
89                 cursor.close();
90             }
91         }
92         return new Contact(contactLookupUri, phoneUri, name, phoneNumber, phoneType, photo);
93     }
94 
95     /** Returns whether the phone uri is not null and corresponds to an existing phone number. */
isValidEmergencyContact(Context context, Uri phoneUri)96     public static boolean isValidEmergencyContact(Context context, Uri phoneUri) {
97         return phoneUri != null && phoneExists(context, phoneUri);
98     }
99 
phoneExists(Context context, Uri phoneUri)100     private static boolean phoneExists(Context context, Uri phoneUri) {
101         Cursor cursor = null;
102         try {
103             cursor = context.getContentResolver().query(phoneUri, null, null, null, null);
104             if (cursor != null && cursor.moveToFirst()) {
105                 MetricsLogger.action(context, MetricsEvent.ACTION_PHONE_EXISTS, 1);
106                 return true;
107             }
108         } catch (SecurityException e) {
109             Log.w(TAG, "Unable to read contact information", e);
110             MetricsLogger.action(context, MetricsEvent.ACTION_PHONE_EXISTS, 2);
111             return false;
112         } finally {
113             if (cursor != null) {
114                 cursor.close();
115             }
116         }
117         MetricsLogger.action(context, MetricsEvent.ACTION_PHONE_EXISTS, 0);
118         return false;
119     }
120 
121     /** Wrapper for a contact with a phone number. */
122     public static class Contact {
123         /** The lookup uri is necessary to display the contact. */
124         private final Uri mContactLookupUri;
125         /**
126          * The contact uri is associated to a particular phone number and can be used to reload that
127          * number and keep the number displayed in the preferences fresh.
128          */
129         private final Uri mPhoneUri;
130         /** The display name of the contact. */
131         private final String mName;
132         /** The emergency contact's phone number selected by the user. */
133         private final String mPhoneNumber;
134         /** The emergency contact's phone number type (mobile, work, home, etc). */
135         private final String mPhoneType;
136         /** The contact's photo. */
137         private final Bitmap mPhoto;
138 
139         /** Constructs a new contact. */
Contact(Uri contactLookupUri, Uri phoneUri, String name, String phoneNumber, String phoneType, Bitmap photo)140         public Contact(Uri contactLookupUri,
141                        Uri phoneUri,
142                        String name,
143                        String phoneNumber,
144                        String phoneType,
145                        Bitmap photo) {
146             mContactLookupUri = contactLookupUri;
147             mPhoneUri = phoneUri;
148             mName = name;
149             mPhoneNumber = phoneNumber;
150             mPhoneType = phoneType;
151             mPhoto = photo;
152         }
153 
154         /** Returns the contact's CONTENT_LOOKUP_URI. Use this to display the contact. */
getContactLookupUri()155         public Uri getContactLookupUri() {
156             return mContactLookupUri;
157         }
158 
159         /**
160          * The phone uri as defined in ContactsContract.CommonDataKinds.Phone.CONTENT_URI. Use
161          * this to reload the contact. This links to a particular phone number of the emergency
162          * contact.
163          */
getPhoneUri()164         public Uri getPhoneUri() {
165             return mPhoneUri;
166         }
167 
168         /** Returns the display name of the contact. */
getName()169         public String getName() {
170             return mName;
171         }
172 
173         /** Returns the phone number selected by the user. */
getPhoneNumber()174         public String getPhoneNumber() {
175             return mPhoneNumber;
176         }
177 
178         /** Returns the phone type (e.g. mobile, work, home, etc.) . */
getPhoneType()179         public String getPhoneType() {
180             return mPhoneType;
181         }
182 
183         /** Returns the photo assigned to this contact. */
getPhoto()184         public Bitmap getPhoto() {
185             return mPhoto;
186         }
187     }
188 }
189