1 /*
2 * Copyright 2015 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 
17 package com.example.android.system.runtimepermissions.contacts;
18 
19 import com.example.android.common.logger.Log;
20 import com.example.android.system.runtimepermissions.R;
21 
22 import android.content.ContentProviderOperation;
23 import android.content.ContentResolver;
24 import android.content.OperationApplicationException;
25 import android.database.Cursor;
26 import android.os.Bundle;
27 import android.os.RemoteException;
28 import android.provider.ContactsContract;
29 import android.support.annotation.Nullable;
30 import android.support.v4.app.Fragment;
31 import android.support.v4.app.LoaderManager;
32 import android.support.v4.content.CursorLoader;
33 import android.support.v4.content.Loader;
34 import android.view.LayoutInflater;
35 import android.view.View;
36 import android.view.ViewGroup;
37 import android.widget.Button;
38 import android.widget.TextView;
39 
40 import java.util.ArrayList;
41 
42 /**
43  * Displays the first contact stored on the device and contains an option to add a dummy contact.
44  * <p>
45  * This Fragment is only used to illustrate that access to the Contacts ContentProvider API has
46  * been granted (or denied) as part of the runtime permissions model. It is not relevant for the
47  * use
48  * of the permissions API.
49  * <p>
50  * This fragments demonstrates a basic use case for accessing the Contacts Provider. The
51  * implementation is based on the training guide available here:
52  * https://developer.android.com/training/contacts-provider/retrieve-names.html
53  */
54 public class ContactsFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
55 
56     private static final String TAG = "Contacts";
57     private TextView mMessageText = null;
58 
59     private static String DUMMY_CONTACT_NAME = "__DUMMY CONTACT from runtime permissions sample";
60 
61     /**
62      * Projection for the content provider query includes the id and primary name of a contact.
63      */
64     private static final String[] PROJECTION = {ContactsContract.Contacts._ID,
65             ContactsContract.Contacts.DISPLAY_NAME_PRIMARY};
66     /**
67      * Sort order for the query. Sorted by primary name in ascending order.
68      */
69     private static final String ORDER = ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + " ASC";
70 
71 
72     /**
73      * Creates a new instance of a ContactsFragment.
74      */
newInstance()75     public static ContactsFragment newInstance() {
76         return new ContactsFragment();
77     }
78 
79 
80     @Nullable
81     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)82     public View onCreateView(LayoutInflater inflater, ViewGroup container,
83             Bundle savedInstanceState) {
84         View rootView = inflater.inflate(R.layout.fragment_contacts, container, false);
85 
86         mMessageText = (TextView) rootView.findViewById(R.id.contact_message);
87 
88         // Register a listener to add a dummy contact when a button is clicked.
89         Button button = (Button) rootView.findViewById(R.id.contact_add);
90         button.setOnClickListener(new View.OnClickListener() {
91             @Override
92             public void onClick(View view) {
93                 insertDummyContact();
94             }
95         });
96 
97         // Register a listener to display the first contact when a button is clicked.
98         button = (Button) rootView.findViewById(R.id.contact_load);
99         button.setOnClickListener(new View.OnClickListener() {
100             @Override
101             public void onClick(View view) {
102                 loadContact();
103             }
104         });
105         return rootView;
106     }
107 
108     /**
109      * Restart the Loader to query the Contacts content provider to display the first contact.
110      */
loadContact()111     private void loadContact() {
112         getLoaderManager().restartLoader(0, null, this);
113     }
114 
115     /**
116      * Initialises a new {@link CursorLoader} that queries the {@link ContactsContract}.
117      */
118     @Override
onCreateLoader(int i, Bundle bundle)119     public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
120         return new CursorLoader(getActivity(), ContactsContract.Contacts.CONTENT_URI, PROJECTION,
121                 null, null, ORDER);
122     }
123 
124 
125     /**
126      * Dislays either the name of the first contact or a message.
127      */
128     @Override
onLoadFinished(Loader<Cursor> loader, Cursor cursor)129     public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
130 
131         if (cursor != null) {
132             final int totalCount = cursor.getCount();
133             if (totalCount > 0) {
134                 cursor.moveToFirst();
135                 String name = cursor
136                         .getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
137                 mMessageText.setText(
138                         getResources().getString(R.string.contacts_string, totalCount, name));
139                 Log.d(TAG, "First contact loaded: " + name);
140                 Log.d(TAG, "Total number of contacts: " + totalCount);
141                 Log.d(TAG, "Total number of contacts: " + totalCount);
142             } else {
143                 Log.d(TAG, "List of contacts is empty.");
144                 mMessageText.setText(R.string.contacts_empty);
145             }
146         }
147     }
148 
149     @Override
onLoaderReset(Loader<Cursor> loader)150     public void onLoaderReset(Loader<Cursor> loader) {
151         mMessageText.setText(R.string.contacts_empty);
152     }
153 
154     /**
155      * Accesses the Contacts content provider directly to insert a new contact.
156      * <p>
157      * The contact is called "__DUMMY ENTRY" and only contains a name.
158      */
insertDummyContact()159     private void insertDummyContact() {
160         // Two operations are needed to insert a new contact.
161         ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(2);
162 
163         // First, set up a new raw contact.
164         ContentProviderOperation.Builder op =
165                 ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
166                         .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null)
167                         .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null);
168         operations.add(op.build());
169 
170         // Next, set the name for the contact.
171         op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
172                 .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
173                 .withValue(ContactsContract.Data.MIMETYPE,
174                         ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
175                 .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
176                         DUMMY_CONTACT_NAME);
177         operations.add(op.build());
178 
179         // Apply the operations.
180         ContentResolver resolver = getActivity().getContentResolver();
181         try {
182             resolver.applyBatch(ContactsContract.AUTHORITY, operations);
183         } catch (RemoteException e) {
184             Log.d(TAG, "Could not add a new contact: " + e.getMessage());
185         } catch (OperationApplicationException e) {
186             Log.d(TAG, "Could not add a new contact: " + e.getMessage());
187         }
188     }
189 }
190