From 271feecc2883b1e295903cfb91fb58f8b7e901b1 Mon Sep 17 00:00:00 2001 From: KeithYokoma Date: Fri, 3 Oct 2014 17:54:16 +0900 Subject: [PATCH] Add provider compatibility and os class port --- .../android/os/ServiceManagerPort.java | 46 ++++++++++ .../android/provider/CallLogCompat.java | 86 +++++++++++++++++++ .../android/provider/TelephonyCompat.java | 52 +++++++++++ 3 files changed, 184 insertions(+) create mode 100644 AndroidDeviceCompatibility/src/main/java/jp/mixi/compatibility/android/os/ServiceManagerPort.java create mode 100644 AndroidDeviceCompatibility/src/main/java/jp/mixi/compatibility/android/provider/CallLogCompat.java create mode 100644 AndroidDeviceCompatibility/src/main/java/jp/mixi/compatibility/android/provider/TelephonyCompat.java diff --git a/AndroidDeviceCompatibility/src/main/java/jp/mixi/compatibility/android/os/ServiceManagerPort.java b/AndroidDeviceCompatibility/src/main/java/jp/mixi/compatibility/android/os/ServiceManagerPort.java new file mode 100644 index 0000000..83fee60 --- /dev/null +++ b/AndroidDeviceCompatibility/src/main/java/jp/mixi/compatibility/android/os/ServiceManagerPort.java @@ -0,0 +1,46 @@ +package jp.mixi.compatibility.android.os; + +import android.os.IBinder; +import android.util.Log; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * @author KeithYokoma + */ +@SuppressWarnings("unused") +public final class ServiceManagerPort { + public static final String TAG = ServiceManagerPort.class.getSimpleName(); + private static final String CLASS_SERVICE_MANAGER = "android.os.ServiceManager"; + private static final String METHOD_GET_SERVICE = "getService"; + + private ServiceManagerPort() { + throw new AssertionError(); + } + + public static IBinder tryGetServiceManagerBinder(String serviceName) { + try { + return getServiceManagerBinder(serviceName); + } catch (ClassNotFoundException e) { + Log.e(TAG, "class not found: ", e); + } catch (NoSuchMethodException e) { + Log.e(TAG, "method not found: ", e); + } catch (InvocationTargetException e) { + Log.e(TAG, "invocation target error: ", e); + } catch (IllegalAccessException e) { + Log.e(TAG, "illegal access to the method: ", e); + } + return null; + } + + public static IBinder getServiceManagerBinder(String serviceName) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class serviceManager = ServiceManagerPort.class.getClassLoader().loadClass(CLASS_SERVICE_MANAGER); + Method getService = serviceManager.getDeclaredMethod(METHOD_GET_SERVICE, String.class); + if (!getService.isAccessible()) { + getService.setAccessible(true); + } + return (IBinder) getService.invoke(null, serviceName); + } +} + diff --git a/AndroidDeviceCompatibility/src/main/java/jp/mixi/compatibility/android/provider/CallLogCompat.java b/AndroidDeviceCompatibility/src/main/java/jp/mixi/compatibility/android/provider/CallLogCompat.java new file mode 100644 index 0000000..6ef8246 --- /dev/null +++ b/AndroidDeviceCompatibility/src/main/java/jp/mixi/compatibility/android/provider/CallLogCompat.java @@ -0,0 +1,86 @@ +package jp.mixi.compatibility.android.provider; + +import android.annotation.SuppressLint; +import android.content.ContentResolver; +import android.database.Cursor; +import android.os.Build; +import android.provider.CallLog; +import android.text.TextUtils; +import android.util.Log; + +/** + * Compatibility for the variety of call log data store. + * + * @author jfsso + * @author KeithYokoma + */ +@SuppressWarnings("unused") +public final class CallLogCompat { + public static final String TAG = CallLogCompat.class.getSimpleName(); + public static final String COLUMN_CACHED_LOOKUP_URI = "lookup_uri"; + public static final String UNKNOWN_NUMBER = "-1"; + public static final String PRIVATE_NUMBER = "-2"; + public static final String PAYPHONE_NUMBER = "-3"; + + private CallLogCompat() { + throw new AssertionError(); + } + + public static String getName(Cursor cursor, String fallback) { + String name = cursor.getString(cursor.getColumnIndexOrThrow(CallLog.Calls.CACHED_NAME)); + if (TextUtils.isEmpty(name)) + return fallback; + else + return name; + } + + @SuppressLint("InlinedApi") + public static int getNumberPresentation(Cursor cursor, String number) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + return cursor.getInt(cursor.getColumnIndex(CallLog.Calls.NUMBER_PRESENTATION)); + } else { + if (UNKNOWN_NUMBER.equals(number)) { + return CallLog.Calls.PRESENTATION_UNKNOWN; + } else if (PRIVATE_NUMBER.equals(number)) { + return CallLog.Calls.PRESENTATION_RESTRICTED; + } else if (PAYPHONE_NUMBER.equals(number)) { + return CallLog.Calls.PRESENTATION_PAYPHONE; + } + } + return CallLog.Calls.PRESENTATION_ALLOWED; + } + + /** + * Compatibility support method for Samsung devices to correctly fetch call logs. + * Many thanks to CallLogCalendar :), and the code reference is here(https://gist.github.com/jfsso/391c4bc757bc4210f4bc). + * + * @param resolver the resolver, not nullable. + * @param selection the selection, can be null. + */ + public static String addTypeSelectionIfNeeded(ContentResolver resolver, String selection) { + // Some samsung devices add all types of logs to the call log. + // Here we will make an attempt to know if it is one of those. + // It simply will check for the "logtype" column: + // if it exists, the phone is a troublesome one: add some selection parameters + // if not, the phone is a nice one ;) + try { + if (Build.BRAND.toLowerCase().equals("samsung")) { + resolver.query(CallLog.Calls.CONTENT_URI, new String[] { "logtype" }, null, null, null); + // no exception thrown + // yes, it is a galaxy phone! lets add the parameter to select + // only call logs + Log.v(TAG, "oops, this one needs some hack for call logs X("); + if(TextUtils.isEmpty(selection)) { + selection = "logtype IN (100, 500)"; + } else { + selection += " AND logtype IN (100, 500)"; + } + } + } catch (Exception e) { + // ignore + Log.v(TAG, "nice one! no need to hack call logs :)"); + } + Log.v(TAG, "after: " + selection); + return selection; + } +} diff --git a/AndroidDeviceCompatibility/src/main/java/jp/mixi/compatibility/android/provider/TelephonyCompat.java b/AndroidDeviceCompatibility/src/main/java/jp/mixi/compatibility/android/provider/TelephonyCompat.java new file mode 100644 index 0000000..a1520c0 --- /dev/null +++ b/AndroidDeviceCompatibility/src/main/java/jp/mixi/compatibility/android/provider/TelephonyCompat.java @@ -0,0 +1,52 @@ +package jp.mixi.compatibility.android.provider; + +import android.annotation.SuppressLint; +import android.net.Uri; +import android.os.Build; +import android.provider.Telephony; + +/** + * @author jfsso + * @author KeithYokoma + */ +@SuppressWarnings("unused") +public final class TelephonyCompat { + private TelephonyCompat() { + throw new AssertionError(); + } + + public static final class Sms { + public static final String TAG = Sms.class.getSimpleName(); + public static final String URI_CONTENT_SMS = "content://sms"; + public static final int TYPE_INBOX = 1; + public static final int TYPE_SENT = 2; + public static final String COLUMN_DATE = "date"; + public static final String COLUMN_TYPE = "type"; + public static final String COLUMN_BODY = "body"; + public static final String COLUMN_ADDRESS = "address"; + public static final String COLUMN_SUBJECT = "subject"; + public static final String COLUMN_THREAD_ID = "thread_id"; + public static final String COLUMN_PERSON = "person"; + public static final String DEFAULT_SORT_ORDER = "date DESC"; + + private Sms() { + throw new AssertionError(); + } + + @SuppressLint("NewApi") // good to go with our compatibility + public static Uri getContentUri() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) + return Telephony.Sms.CONTENT_URI; + else + return Uri.parse(URI_CONTENT_SMS); + } + + @SuppressLint("NewApi") // we port from the newer api + public static String getDefaultSortOrder() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) + return Telephony.Sms.DEFAULT_SORT_ORDER; + else + return DEFAULT_SORT_ORDER; + } + } +} \ No newline at end of file