Home | History | Annotate | Download | only in facade
      1 /*
      2  * Copyright (C) 2016 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * 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, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License.
     15  */
     16 
     17 package com.googlecode.android_scripting.facade;
     18 
     19 import android.app.Service;
     20 import android.content.ContentResolver;
     21 import android.content.ContentUris;
     22 import android.content.Intent;
     23 import android.database.Cursor;
     24 import android.net.Uri;
     25 import android.provider.ContactsContract;
     26 
     27 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
     28 import com.googlecode.android_scripting.rpc.Rpc;
     29 import com.googlecode.android_scripting.rpc.RpcOptional;
     30 import com.googlecode.android_scripting.rpc.RpcParameter;
     31 
     32 import java.util.ArrayList;
     33 import java.util.List;
     34 
     35 import org.json.JSONArray;
     36 import org.json.JSONException;
     37 import org.json.JSONObject;
     38 
     39 /**
     40  * Provides access to contacts related functionality.
     41  *
     42  * @author MeanEYE.rcf (meaneye.rcf (at) gmail.com
     43  */
     44 public class ContactsFacade extends RpcReceiver {
     45   private static final Uri CONTACTS_URI = Uri.parse("content://contacts/people");
     46   private final ContentResolver mContentResolver;
     47   private final Service mService;
     48   private final CommonIntentsFacade mCommonIntentsFacade;
     49   public Uri mPhoneContent = null;
     50   public String mContactId;
     51   public String mPrimary;
     52   public String mPhoneNumber;
     53   public String mHasPhoneNumber;
     54 
     55   public ContactsFacade(FacadeManager manager) {
     56     super(manager);
     57     mService = manager.getService();
     58     mContentResolver = mService.getContentResolver();
     59     mCommonIntentsFacade = manager.getReceiver(CommonIntentsFacade.class);
     60     try {
     61       // Backward compatibility... get contract stuff using reflection
     62       Class<?> phone = Class.forName("android.provider.ContactsContract$CommonDataKinds$Phone");
     63       mPhoneContent = (Uri) phone.getField("CONTENT_URI").get(null);
     64       mContactId = (String) phone.getField("CONTACT_ID").get(null);
     65       mPrimary = (String) phone.getField("IS_PRIMARY").get(null);
     66       mPhoneNumber = (String) phone.getField("NUMBER").get(null);
     67       mHasPhoneNumber = (String) phone.getField("HAS_PHONE_NUMBER").get(null);
     68     } catch (Exception e) {
     69     }
     70   }
     71 
     72   private Uri buildUri(Integer id) {
     73     Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, id);
     74     return uri;
     75   }
     76 
     77   @Rpc(description = "Displays a list of contacts to pick from.", returns = "A map of result values.")
     78   public Intent pickContact() throws JSONException {
     79     return mCommonIntentsFacade.pick("content://contacts/people");
     80   }
     81 
     82   @Rpc(description = "Displays a list of phone numbers to pick from.", returns = "The selected phone number.")
     83   public String pickPhone() throws JSONException {
     84     String result = null;
     85     Intent data = mCommonIntentsFacade.pick("content://contacts/phones");
     86     if (data != null) {
     87       Uri phoneData = data.getData();
     88       Cursor cursor = mService.getContentResolver().query(phoneData, null, null, null, null);
     89       if (cursor != null) {
     90         if (cursor.moveToFirst()) {
     91           result = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.PhoneLookup.NUMBER));
     92         }
     93         cursor.close();
     94       }
     95     }
     96     return result;
     97   }
     98 
     99   @Rpc(description = "Returns a List of all possible attributes for contacts.")
    100   public List<String> contactsGetAttributes() {
    101     List<String> result = new ArrayList<String>();
    102     Cursor cursor = mContentResolver.query(CONTACTS_URI, null, null, null, null);
    103     if (cursor != null) {
    104       String[] columns = cursor.getColumnNames();
    105       for (int i = 0; i < columns.length; i++) {
    106         result.add(columns[i]);
    107       }
    108       cursor.close();
    109     }
    110     return result;
    111   }
    112 
    113   // TODO(MeanEYE.rcf): Add ability to narrow selection by providing named pairs of attributes.
    114   @Rpc(description = "Returns a List of all contact IDs.")
    115   public List<Integer> contactsGetIds() {
    116     List<Integer> result = new ArrayList<Integer>();
    117     String[] columns = { "_id" };
    118     Cursor cursor = mContentResolver.query(CONTACTS_URI, columns, null, null, null);
    119     if (cursor != null) {
    120       while (cursor.moveToNext()) {
    121         result.add(cursor.getInt(0));
    122       }
    123       cursor.close();
    124     }
    125     return result;
    126   }
    127 
    128   @Rpc(description = "Returns a List of all contacts.", returns = "a List of contacts as Maps")
    129   public List<JSONObject> contactsGet(
    130       @RpcParameter(name = "attributes") @RpcOptional JSONArray attributes) throws JSONException {
    131     List<JSONObject> result = new ArrayList<JSONObject>();
    132     String[] columns;
    133     if (attributes == null || attributes.length() == 0) {
    134       // In case no attributes are specified we set the default ones.
    135       columns = new String[] { "_id", "name", "primary_phone", "primary_email", "type" };
    136     } else {
    137       // Convert selected attributes list into usable string list.
    138       columns = new String[attributes.length()];
    139       for (int i = 0; i < attributes.length(); i++) {
    140         columns[i] = attributes.getString(i);
    141       }
    142     }
    143     List<String> queryList = new ArrayList<String>();
    144     for (String s : columns) {
    145       queryList.add(s);
    146     }
    147     if (!queryList.contains("_id")) {
    148       queryList.add("_id");
    149     }
    150 
    151     String[] query = queryList.toArray(new String[queryList.size()]);
    152     Cursor cursor = mContentResolver.query(CONTACTS_URI, query, null, null, null);
    153     if (cursor != null) {
    154       int idIndex = cursor.getColumnIndex("_id");
    155       while (cursor.moveToNext()) {
    156         String id = cursor.getString(idIndex);
    157         JSONObject message = new JSONObject();
    158         for (int i = 0; i < columns.length; i++) {
    159           String key = columns[i];
    160           String value = cursor.getString(cursor.getColumnIndex(key));
    161           if (mPhoneNumber != null) {
    162             if (key.equals("primary_phone")) {
    163               value = findPhone(id);
    164             }
    165           }
    166           message.put(key, value);
    167         }
    168         result.add(message);
    169       }
    170       cursor.close();
    171     }
    172     return result;
    173   }
    174 
    175   private String findPhone(String id) {
    176     String result = null;
    177     if (id == null || id.equals("")) {
    178       return result;
    179     }
    180     try {
    181       if (Integer.parseInt(id) > 0) {
    182         Cursor pCur =
    183             mContentResolver.query(mPhoneContent, new String[] { mPhoneNumber }, mContactId
    184                 + " = ? and " + mPrimary + "=1", new String[] { id }, null);
    185         if (pCur != null) {
    186           pCur.getColumnNames();
    187           while (pCur.moveToNext()) {
    188             result = pCur.getString(0);
    189             break;
    190           }
    191         }
    192         pCur.close();
    193       }
    194     } catch (Exception e) {
    195       return null;
    196     }
    197     return result;
    198   }
    199 
    200   @Rpc(description = "Returns contacts by ID.")
    201   public JSONObject contactsGetById(@RpcParameter(name = "id") Integer id,
    202       @RpcParameter(name = "attributes") @RpcOptional JSONArray attributes) throws JSONException {
    203     JSONObject result = null;
    204     Uri uri = buildUri(id);
    205     String[] columns;
    206     if (attributes == null || attributes.length() == 0) {
    207       // In case no attributes are specified we set the default ones.
    208       columns = new String[] { "_id", "name", "primary_phone", "primary_email", "type" };
    209     } else {
    210       // Convert selected attributes list into usable string list.
    211       columns = new String[attributes.length()];
    212       for (int i = 0; i < attributes.length(); i++) {
    213         columns[i] = attributes.getString(i);
    214       }
    215     }
    216     Cursor cursor = mContentResolver.query(uri, columns, null, null, null);
    217     if (cursor != null) {
    218       result = new JSONObject();
    219       cursor.moveToFirst();
    220       for (int i = 0; i < columns.length; i++) {
    221         result.put(columns[i], cursor.getString(i));
    222       }
    223       cursor.close();
    224     }
    225     return result;
    226   }
    227 
    228   // TODO(MeanEYE.rcf): Add ability to narrow selection by providing named pairs of attributes.
    229   @Rpc(description = "Returns the number of contacts.")
    230   public Integer contactsGetCount() {
    231     Integer result = 0;
    232     Cursor cursor = mContentResolver.query(CONTACTS_URI, null, null, null, null);
    233     if (cursor != null) {
    234       result = cursor.getCount();
    235       cursor.close();
    236     }
    237     return result;
    238   }
    239 
    240   private String[] jsonToArray(JSONArray array) throws JSONException {
    241     String[] result = null;
    242     if (array != null && array.length() > 0) {
    243       result = new String[array.length()];
    244       for (int i = 0; i < array.length(); i++) {
    245         result[i] = array.getString(i);
    246       }
    247     }
    248     return result;
    249   }
    250 
    251   /**
    252    * Exactly as per <a href=
    253    * "http://developer.android.com/reference/android/content/ContentResolver.html#query%28android.net.Uri,%20java.lang.String[],%20java.lang.String,%20java.lang.String[],%20java.lang.String%29"
    254    * >ContentResolver.query</a>
    255    */
    256   @Rpc(description = "Content Resolver Query", returns = "result of query as Maps")
    257   public List<JSONObject> queryContent(
    258       @RpcParameter(name = "uri", description = "The URI, using the content:// scheme, for the content to retrieve.") String uri,
    259       @RpcParameter(name = "attributes", description = "A list of which columns to return. Passing null will return all columns") @RpcOptional JSONArray attributes,
    260       @RpcParameter(name = "selection", description = "A filter declaring which rows to return") @RpcOptional String selection,
    261       @RpcParameter(name = "selectionArgs", description = "You may include ?s in selection, which will be replaced by the values from selectionArgs") @RpcOptional JSONArray selectionArgs,
    262       @RpcParameter(name = "order", description = "How to order the rows") @RpcOptional String order)
    263       throws JSONException {
    264     List<JSONObject> result = new ArrayList<JSONObject>();
    265     String[] columns = jsonToArray(attributes);
    266     String[] args = jsonToArray(selectionArgs);
    267     Cursor cursor = mContentResolver.query(Uri.parse(uri), columns, selection, args, order);
    268     if (cursor != null) {
    269       String[] names = cursor.getColumnNames();
    270       while (cursor.moveToNext()) {
    271         JSONObject message = new JSONObject();
    272         for (int i = 0; i < cursor.getColumnCount(); i++) {
    273           String key = names[i];
    274           String value = cursor.getString(i);
    275           message.put(key, value);
    276         }
    277         result.add(message);
    278       }
    279       cursor.close();
    280     }
    281     return result;
    282   }
    283 
    284   @Rpc(description = "Content Resolver Query Attributes", returns = "a list of available columns for a given content uri")
    285   public JSONArray queryAttributes(
    286       @RpcParameter(name = "uri", description = "The URI, using the content:// scheme, for the content to retrieve.") String uri)
    287       throws JSONException {
    288     JSONArray result = new JSONArray();
    289     Cursor cursor = mContentResolver.query(Uri.parse(uri), null, "1=0", null, null);
    290     if (cursor != null) {
    291       String[] names = cursor.getColumnNames();
    292       for (String name : names) {
    293         result.put(name);
    294       }
    295       cursor.close();
    296     }
    297     return result;
    298   }
    299 
    300   @Override
    301   public void shutdown() {
    302   }
    303 }
    304