content: /*******************************************************************************
   * Software Name : RCS IMS Stack
   *
   * Copyright (C) 2010-2016 Orange
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   ******************************************************************************/
  
  package com.gsma.rcs.ri.utils;
  
  import com.gsma.rcs.api.connection.ConnectionManager;
  import com.gsma.rcs.api.connection.utils.ExceptionUtil;
  import com.gsma.rcs.ri.R;
  import com.gsma.services.rcs.RcsServiceException;
  import com.gsma.services.rcs.RcsServiceNotAvailableException;
  import com.gsma.services.rcs.contact.ContactId;
  import com.gsma.services.rcs.contact.ContactService;
  import com.gsma.services.rcs.contact.RcsContact;
  
  import android.content.ContentResolver;
  import android.content.ContentUris;
  import android.content.Context;
  import android.database.Cursor;
  import android.database.SQLException;
  import android.graphics.Bitmap;
  import android.graphics.BitmapFactory;
  import android.net.Uri;
  import android.provider.ContactsContract;
  import android.support.v4.util.LruCache;
  import android.util.Log;
  
  import java.io.InputStream;
  
  /**
   * Utilities to manage the RCS display name
   * 
   * @author Philippe LEMORDANT
   */
  public class RcsContactUtil {
  
      private static final int MAX_DISPLAY_NAME_IN_CACHE = 200;
      private static final String LOGTAG = LogUtils.getTag(RcsContactUtil.class.getSimpleName());
  
      private static volatile RcsContactUtil sInstance;
      private final ContentResolver mResolver;
      private ContactService mService;
      private final String mDefaultDisplayName;
      private LruCache<ContactId, String> mDisplayNameAndroidCache;
      private LruCache<ContactId, Bitmap> mPhotoContactCache;
  
      private static final String[] PROJ_DISPLAY_NAME = new String[] {
          ContactsContract.PhoneLookup.DISPLAY_NAME
      };
  
      private static final String[] PROJ_CONTACT_ID = new String[] {
          ContactsContract.PhoneLookup._ID
      };
  
      /**
       * Constructor
       * 
       * @param context the context
       */
      private RcsContactUtil(Context context) {
          mService = ConnectionManager.getInstance().getContactApi();
          mResolver = context.getContentResolver();
          mDefaultDisplayName = context.getString(R.string.label_no_contact);
          mDisplayNameAndroidCache = new LruCache<>(MAX_DISPLAY_NAME_IN_CACHE);
          mPhotoContactCache = new LruCache<>(MAX_DISPLAY_NAME_IN_CACHE);
      }
  
      /**
       * Get an instance of RcsDisplayName.
       * 
       * @param context the context
       * @return the singleton instance.
       */
      public static RcsContactUtil getInstance(Context context) {
          if (sInstance != null) {
              return sInstance;
          }
          synchronized (RcsContactUtil.class) {
              if (sInstance == null) {
                  sInstance = new RcsContactUtil(context);
              }
              return sInstance;
          }
      }
  
      private String getDisplayNameFromAddressBook(ContactId contact) {
          /* First try to get it from cache */
          String displayName = mDisplayNameAndroidCache.get(contact);
          if (displayName != null) {
              return displayName;
          }
          /* Not found in cache: query the Android address book */
          Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
                  Uri.encode(contact.toString()));
          Cursor cursor = null;
          try {
              cursor = mResolver.query(uri, PROJ_DISPLAY_NAME, null, null, null);
              if (cursor == null) {
                  throw new SQLException("Cannot query display name for contact=" + contact);
              }
              if (!cursor.moveToFirst()) {
                  return null;
              }
              displayName = cursor.getString(cursor
                      .getColumnIndexOrThrow(ContactsContract.PhoneLookup.DISPLAY_NAME));
              /* Insert in cache */
              mDisplayNameAndroidCache.put(contact, displayName);
              return displayName;
  
          } finally {
              if (cursor != null) {
                  cursor.close();
              }
          }
      }
  
      /**
       * Returns display name which can be displayed on UI
       * 
       * @param contact the contact
       * @return the display name
       */
      public String getDisplayName(ContactId contact) {
          if (contact == null) {
              return mDefaultDisplayName;
          }
          String displayName = getDisplayNameFromAddressBook(contact);
          if (displayName != null) {
              /*
               * Contact exists in Android Address Book: returns the AB display name.
               */
              return displayName;
          }
          try {
              if (mService == null) {
                  mService = ConnectionManager.getInstance().getContactApi();
              }
              RcsContact rcsContact = mService.getRcsContact(contact);
              if (rcsContact == null) {
                  /*
                   * Contact is not RCS: returns the phone number.
                   */
                  return contact.toString();
              }
              displayName = rcsContact.getDisplayName();
              if (displayName == null) {
                  return contact.toString();
              } else {
                  return displayName;
              }
          } catch (RcsServiceNotAvailableException ignore) {
  
          } catch (RcsServiceException e) {
              Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
          }
          /* By default display name is set to the MSISDN */
          return contact.toString();
      }
  
      /**
       * get RCS display in a String which can be displayed on UI
       * 
       * @param number the phone number
       * @return the name which can be displayed on UI
       */
      public String getDisplayName(String number) {
          if (number == null) {
              return mDefaultDisplayName;
          }
          if (!ContactUtil.isValidContact(number)) {
              return number;
          }
          ContactId contact = ContactUtil.formatContact(number);
          return getDisplayName(contact);
      }
  
      /**
       * Gets the photo of a contact, or null if no photo is present
       * 
       * @param contact the contact ID
       * @return an Bitmap of the photo, or null if no photo is present
       */
      public Bitmap getPhotoFromContactId(ContactId contact) {
          /* First try to get it from cache */
          Bitmap photo = mPhotoContactCache.get(contact);
          if (photo != null) {
              return photo;
          }
          Uri contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_FILTER_URI,
                  Uri.encode(contact.toString()));
          Cursor cursor = null;
          try {
              cursor = mResolver.query(contactUri, PROJ_CONTACT_ID, null, null, null);
              if (cursor == null) {
                  throw new SQLException("Cannot query photo for contact=" + contact);
              }
              if (!cursor.moveToFirst()) {
                  return null;
              }
              long contactId = cursor.getLong(cursor
                      .getColumnIndexOrThrow(ContactsContract.PhoneLookup._ID));
              InputStream photoInputStream = ContactsContract.Contacts.openContactPhotoInputStream(
                      mResolver,
                      ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contactId));
              if (photoInputStream != null) {
                  photo = BitmapFactory.decodeStream(photoInputStream);
                  /* Insert in cache */
                  mPhotoContactCache.put(contact, photo);
                  return photo;
              }
              return null;
  
          } finally {
              if (cursor != null) {
                  cursor.close();
              }
          }
      }
  }
  