/**
* Copyright (C) 2015 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package org.thoughtcrime.securesms.util;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import androidx.annotation.IdRes;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.widget.TextView;
import com.b44t.messenger.util.concurrent.ListenableFuture;
import com.b44t.messenger.util.concurrent.SettableFuture;
import org.thoughtcrime.securesms.util.views.Stub;
public class ViewUtil {
@SuppressWarnings("deprecation")
public static void setBackground(final @NonNull View v, final @Nullable Drawable drawable) {
v.setBackground(drawable);
}
public static float getY(final @NonNull View v) {
return ViewCompat.getY(v);
}
public static void setX(final @NonNull View v, final int x) {
ViewCompat.setX(v, x);
}
public static float getX(final @NonNull View v) {
return ViewCompat.getX(v);
}
public static void swapChildInPlace(ViewGroup parent, View toRemove, View toAdd, int defaultIndex) {
int childIndex = parent.indexOfChild(toRemove);
if (childIndex > -1) parent.removeView(toRemove);
parent.addView(toAdd, childIndex > -1 ? childIndex : defaultIndex);
}
@SuppressWarnings("unchecked")
public static T inflateStub(@NonNull View parent, @IdRes int stubId) {
return (T)((ViewStub)parent.findViewById(stubId)).inflate();
}
@SuppressWarnings("unchecked")
public static T findById(@NonNull View parent, @IdRes int resId) {
return (T) parent.findViewById(resId);
}
@SuppressWarnings("unchecked")
public static T findById(@NonNull Activity parent, @IdRes int resId) {
return (T) parent.findViewById(resId);
}
public static Stub findStubById(@NonNull Activity parent, @IdRes int resId) {
return new Stub((ViewStub)parent.findViewById(resId));
}
private static Animation getAlphaAnimation(float from, float to, int duration) {
final Animation anim = new AlphaAnimation(from, to);
anim.setInterpolator(new FastOutSlowInInterpolator());
anim.setDuration(duration);
return anim;
}
public static void fadeIn(final @NonNull View view, final int duration) {
animateIn(view, getAlphaAnimation(0f, 1f, duration));
}
public static ListenableFuture fadeOut(final @NonNull View view, final int duration) {
return fadeOut(view, duration, View.GONE);
}
public static ListenableFuture fadeOut(@NonNull View view, int duration, int visibility) {
return animateOut(view, getAlphaAnimation(1f, 0f, duration), visibility);
}
public static ListenableFuture animateOut(final @NonNull View view, final @NonNull Animation animation, final int visibility) {
final SettableFuture future = new SettableFuture();
if (view.getVisibility() == visibility) {
future.set(true);
} else if (AccessibilityUtil.areAnimationsDisabled(view.getContext())) {
view.setVisibility(visibility);
future.set(true);
} else {
view.clearAnimation();
animation.reset();
animation.setStartTime(0);
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationRepeat(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {
view.setVisibility(visibility);
future.set(true);
}
});
view.startAnimation(animation);
}
return future;
}
public static void animateIn(final @NonNull View view, final @NonNull Animation animation) {
if (view.getVisibility() == View.VISIBLE) return;
if (AccessibilityUtil.areAnimationsDisabled(view.getContext())) {
view.setVisibility(View.VISIBLE);
return;
}
view.clearAnimation();
animation.reset();
animation.setStartTime(0);
view.setVisibility(View.VISIBLE);
view.startAnimation(animation);
}
@SuppressWarnings("unchecked")
public static T inflate(@NonNull LayoutInflater inflater,
@NonNull ViewGroup parent,
@LayoutRes int layoutResId)
{
return (T)(inflater.inflate(layoutResId, parent, false));
}
@SuppressLint("RtlHardcoded")
public static void setTextViewGravityStart(final @NonNull TextView textView, @NonNull Context context) {
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
if (Util.getLayoutDirection(context) == View.LAYOUT_DIRECTION_RTL) {
textView.setGravity(Gravity.RIGHT);
} else {
textView.setGravity(Gravity.LEFT);
}
}
}
public static void mirrorIfRtl(View view, Context context) {
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1 &&
Util.getLayoutDirection(context) == View.LAYOUT_DIRECTION_RTL) {
view.setScaleX(-1.0f);
}
}
public static boolean isLtr(@NonNull View view) {
return isLtr(view.getContext());
}
public static boolean isLtr(@NonNull Context context) {
return Util.getLayoutDirection(context) == ViewCompat.LAYOUT_DIRECTION_LTR;
}
public static boolean isRtl(@NonNull View view) {
return isRtl(view.getContext());
}
public static boolean isRtl(@NonNull Context context) {
return Util.getLayoutDirection(context) == ViewCompat.LAYOUT_DIRECTION_RTL;
}
public static int dpToPx(Context context, int dp) {
return (int)((dp * context.getResources().getDisplayMetrics().density) + 0.5);
}
public static void updateLayoutParams(@NonNull View view, int width, int height) {
view.getLayoutParams().width = width;
view.getLayoutParams().height = height;
view.requestLayout();
}
public static int getLeftMargin(@NonNull View view) {
if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
return ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).leftMargin;
}
return ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).rightMargin;
}
public static int getRightMargin(@NonNull View view) {
if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
return ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).rightMargin;
}
return ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).leftMargin;
}
public static void setLeftMargin(@NonNull View view, int margin) {
if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
((ViewGroup.MarginLayoutParams) view.getLayoutParams()).leftMargin = margin;
} else {
((ViewGroup.MarginLayoutParams) view.getLayoutParams()).rightMargin = margin;
}
view.forceLayout();
view.requestLayout();
}
public static void setRightMargin(@NonNull View view, int margin) {
if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
((ViewGroup.MarginLayoutParams) view.getLayoutParams()).rightMargin = margin;
} else {
((ViewGroup.MarginLayoutParams) view.getLayoutParams()).leftMargin = margin;
}
view.forceLayout();
view.requestLayout();
}
public static void setTopMargin(@NonNull View view, int margin) {
((ViewGroup.MarginLayoutParams) view.getLayoutParams()).topMargin = margin;
view.requestLayout();
}
public static void setPaddingTop(@NonNull View view, int padding) {
view.setPadding(view.getPaddingLeft(), padding, view.getPaddingRight(), view.getPaddingBottom());
}
public static void setPaddingBottom(@NonNull View view, int padding) {
view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), view.getPaddingRight(), padding);
}
public static int dpToPx(int dp) {
return Math.round(dp * Resources.getSystem().getDisplayMetrics().density);
}
public static int getStatusBarHeight(@NonNull View view) {
final WindowInsetsCompat rootWindowInsets = ViewCompat.getRootWindowInsets(view);
if (Build.VERSION.SDK_INT > 29 && rootWindowInsets != null) {
return rootWindowInsets.getInsets(WindowInsetsCompat.Type.statusBars()).top;
} else {
int result = 0;
int resourceId = view.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = view.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
}
}