// Copyright 2010 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.android.stardroid.util;
import android.app.Activity;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Utilities for getting the version number of Android we're running under
* and running methods that only exist in later versions. The point of
* keeping them altogether is so that they're easier to find once we
* stop supporting lower versions. This class should be reviewed periodically
* and any methods that are no longer required inlined.
*
* @author John Taylor
*/
public class OsVersions {
private static final String TAG = MiscUtil.getTag(OsVersions.class);
private static final int VIEW_STATUS_BAR_HIDDEN = 1;
private static final int VIEW_STATUS_BAR_VISIBLE = 0;
private OsVersions() {}
/**
* Returns the android release code number. e.g. CUPCAKE = 3, FROYO = 8.
*/
public static int version() {
// TODO(johntaylor): this method can simply return android.os.Build.VERSION.SDK_INT
// once we no longer support 1.5.
String versionString = android.os.Build.VERSION.SDK;
return Integer.parseInt(versionString);
}
/**
* Invokes Activity.overridePendingTransition if possible.
*
* Eclair (v5) and above.
*/
public static void overridePendingTransition(Activity caller, int in, int out) {
invokeByReflection(caller, new Class<?>[] {int.class, int.class},
new Object[] {in, out}, "overridePendingTransition");
}
// MotionEvent methods
/**
* Invokes MotionEvent.getPointerCount if possible.
*
* Eclair (v5) and above.
*/
public static int getPointerCount(MotionEvent caller) {
try {
return (Integer) invokeByReflection(
caller, new Class<?>[0], new Object[0], "getPointerCount");
} catch (UnsupportedOperationException e) {
// Obviously if we're pre Eclair, there can only be one pointer.
return 1;
}
}
/**
* Invokes MotionEvent.getX(int) if possible.
*
* Eclair (v5) and above.
*/
public static float getX(MotionEvent caller, int index) {
try {
return (Float) invokeByReflection(
caller, new Class<?>[]{int.class}, new Object[]{index}, "getX");
} catch (UnsupportedOperationException e) {
// Obviously if we're pre-Eclair, there can only be one pointer.
return caller.getX();
}
}
/**
* Invokes MotionEvent.getY(int) if possible.
*
* Eclair (v5) and above.
*/
public static float getY(MotionEvent caller, int index) {
try {
return (Float) invokeByReflection(
caller, new Class<?>[]{int.class}, new Object[]{index}, "getY");
} catch (UnsupportedOperationException e) {
// Obviously if we're pre-Eclair, there can only be one pointer.
return caller.getY();
}
}
/**
* Sets button backlight brightness. Only works from Eclair (8) onwards.
*/
public static void setButtonBrightness(float buttonBrightness,
WindowManager.LayoutParams params) {
try {
Log.d(TAG, "Setting button brightness");
setByReflection(params, "buttonBrightness", buttonBrightness);
} catch (UnsupportedOperationException e) {
Log.e(TAG, "Unable to change button brightness");
}
}
/**
* Returns the value of Windows.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
* in Eclair and above. Otherwise returns -1.0, which seems to
* give equivalent behavior on Cupcake devices.
*/
public static float brightnessOverrideNoneValue() {
try {
return (Float) getByReflection(WindowManager.LayoutParams.class, "BRIGHTNESS_OVERRIDE_NONE");
} catch (UnsupportedOperationException e) {
return -1.0f;
}
}
/**
* Returns the value of Windows.LayoutParams.BRIGHTNESS_OVERRIDE_OFF
* in Eclair and above. Otherwise returns 0.0, which is equivalent
* at present.
*/
public static float brightnessOverrideOffValue() {
try {
return (Float) getByReflection(WindowManager.LayoutParams.class, "BRIGHTNESS_OVERRIDE_OFF");
} catch (UnsupportedOperationException e) {
return 0.0f;
}
}
/**
* Request that the system status bar be made visible, or hidden. Honeycomb and above only.
*/
public static void setSystemStatusBarVisible(View view, boolean visible) {
try {
int status = visible ? VIEW_STATUS_BAR_VISIBLE : VIEW_STATUS_BAR_HIDDEN;
invokeByReflection(view, new Class[] {int.class}, new Object[] {status},
"setSystemUiVisibility");
} catch (UnsupportedOperationException e) {
// Doesn't matter.
}
}
private static Object invokeByReflection(Object caller, Class<?>[] classes,
Object[] args, String methodName) {
try {
Method m = caller.getClass().getMethod(methodName, classes);
return m.invoke(caller, args);
} catch (IllegalAccessException e) {
Log.e(TAG, "Failed to invoke method", e);
throw new UnsupportedOperationException(methodName + " not supported");
} catch (InvocationTargetException e) {
Log.e(TAG, "Failed to invoke method", e);
throw new UnsupportedOperationException(methodName + " not supported");
} catch (NoSuchMethodException e) {
Log.e(TAG, "Failed to invoke method", e);
throw new UnsupportedOperationException(methodName + " not supported");
}
}
private static void setByReflection(Object object, String fieldName, Object value) {
try {
Field f = object.getClass().getField(fieldName);
f.set(object, value);
} catch (IllegalAccessException e) {
Log.e(TAG, "Failed to get field", e);
throw new UnsupportedOperationException(fieldName + " not supported");
} catch (SecurityException e) {
Log.e(TAG, "Failed to get field", e);
throw new UnsupportedOperationException(fieldName + " not supported");
} catch (NoSuchFieldException e) {
Log.e(TAG, "Failed to get field", e);
throw new UnsupportedOperationException(fieldName + " not supported");
}
}
private static Object getByReflection(Object object, String fieldName) {
try {
Field f = object.getClass().getField(fieldName);
return f.get(object);
} catch (IllegalAccessException e) {
Log.e(TAG, "Failed to get field", e);
throw new UnsupportedOperationException(fieldName + " not supported");
} catch (SecurityException e) {
Log.e(TAG, "Failed to get field", e);
throw new UnsupportedOperationException(fieldName + " not supported");
} catch (NoSuchFieldException e) {
Log.e(TAG, "Failed to get field", e);
throw new UnsupportedOperationException(fieldName + " not supported");
}
}
private static Object getByReflection(Class<?> clazz, String staticFieldName) {
try {
Field f = clazz.getField(staticFieldName);
return f.get(null);
} catch (IllegalAccessException e) {
Log.e(TAG, "Failed to get field", e);
throw new UnsupportedOperationException(staticFieldName + " not supported");
} catch (SecurityException e) {
Log.e(TAG, "Failed to get field", e);
throw new UnsupportedOperationException(staticFieldName + " not supported");
} catch (NoSuchFieldException e) {
Log.e(TAG, "Failed to get field", e);
throw new UnsupportedOperationException(staticFieldName + " not supported");
}
}
}