• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
isInsertCompat(CPOWrapper cpoWrapper)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      */
isUpdateCompat(CPOWrapper cpoWrapper)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      */
isDeleteCompat(CPOWrapper cpoWrapper)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      */
isAssertQueryCompat(CPOWrapper cpoWrapper)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      */
hasPrioritizedMimeType()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      */
isMSIMCompatible()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      */
isVideoCompatible()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      */
isVideoPresenceCompatible()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      */
isCallSubjectCompatible()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      */
isDefaultDialerCompatible()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      */
isLollipopMr1Compatible()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      */
isMarshmallowCompatible()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      */
isNCompatible()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      */
isClassAvailable(@ullable String className)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      */
isMethodAvailable(@ullable String className, @Nullable String methodName, Class<?>... parameterTypes)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
invokeMethod(@ullable Object instance, @Nullable String methodName, Class<?>[] parameterTypes, Object[] parameters)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      */
isLollipopCompatible()268     public static boolean isLollipopCompatible() {
269         return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP)
270                 >= Build.VERSION_CODES.LOLLIPOP;
271     }
272 }
273