package com.reactnativenavigation.utils; import android.content.Context; import android.content.res.Resources; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.Drawable; import android.os.Build; import android.support.annotation.Nullable; import android.text.SpannableString; import android.text.Spanned; import android.text.SpannedString; import android.text.style.ForegroundColorSpan; import android.util.DisplayMetrics; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import android.view.ViewTreeObserver; import android.view.WindowManager; import android.widget.TextView; import com.reactnativenavigation.NavigationApplication; import com.reactnativenavigation.params.AppStyle; import com.reactnativenavigation.screens.Screen; import com.reactnativenavigation.views.utils.Point; import java.util.concurrent.atomic.AtomicInteger; public class ViewUtils { private static final AtomicInteger viewId = new AtomicInteger(1); private static int statusBarHeight = -1; public static void runOnPreDraw(final View view, final Runnable task) { view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { if (!view.getViewTreeObserver().isAlive()) { return true; } view.getViewTreeObserver().removeOnPreDrawListener(this); task.run(); return true; } }); } public static void tintDrawable(Drawable drawable, int tint, boolean enabled) { drawable.setColorFilter(new PorterDuffColorFilter(enabled ? tint : AppStyle.appStyle.titleBarDisabledButtonColor.getColor(), PorterDuff.Mode.SRC_IN)); } public static float convertDpToPixel(float dp) { float scale = NavigationApplication.instance.getResources().getDisplayMetrics().density; return dp * scale + 0.5f; } public static float convertPixelToSp(float pixels) { float scaledDensity = NavigationApplication.instance.getResources().getDisplayMetrics().scaledDensity; return pixels / scaledDensity; } public static float convertSpToPixel(float pixels) { float scaledDensity = NavigationApplication.instance.getResources().getDisplayMetrics().scaledDensity; return pixels * scaledDensity; } public static int generateViewId() { if (Build.VERSION.SDK_INT >= 17) { return View.generateViewId(); } else { return compatGenerateViewId(); } } public static float getScreenHeight() { WindowManager wm = (WindowManager) NavigationApplication.instance.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics metrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(metrics); return metrics.heightPixels; } public static float getScreenWidth() { WindowManager wm = (WindowManager) NavigationApplication.instance.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics metrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(metrics); return metrics.widthPixels; } private static int compatGenerateViewId() { for (; ; ) { final int result = viewId.get(); // aapt-generated IDs have the high byte nonzero; clamp to the range under that. int newValue = result + 1; if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0. if (viewId.compareAndSet(result, newValue)) { return result; } } } public interface Matcher<T> { boolean match(T child); } /** * Returns the first instance of clazz in root */ @Nullable public static <T> T findChildByClass(ViewGroup root, Class clazz) { return findChildByClass(root, clazz, null); } @Nullable public static <T> T findChildByClass(ViewGroup root, Class clazz, Matcher<T> matcher) { for (int i = 0; i < root.getChildCount(); i++) { View view = root.getChildAt(i); if (clazz.isAssignableFrom(view.getClass())) { return (T) view; } if (view instanceof ViewGroup) { view = (View) findChildByClass((ViewGroup) view, clazz, matcher); if (view != null && clazz.isAssignableFrom(view.getClass())) { if (matcher == null) { return (T) view; } if (matcher.match((T) view)) { return (T) view; } } } } return null; } public static void performOnChildren(ViewGroup root, PerformOnViewTask task) { for (int i = 0; i < root.getChildCount(); i++) { View child = root.getChildAt(i); if (child instanceof ViewGroup) { performOnChildren((ViewGroup) child, task); } task.runOnView(child); } } public interface PerformOnViewTask { void runOnView(View view); } public static void performOnParentScreen(View child, Task<Screen> task) { Screen parentScreen = findParentScreen(child.getParent()); if (parentScreen != null) { task.run(parentScreen); } } private static Screen findParentScreen(ViewParent parent) { if (parent == null) { return null; } if (parent instanceof Screen) { return (Screen) parent; } return findParentScreen(parent.getParent()); } public static Point getLocationOnScreen(View view) { int[] xy = new int[2]; view.getLocationOnScreen(xy); xy[1] -= getStatusBarHeight(); return new Point(xy[0], xy[1]); } private static int getStatusBarHeight() { if (statusBarHeight > 0) { return statusBarHeight; } final Resources resources = NavigationApplication.instance.getResources(); final int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android"); statusBarHeight = resourceId > 0 ? resources.getDimensionPixelSize(resourceId) : (int) convertDpToPixel(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? 24 : 25); return statusBarHeight; } public static ForegroundColorSpan[] getForegroundColorSpans(TextView view) { SpannedString text = new SpannedString(view.getText()); return text.getSpans(0, text.length(), ForegroundColorSpan.class); } public static void setSpanColor(SpannableString span, int color) { span.setSpan(new ForegroundColorSpan(color), 0, span.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } }