blob: b94bed3160c51ffd74a1087f07661919edd66f1b [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.base;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.PowerManager;
import android.os.Process;
import android.provider.Settings;
import android.view.View;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
/**
* Utility class to use new APIs that were added after ICS (API level 14).
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class ApiCompatibilityUtils {
private ApiCompatibilityUtils() {
}
/**
* Returns true if view's layout direction is right-to-left.
*
* @param view the View whose layout is being considered
*/
public static boolean isLayoutRtl(View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
return view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
} else {
// All layouts are LTR before JB MR1.
return false;
}
}
/**
* @see Configuration#getLayoutDirection()
*/
public static int getLayoutDirection(Configuration configuration) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
return configuration.getLayoutDirection();
} else {
// All layouts are LTR before JB MR1.
return View.LAYOUT_DIRECTION_LTR;
}
}
/**
* @return True if the running version of the Android supports printing.
*/
public static boolean isPrintingSupported() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
}
/**
* @see android.view.View#setLayoutDirection(int)
*/
public static void setLayoutDirection(View view, int layoutDirection) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
view.setLayoutDirection(layoutDirection);
} else {
// Do nothing. RTL layouts aren't supported before JB MR1.
}
}
/**
* @see android.view.View#setTextAlignment(int)
*/
public static void setTextAlignment(View view, int textAlignment) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
view.setTextAlignment(textAlignment);
} else {
// Do nothing. RTL text isn't supported before JB MR1.
}
}
/**
* @see android.view.View#setTextDirection(int)
*/
public static void setTextDirection(View view, int textDirection) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
view.setTextDirection(textDirection);
} else {
// Do nothing. RTL text isn't supported before JB MR1.
}
}
/**
* @see android.view.ViewGroup.MarginLayoutParams#setMarginEnd(int)
*/
public static void setMarginEnd(MarginLayoutParams layoutParams, int end) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
layoutParams.setMarginEnd(end);
} else {
layoutParams.rightMargin = end;
}
}
/**
* @see android.view.ViewGroup.MarginLayoutParams#getMarginEnd()
*/
public static int getMarginEnd(MarginLayoutParams layoutParams) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
return layoutParams.getMarginEnd();
} else {
return layoutParams.rightMargin;
}
}
/**
* @see android.view.ViewGroup.MarginLayoutParams#setMarginStart(int)
*/
public static void setMarginStart(MarginLayoutParams layoutParams, int start) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
layoutParams.setMarginStart(start);
} else {
layoutParams.leftMargin = start;
}
}
/**
* @see android.view.ViewGroup.MarginLayoutParams#getMarginStart()
*/
public static int getMarginStart(MarginLayoutParams layoutParams) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
return layoutParams.getMarginStart();
} else {
return layoutParams.leftMargin;
}
}
/**
* @see android.view.View#setPaddingRelative(int, int, int, int)
*/
public static void setPaddingRelative(View view, int start, int top, int end, int bottom) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
view.setPaddingRelative(start, top, end, bottom);
} else {
// Before JB MR1, all layouts are left-to-right, so start == left, etc.
view.setPadding(start, top, end, bottom);
}
}
/**
* @see android.view.View#getPaddingStart()
*/
public static int getPaddingStart(View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
return view.getPaddingStart();
} else {
// Before JB MR1, all layouts are left-to-right, so start == left.
return view.getPaddingLeft();
}
}
/**
* @see android.view.View#getPaddingEnd()
*/
public static int getPaddingEnd(View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
return view.getPaddingEnd();
} else {
// Before JB MR1, all layouts are left-to-right, so end == right.
return view.getPaddingRight();
}
}
/**
* @see android.widget.TextView#setCompoundDrawablesRelative(Drawable, Drawable, Drawable,
* Drawable)
*/
public static void setCompoundDrawablesRelative(TextView textView, Drawable start, Drawable top,
Drawable end, Drawable bottom) {
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN_MR1) {
// On JB MR1, due to a platform bug, setCompoundDrawablesRelative() is a no-op if the
// view has ever been measured. As a workaround, use setCompoundDrawables() directly.
// See: http://crbug.com/368196 and http://crbug.com/361709
boolean isRtl = isLayoutRtl(textView);
textView.setCompoundDrawables(isRtl ? end : start, top, isRtl ? start : end, bottom);
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR1) {
textView.setCompoundDrawablesRelative(start, top, end, bottom);
} else {
textView.setCompoundDrawables(start, top, end, bottom);
}
}
/**
* @see android.widget.TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable,
* Drawable, Drawable, Drawable)
*/
public static void setCompoundDrawablesRelativeWithIntrinsicBounds(TextView textView,
Drawable start, Drawable top, Drawable end, Drawable bottom) {
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN_MR1) {
// Work around the platform bug described in setCompoundDrawablesRelative() above.
boolean isRtl = isLayoutRtl(textView);
textView.setCompoundDrawablesWithIntrinsicBounds(isRtl ? end : start, top,
isRtl ? start : end, bottom);
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR1) {
textView.setCompoundDrawablesRelativeWithIntrinsicBounds(start, top, end, bottom);
} else {
textView.setCompoundDrawablesWithIntrinsicBounds(start, top, end, bottom);
}
}
/**
* @see android.widget.TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int,
* int)
*/
public static void setCompoundDrawablesRelativeWithIntrinsicBounds(TextView textView,
int start, int top, int end, int bottom) {
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN_MR1) {
// Work around the platform bug described in setCompoundDrawablesRelative() above.
boolean isRtl = isLayoutRtl(textView);
textView.setCompoundDrawablesWithIntrinsicBounds(isRtl ? end : start, top,
isRtl ? start : end, bottom);
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR1) {
textView.setCompoundDrawablesRelativeWithIntrinsicBounds(start, top, end, bottom);
} else {
textView.setCompoundDrawablesWithIntrinsicBounds(start, top, end, bottom);
}
}
// These methods have a new name, and the old name is deprecated.
/**
* @see android.app.PendingIntent#getCreatorPackage()
*/
@SuppressWarnings("deprecation")
public static String getCreatorPackage(PendingIntent intent) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
return intent.getCreatorPackage();
} else {
return intent.getTargetPackage();
}
}
/**
* @see android.provider.Settings.Global#DEVICE_PROVISIONED
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public static boolean isDeviceProvisioned(Context context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) return true;
if (context == null) return true;
if (context.getContentResolver() == null) return true;
return Settings.Global.getInt(
context.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0;
}
/**
* @see android.app.Activity#finishAndRemoveTask()
*/
public static void finishAndRemoveTask(Activity activity) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
activity.finishAndRemoveTask();
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) {
// crbug.com/395772 : Fallback for Activity.finishAndRemoveTask() failing.
new FinishAndRemoveTaskWithRetry(activity).run();
} else {
activity.finish();
}
}
private static class FinishAndRemoveTaskWithRetry implements Runnable {
private static final long RETRY_DELAY_MS = 500;
private static final long MAX_TRY_COUNT = 3;
private final Activity mActivity;
private int mTryCount;
FinishAndRemoveTaskWithRetry(Activity activity) {
mActivity = activity;
}
@Override
public void run() {
mActivity.finishAndRemoveTask();
mTryCount++;
if (!mActivity.isFinishing()) {
if (mTryCount < MAX_TRY_COUNT) {
ThreadUtils.postOnUiThreadDelayed(this, RETRY_DELAY_MS);
} else {
mActivity.finish();
}
}
}
}
/**
* @return Whether the screen of the device is interactive.
*/
@SuppressWarnings("deprecation")
public static boolean isInteractive(Context context) {
PowerManager manager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
return manager.isInteractive();
} else {
return manager.isScreenOn();
}
}
@SuppressWarnings("deprecation")
public static int getActivityNewDocumentFlag() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
} else {
return Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET;
}
}
/**
* @see android.provider.Settings.Secure#SKIP_FIRST_USE_HINTS
*/
public static boolean shouldSkipFirstUseHints(ContentResolver contentResolver) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return Settings.Secure.getInt(
contentResolver, Settings.Secure.SKIP_FIRST_USE_HINTS, 0) != 0;
} else {
return false;
}
}
/**
* @param activity Activity that should get the task description update.
* @param title Title of the activity.
* @param icon Icon of the activity.
* @param color Color of the activity. It must be a fully opaque color.
*/
public static void setTaskDescription(Activity activity, String title, Bitmap icon, int color) {
// TaskDescription requires an opaque color.
assert Color.alpha(color) == 255;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
ActivityManager.TaskDescription description =
new ActivityManager.TaskDescription(title, icon, color);
activity.setTaskDescription(description);
}
}
/**
* @see android.view.Window#setStatusBarColor(int color).
*/
public static void setStatusBarColor(Window window, int statusBarColor) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// If both system bars are black, we can remove these from our layout,
// removing or shrinking the SurfaceFlinger overlay required for our views.
if (statusBarColor == Color.BLACK && window.getNavigationBarColor() == Color.BLACK) {
window.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
} else {
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
}
window.setStatusBarColor(statusBarColor);
}
}
/**
* @see android.content.res.Resources#getDrawable(int id).
*/
@SuppressWarnings("deprecation")
public static Drawable getDrawable(Resources res, int id) throws NotFoundException {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return res.getDrawable(id, null);
} else {
return res.getDrawable(id);
}
}
/**
* @see android.content.res.Resources#getDrawableForDensity(int id, int density).
*/
@SuppressWarnings("deprecation")
public static Drawable getDrawableForDensity(Resources res, int id, int density) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return res.getDrawableForDensity(id, density, null);
} else {
return res.getDrawableForDensity(id, density);
}
}
/**
* @see android.app.Activity#finishAfterTransition().
*/
public static void finishAfterTransition(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.finishAfterTransition();
} else {
activity.finish();
}
}
/**
* @see android.content.pm.PackageManager#getUserBadgedIcon(Drawable, android.os.UserHandle).
*/
public static Drawable getUserBadgedIcon(Context context, int id) {
Drawable drawable = getDrawable(context.getResources(), id);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
PackageManager packageManager = context.getPackageManager();
drawable = packageManager.getUserBadgedIcon(drawable, Process.myUserHandle());
}
return drawable;
}
/**
* @see android.content.res.Resources#getColor(int id).
*/
@SuppressWarnings("deprecation")
public static int getColor(Resources res, int id) throws NotFoundException {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return res.getColor(id, null);
} else {
return res.getColor(id);
}
}
/**
* @see android.content.res.Resources#getColorStateList(int id).
*/
@SuppressWarnings("deprecation")
public static ColorStateList getColorStateList(Resources res, int id) throws NotFoundException {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return res.getColorStateList(id, null);
} else {
return res.getColorStateList(id);
}
}
/**
* @see android.widget.TextView#setTextAppearance(int id).
*/
@SuppressWarnings("deprecation")
public static void setTextAppearance(TextView view, int id) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
view.setTextAppearance(id);
} else {
view.setTextAppearance(view.getContext(), id);
}
}
}