Home | History | Annotate | Download | only in content
      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
     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
    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
    127     protected void onPause() {
    128         super.onPause();
    129         unregisterReceiver(mReceiver);
    130     }
    131 
    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 
    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 
    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 
    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
    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 
    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 
    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
    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
    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          */
    261         public DeleteCursorWrapper(Cursor cursor) {
    262             super(cursor);
    263         }
    264 
    265         @Override
    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 
    278         public DeleteAdapter(Context context, Cursor c, int flags) {
    279             super(context, c, flags);
    280             this.mContext = context;
    281         }
    282 
    283         @Override
    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
    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 
    308         public ChangeAdapter(Context context, Cursor c, int flags) {
    309             super(context, c, flags);
    310             mContext = context;
    311         }
    312 
    313         @Override
    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
    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 
    338     private static void setText(View view, String value) {
    339         TextView text = (TextView) view;
    340         text.setText(value);
    341     }
    342 
    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