blob: f41b67710f861438acfd850641b2147ea08f3fef [file] [log] [blame]
package net.osmand;
import android.app.Activity;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Typeface;
import android.graphics.drawable.ClipDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.StateListDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.StatFs;
import android.support.annotation.AttrRes;
import android.support.annotation.ColorInt;
import android.support.annotation.ColorRes;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.support.v4.text.TextUtilsCompat;
import android.support.v4.view.ViewCompat;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.text.style.CharacterStyle;
import android.text.style.ImageSpan;
import android.text.style.StyleSpan;
import android.text.style.URLSpan;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewParent;
import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.TextView;
import net.osmand.plus.R;
import net.osmand.plus.download.DownloadActivity;
import java.io.File;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import static android.content.Context.POWER_SERVICE;
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
import static android.util.TypedValue.COMPLEX_UNIT_SP;
public class AndroidUtils {
public static final String STRING_PLACEHOLDER = "%s";
/**
* @param context
* @return true if Hardware keyboard is available
*/
public static boolean isHardwareKeyboardAvailable(Context context) {
return context.getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS;
}
public static void softKeyboardDelayed(final View view) {
view.post(new Runnable() {
@Override
public void run() {
if (!isHardwareKeyboardAvailable(view.getContext())) {
showSoftKeyboard(view);
}
}
});
}
public static void showSoftKeyboard(final View view) {
InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
}
}
public static void hideSoftKeyboard(final Activity activity, final View input) {
InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
if (inputMethodManager != null) {
if (input != null) {
IBinder windowToken = input.getWindowToken();
if (windowToken != null) {
inputMethodManager.hideSoftInputFromWindow(windowToken, 0);
}
}
}
}
public static Bitmap scaleBitmap(Bitmap bm, int newWidth, int newHeight, boolean keepOriginalBitmap) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
if (!keepOriginalBitmap) {
bm.recycle();
}
return resizedBitmap;
}
public static ColorStateList createBottomNavColorStateList(Context ctx, boolean nightMode) {
return AndroidUtils.createCheckedColorStateList(ctx, nightMode,
R.color.icon_color_default_light, R.color.wikivoyage_active_light,
R.color.icon_color_default_light, R.color.wikivoyage_active_dark);
}
public static String trimExtension(String src) {
if (src != null) {
int index = src.lastIndexOf('.');
if (index != -1) {
return src.substring(0, index);
}
}
return src;
}
public static Uri getUriForFile(Context context, File file) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
return Uri.fromFile(file);
} else {
return FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", file);
}
}
public static Spannable replaceCharsWithIcon(String text, Drawable icon, String[] chars) {
Spannable spannable = new SpannableString(text);
for (String entry : chars) {
int i = text.indexOf(entry, 0);
while (i < text.length() && i != -1) {
ImageSpan span = new ImageSpan(icon) {
public void draw(Canvas canvas, CharSequence text, int start, int end,
float x, int top, int y, int bottom, Paint paint) {
Drawable drawable = getDrawable();
canvas.save();
int transY = bottom - drawable.getBounds().bottom;
transY -= paint.getFontMetricsInt().descent / 2;
canvas.translate(x, transY);
drawable.draw(canvas);
canvas.restore();
}
};
spannable.setSpan(span, i, i + entry.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
i = text.indexOf(entry, i + entry.length());
}
}
return spannable;
}
public static void removeLinkUnderline(TextView textView) {
Spannable s = new SpannableString(textView.getText());
for (URLSpan span : s.getSpans(0, s.length(), URLSpan.class)) {
int start = s.getSpanStart(span);
int end = s.getSpanEnd(span);
s.removeSpan(span);
span = new URLSpan(span.getURL()) {
@Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(false);
}
};
s.setSpan(span, start, end, 0);
}
textView.setText(s);
}
public static String formatDate(Context ctx, long time) {
return DateFormat.getDateFormat(ctx).format(new Date(time));
}
public static String formatDateTime(Context ctx, long time) {
Date d = new Date(time);
return DateFormat.getDateFormat(ctx).format(d) +
" " + DateFormat.getTimeFormat(ctx).format(d);
}
public static String formatTime(Context ctx, long time) {
return DateFormat.getTimeFormat(ctx).format(new Date(time));
}
public static String formatSize(long sizeBytes) {
int sizeKb = (int) ((sizeBytes + 512) >> 10);
if (sizeKb > 0) {
if (sizeKb > 1 << 20) {
return DownloadActivity.formatGb.format(new Object[]{(float) sizeKb / (1 << 20)});
} else if (sizeBytes > (100 * (1 << 10))) {
return DownloadActivity.formatMb.format(new Object[]{(float) sizeBytes / (1 << 20)});
} else {
return DownloadActivity.formatKb.format(new Object[]{(float) sizeBytes / (1 << 10)});
}
}
return "";
}
public static View findParentViewById(View view, int id) {
ViewParent viewParent = view.getParent();
while (viewParent != null && viewParent instanceof View) {
View parentView = (View) viewParent;
if (parentView.getId() == id)
return parentView;
viewParent = parentView.getParent();
}
return null;
}
public static ColorStateList createCheckedColorStateList(Context ctx, @ColorRes int normal, @ColorRes int checked) {
return createCheckedColorStateList(ctx, false, normal, checked, 0, 0);
}
public static ColorStateList createCheckedColorStateList(Context ctx, boolean night,
@ColorRes int lightNormal, @ColorRes int lightChecked,
@ColorRes int darkNormal, @ColorRes int darkChecked) {
return createColorStateList(ctx, night, android.R.attr.state_checked,
lightNormal, lightChecked, darkNormal, darkChecked);
}
public static ColorStateList createPressedColorStateList(Context ctx, @ColorRes int normal, @ColorRes int pressed) {
return createPressedColorStateList(ctx, false, normal, pressed, 0, 0);
}
public static ColorStateList createPressedColorStateList(Context ctx, boolean night,
@ColorRes int lightNormal, @ColorRes int lightPressed,
@ColorRes int darkNormal, @ColorRes int darkPressed) {
return createColorStateList(ctx, night, android.R.attr.state_pressed,
lightNormal, lightPressed, darkNormal, darkPressed);
}
private static ColorStateList createColorStateList(Context ctx, boolean night, int state,
@ColorRes int lightNormal, @ColorRes int lightState,
@ColorRes int darkNormal, @ColorRes int darkState) {
return new ColorStateList(
new int[][]{
new int[]{state},
new int[]{}
},
new int[]{
ContextCompat.getColor(ctx, night ? darkState : lightState),
ContextCompat.getColor(ctx, night ? darkNormal : lightNormal)
}
);
}
public static StateListDrawable createCheckedStateListDrawable(Drawable normal, Drawable checked) {
return createStateListDrawable(normal, checked, android.R.attr.state_checked);
}
public static StateListDrawable createPressedStateListDrawable(Drawable normal, Drawable pressed) {
return createStateListDrawable(normal, pressed, android.R.attr.state_pressed);
}
private static StateListDrawable createStateListDrawable(Drawable normal, Drawable stateDrawable, int state) {
StateListDrawable res = new StateListDrawable();
res.addState(new int[]{state}, stateDrawable);
res.addState(new int[]{}, normal);
return res;
}
public static LayerDrawable createProgressDrawable(@ColorInt int bgColor, @ColorInt int progressColor) {
ShapeDrawable bg = new ShapeDrawable();
bg.getPaint().setColor(bgColor);
ShapeDrawable progress = new ShapeDrawable();
progress.getPaint().setColor(progressColor);
LayerDrawable res = new LayerDrawable(new Drawable[]{
bg,
new ClipDrawable(progress, Gravity.START, ClipDrawable.HORIZONTAL)
});
res.setId(0, android.R.id.background);
res.setId(1, android.R.id.progress);
return res;
}
public static void setSnackbarTextColor(Snackbar snackbar, @ColorRes int colorId) {
View view = snackbar.getView();
TextView tv = (TextView) view.findViewById(android.support.design.R.id.snackbar_action);
tv.setTextColor(ContextCompat.getColor(view.getContext(), colorId));
}
public static void setSnackbarTextMaxLines(Snackbar snackbar, int maxLines) {
View view = snackbar.getView();
TextView tv = (TextView) view.findViewById(android.support.design.R.id.snackbar_text);
tv.setMaxLines(maxLines);
}
public static void setBackground(Context ctx, View view, boolean night, int lightResId, int darkResId) {
Drawable drawable;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
drawable = ctx.getResources().getDrawable(night ? darkResId : lightResId, ctx.getTheme());
} else {
drawable = ctx.getResources().getDrawable(night ? darkResId : lightResId);
}
setBackground(view, drawable);
}
public static void setBackground(View view, Drawable drawable) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
view.setBackground(drawable);
} else {
view.setBackgroundDrawable(drawable);
}
}
public static void setForeground(Context ctx, View view, boolean night, int lightResId, int darkResId) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
view.setForeground(ctx.getResources().getDrawable(night ? darkResId : lightResId,
ctx.getTheme()));
} else if (view instanceof FrameLayout) {
((FrameLayout) view).setForeground(ctx.getResources().getDrawable(night ? darkResId : lightResId));
}
}
public static void updateImageButton(Context ctx, ImageButton button, int iconLightId, int iconDarkId, int bgLightId, int bgDarkId, boolean night) {
button.setImageDrawable(ctx.getResources().getDrawable(night ? iconDarkId : iconLightId));
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
button.setBackground(ctx.getResources().getDrawable(night ? bgDarkId : bgLightId, ctx.getTheme()));
} else {
button.setBackgroundDrawable(ctx.getResources().getDrawable(night ? bgDarkId : bgLightId));
}
}
public static void setDashButtonBackground(Context ctx, View view, boolean night) {
setBackground(ctx, view, night, R.drawable.dashboard_button_light, R.drawable.dashboard_button_dark);
}
public static void setBackgroundColor(Context ctx, View view, boolean night, int lightResId, int darkResId) {
view.setBackgroundColor(ctx.getResources().getColor(night ? darkResId : lightResId));
}
public static void setListItemBackground(Context ctx, View view, boolean night) {
setBackgroundColor(ctx, view, night, R.color.list_background_color_light, R.color.list_background_color_dark);
}
public static void setListBackground(Context ctx, View view, boolean night) {
setBackgroundColor(ctx, view, night, R.color.activity_background_color_light, R.color.activity_background_color_dark);
}
public static void setTextPrimaryColor(Context ctx, TextView textView, boolean night) {
textView.setTextColor(night ?
ctx.getResources().getColor(R.color.text_color_primary_dark)
: ctx.getResources().getColor(R.color.text_color_primary_light));
}
public static void setTextSecondaryColor(Context ctx, TextView textView, boolean night) {
textView.setTextColor(night ?
ctx.getResources().getColor(R.color.text_color_secondary_dark)
: ctx.getResources().getColor(R.color.text_color_secondary_light));
}
public static void setHintTextSecondaryColor(Context ctx, TextView textView, boolean night) {
textView.setHintTextColor(night ?
ctx.getResources().getColor(R.color.text_color_secondary_dark)
: ctx.getResources().getColor(R.color.text_color_secondary_light));
}
public static int getTextWidth(float textSize, String text) {
Paint paint = new Paint();
paint.setTextSize(textSize);
return (int) paint.measureText(text);
}
public static int dpToPx(Context ctx, float dp) {
Resources r = ctx.getResources();
return (int) TypedValue.applyDimension(
COMPLEX_UNIT_DIP,
dp,
r.getDisplayMetrics()
);
}
public static int spToPx(Context ctx, float sp) {
Resources r = ctx.getResources();
return (int) TypedValue.applyDimension(
COMPLEX_UNIT_SP,
sp,
r.getDisplayMetrics()
);
}
@ColorInt
public static int getColorFromAttr(@NonNull Context ctx, @AttrRes int colorAttribute) {
TypedValue typedValue = new TypedValue();
ctx.getTheme().resolveAttribute(colorAttribute, typedValue, true);
return typedValue.data;
}
public static int resolveAttribute(Context ctx, int attribute) {
TypedValue outValue = new TypedValue();
ctx.getTheme().resolveAttribute(attribute, outValue, true);
return outValue.resourceId;
}
public static float getFloatValueFromRes(Context ctx, int resId) {
TypedValue outValue = new TypedValue();
ctx.getResources().getValue(resId, outValue, true);
return outValue.getFloat();
}
public static int getStatusBarHeight(Context ctx) {
int result = 0;
int resourceId = ctx.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = ctx.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
public static void addStatusBarPadding21v(Context ctx, View view) {
if (Build.VERSION.SDK_INT >= 21) {
int paddingLeft = view.getPaddingLeft();
int paddingTop = view.getPaddingTop();
int paddingRight = view.getPaddingRight();
int paddingBottom = view.getPaddingBottom();
view.setPadding(paddingLeft, paddingTop + getStatusBarHeight(ctx), paddingRight, paddingBottom);
}
}
public static int getNavBarHeight(Context ctx) {
if (!hasNavBar(ctx)) {
return 0;
}
boolean landscape = ctx.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
boolean isSmartphone = ctx.getResources().getConfiguration().smallestScreenWidthDp < 600;
if (isSmartphone && landscape) {
return 0;
}
int id = ctx.getResources().getIdentifier(landscape ? "navigation_bar_height_landscape" : "navigation_bar_height", "dimen", "android");
if (id > 0) {
return ctx.getResources().getDimensionPixelSize(id);
}
return 0;
}
public static boolean hasNavBar(Context ctx) {
int id = ctx.getResources().getIdentifier("config_showNavigationBar", "bool", "android");
return id > 0 && ctx.getResources().getBoolean(id);
}
public static int getScreenHeight(Activity activity) {
DisplayMetrics dm = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
return dm.heightPixels;
}
public static int getScreenWidth(Activity activity) {
DisplayMetrics dm = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
return dm.widthPixels;
}
public static boolean isValidEmail(CharSequence target) {
return !TextUtils.isEmpty(target) && android.util.Patterns.EMAIL_ADDRESS.matcher(target).matches();
}
public static PointF centroidForPoly(PointF[] points) {
float centroidX = 0, centroidY = 0;
for (PointF point : points) {
centroidX += point.x / points.length;
centroidY += point.y / points.length;
}
return new PointF(centroidX, centroidY);
}
public static void showNavBar(Activity activity) {
if (Build.VERSION.SDK_INT >= 19 && !isNavBarVisible(activity)) {
switchNavBarVisibility(activity);
}
}
public static void hideNavBar(Activity activity) {
if (Build.VERSION.SDK_INT >= 19 && isNavBarVisible(activity)) {
switchNavBarVisibility(activity);
}
}
public static boolean isNavBarVisible(Activity activity) {
if (Build.VERSION.SDK_INT >= 19) {
int uiOptions = activity.getWindow().getDecorView().getSystemUiVisibility();
return !((uiOptions | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == uiOptions);
}
return true;
}
public static void switchNavBarVisibility(Activity activity) {
if (Build.VERSION.SDK_INT < 19) {
return;
}
View decorView = activity.getWindow().getDecorView();
int uiOptions = decorView.getSystemUiVisibility();
uiOptions ^= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
uiOptions ^= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
decorView.setSystemUiVisibility(uiOptions);
}
public static int[] getCenterViewCoordinates(View view) {
int[] coordinates = new int[2];
view.getLocationOnScreen(coordinates);
coordinates[0] += view.getWidth() / 2;
coordinates[1] += view.getHeight() / 2;
return coordinates;
}
public static void enterToFullScreen(Activity activity) {
if (Build.VERSION.SDK_INT >= 21) {
activity.getWindow().getDecorView()
.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
}
public static void exitFromFullScreen(Activity activity) {
if (Build.VERSION.SDK_INT >= 21) {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
}
}
public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
List<Map.Entry<K, V>> list = new LinkedList<>(map.entrySet());
Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
return (o1.getValue()).compareTo(o2.getValue());
}
});
Map<K, V> result = new LinkedHashMap<>();
for (Map.Entry<K, V> entry : list) {
result.put(entry.getKey(), entry.getValue());
}
return result;
}
public static boolean isScreenOn(Context context) {
PowerManager powerManager = (PowerManager) context.getSystemService(POWER_SERVICE);
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH && powerManager.isInteractive()
|| Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT_WATCH && powerManager.isScreenOn();
}
public static boolean isScreenLocked(Context context) {
KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
return keyguardManager.inKeyguardRestrictedInputMode();
}
public static CharSequence getStyledString(CharSequence baseString, CharSequence stringToInsertAndStyle, int typefaceStyle) {
if (typefaceStyle == Typeface.NORMAL || typefaceStyle == Typeface.BOLD
|| typefaceStyle == Typeface.ITALIC || typefaceStyle == Typeface.BOLD_ITALIC
|| baseString.toString().contains(STRING_PLACEHOLDER)) {
return getStyledString(baseString, stringToInsertAndStyle, null, new StyleSpan(typefaceStyle));
} else {
return baseString;
}
}
public static float getFreeSpaceGb(File dir) {
if (dir.canRead()) {
StatFs fs = new StatFs(dir.getAbsolutePath());
return (float) (fs.getBlockSize()) * fs.getAvailableBlocks() / (1 << 30);
}
return -1;
}
public static float getTotalSpaceGb(File dir) {
if (dir.canRead()) {
return (float) (dir.getTotalSpace()) / (1 << 30);
}
return -1;
}
public static float getUsedSpaceGb(File dir) {
if (dir.canRead()) {
return getTotalSpaceGb(dir) - getFreeSpaceGb(dir);
}
return -1;
}
public static CharSequence getStyledString(CharSequence baseString, CharSequence stringToInsertAndStyle,
CharacterStyle baseStyle, CharacterStyle replaceStyle) {
int indexOfPlaceholder = baseString.toString().indexOf(STRING_PLACEHOLDER);
if (replaceStyle != null || baseStyle != null || indexOfPlaceholder != -1) {
String nStr = baseString.toString().replace(STRING_PLACEHOLDER, stringToInsertAndStyle);
SpannableStringBuilder ssb = new SpannableStringBuilder(nStr);
if(baseStyle != null) {
if(indexOfPlaceholder > 0) {
ssb.setSpan(baseStyle, 0, indexOfPlaceholder, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if(indexOfPlaceholder + stringToInsertAndStyle.length() < nStr.length()) {
ssb.setSpan(baseStyle,
indexOfPlaceholder + stringToInsertAndStyle.length(),
nStr.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
if(replaceStyle != null) {
ssb.setSpan(replaceStyle, indexOfPlaceholder,
indexOfPlaceholder + stringToInsertAndStyle.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return ssb;
} else {
return baseString;
}
}
public static boolean isRTL() {
return TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == ViewCompat.LAYOUT_DIRECTION_RTL;
}
}