• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 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.apis.content;
18 
19 import android.app.Activity;
20 import android.app.LoaderManager;
21 import android.content.BroadcastReceiver;
22 import android.content.Context;
23 import android.content.CursorLoader;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.content.Loader;
27 import android.content.SharedPreferences;
28 import android.database.Cursor;
29 import android.database.CursorWrapper;
30 import android.os.Bundle;
31 import android.provider.ContactsContract;
32 import android.view.View;
33 import android.view.ViewGroup;
34 import android.widget.Button;
35 import android.widget.CursorAdapter;
36 import android.widget.LinearLayout;
37 import android.widget.ListView;
38 import android.widget.TextView;
39 import android.widget.Toast;
40 
41 /**
42  * Demonstrates selecting contacts that have changed since a certain time.
43  */
44 public class ChangedContacts extends Activity implements LoaderManager.LoaderCallbacks<Cursor> {
45 
46     private static final String CLASS = ChangedContacts.class.getSimpleName();
47 
48     private static final String PREF_KEY_CHANGE = "timestamp_change";
49     private static final String PREF_KEY_DELETE = "timestamp_delete";
50 
51     private static final int ID_CHANGE_LOADER = 1;
52     private static final int ID_DELETE_LOADER = 2;
53 
54     /**
55      * To see this in action, "clear data" for the contacts storage app in the system settings.
56      * Then come into this app and hit any of the delta buttons.  This will cause the contacts
57      * database to be re-created.
58      */
59     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
60         @Override
61         public void onReceive(Context context, Intent intent) {
62             Toast toast = Toast.makeText(context, "Contacts database created.", Toast.LENGTH_SHORT);
63             toast.show();
64         }
65     };
66 
67     private DeleteAdapter mDeleteAdapter;
68     private ChangeAdapter mChangeAdapter;
69     private long mSearchTime;
70     private TextView mDisplayView;
71     private ListView mList;
72     private Button mDeleteButton;
73     private Button mChangeButton;
74 
75     @Override
onCreate(Bundle savedInstanceState)76     protected void onCreate(Bundle savedInstanceState) {
77         super.onCreate(savedInstanceState);
78 
79         mDeleteAdapter = new DeleteAdapter(this, null, 0);
80         mChangeAdapter = new ChangeAdapter(this, null, 0);
81 
82         LinearLayout main = new LinearLayout(this);
83         main.setOrientation(LinearLayout.VERTICAL);
84 
85         mChangeButton = new Button(this);
86         mChangeButton.setText("Changed since " + getLastTimestamp(0, PREF_KEY_CHANGE));
87         mChangeButton.setOnClickListener(new View.OnClickListener() {
88             @Override
89             public void onClick(View v) {
90                 changeClick();
91             }
92         });
93 
94         mDeleteButton = new Button(this);
95         mDeleteButton.setText("Deleted since " + getLastTimestamp(0, PREF_KEY_DELETE));
96         mDeleteButton.setOnClickListener(new View.OnClickListener() {
97             @Override
98             public void onClick(View v) {
99                 deleteClick();
100             }
101         });
102 
103         main.addView(mChangeButton);
104         main.addView(mDeleteButton);
105 
106         mDisplayView = new TextView(this);
107         mDisplayView.setPadding(5, 5, 5, 5);
108         main.addView(mDisplayView);
109 
110         mList = new ListView(this);
111         mList.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
112                 ViewGroup.LayoutParams.WRAP_CONTENT, 1f));
113         main.addView(mList);
114 
115         setContentView(main);
116     }
117 
118     @Override
onResume()119     protected void onResume() {
120         super.onResume();
121         IntentFilter filter = new IntentFilter();
122         filter.addAction(ContactsContract.Intents.CONTACTS_DATABASE_CREATED);
123         registerReceiver(mReceiver, filter);
124     }
125 
126     @Override
onPause()127     protected void onPause() {
128         super.onPause();
129         unregisterReceiver(mReceiver);
130     }
131 
changeClick()132     private void changeClick() {
133         mChangeAdapter.swapCursor(null);
134         LoaderManager manager = getLoaderManager();
135         manager.destroyLoader(ID_DELETE_LOADER);
136         manager.restartLoader(ID_CHANGE_LOADER, null, this);
137     }
138 
deleteClick()139     private void deleteClick() {
140         mChangeAdapter.swapCursor(null);
141         LoaderManager manager = getLoaderManager();
142         manager.destroyLoader(ID_CHANGE_LOADER);
143         manager.restartLoader(ID_DELETE_LOADER, null, this);
144     }
145 
saveLastTimestamp(long time, String key)146     private void saveLastTimestamp(long time, String key) {
147         SharedPreferences pref = getSharedPreferences(CLASS, Context.MODE_PRIVATE);
148         SharedPreferences.Editor editor = pref.edit();
149         editor.putLong(key, time);
150         editor.commit();
151     }
152 
getLastTimestamp(long time, String key)153     private long getLastTimestamp(long time, String key) {
154         SharedPreferences pref = getSharedPreferences(CLASS, Context.MODE_PRIVATE);
155         return pref.getLong(key, time);
156     }
157 
158     @Override
onCreateLoader(int id, Bundle args)159     public Loader<Cursor> onCreateLoader(int id, Bundle args) {
160         switch(id) {
161             case ID_CHANGE_LOADER:
162                 return getChangeLoader();
163             case ID_DELETE_LOADER:
164                 return getDeleteLoader();
165         }
166         return null;
167     }
168 
getChangeLoader()169     private CursorLoader getChangeLoader() {
170         String[] projection = new String[]{
171                 ContactsContract.Data._ID,
172                 ContactsContract.Data.CONTACT_ID,
173                 ContactsContract.Data.DISPLAY_NAME,
174                 ContactsContract.Data.CONTACT_LAST_UPDATED_TIMESTAMP
175         };
176 
177         mSearchTime = getLastTimestamp(0, PREF_KEY_CHANGE);
178 
179         String selection = ContactsContract.Data.CONTACT_LAST_UPDATED_TIMESTAMP + " > ?";
180         String[] bindArgs = new String[]{mSearchTime + ""};
181         return new CursorLoader(this, ContactsContract.Data.CONTENT_URI, projection,
182                 selection, bindArgs, ContactsContract.Data.CONTACT_LAST_UPDATED_TIMESTAMP
183                 + " desc, " + ContactsContract.Data.CONTACT_ID + " desc");
184     }
185 
getDeleteLoader()186     private CursorLoader getDeleteLoader() {
187         String[] projection = new String[]{
188                 ContactsContract.DeletedContacts.CONTACT_ID,
189                 ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP
190         };
191 
192         mSearchTime = getLastTimestamp(0, PREF_KEY_DELETE);
193 
194         String selection = ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP + " > ?";
195         String[] bindArgs = new String[]{mSearchTime + ""};
196         return new CursorLoader(this, ContactsContract.DeletedContacts.CONTENT_URI, projection,
197                 selection, bindArgs, ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP +
198                 " desc");
199     }
200 
201     @Override
onLoadFinished(Loader<Cursor> cursorLoader, Cursor data)202     public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor data) {
203         long timestamp = 0;
204 
205 
206         switch (cursorLoader.getId()) {
207             case ID_CHANGE_LOADER:
208                 mDisplayView.setText(data.getCount() + " change(s) since " + mSearchTime);
209                 mList.setAdapter(mChangeAdapter);
210                 mChangeAdapter.swapCursor(data);
211 
212                 // Save the largest timestamp returned.  Only need the first one due to the sort
213                 // order.
214                 if (data.moveToNext()) {
215                     timestamp = data.getLong(data.getColumnIndex(
216                             ContactsContract.Data.CONTACT_LAST_UPDATED_TIMESTAMP));
217                     data.moveToPrevious();
218                 }
219                 if (timestamp > 0) {
220                     saveLastTimestamp(timestamp, PREF_KEY_CHANGE);
221                     mChangeButton.setText("Changed since " + timestamp);
222                 }
223                 break;
224             case ID_DELETE_LOADER:
225                 mDisplayView.setText(data.getCount() + " delete(s) since " + mSearchTime);
226                 mList.setAdapter(mDeleteAdapter);
227                 mDeleteAdapter.swapCursor(new DeleteCursorWrapper(data));
228                 if (data.moveToNext()) {
229                     timestamp = data.getLong(data.getColumnIndex(
230                             ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP));
231                     data.moveToPrevious();
232                 }
233                 if (timestamp > 0) {
234                     saveLastTimestamp(timestamp, PREF_KEY_DELETE);
235                     mDeleteButton.setText("Deleted since " + timestamp);
236                 }
237                 break;
238         }
239     }
240 
241     @Override
onLoaderReset(Loader<Cursor> cursorLoader)242     public void onLoaderReset(Loader<Cursor> cursorLoader) {
243         mDisplayView.setText("");
244         switch (cursorLoader.getId()) {
245             case ID_CHANGE_LOADER:
246                 mChangeAdapter.swapCursor(null);
247                 break;
248             case ID_DELETE_LOADER:
249                 mDeleteAdapter.swapCursor(null);
250                 break;
251         }
252     }
253 
254     private class DeleteCursorWrapper extends CursorWrapper {
255 
256         /**
257          * Creates a cursor wrapper.
258          *
259          * @param cursor The underlying cursor to wrap.
260          */
DeleteCursorWrapper(Cursor cursor)261         public DeleteCursorWrapper(Cursor cursor) {
262             super(cursor);
263         }
264 
265         @Override
getColumnIndexOrThrow(String columnName)266         public int getColumnIndexOrThrow(String columnName) {
267             if (columnName.equals("_id")) {
268                 return super.getColumnIndex(ContactsContract.DeletedContacts.CONTACT_ID);
269             }
270             return super.getColumnIndex(columnName);
271         }
272     }
273 
274     private static class DeleteAdapter extends CursorAdapter {
275 
276         private Context mContext;
277 
DeleteAdapter(Context context, Cursor c, int flags)278         public DeleteAdapter(Context context, Cursor c, int flags) {
279             super(context, c, flags);
280             this.mContext = context;
281         }
282 
283         @Override
newView(Context context, Cursor cursor, ViewGroup parent)284         public View newView(Context context, Cursor cursor, ViewGroup parent) {
285             LinearLayout item = new LinearLayout(mContext);
286             item.addView(buildText(context));
287             item.addView(buildText(context));
288             return item;
289         }
290 
291         @Override
bindView(View view, Context context, Cursor cursor)292         public void bindView(View view, Context context, Cursor cursor) {
293             LinearLayout item = (LinearLayout) view;
294             String id = cursor.getString(cursor.getColumnIndex(
295                     ContactsContract.DeletedContacts.CONTACT_ID));
296             String timestamp = cursor.getString(cursor.getColumnIndex(
297                     ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP));
298 
299             setText(item.getChildAt(0), id);
300             setText(item.getChildAt(1), timestamp);
301         }
302     }
303 
304     private static class ChangeAdapter extends CursorAdapter {
305 
306         private Context mContext;
307 
ChangeAdapter(Context context, Cursor c, int flags)308         public ChangeAdapter(Context context, Cursor c, int flags) {
309             super(context, c, flags);
310             mContext = context;
311         }
312 
313         @Override
newView(Context context, Cursor cursor, ViewGroup parent)314         public View newView(Context context, Cursor cursor, ViewGroup parent) {
315             LinearLayout item = new LinearLayout(mContext);
316             item.addView(buildText(context));
317             item.addView(buildText(context));
318             item.addView(buildText(context));
319             return item;
320         }
321 
322         @Override
bindView(View view, Context context, Cursor cursor)323         public void bindView(View view, Context context, Cursor cursor) {
324             LinearLayout item = (LinearLayout) view;
325 
326             String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.CONTACT_ID));
327             String name = cursor.getString(cursor.getColumnIndex(
328                     ContactsContract.Data.DISPLAY_NAME));
329             String timestamp = cursor.getString(cursor.getColumnIndex(
330                     ContactsContract.Data.CONTACT_LAST_UPDATED_TIMESTAMP));
331 
332             setText(item.getChildAt(0), id);
333             setText(item.getChildAt(1), name);
334             setText(item.getChildAt(2), timestamp);
335         }
336     }
337 
setText(View view, String value)338     private static void setText(View view, String value) {
339         TextView text = (TextView) view;
340         text.setText(value);
341     }
342 
buildText(Context context)343     private static TextView buildText(Context context) {
344         TextView view = new TextView(context);
345         view.setPadding(3, 3, 3, 3);
346         return view;
347     }
348 }
349