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.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.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     public static boolean isNougatMr1Compatible() {
    178         return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.N_MR1)
    179                 >= Build.VERSION_CODES.N_MR1;
    180     }
    181 
    182     public static boolean isLauncherShortcutCompatible() {
    183         return isNougatMr1Compatible();
    184     }
    185 
    186     /**
    187      * Determines if the given class is available. Can be used to check if system apis exist at
    188      * runtime.
    189      *
    190      * @param className the name of the class to look for.
    191      * @return {@code true} if the given class is available, {@code false} otherwise or if className
    192      * is empty.
    193      */
    194     public static boolean isClassAvailable(@Nullable String className) {
    195         if (TextUtils.isEmpty(className)) {
    196             return false;
    197         }
    198         try {
    199             Class.forName(className);
    200             return true;
    201         } catch (ClassNotFoundException e) {
    202             return false;
    203         } catch (Throwable t) {
    204             Log.e(TAG, "Unexpected exception when checking if class:" + className + " exists at "
    205                     + "runtime", t);
    206             return false;
    207         }
    208     }
    209 
    210     /**
    211      * Determines if the given class's method is available to call. Can be used to check if system
    212      * apis exist at runtime.
    213      *
    214      * @param className the name of the class to look for
    215      * @param methodName the name of the method to look for
    216      * @param parameterTypes the needed parameter types for the method to look for
    217      * @return {@code true} if the given class is available, {@code false} otherwise or if className
    218      * or methodName are empty.
    219      */
    220     public static boolean isMethodAvailable(@Nullable String className, @Nullable String methodName,
    221             Class<?>... parameterTypes) {
    222         if (TextUtils.isEmpty(className) || TextUtils.isEmpty(methodName)) {
    223             return false;
    224         }
    225 
    226         try {
    227             Class.forName(className).getMethod(methodName, parameterTypes);
    228             return true;
    229         } catch (ClassNotFoundException | NoSuchMethodException e) {
    230             if (Log.isLoggable(TAG, Log.VERBOSE)) {
    231                 Log.v(TAG, "Could not find method: " + className + "#" + methodName);
    232             }
    233             return false;
    234         } catch (Throwable t) {
    235             Log.e(TAG, "Unexpected exception when checking if method: " + className + "#"
    236                     + methodName + " exists at runtime", t);
    237             return false;
    238         }
    239     }
    240 
    241     /**
    242      * Invokes a given class's method using reflection. Can be used to call system apis that exist
    243      * at runtime but not in the SDK.
    244      *
    245      * @param instance The instance of the class to invoke the method on.
    246      * @param methodName The name of the method to invoke.
    247      * @param parameterTypes The needed parameter types for the method.
    248      * @param parameters The parameter values to pass into the method.
    249      * @return The result of the invocation or {@code null} if instance or methodName are empty, or
    250      * if the reflection fails.
    251      */
    252     @Nullable
    253     public static Object invokeMethod(@Nullable Object instance, @Nullable String methodName,
    254             Class<?>[] parameterTypes, Object[] parameters) {
    255         if (instance == null || TextUtils.isEmpty(methodName)) {
    256             return null;
    257         }
    258 
    259         String className = instance.getClass().getName();
    260         try {
    261             return Class.forName(className).getMethod(methodName, parameterTypes)
    262                     .invoke(instance, parameters);
    263         } catch (ClassNotFoundException | NoSuchMethodException | IllegalArgumentException
    264                 | IllegalAccessException | InvocationTargetException e) {
    265             if (Log.isLoggable(TAG, Log.VERBOSE)) {
    266                 Log.v(TAG, "Could not invoke method: " + className + "#" + methodName);
    267             }
    268             return null;
    269         } catch (Throwable t) {
    270             Log.e(TAG, "Unexpected exception when invoking method: " + className
    271                     + "#" + methodName + " at runtime", t);
    272             return null;
    273         }
    274     }
    275 
    276     /**
    277      * Determines if this version is compatible with Lollipop-specific APIs. Can also force the
    278      * version to be lower through SdkVersionOverride.
    279      *
    280      * @return {@code true} if call subject is a feature on this device, {@code false} otherwise.
    281      */
    282     public static boolean isLollipopCompatible() {
    283         return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP)
    284                 >= Build.VERSION_CODES.LOLLIPOP;
    285     }
    286 }
    287