Home | History | Annotate | Download | only in compat
      1 /*
      2  * Copyright (C) 2015 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 package com.android.contacts.common.compat;
     17 
     18 import android.os.Build;
     19 import android.os.Build.VERSION;
     20 import android.support.annotation.Nullable;
     21 import android.support.v4.os.BuildCompat;
     22 import android.text.TextUtils;
     23 import android.util.Log;
     24 
     25 import com.android.contacts.common.model.CPOWrapper;
     26 
     27 import java.lang.reflect.InvocationTargetException;
     28 import java.lang.reflect.Method;
     29 
     30 public final class CompatUtils {
     31 
     32     private static final String TAG = CompatUtils.class.getSimpleName();
     33 
     34     /**
     35      * These 4 variables are copied from ContentProviderOperation for compatibility.
     36      */
     37     public final static int TYPE_INSERT = 1;
     38 
     39     public final static int TYPE_UPDATE = 2;
     40 
     41     public final static int TYPE_DELETE = 3;
     42 
     43     public final static int TYPE_ASSERT = 4;
     44 
     45     /**
     46      * Returns whether the operation in CPOWrapper is of TYPE_INSERT;
     47      */
     48     public static boolean isInsertCompat(CPOWrapper cpoWrapper) {
     49         if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) >= Build.VERSION_CODES.M) {
     50             return cpoWrapper.getOperation().isInsert();
     51         }
     52         return (cpoWrapper.getType() == TYPE_INSERT);
     53     }
     54 
     55     /**
     56      * Returns whether the operation in CPOWrapper is of TYPE_UPDATE;
     57      */
     58     public static boolean isUpdateCompat(CPOWrapper cpoWrapper) {
     59         if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) >= Build.VERSION_CODES.M) {
     60             return cpoWrapper.getOperation().isUpdate();
     61         }
     62         return (cpoWrapper.getType() == TYPE_UPDATE);
     63     }
     64 
     65     /**
     66      * Returns whether the operation in CPOWrapper is of TYPE_DELETE;
     67      */
     68     public static boolean isDeleteCompat(CPOWrapper cpoWrapper) {
     69         if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) >= Build.VERSION_CODES.M) {
     70             return cpoWrapper.getOperation().isDelete();
     71         }
     72         return (cpoWrapper.getType() == TYPE_DELETE);
     73     }
     74     /**
     75      * Returns whether the operation in CPOWrapper is of TYPE_ASSERT;
     76      */
     77     public static boolean isAssertQueryCompat(CPOWrapper cpoWrapper) {
     78         if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) >= Build.VERSION_CODES.M) {
     79             return cpoWrapper.getOperation().isAssertQuery();
     80         }
     81         return (cpoWrapper.getType() == TYPE_ASSERT);
     82     }
     83 
     84     /**
     85      * PrioritizedMimeType is added in API level 23.
     86      */
     87     public static boolean hasPrioritizedMimeType() {
     88         return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M)
     89                 >= Build.VERSION_CODES.M;
     90     }
     91 
     92     /**
     93      * Determines if this version is compatible with multi-SIM and the phone account APIs. Can also
     94      * force the version to be lower through SdkVersionOverride.
     95      *
     96      * @return {@code true} if multi-SIM capability is available, {@code false} otherwise.
     97      */
     98     public static boolean isMSIMCompatible() {
     99         return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP)
    100                 >= Build.VERSION_CODES.LOLLIPOP_MR1;
    101     }
    102 
    103     /**
    104      * Determines if this version is compatible with video calling. Can also force the version to be
    105      * lower through SdkVersionOverride.
    106      *
    107      * @return {@code true} if video calling is allowed, {@code false} otherwise.
    108      */
    109     public static boolean isVideoCompatible() {
    110         return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP)
    111                 >= Build.VERSION_CODES.M;
    112     }
    113 
    114     /**
    115      * Determines if this version is capable of using presence checking for video calling. Support
    116      * for video call presence indication is added in SDK 24.
    117      *
    118      * @return {@code true} if video presence checking is allowed, {@code false} otherwise.
    119      */
    120     public static boolean isVideoPresenceCompatible() {
    121         return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M)
    122                 > Build.VERSION_CODES.M;
    123     }
    124 
    125     /**
    126      * Determines if this version is compatible with call subject. Can also force the version to be
    127      * lower through SdkVersionOverride.
    128      *
    129      * @return {@code true} if call subject is a feature on this device, {@code false} otherwise.
    130      */
    131     public static boolean isCallSubjectCompatible() {
    132         return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP)
    133                 >= Build.VERSION_CODES.M;
    134     }
    135 
    136     /**
    137      * Determines if this version is compatible with a default dialer. Can also force the version to
    138      * be lower through {@link SdkVersionOverride}.
    139      *
    140      * @return {@code true} if default dialer is a feature on this device, {@code false} otherwise.
    141      */
    142     public static boolean isDefaultDialerCompatible() {
    143         return isMarshmallowCompatible();
    144     }
    145 
    146     /**
    147      * Determines if this version is compatible with Lollipop Mr1-specific APIs. Can also force the
    148      * version to be lower through SdkVersionOverride.
    149      *
    150      * @return {@code true} if runtime sdk is compatible with Lollipop MR1, {@code false} otherwise.
    151      */
    152     public static boolean isLollipopMr1Compatible() {
    153         return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP_MR1)
    154                 >= Build.VERSION_CODES.LOLLIPOP_MR1;
    155     }
    156 
    157     /**
    158      * Determines if this version is compatible with Marshmallow-specific APIs. Can also force the
    159      * version to be lower through SdkVersionOverride.
    160      *
    161      * @return {@code true} if runtime sdk is compatible with Marshmallow, {@code false} otherwise.
    162      */
    163     public static boolean isMarshmallowCompatible() {
    164         return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP)
    165                 >= Build.VERSION_CODES.M;
    166     }
    167 
    168     /**
    169      * Determines if this version is compatible with N-specific APIs.
    170      *
    171      * @return {@code true} if runtime sdk is compatible with N and the app is built with N, {@code
    172      * false} otherwise.
    173      */
    174     public static boolean isNCompatible() {
    175         return BuildCompat.isAtLeastN();
    176     }
    177 
    178     /**
    179      * Determines if the given class is available. Can be used to check if system apis exist at
    180      * runtime.
    181      *
    182      * @param className the name of the class to look for.
    183      * @return {@code true} if the given class is available, {@code false} otherwise or if className
    184      * is empty.
    185      */
    186     public static boolean isClassAvailable(@Nullable String className) {
    187         if (TextUtils.isEmpty(className)) {
    188             return false;
    189         }
    190         try {
    191             Class.forName(className);
    192             return true;
    193         } catch (ClassNotFoundException e) {
    194             return false;
    195         } catch (Throwable t) {
    196             Log.e(TAG, "Unexpected exception when checking if class:" + className + " exists at "
    197                     + "runtime", t);
    198             return false;
    199         }
    200     }
    201 
    202     /**
    203      * Determines if the given class's method is available to call. Can be used to check if system
    204      * apis exist at runtime.
    205      *
    206      * @param className the name of the class to look for
    207      * @param methodName the name of the method to look for
    208      * @param parameterTypes the needed parameter types for the method to look for
    209      * @return {@code true} if the given class is available, {@code false} otherwise or if className
    210      * or methodName are empty.
    211      */
    212     public static boolean isMethodAvailable(@Nullable String className, @Nullable String methodName,
    213             Class<?>... parameterTypes) {
    214         if (TextUtils.isEmpty(className) || TextUtils.isEmpty(methodName)) {
    215             return false;
    216         }
    217 
    218         try {
    219             Class.forName(className).getMethod(methodName, parameterTypes);
    220             return true;
    221         } catch (ClassNotFoundException | NoSuchMethodException e) {
    222             Log.v(TAG, "Could not find method: " + className + "#" + methodName);
    223             return false;
    224         } catch (Throwable t) {
    225             Log.e(TAG, "Unexpected exception when checking if method: " + className + "#"
    226                     + methodName + " exists at runtime", t);
    227             return false;
    228         }
    229     }
    230 
    231     /**
    232      * Invokes a given class's method using reflection. Can be used to call system apis that exist
    233      * at runtime but not in the SDK.
    234      *
    235      * @param instance The instance of the class to invoke the method on.
    236      * @param methodName The name of the method to invoke.
    237      * @param parameterTypes The needed parameter types for the method.
    238      * @param parameters The parameter values to pass into the method.
    239      * @return The result of the invocation or {@code null} if instance or methodName are empty, or
    240      * if the reflection fails.
    241      */
    242     @Nullable
    243     public static Object invokeMethod(@Nullable Object instance, @Nullable String methodName,
    244             Class<?>[] parameterTypes, Object[] parameters) {
    245         if (instance == null || TextUtils.isEmpty(methodName)) {
    246             return null;
    247         }
    248 
    249         String className = instance.getClass().getName();
    250         try {
    251             return Class.forName(className).getMethod(methodName, parameterTypes)
    252                     .invoke(instance, parameters);
    253         } catch (ClassNotFoundException | NoSuchMethodException | IllegalArgumentException
    254                 | IllegalAccessException | InvocationTargetException e) {
    255             Log.v(TAG, "Could not invoke method: " + className + "#" + methodName);
    256             return null;
    257         } catch (Throwable t) {
    258             Log.e(TAG, "Unexpected exception when invoking method: " + className
    259                     + "#" + methodName + " at runtime", t);
    260             return null;
    261         }
    262     }
    263 
    264     /**
    265      * Determines if this version is compatible with Lollipop-specific APIs. Can also force the
    266      * version to be lower through SdkVersionOverride.
    267      *
    268      * @return {@code true} if call subject is a feature on this device, {@code false} otherwise.
    269      */
    270     public static boolean isLollipopCompatible() {
    271         return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP)
    272                 >= Build.VERSION_CODES.LOLLIPOP;
    273     }
    274 }
    275