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