() {
+
+ @Override
+ public CustomState[] newArray(int size) {
+ return new CustomState[size];
+ }
+
+ @Override
+ public CustomState createFromParcel(Parcel incoming) {
+ return new CustomState(incoming);
+ }
+ };
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/Marker.java b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/Marker.java
new file mode 100644
index 000000000..cd354b290
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/Marker.java
@@ -0,0 +1,196 @@
+package com.yunbao.faceunity.seekbar.internal;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import androidx.core.view.ViewCompat;
+
+import com.yunbao.faceunity.R;
+import com.yunbao.faceunity.seekbar.internal.compat.SeekBarCompat;
+import com.yunbao.faceunity.seekbar.internal.drawable.MarkerDrawable;
+
+
+/**
+ * {@link ViewGroup} to be used as the real indicator.
+ *
+ * I've used this to be able to accommodate the TextView
+ * and the {@link MarkerDrawable}
+ * with the required positions and offsets
+ *
+ *
+ * @hide
+ */
+public class Marker extends ViewGroup implements MarkerDrawable.MarkerAnimationListener {
+ private static final int PADDING_DP = 1;
+ private static final int ELEVATION_DP = 8;
+ //The TextView to show the info
+ private TextView mNumber;
+ //The max width of this View
+ private int mWidth;
+ //some distance between the thumb and our bubble marker.
+ //This will be added to our measured height
+ private int mSeparation;
+ MarkerDrawable mMarkerDrawable;
+
+ public Marker(Context context, AttributeSet attrs, int defStyleAttr, String maxValue, int thumbSize, int separation) {
+ super(context, attrs, defStyleAttr);
+ //as we're reading the parent DiscreteSeekBar attributes, it may wrongly set this view's visibility.
+ setVisibility(View.VISIBLE);
+
+ DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DiscreteSeekBar,
+ R.attr.discreteSeekBarStyle, R.style.Widget_DiscreteSeekBar);
+
+ int padding = (int) (PADDING_DP * displayMetrics.density) * 2;
+ int textAppearanceId = a.getResourceId(R.styleable.DiscreteSeekBar_dsb_indicatorTextAppearance,
+ R.style.Widget_DiscreteIndicatorTextAppearance);
+ mNumber = new TextView(context);
+ //Add some padding to this textView so the bubble has some space to breath
+ mNumber.setPadding(padding, 0, padding, 0);
+ mNumber.setTextAppearance(context, textAppearanceId);
+ mNumber.setGravity(Gravity.CENTER);
+ mNumber.setText(maxValue);
+ mNumber.setMaxLines(1);
+ mNumber.setSingleLine(true);
+ SeekBarCompat.setTextDirection(mNumber, TEXT_DIRECTION_LOCALE);
+ mNumber.setVisibility(View.INVISIBLE);
+
+ //add some padding for the elevation shadow not to be clipped
+ //I'm sure there are better ways of doing this...
+ setPadding(padding, padding, padding, padding);
+
+ resetSizes(maxValue);
+
+ mSeparation = separation;
+ ColorStateList color = a.getColorStateList(R.styleable.DiscreteSeekBar_dsb_indicatorColor);
+ mMarkerDrawable = new MarkerDrawable(color, thumbSize);
+ mMarkerDrawable.setCallback(this);
+ mMarkerDrawable.setMarkerListener(this);
+ mMarkerDrawable.setExternalOffset(padding);
+
+ //Elevation for anroid 5+
+ float elevation = a.getDimension(R.styleable.DiscreteSeekBar_dsb_indicatorElevation, ELEVATION_DP * displayMetrics.density);
+ ViewCompat.setElevation(this, elevation);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ SeekBarCompat.setOutlineProvider(this, mMarkerDrawable);
+ }
+ a.recycle();
+ }
+
+ public void resetSizes(String maxValue) {
+ DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+ //Account for negative numbers... is there any proper way of getting the biggest string between our range????
+ mNumber.setText("-" + maxValue);
+ //Do a first forced measure call for the TextView (with the biggest text content),
+ //to calculate the max width and use always the same.
+ //this avoids the TextView from shrinking and growing when the text content changes
+ int wSpec = MeasureSpec.makeMeasureSpec(displayMetrics.widthPixels, MeasureSpec.AT_MOST);
+ int hSpec = MeasureSpec.makeMeasureSpec(displayMetrics.heightPixels, MeasureSpec.AT_MOST);
+ mNumber.measure(wSpec, hSpec);
+ mWidth = Math.max(mNumber.getMeasuredWidth(), mNumber.getMeasuredHeight());
+ removeView(mNumber);
+ addView(mNumber, new FrameLayout.LayoutParams(mWidth, mWidth, Gravity.LEFT | Gravity.TOP));
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ mMarkerDrawable.draw(canvas);
+ super.dispatchDraw(canvas);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ measureChildren(widthMeasureSpec, heightMeasureSpec);
+ int widthSize = mWidth + getPaddingLeft() + getPaddingRight();
+ int heightSize = mWidth + getPaddingTop() + getPaddingBottom();
+ //This diff is the basic calculation of the difference between
+ //a square side size and its diagonal
+ //this helps us account for the visual offset created by MarkerDrawable
+ //when leaving one of the corners un-rounded
+ int diff = (int) ((1.41f * mWidth) - mWidth) / 2;
+ setMeasuredDimension(widthSize, heightSize + diff + mSeparation);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int left = getPaddingLeft();
+ int top = getPaddingTop();
+ int right = getWidth() - getPaddingRight();
+ int bottom = getHeight() - getPaddingBottom();
+ //the TetView is always layout at the top
+ mNumber.layout(left, top, left + mWidth, top + mWidth);
+ //the MarkerDrawable uses the whole view, it will adapt itself...
+ // or it seems so...
+ mMarkerDrawable.setBounds(left, top, right, bottom);
+ }
+
+ @Override
+ protected boolean verifyDrawable(Drawable who) {
+ return who == mMarkerDrawable || super.verifyDrawable(who);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ //HACK: Sometimes, the animateOpen() call is made before the View is attached
+ //so the drawable cannot schedule itself to run the animation
+ //I think we can call it here safely.
+ //I've seen it happen in android 2.3.7
+ animateOpen();
+ }
+
+ public void setValue(CharSequence value) {
+ mNumber.setText(value);
+ }
+
+ public CharSequence getValue() {
+ return mNumber.getText();
+ }
+
+ public void animateOpen() {
+ mMarkerDrawable.stop();
+ mMarkerDrawable.animateToPressed();
+ }
+
+ public void animateClose() {
+ mMarkerDrawable.stop();
+ mNumber.setVisibility(View.INVISIBLE);
+ mMarkerDrawable.animateToNormal();
+ }
+
+ @Override
+ public void onOpeningComplete() {
+ mNumber.setVisibility(View.VISIBLE);
+ if (getParent() instanceof MarkerDrawable.MarkerAnimationListener) {
+ ((MarkerDrawable.MarkerAnimationListener) getParent()).onOpeningComplete();
+ }
+ }
+
+ @Override
+ public void onClosingComplete() {
+ if (getParent() instanceof MarkerDrawable.MarkerAnimationListener) {
+ ((MarkerDrawable.MarkerAnimationListener) getParent()).onClosingComplete();
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mMarkerDrawable.stop();
+ }
+
+ public void setColors(int startColor, int endColor) {
+ mMarkerDrawable.setColors(startColor, endColor);
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/PopupIndicator.java b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/PopupIndicator.java
new file mode 100644
index 000000000..d968abd51
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/PopupIndicator.java
@@ -0,0 +1,256 @@
+package com.yunbao.faceunity.seekbar.internal;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+import androidx.core.view.GravityCompat;
+
+import com.yunbao.faceunity.seekbar.internal.compat.SeekBarCompat;
+import com.yunbao.faceunity.seekbar.internal.drawable.MarkerDrawable;
+
+
+/**
+ * Class to manage the floating bubble thing, similar (but quite worse tested than {@link android.widget.PopupWindow}
+ *
+ *
+ * This will attach a View to the Window (full-width, measured-height, positioned just under the thumb)
+ *
+ *
+ * @hide
+ * @see #showIndicator(View, Rect)
+ * @see #dismiss()
+ * @see #dismissComplete()
+ * @see Floater
+ */
+public class PopupIndicator {
+
+ private final WindowManager mWindowManager;
+ private boolean mShowing;
+ private Floater mPopupView;
+ //Outside listener for the DiscreteSeekBar to get MarkerDrawable animation events.
+ //The whole chain of events goes this way:
+ //MarkerDrawable->Marker->Floater->mListener->DiscreteSeekBar....
+ //... phew!
+ private MarkerDrawable.MarkerAnimationListener mListener;
+ private int[] mDrawingLocation = new int[2];
+ Point screenSize = new Point();
+
+ public PopupIndicator(Context context, AttributeSet attrs, int defStyleAttr, String maxValue, int thumbSize, int separation) {
+ mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ mPopupView = new Floater(context, attrs, defStyleAttr, maxValue, thumbSize, separation);
+ }
+
+ public void updateSizes(String maxValue) {
+ dismissComplete();
+ if (mPopupView != null) {
+ mPopupView.mMarker.resetSizes(maxValue);
+ }
+ }
+
+ public void setListener(MarkerDrawable.MarkerAnimationListener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * We want the Floater to be full-width because the contents will be moved from side to side.
+ * We may/should change this in the future to use just the PARENT View width and/or pass it in the constructor
+ */
+ private void measureFloater() {
+ int specWidth = View.MeasureSpec.makeMeasureSpec(screenSize.x, View.MeasureSpec.EXACTLY);
+ int specHeight = View.MeasureSpec.makeMeasureSpec(screenSize.y, View.MeasureSpec.AT_MOST);
+ mPopupView.measure(specWidth, specHeight);
+ }
+
+ public void setValue(CharSequence value) {
+ mPopupView.mMarker.setValue(value);
+ }
+
+ public boolean isShowing() {
+ return mShowing;
+ }
+
+ public void showIndicator(View parent, Rect touchBounds) {
+ if (isShowing()) {
+ mPopupView.mMarker.animateOpen();
+ return;
+ }
+
+ IBinder windowToken = parent.getWindowToken();
+ if (windowToken != null) {
+ WindowManager.LayoutParams p = createPopupLayout(windowToken);
+
+ p.gravity = Gravity.TOP | GravityCompat.START;
+ updateLayoutParamsForPosiion(parent, p, touchBounds.bottom);
+ mShowing = true;
+
+ translateViewIntoPosition(touchBounds.centerX());
+ invokePopup(p);
+ }
+ }
+
+ public void move(int x) {
+ if (!isShowing()) {
+ return;
+ }
+ translateViewIntoPosition(x);
+ }
+
+ public void setColors(int startColor, int endColor) {
+ mPopupView.setColors(startColor, endColor);
+ }
+
+ /**
+ * This will start the closing animation of the Marker and call onClosingComplete when finished
+ */
+ public void dismiss() {
+ mPopupView.mMarker.animateClose();
+ }
+
+ /**
+ * FORCE the popup window to be removed.
+ * You typically calls this when the parent view is being removed from the window to avoid a Window Leak
+ */
+ public void dismissComplete() {
+ if (isShowing()) {
+ mShowing = false;
+ try {
+ mWindowManager.removeViewImmediate(mPopupView);
+ } finally {
+ }
+ }
+ }
+
+
+ private void updateLayoutParamsForPosiion(View anchor, WindowManager.LayoutParams p, int yOffset) {
+ DisplayMetrics displayMetrics = anchor.getResources().getDisplayMetrics();
+ screenSize.set(displayMetrics.widthPixels, displayMetrics.heightPixels);
+
+ measureFloater();
+ int measuredHeight = mPopupView.getMeasuredHeight();
+ int paddingBottom = mPopupView.mMarker.getPaddingBottom();
+ anchor.getLocationInWindow(mDrawingLocation);
+ p.x = 0;
+ p.y = mDrawingLocation[1] - measuredHeight + yOffset + paddingBottom;
+ p.width = screenSize.x;
+ p.height = measuredHeight;
+ }
+
+ private void translateViewIntoPosition(final int x) {
+ mPopupView.setFloatOffset(x + mDrawingLocation[0]);
+ }
+
+ private void invokePopup(WindowManager.LayoutParams p) {
+ mWindowManager.addView(mPopupView, p);
+ mPopupView.mMarker.animateOpen();
+ }
+
+ private WindowManager.LayoutParams createPopupLayout(IBinder token) {
+ WindowManager.LayoutParams p = new WindowManager.LayoutParams();
+ p.gravity = Gravity.START | Gravity.TOP;
+ p.width = ViewGroup.LayoutParams.MATCH_PARENT;
+ p.height = ViewGroup.LayoutParams.MATCH_PARENT;
+ p.format = PixelFormat.TRANSLUCENT;
+ p.flags = computeFlags(p.flags);
+ p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+ p.token = token;
+ p.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
+ p.setTitle("DiscreteSeekBar Indicator:" + Integer.toHexString(hashCode()));
+
+ return p;
+ }
+
+ /**
+ * I'm NOT completely sure how all this bitwise things work...
+ *
+ * @param curFlags
+ * @return
+ */
+ private int computeFlags(int curFlags) {
+ curFlags &= ~(
+ WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES |
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
+ WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
+ WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
+ WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
+ WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+ curFlags |= WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
+ curFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+ return curFlags;
+ }
+
+ /**
+ * Small FrameLayout class to hold and move the bubble around when requested
+ * I wanted to use the {@link Marker} directly
+ * but doing so would make some things harder to implement
+ * (like moving the marker around, having the Marker's outline to work, etc)
+ */
+ private class Floater extends FrameLayout implements MarkerDrawable.MarkerAnimationListener {
+ private Marker mMarker;
+ private int mOffset;
+
+ public Floater(Context context, AttributeSet attrs, int defStyleAttr, String maxValue, int thumbSize, int separation) {
+ super(context);
+ mMarker = new Marker(context, attrs, defStyleAttr, maxValue, thumbSize, separation);
+ addView(mMarker, new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP));
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ measureChildren(widthMeasureSpec, heightMeasureSpec);
+ int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ int heightSie = mMarker.getMeasuredHeight();
+ setMeasuredDimension(widthSize, heightSie);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int centerDiffX = mMarker.getMeasuredWidth() / 2;
+ int offset = (mOffset - centerDiffX);
+ mMarker.layout(offset, 0, offset + mMarker.getMeasuredWidth(), mMarker.getMeasuredHeight());
+ }
+
+ public void setFloatOffset(int x) {
+ mOffset = x;
+ int centerDiffX = mMarker.getMeasuredWidth() / 2;
+ int offset = (x - centerDiffX);
+ mMarker.offsetLeftAndRight(offset - mMarker.getLeft());
+ //Without hardware acceleration (or API levels<11), offsetting a view seems to NOT invalidate the proper area.
+ //We should calc the proper invalidate Rect but this will be for now...
+ if (!SeekBarCompat.isHardwareAccelerated(this)) {
+ invalidate();
+ }
+ }
+
+ @Override
+ public void onClosingComplete() {
+ if (mListener != null) {
+ mListener.onClosingComplete();
+ }
+ dismissComplete();
+ }
+
+ @Override
+ public void onOpeningComplete() {
+ if (mListener != null) {
+ mListener.onOpeningComplete();
+ }
+ }
+
+ public void setColors(int startColor, int endColor) {
+ mMarker.setColors(startColor, endColor);
+ }
+ }
+
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/compat/AnimatorCompat.java b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/compat/AnimatorCompat.java
new file mode 100644
index 000000000..94f11d8f0
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/compat/AnimatorCompat.java
@@ -0,0 +1,73 @@
+package com.yunbao.faceunity.seekbar.internal.compat;
+
+
+import com.yunbao.faceunity.seekbar.DiscreteSeekBar;
+
+/**
+ * Currently, there's no {@link android.animation.ValueAnimator} compatibility version
+ * and as we didn't want to throw in external dependencies, we made this small class.
+ *
+ *
+ * This will work like {@link android.support.v4.view.ViewPropertyAnimatorCompat}, that is,
+ * not doing anything on API<11 and using the default {@link android.animation.ValueAnimator}
+ * on API>=11
+ *
+ *
+ * This class is used to provide animation to the {@link DiscreteSeekBar}
+ * when navigating with the Keypad
+ *
+ *
+ * @hide
+ */
+public abstract class AnimatorCompat {
+ public interface AnimationFrameUpdateListener {
+ public void onAnimationFrame(float currentValue);
+ }
+
+ AnimatorCompat() {
+
+ }
+
+ public abstract void cancel();
+
+ public abstract boolean isRunning();
+
+ public abstract void setDuration(int progressAnimationDuration);
+
+ public abstract void start();
+
+ public static final AnimatorCompat create(float start, float end, AnimationFrameUpdateListener listener) {
+ return new AnimatorCompatBase(start, end, listener);
+ }
+
+ private static class AnimatorCompatBase extends AnimatorCompat {
+
+ private final AnimationFrameUpdateListener mListener;
+ private final float mEndValue;
+
+ public AnimatorCompatBase(float start, float end, AnimationFrameUpdateListener listener) {
+ mListener = listener;
+ mEndValue = end;
+ }
+
+ @Override
+ public void cancel() {
+
+ }
+
+ @Override
+ public boolean isRunning() {
+ return false;
+ }
+
+ @Override
+ public void setDuration(int progressAnimationDuration) {
+
+ }
+
+ @Override
+ public void start() {
+ mListener.onAnimationFrame(mEndValue);
+ }
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/compat/SeekBarCompat.java b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/compat/SeekBarCompat.java
new file mode 100644
index 000000000..55b9e583f
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/compat/SeekBarCompat.java
@@ -0,0 +1,130 @@
+package com.yunbao.faceunity.seekbar.internal.compat;
+
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
+import android.os.Build;
+import android.view.View;
+import android.view.ViewParent;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.core.graphics.drawable.DrawableCompat;
+
+import com.yunbao.faceunity.seekbar.internal.drawable.AlmostRippleDrawable;
+import com.yunbao.faceunity.seekbar.internal.drawable.MarkerDrawable;
+
+
+/**
+ * Wrapper compatibility class to call some API-Specific methods
+ * And offer alternate procedures when possible
+ *
+ * @hide
+ */
+public class SeekBarCompat {
+
+ /**
+ * Sets the custom Outline provider on API>=21.
+ * Does nothing on API<21
+ *
+ * @param view
+ * @param markerDrawable
+ */
+ public static void setOutlineProvider(View view, final MarkerDrawable markerDrawable) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ SeekBarCompatDontCrash.setOutlineProvider(view, markerDrawable);
+ }
+ }
+
+ /**
+ * Our DiscreteSeekBar implementation uses a circular drawable on API < 21
+ * because we don't set it as Background, but draw it ourselves
+ *
+ * @param colorStateList
+ * @return
+ */
+ public static Drawable getRipple(ColorStateList colorStateList) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ return SeekBarCompatDontCrash.getRipple(colorStateList);
+ } else {
+ return new AlmostRippleDrawable(colorStateList);
+ }
+ }
+
+ /**
+ * Sets the color of the seekbar ripple
+ *
+ * @param drawable
+ * @param colorStateList The ColorStateList the track ripple will be changed to
+ */
+ public static void setRippleColor(@NonNull Drawable drawable, ColorStateList colorStateList) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ ((RippleDrawable) drawable).setColor(colorStateList);
+ } else {
+ ((AlmostRippleDrawable) drawable).setColor(colorStateList);
+ }
+ }
+
+ /**
+ * As our DiscreteSeekBar implementation uses a circular drawable on API < 21
+ * we want to use the same method to set its bounds as the Ripple's hotspot bounds.
+ *
+ * @param drawable
+ * @param left
+ * @param top
+ * @param right
+ * @param bottom
+ */
+ public static void setHotspotBounds(Drawable drawable, int left, int top, int right, int bottom) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ //We don't want the full size rect, Lollipop ripple would be too big
+ int size = (right - left) / 8;
+ DrawableCompat.setHotspotBounds(drawable, left + size, top + size, right - size, bottom - size);
+ } else {
+ drawable.setBounds(left, top, right, bottom);
+ }
+ }
+
+ /**
+ * android.support.v4.view.ViewCompat SHOULD include this once and for all!!
+ * But it doesn't...
+ *
+ * @param view
+ * @param background
+ */
+ @SuppressWarnings("deprecation")
+ public static void setBackground(View view, Drawable background) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ SeekBarCompatDontCrash.setBackground(view, background);
+ } else {
+ view.setBackgroundDrawable(background);
+ }
+ }
+
+ /**
+ * Sets the TextView text direction attribute when possible
+ *
+ * @param textView
+ * @param textDirection
+ * @see TextView#setTextDirection(int)
+ */
+ public static void setTextDirection(TextView textView, int textDirection) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ SeekBarCompatDontCrash.setTextDirection(textView, textDirection);
+ }
+ }
+
+ public static boolean isInScrollingContainer(ViewParent p) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ return SeekBarCompatDontCrash.isInScrollingContainer(p);
+ }
+ return false;
+ }
+
+ public static boolean isHardwareAccelerated(View view) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ return SeekBarCompatDontCrash.isHardwareAccelerated(view);
+ }
+ return false;
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/compat/SeekBarCompatDontCrash.java b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/compat/SeekBarCompatDontCrash.java
new file mode 100644
index 000000000..ef13919fb
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/compat/SeekBarCompatDontCrash.java
@@ -0,0 +1,59 @@
+package com.yunbao.faceunity.seekbar.internal.compat;
+
+import android.annotation.TargetApi;
+import android.content.res.ColorStateList;
+import android.graphics.Outline;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.view.ViewParent;
+import android.widget.TextView;
+
+import com.yunbao.faceunity.seekbar.internal.drawable.MarkerDrawable;
+
+
+/**
+ * Wrapper compatibility class to call some API-Specific methods
+ * And offer alternate procedures when possible
+ *
+ * @hide
+ */
+@TargetApi(21)
+class SeekBarCompatDontCrash {
+ public static void setOutlineProvider(View marker, final MarkerDrawable markerDrawable) {
+ marker.setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setConvexPath(markerDrawable.getPath());
+ }
+ });
+ }
+
+ public static Drawable getRipple(ColorStateList colorStateList) {
+ return new RippleDrawable(colorStateList, null, null);
+ }
+
+ public static void setBackground(View view, Drawable background) {
+ view.setBackground(background);
+ }
+
+ public static void setTextDirection(TextView number, int textDirection) {
+ number.setTextDirection(textDirection);
+ }
+
+ public static boolean isInScrollingContainer(ViewParent p) {
+ while (p != null && p instanceof ViewGroup) {
+ if (((ViewGroup) p).shouldDelayChildPressedState()) {
+ return true;
+ }
+ p = p.getParent();
+ }
+ return false;
+ }
+
+ public static boolean isHardwareAccelerated(View view) {
+ return view.isHardwareAccelerated();
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/AlmostRippleDrawable.java b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/AlmostRippleDrawable.java
new file mode 100644
index 000000000..e69e2df5c
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/AlmostRippleDrawable.java
@@ -0,0 +1,206 @@
+package com.yunbao.faceunity.seekbar.internal.drawable;
+
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Animatable;
+import android.os.SystemClock;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import androidx.annotation.NonNull;
+
+
+public class AlmostRippleDrawable extends StateDrawable implements Animatable {
+ private static final long FRAME_DURATION = 1000 / 60;
+ private static final int ANIMATION_DURATION = 250;
+
+ private static final float INACTIVE_SCALE = 0f;
+ private static final float ACTIVE_SCALE = 1f;
+ private float mCurrentScale = INACTIVE_SCALE;
+ private Interpolator mInterpolator;
+ private long mStartTime;
+ private boolean mReverse = false;
+ private boolean mRunning = false;
+ private int mDuration = ANIMATION_DURATION;
+ private float mAnimationInitialValue;
+ //We don't use colors just with our drawable state because of animations
+ private int mPressedColor;
+ private int mFocusedColor;
+ private int mDisabledColor;
+ private int mRippleColor;
+ private int mRippleBgColor;
+
+ public AlmostRippleDrawable(@NonNull ColorStateList tintStateList) {
+ super(tintStateList);
+ mInterpolator = new AccelerateDecelerateInterpolator();
+ setColor(tintStateList);
+ }
+
+ public void setColor(@NonNull ColorStateList tintStateList) {
+ int defaultColor = tintStateList.getDefaultColor();
+ mFocusedColor = tintStateList.getColorForState(new int[]{android.R.attr.state_enabled, android.R.attr.state_focused}, defaultColor);
+ mPressedColor = tintStateList.getColorForState(new int[]{android.R.attr.state_enabled, android.R.attr.state_pressed}, defaultColor);
+ mDisabledColor = tintStateList.getColorForState(new int[]{-android.R.attr.state_enabled}, defaultColor);
+
+ //The ripple should be partially transparent
+ mFocusedColor = getModulatedAlphaColor(130, mFocusedColor);
+ mPressedColor = getModulatedAlphaColor(130, mPressedColor);
+ mDisabledColor = getModulatedAlphaColor(130, mDisabledColor);
+ }
+
+ private static int getModulatedAlphaColor(int alphaValue, int originalColor) {
+ int alpha = Color.alpha(originalColor);
+ int scale = alphaValue + (alphaValue >> 7);
+ alpha = alpha * scale >> 8;
+ return Color.argb(alpha, Color.red(originalColor), Color.green(originalColor), Color.blue(originalColor));
+ }
+
+ @Override
+ public void doDraw(Canvas canvas, Paint paint) {
+ Rect bounds = getBounds();
+ int size = Math.min(bounds.width(), bounds.height());
+ float scale = mCurrentScale;
+ int rippleColor = mRippleColor;
+ int bgColor = mRippleBgColor;
+ float radius = (size / 2);
+ float radiusAnimated = radius * scale;
+ if (scale > INACTIVE_SCALE) {
+ if (bgColor != 0) {
+ paint.setColor(bgColor);
+ paint.setAlpha(decreasedAlpha(Color.alpha(bgColor)));
+ canvas.drawCircle(bounds.centerX(), bounds.centerY(), radius, paint);
+ }
+ if (rippleColor != 0) {
+ paint.setColor(rippleColor);
+ paint.setAlpha(modulateAlpha(Color.alpha(rippleColor)));
+ canvas.drawCircle(bounds.centerX(), bounds.centerY(), radiusAnimated, paint);
+ }
+ }
+ }
+
+ private int decreasedAlpha(int alpha) {
+ int scale = 100 + (100 >> 7);
+ return alpha * scale >> 8;
+ }
+
+ @Override
+ public boolean setState(int[] stateSet) {
+ int[] oldState = getState();
+ boolean oldPressed = false;
+ for (int i : oldState) {
+ if (i == android.R.attr.state_pressed) {
+ oldPressed = true;
+ }
+ }
+ super.setState(stateSet);
+ boolean focused = false;
+ boolean pressed = false;
+ boolean disabled = true;
+ for (int i : stateSet) {
+ if (i == android.R.attr.state_focused) {
+ focused = true;
+ } else if (i == android.R.attr.state_pressed) {
+ pressed = true;
+ } else if (i == android.R.attr.state_enabled) {
+ disabled = false;
+ }
+ }
+
+ if (disabled) {
+ unscheduleSelf(mUpdater);
+ mRippleColor = mDisabledColor;
+ mRippleBgColor = 0;
+ mCurrentScale = ACTIVE_SCALE / 2;
+ invalidateSelf();
+ } else {
+ if (pressed) {
+ animateToPressed();
+ mRippleColor = mRippleBgColor = mPressedColor;
+ } else if (oldPressed) {
+ mRippleColor = mRippleBgColor = mPressedColor;
+ animateToNormal();
+ } else if (focused) {
+ mRippleColor = mFocusedColor;
+ mRippleBgColor = 0;
+ mCurrentScale = ACTIVE_SCALE;
+ invalidateSelf();
+ } else {
+ mRippleColor = 0;
+ mRippleBgColor = 0;
+ mCurrentScale = INACTIVE_SCALE;
+ invalidateSelf();
+ }
+ }
+ return true;
+ }
+
+ public void animateToPressed() {
+ unscheduleSelf(mUpdater);
+ if (mCurrentScale < ACTIVE_SCALE) {
+ mReverse = false;
+ mRunning = true;
+ mAnimationInitialValue = mCurrentScale;
+ float durationFactor = 1f - ((mAnimationInitialValue - INACTIVE_SCALE) / (ACTIVE_SCALE - INACTIVE_SCALE));
+ mDuration = (int) (ANIMATION_DURATION * durationFactor);
+ mStartTime = SystemClock.uptimeMillis();
+ scheduleSelf(mUpdater, mStartTime + FRAME_DURATION);
+ }
+ }
+
+ public void animateToNormal() {
+ unscheduleSelf(mUpdater);
+ if (mCurrentScale > INACTIVE_SCALE) {
+ mReverse = true;
+ mRunning = true;
+ mAnimationInitialValue = mCurrentScale;
+ float durationFactor = 1f - ((mAnimationInitialValue - ACTIVE_SCALE) / (INACTIVE_SCALE - ACTIVE_SCALE));
+ mDuration = (int) (ANIMATION_DURATION * durationFactor);
+ mStartTime = SystemClock.uptimeMillis();
+ scheduleSelf(mUpdater, mStartTime + FRAME_DURATION);
+ }
+ }
+
+ private void updateAnimation(float factor) {
+ float initial = mAnimationInitialValue;
+ float destination = mReverse ? INACTIVE_SCALE : ACTIVE_SCALE;
+ mCurrentScale = initial + (destination - initial) * factor;
+ invalidateSelf();
+ }
+
+ private final Runnable mUpdater = new Runnable() {
+
+ @Override
+ public void run() {
+
+ long currentTime = SystemClock.uptimeMillis();
+ long diff = currentTime - mStartTime;
+ if (diff < mDuration) {
+ float interpolation = mInterpolator.getInterpolation((float) diff / (float) mDuration);
+ scheduleSelf(mUpdater, currentTime + FRAME_DURATION);
+ updateAnimation(interpolation);
+ } else {
+ unscheduleSelf(mUpdater);
+ mRunning = false;
+ updateAnimation(1f);
+ }
+ }
+ };
+
+ @Override
+ public void start() {
+ //No-Op. We control our own animation
+ }
+
+ @Override
+ public void stop() {
+ //No-Op. We control our own animation
+ }
+
+ @Override
+ public boolean isRunning() {
+ return mRunning;
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/MarkerDrawable.java b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/MarkerDrawable.java
new file mode 100644
index 000000000..8c5a04745
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/MarkerDrawable.java
@@ -0,0 +1,235 @@
+package com.yunbao.faceunity.seekbar.internal.drawable;
+
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Animatable;
+import android.os.SystemClock;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import androidx.annotation.NonNull;
+
+
+/**
+ * Implementation of {@link StateDrawable} to draw a morphing marker symbol.
+ *
+ * It's basically an implementation of an {@link Animatable} Drawable with the following details:
+ *
+ *
+ * - Animates from a circle shape to a "marker" shape just using a RoundRect
+ * - Animates color change from the normal state color to the pressed state color
+ * - Uses a {@link Path} to also serve as Outline for API>=21
+ *
+ *
+ * @hide
+ */
+public class MarkerDrawable extends StateDrawable implements Animatable {
+ private static final long FRAME_DURATION = 1000 / 60;
+ private static final int ANIMATION_DURATION = 250;
+
+ private float mCurrentScale = 0f;
+ private Interpolator mInterpolator;
+ private long mStartTime;
+ private boolean mReverse = false;
+ private boolean mRunning = false;
+ private int mDuration = ANIMATION_DURATION;
+ //size of the actual thumb drawable to use as circle state size
+ private float mClosedStateSize;
+ //value to store que current scale when starting an animation and interpolate from it
+ private float mAnimationInitialValue;
+ //extra offset directed from the View to account
+ //for its internal padding between circle state and marker state
+ private int mExternalOffset;
+ //colors for interpolation
+ private int mStartColor;//Color when the Marker is OPEN
+ private int mEndColor;//Color when the arker is CLOSED
+
+ Path mPath = new Path();
+ RectF mRect = new RectF();
+ Matrix mMatrix = new Matrix();
+ private MarkerAnimationListener mMarkerListener;
+
+ public MarkerDrawable(@NonNull ColorStateList tintList, int closedSize) {
+ super(tintList);
+ mInterpolator = new AccelerateDecelerateInterpolator();
+ mClosedStateSize = closedSize;
+ mStartColor = tintList.getColorForState(new int[]{android.R.attr.state_enabled, android.R.attr.state_pressed}, tintList.getDefaultColor());
+ mEndColor = tintList.getDefaultColor();
+
+ }
+
+ public void setExternalOffset(int offset) {
+ mExternalOffset = offset;
+ }
+
+ /**
+ * The two colors that will be used for the seek thumb.
+ *
+ * @param startColor Color used for the seek thumb
+ * @param endColor Color used for popup indicator
+ */
+ public void setColors(int startColor, int endColor) {
+ mStartColor = startColor;
+ mEndColor = endColor;
+ }
+
+ @Override
+ void doDraw(Canvas canvas, Paint paint) {
+ if (!mPath.isEmpty()) {
+ paint.setStyle(Paint.Style.FILL);
+ int color = blendColors(mStartColor, mEndColor, mCurrentScale);
+ paint.setColor(color);
+ canvas.drawPath(mPath, paint);
+ }
+ }
+
+ public Path getPath() {
+ return mPath;
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+ computePath(bounds);
+ }
+
+ private void computePath(Rect bounds) {
+ final float currentScale = mCurrentScale;
+ final Path path = mPath;
+ final RectF rect = mRect;
+ final Matrix matrix = mMatrix;
+
+ path.reset();
+ int totalSize = Math.min(bounds.width(), bounds.height());
+
+ float initial = mClosedStateSize;
+ float destination = totalSize;
+ float currentSize = initial + (destination - initial) * currentScale;
+
+ float halfSize = currentSize / 2f;
+ float inverseScale = 1f - currentScale;
+ float cornerSize = halfSize * inverseScale;
+ float[] corners = new float[]{halfSize, halfSize, halfSize, halfSize, halfSize, halfSize, cornerSize, cornerSize};
+ rect.set(bounds.left, bounds.top, bounds.left + currentSize, bounds.top + currentSize);
+ path.addRoundRect(rect, corners, Path.Direction.CCW);
+ matrix.reset();
+ matrix.postRotate(-45, bounds.left + halfSize, bounds.top + halfSize);
+ matrix.postTranslate((bounds.width() - currentSize) / 2, 0);
+ float hDiff = (bounds.bottom - currentSize - mExternalOffset) * inverseScale;
+ matrix.postTranslate(0, hDiff);
+ path.transform(matrix);
+ }
+
+ private void updateAnimation(float factor) {
+ float initial = mAnimationInitialValue;
+ float destination = mReverse ? 0f : 1f;
+ mCurrentScale = initial + (destination - initial) * factor;
+ computePath(getBounds());
+ invalidateSelf();
+ }
+
+ public void animateToPressed() {
+ unscheduleSelf(mUpdater);
+ mReverse = false;
+ if (mCurrentScale < 1) {
+ mRunning = true;
+ mAnimationInitialValue = mCurrentScale;
+ float durationFactor = 1f - mCurrentScale;
+ mDuration = (int) (ANIMATION_DURATION * durationFactor);
+ mStartTime = SystemClock.uptimeMillis();
+ scheduleSelf(mUpdater, mStartTime + FRAME_DURATION);
+ } else {
+ notifyFinishedToListener();
+ }
+ }
+
+ public void animateToNormal() {
+ mReverse = true;
+ unscheduleSelf(mUpdater);
+ if (mCurrentScale > 0) {
+ mRunning = true;
+ mAnimationInitialValue = mCurrentScale;
+ float durationFactor = 1f - mCurrentScale;
+ mDuration = ANIMATION_DURATION - (int) (ANIMATION_DURATION * durationFactor);
+ mStartTime = SystemClock.uptimeMillis();
+ scheduleSelf(mUpdater, mStartTime + FRAME_DURATION);
+ } else {
+ notifyFinishedToListener();
+ }
+ }
+
+ private final Runnable mUpdater = new Runnable() {
+
+ @Override
+ public void run() {
+
+ long currentTime = SystemClock.uptimeMillis();
+ long diff = currentTime - mStartTime;
+ if (diff < mDuration) {
+ float interpolation = mInterpolator.getInterpolation((float) diff / (float) mDuration);
+ scheduleSelf(mUpdater, currentTime + FRAME_DURATION);
+ updateAnimation(interpolation);
+ } else {
+ unscheduleSelf(mUpdater);
+ mRunning = false;
+ updateAnimation(1f);
+ notifyFinishedToListener();
+ }
+ }
+ };
+
+ public void setMarkerListener(MarkerAnimationListener listener) {
+ mMarkerListener = listener;
+ }
+
+ private void notifyFinishedToListener() {
+ if (mMarkerListener != null) {
+ if (mReverse) {
+ mMarkerListener.onClosingComplete();
+ } else {
+ mMarkerListener.onOpeningComplete();
+ }
+ }
+ }
+
+ @Override
+ public void start() {
+ //No-Op. We control our own animation
+ }
+
+ @Override
+ public void stop() {
+ unscheduleSelf(mUpdater);
+ }
+
+ @Override
+ public boolean isRunning() {
+ return mRunning;
+ }
+
+ private static int blendColors(int color1, int color2, float factor) {
+ final float inverseFactor = 1f - factor;
+ float a = (Color.alpha(color1) * factor) + (Color.alpha(color2) * inverseFactor);
+ float r = (Color.red(color1) * factor) + (Color.red(color2) * inverseFactor);
+ float g = (Color.green(color1) * factor) + (Color.green(color2) * inverseFactor);
+ float b = (Color.blue(color1) * factor) + (Color.blue(color2) * inverseFactor);
+ return Color.argb((int) a, (int) r, (int) g, (int) b);
+ }
+
+
+ /**
+ * A listener interface to porpagate animation events
+ * This is the "poor's man" AnimatorListener for this Drawable
+ */
+ public interface MarkerAnimationListener {
+ public void onClosingComplete();
+
+ public void onOpeningComplete();
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/StateDrawable.java b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/StateDrawable.java
new file mode 100644
index 000000000..9c24bb35c
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/StateDrawable.java
@@ -0,0 +1,105 @@
+package com.yunbao.faceunity.seekbar.internal.drawable;
+
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+
+import androidx.annotation.NonNull;
+
+
+/**
+ * A drawable that changes it's Paint color depending on the Drawable State
+ *
+ * Subclasses should implement {@link #doDraw(Canvas, Paint)}
+ *
+ *
+ * @hide
+ */
+public abstract class StateDrawable extends Drawable {
+ private ColorStateList mTintStateList;
+ private final Paint mPaint;
+ private int mCurrentColor;
+ private int mAlpha = 255;
+
+ public StateDrawable(@NonNull ColorStateList tintStateList) {
+ super();
+ setColorStateList(tintStateList);
+ mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ }
+
+ @Override
+ public boolean isStateful() {
+ return (mTintStateList.isStateful()) || super.isStateful();
+ }
+
+ @Override
+ public boolean setState(int[] stateSet) {
+ boolean handled = super.setState(stateSet);
+ handled = updateTint(stateSet) || handled;
+ return handled;
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ private boolean updateTint(int[] state) {
+ final int color = mTintStateList.getColorForState(state, mCurrentColor);
+ if (color != mCurrentColor) {
+ mCurrentColor = color;
+ //We've changed states
+ invalidateSelf();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ mPaint.setColor(mCurrentColor);
+ int alpha = modulateAlpha(Color.alpha(mCurrentColor));
+ mPaint.setAlpha(alpha);
+ doDraw(canvas, mPaint);
+ }
+
+ public void setColorStateList(@NonNull ColorStateList tintStateList) {
+ mTintStateList = tintStateList;
+ mCurrentColor = tintStateList.getDefaultColor();
+ }
+
+ /**
+ * Subclasses should implement this method to do the actual drawing
+ *
+ * @param canvas The current {@link Canvas} to draw into
+ * @param paint The {@link Paint} preconfigurred with the current
+ * {@link ColorStateList} color
+ */
+ abstract void doDraw(Canvas canvas, Paint paint);
+
+ @Override
+ public void setAlpha(int alpha) {
+ mAlpha = alpha;
+ invalidateSelf();
+ }
+
+ int modulateAlpha(int alpha) {
+ int scale = mAlpha + (mAlpha >> 7);
+ return alpha * scale >> 8;
+ }
+
+ @Override
+ public int getAlpha() {
+ return mAlpha;
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ mPaint.setColorFilter(cf);
+ }
+
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/ThumbDrawable.java b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/ThumbDrawable.java
new file mode 100644
index 000000000..54256e479
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/ThumbDrawable.java
@@ -0,0 +1,96 @@
+package com.yunbao.faceunity.seekbar.internal.drawable;
+
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Animatable;
+import android.os.SystemClock;
+
+import androidx.annotation.NonNull;
+
+import com.yunbao.faceunity.seekbar.internal.Marker;
+
+
+/**
+ * HACK
+ *
+ * Special {@link StateDrawable} implementation
+ * to draw the Thumb circle.
+ *
+ *
+ * It's special because it will stop drawing once the state is pressed/focused BUT only after a small delay.
+ *
+ *
+ * This special delay is meant to help avoiding frame glitches while the {@link Marker} is added to the Window
+ *
+ *
+ * @hide
+ */
+public class ThumbDrawable extends StateDrawable implements Animatable {
+ //The current size for this drawable. Must be converted to real DPs
+ public static final int DEFAULT_SIZE_DP = 12;
+ private final int mSize;
+ private boolean mOpen;
+ private boolean mRunning;
+
+ public ThumbDrawable(@NonNull ColorStateList tintStateList, int size) {
+ super(tintStateList);
+ mSize = size;
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mSize;
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mSize;
+ }
+
+ @Override
+ public void doDraw(Canvas canvas, Paint paint) {
+ if (!mOpen) {
+ Rect bounds = getBounds();
+ float radius = (mSize / 2);
+ canvas.drawCircle(bounds.centerX(), bounds.centerY(), radius, paint);
+ }
+ }
+
+ public void animateToPressed() {
+ scheduleSelf(opener, SystemClock.uptimeMillis() + 100);
+ mRunning = true;
+ }
+
+ public void animateToNormal() {
+ mOpen = false;
+ mRunning = false;
+ unscheduleSelf(opener);
+ invalidateSelf();
+ }
+
+ private Runnable opener = new Runnable() {
+ @Override
+ public void run() {
+ mOpen = true;
+ invalidateSelf();
+ mRunning = false;
+ }
+ };
+
+ @Override
+ public void start() {
+ //NOOP
+ }
+
+ @Override
+ public void stop() {
+ animateToNormal();
+ }
+
+ @Override
+ public boolean isRunning() {
+ return mRunning;
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/TrackOvalDrawable.java b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/TrackOvalDrawable.java
new file mode 100644
index 000000000..f3a5898b1
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/TrackOvalDrawable.java
@@ -0,0 +1,30 @@
+package com.yunbao.faceunity.seekbar.internal.drawable;
+
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+
+import androidx.annotation.NonNull;
+
+
+/**
+ * Simple {@link StateDrawable} implementation
+ * to draw circles/ovals
+ *
+ * @hide
+ */
+public class TrackOvalDrawable extends StateDrawable {
+ private RectF mRectF = new RectF();
+
+ public TrackOvalDrawable(@NonNull ColorStateList tintStateList) {
+ super(tintStateList);
+ }
+
+ @Override
+ void doDraw(Canvas canvas, Paint paint) {
+ mRectF.set(getBounds());
+ canvas.drawOval(mRectF, paint);
+ }
+
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/TrackRectDrawable.java b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/TrackRectDrawable.java
new file mode 100644
index 000000000..165e24f03
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/TrackRectDrawable.java
@@ -0,0 +1,26 @@
+package com.yunbao.faceunity.seekbar.internal.drawable;
+
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+
+import androidx.annotation.NonNull;
+
+
+/**
+ * Simple {@link StateDrawable} implementation
+ * to draw rectangles
+ *
+ * @hide
+ */
+public class TrackRectDrawable extends StateDrawable {
+ public TrackRectDrawable(@NonNull ColorStateList tintStateList) {
+ super(tintStateList);
+ }
+
+ @Override
+ void doDraw(Canvas canvas, Paint paint) {
+ canvas.drawRect(getBounds(), paint);
+ }
+
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/ui/FaceUnityView.java b/FaceUnity/src/main/java/com/yunbao/faceunity/ui/FaceUnityView.java
new file mode 100644
index 000000000..2264afcf1
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/ui/FaceUnityView.java
@@ -0,0 +1,479 @@
+package com.yunbao.faceunity.ui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.google.android.material.tabs.TabLayout;
+import com.yunbao.common.utils.ToastUtil;
+import com.yunbao.common.utils.WordUtil;
+import com.yunbao.faceunity.R;
+import com.yunbao.faceunity.adapters.ContainerRecyclerAdapter;
+import com.yunbao.faceunity.adapters.MenuGroupRecyclerAdapter;
+import com.yunbao.faceunity.adapters.vh.StyleViewHolder;
+import com.yunbao.faceunity.data.FaceParam;
+import com.yunbao.faceunity.data.FineStickerDataFactory;
+import com.yunbao.faceunity.entity.BaseBean;
+import com.yunbao.faceunity.entity.FunctionEnum;
+import com.yunbao.faceunity.entity.MakeupCustomClassBean;
+import com.yunbao.faceunity.entity.MenuGroupBean;
+import com.yunbao.faceunity.entity.net.FineStickerEntity;
+import com.yunbao.faceunity.entity.net.FineStickerTagEntity;
+import com.yunbao.faceunity.repo.AnimojiSource;
+import com.yunbao.faceunity.repo.BodyBeautySource;
+import com.yunbao.faceunity.repo.FaceBeautySource;
+import com.yunbao.faceunity.repo.MakeupSource;
+import com.yunbao.faceunity.repo.PropSource;
+import com.yunbao.faceunity.seekbar.DiscreteSeekBar;
+import com.yunbao.faceunity.utils.FaceSPUtils;
+import com.yunbao.faceunity.utils.net.StickerDownloadHelper;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * DESC:
+ * Created on 2021/4/26
+ */
+public class FaceUnityView extends LinearLayout implements StickerDownloadHelper.Callback {
+
+ private Context mContext;
+ private RecyclerView menuGroup;
+ private RecyclerView containerRecycler;
+ private ContainerRecyclerAdapter containerAdapter;
+ private TabLayout tabLayout;
+ private TextView title;
+ private LinearLayout menu2, menuDiy, reset, menu2Reset;
+ private ImageView menu2Back, back, close, contrast;
+ private DiscreteSeekBar seekBar;
+ private IFaceUnityInter iFaceUnityInter;
+ private ConstraintLayout titleLayout;
+ private static final String TAG = "美颜";
+
+ public FaceUnityView(Context context) {
+ super(context);
+ mContext = context;
+ init();
+ }
+
+ public FaceUnityView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+ init();
+ }
+
+ public FaceUnityView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mContext = context;
+ init();
+ }
+
+
+ private void init() {
+ LayoutInflater.from(mContext).inflate(R.layout.layout_faceunity, this);
+ initView();
+ FineStickerDataFactory.getInstance().addCallback(this);
+ }
+
+ /**
+ * 初始化View
+ */
+ private void initView() {
+ menuGroup = findViewById(R.id.menu_group);
+ containerRecycler = findViewById(R.id.menu2_container_view);
+ tabLayout = findViewById(R.id.menu2_tab);
+ title = findViewById(R.id.menu_title);
+ menu2 = findViewById(R.id.layout_faceunity_menu2);
+ back = findViewById(R.id.menu_back);
+ menu2Back = findViewById(R.id.menu2_back);
+ close = findViewById(R.id.menu_close);
+ reset = findViewById(R.id.menu_reset);
+ menu2Reset = findViewById(R.id.menu2_reset);
+ menuDiy = findViewById(R.id.menu_diy);
+ seekBar = findViewById(R.id.item_seekBar);
+ contrast = findViewById(R.id.item_contrast);
+ titleLayout = findViewById(R.id.titleLayout);
+ initMenuGroup();
+ setContainerRecycler(new ArrayList<>());
+ initViewClick();
+ gotoFaceBeauty();
+ }
+
+ /**
+ * 初始化配置点击事件
+ */
+ private void initViewClick() {
+ menu2Back.setOnClickListener(v -> {
+ Object tag = menu2Back.getTag();
+ if (tag == null) {
+ goBackMainMenu();
+ } else if (tag.equals("makeup")) {
+ title.setText(R.string.home_function_name_makeup);
+ title.setVisibility(VISIBLE);
+ menuDiy.setVisibility(VISIBLE);
+ tabLayout.removeAllTabs();
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCombinations());
+ menu2Back.setTag(null);
+ }
+ });
+ menuDiy.setOnClickListener(v -> {
+ menu2Back.setTag("makeup");
+ changeRecyclerItemCount(5);
+ menuDiy.setVisibility(GONE);
+ ArrayList list = MakeupSource.buildCustomClasses();
+ setTab(createTabs(list));
+ });
+ menu2Reset.setOnClickListener(view -> {
+ containerAdapter.reset();
+ });
+
+ contrast.setOnTouchListener((v, event) -> {
+ if (iFaceUnityInter == null) {
+ return false;
+ }
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ iFaceUnityInter.onPause();
+ } else if (event.getAction() == KeyEvent.ACTION_UP) {
+ iFaceUnityInter.onStart();
+ }
+ return true;
+ });
+ }
+
+ /**
+ * 对比接口
+ */
+ public void setIFaceUnityInter(IFaceUnityInter iFaceUnityInter) {
+ this.iFaceUnityInter = iFaceUnityInter;
+ }
+
+ /**
+ * 回到默认界面
+ */
+ private void goBackMainMenu() {
+ setContainerRecycler(new ArrayList<>());
+ title.setText("美顏特效選擇");
+ titleLayout.setVisibility(VISIBLE);
+ title.setVisibility(VISIBLE);
+ menu2.setVisibility(GONE);
+ menuGroup.setVisibility(VISIBLE);
+ menuDiy.setVisibility(GONE);
+ }
+
+ /**
+ * 目前只需要美颜功能,进入后直奔美颜
+ */
+ private void gotoFaceBeauty() {
+ LinkedHashMap map = new LinkedHashMap<>();
+ map.put(R.string.beauty_radio_skin_beauty, FaceParam.FACE_BEAUTY_SKIN);
+ map.put(R.string.beauty_radio_face_shape, FaceParam.FACE_BEAUTY_SHAPE);
+ map.put(R.string.beauty_radio_filter, FaceParam.FACE_BEAUTY_FILTER);
+ map.put(R.string.beauty_radio_style, FaceParam.FACE_BEAUTY_STYLE);
+ setContainerRecycler(FaceBeautySource.buildSkinParams());
+ changeRecyclerItemCount(2);
+ setTab(createTabs(map));
+ menuGroup.setVisibility(GONE);
+ titleLayout.setVisibility(GONE);
+ back.setVisibility(GONE);
+ menu2Back.setVisibility(GONE);
+ menu2.setVisibility(VISIBLE);
+ }
+
+ /**
+ * 配置主菜单
+ */
+ private void initMenuGroup() {
+ MenuGroupRecyclerAdapter adapter = new MenuGroupRecyclerAdapter(mContext);
+ menuGroup.setLayoutManager(new GridLayoutManager(mContext, 4));
+ List layoutItem = new ArrayList<>();
+ layoutItem.add(new MenuGroupBean(R.string.home_function_name_beauty, R.mipmap.ico_home_beauty));
+ layoutItem.add(new MenuGroupBean(R.string.home_function_name_makeup, R.mipmap.ico_home_makeup));
+ layoutItem.add(new MenuGroupBean(R.string.home_function_name_beauty_body, R.mipmap.ico_home_beauty_body));
+ layoutItem.add(new MenuGroupBean(R.string.home_function_name_big_head, R.mipmap.ico_home_big_head));
+ layoutItem.add(new MenuGroupBean(R.string.home_function_name_animoji, R.mipmap.ico_home_animoji));
+ layoutItem.add(new MenuGroupBean(R.string.home_function_name_sticker, R.mipmap.ico_home_sticker));
+ layoutItem.add(new MenuGroupBean(R.string.home_function_name_fine_sticker, R.mipmap.ico_home_fine_sticker));
+ adapter.setList(layoutItem);
+ adapter.setOnItemClickListener(position -> {
+ LinkedHashMap map = new LinkedHashMap<>();
+ title.setVisibility(GONE);
+ switch (position) {
+ case 0:
+ map.put(R.string.beauty_radio_skin_beauty, FaceParam.FACE_BEAUTY_SKIN);
+ map.put(R.string.beauty_radio_face_shape, FaceParam.FACE_BEAUTY_SHAPE);
+ map.put(R.string.beauty_radio_filter, FaceParam.FACE_BEAUTY_FILTER);
+ map.put(R.string.beauty_radio_style, FaceParam.FACE_BEAUTY_STYLE);
+ setContainerRecycler(FaceBeautySource.buildSkinParams());
+ break;
+ case 1:
+ title.setText(R.string.home_function_name_makeup);
+ title.setVisibility(VISIBLE);
+ menuDiy.setVisibility(VISIBLE);
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCombinations());
+ break;
+ case 2:
+ title.setText(R.string.home_function_name_beauty_body);
+ title.setVisibility(VISIBLE);
+ changeRecyclerItemCount(2);
+ setContainerRecycler(BodyBeautySource.buildBodyBeauty());
+ break;
+ case 3:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(PropSource.buildPropBeans(FunctionEnum.BIG_HEAD));
+ break;
+ case 4:
+ changeRecyclerItemCount(5);
+ map.put(R.string.animoji_filter, FaceParam.FACE_ANIMOJI);
+ map.put(R.string.cartoon_filter, FaceParam.FACE_ANIM);
+ setContainerRecycler(AnimojiSource.buildAnimojis());
+ break;
+ case 5:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(PropSource.buildPropBeans(FunctionEnum.STICKER));
+ break;
+ case 6:
+ List tagList = FineStickerDataFactory.getInstance().loadTagList();
+ if (!tagList.isEmpty()) {
+ setTab(createTabs(tagList));
+ }
+ break;
+ }
+ setTab(createTabs(map));
+ menuGroup.setVisibility(GONE);
+ menu2.setVisibility(VISIBLE);
+ });
+ menuGroup.setAdapter(adapter);
+ }
+
+ /**
+ * 创建Tab
+ */
+ private List createTabs(Map map) {
+ List list = new ArrayList<>();
+ for (Integer key : map.keySet()) {
+ TabLayout.Tab tab = tabLayout.newTab().setText(key);
+ tab.setTag(map.get(key));
+ list.add(tab);
+ }
+ return list;
+ }
+
+ /**
+ * 创建菜单
+ */
+ private List createTabs(List tags) {
+ List list = new ArrayList<>();
+ for (FineStickerTagEntity tag : tags) {
+ TabLayout.Tab tab;
+ if (WordUtil.isZh()) {
+ tab = tabLayout.newTab().setText(tag.getTag().split("/")[0]);
+ } else {
+ tab = tabLayout.newTab().setText(tag.getTag().split("/")[1]);
+ }
+ tab.setTag(tag);
+ list.add(tab);
+ }
+ return list;
+ }
+
+ /**
+ * 创建菜单
+ */
+ private List createTabs(ArrayList tags) {
+ List list = new ArrayList<>();
+ for (MakeupCustomClassBean tag : tags) {
+ TabLayout.Tab tab;
+ tab = tabLayout.newTab().setText(tag.getDesRes());
+ tab.setTag(tag.getBeanType());
+ list.add(tab);
+ }
+ return list;
+ }
+
+ /**
+ * 设置菜单
+ */
+ private void setTab(List tabs) {
+ tabLayout.removeAllTabs();
+ for (TabLayout.Tab tab : tabs) {
+ tabLayout.addTab(tab);
+ if (tab.getTag() instanceof Integer) {
+ if (FaceSPUtils.getInstance().getString(StyleViewHolder.class.getSimpleName() + "_") != null && (int) tab.getTag() == FaceParam.FACE_BEAUTY_STYLE) {
+ ToastUtil.show("请先重置风格推荐");
+ tab.select();
+ changeRecyclerItemCount(5);
+ setContainerRecycler(FaceBeautySource.buildStylesParams());
+ }
+ }
+ }
+
+ tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
+ @Override
+ public void onTabSelected(TabLayout.Tab tab) {
+ Object tabTag = tab.getTag();
+ if (tabTag instanceof Integer) {
+ if (FaceSPUtils.getInstance().getString(StyleViewHolder.class.getSimpleName() + "_") != null && (int) tab.getTag() < FaceParam.FACE_BEAUTY_STYLE) {
+ ToastUtil.show("请先重置风格推荐");
+ tabLayout.getTabAt(3).select();
+ return;
+ }
+ switch ((int) tab.getTag()) {
+ case FaceParam.FACE_BEAUTY_SKIN:
+ changeRecyclerItemCount(2);
+ setContainerRecycler(FaceBeautySource.buildSkinParams());
+ break;
+ case FaceParam.FACE_BEAUTY_SHAPE:
+ changeRecyclerItemCount(2);
+ setContainerRecycler(FaceBeautySource.buildShapeParams());
+ break;
+ case FaceParam.FACE_BEAUTY_FILTER:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(FaceBeautySource.buildFilters());
+ break;
+ case FaceParam.FACE_BEAUTY_STYLE:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(FaceBeautySource.buildStylesParams());
+ break;
+ case FaceParam.FACE_ANIMOJI:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(AnimojiSource.buildAnimojis());
+ break;
+ case FaceParam.FACE_ANIM:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(AnimojiSource.buildFilters());
+ break;
+ case FaceParam.FACE_MAKEUP_TYPE_FOUNDATION:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCustomItemParams().get(MakeupSource.FACE_MAKEUP_TYPE_FOUNDATION));
+ break;
+ case FaceParam.FACE_MAKEUP_TYPE_LIP_STICK:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCustomItemParams().get(MakeupSource.FACE_MAKEUP_TYPE_LIP_STICK));
+ break;
+ case FaceParam.FACE_MAKEUP_TYPE_BLUSHER:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCustomItemParams().get(MakeupSource.FACE_MAKEUP_TYPE_BLUSHER));
+ break;
+ case FaceParam.FACE_MAKEUP_TYPE_EYE_BROW:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCustomItemParams().get(MakeupSource.FACE_MAKEUP_TYPE_EYE_BROW));
+ break;
+ case FaceParam.FACE_MAKEUP_TYPE_EYE_SHADOW:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCustomItemParams().get(MakeupSource.FACE_MAKEUP_TYPE_EYE_SHADOW));
+ break;
+ case FaceParam.FACE_MAKEUP_TYPE_EYE_LINER:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCustomItemParams().get(MakeupSource.FACE_MAKEUP_TYPE_EYE_LINER));
+ break;
+ case FaceParam.FACE_MAKEUP_TYPE_EYE_LASH:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCustomItemParams().get(MakeupSource.FACE_MAKEUP_TYPE_EYE_LASH));
+ break;
+ case FaceParam.FACE_MAKEUP_TYPE_HIGH_LIGHT:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCustomItemParams().get(MakeupSource.FACE_MAKEUP_TYPE_HIGH_LIGHT));
+ break;
+ case FaceParam.FACE_MAKEUP_TYPE_SHADOW:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCustomItemParams().get(MakeupSource.FACE_MAKEUP_TYPE_SHADOW));
+ break;
+ case FaceParam.FACE_MAKEUP_TYPE_EYE_PUPIL:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCustomItemParams().get(MakeupSource.FACE_MAKEUP_TYPE_EYE_PUPIL));
+ break;
+ default:
+ setContainerRecycler(new ArrayList<>());
+ }
+ } else if (tabTag instanceof FineStickerTagEntity) {
+ FineStickerTagEntity tag = (FineStickerTagEntity) tabTag;
+ FineStickerEntity sticker = FineStickerDataFactory.getInstance().loadStickerList(tag);
+ ArrayList list = new ArrayList<>();
+ FineStickerEntity.DocsBean docsBean = new FineStickerEntity.DocsBean();
+ docsBean.set_id("-1");
+ list.add(docsBean);
+ list.addAll(sticker.getDocs());
+ changeRecyclerItemCount(5);
+ setContainerRecycler(list);
+ }
+ }
+
+ @Override
+ public void onTabUnselected(TabLayout.Tab tab) {
+
+ }
+
+ @Override
+ public void onTabReselected(TabLayout.Tab tab) {
+
+ }
+ });
+ }
+
+ /**
+ * 设置美颜选项配置行数,图标为5个,拖动条为2个
+ *
+ * @param count
+ */
+ private void changeRecyclerItemCount(int count) {
+ containerRecycler.setLayoutManager(new GridLayoutManager(mContext, count));
+ }
+
+ /**
+ * 设置美颜Recycler内容
+ *
+ * @param list
+ */
+ private void setContainerRecycler(ArrayList extends BaseBean> list) {
+ if (containerAdapter == null) {
+ containerAdapter = new ContainerRecyclerAdapter(mContext);
+ containerAdapter.setSeekBar(seekBar);
+ containerRecycler.setLayoutManager(new GridLayoutManager(mContext, 2));
+ containerRecycler.setAdapter(containerAdapter);
+ }
+ // titleLayout.setVisibility(title.getVisibility()==GONE?INVISIBLE:VISIBLE);
+ // Log.i(TAG, "setContainerRecycler: " + list.size());
+ containerAdapter.setList(list);
+ containerAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void onGetTags(String[] tags) {
+ List list = FineStickerDataFactory.formatTag(tags);
+ setTab(createTabs(list));
+ }
+
+ @Override
+ public void onGetList(String tag, FineStickerEntity fineSticker) {
+
+ }
+
+ @Override
+ public void onDownload(FineStickerEntity.DocsBean entity) {
+
+ }
+
+ @Override
+ public void onDownloadError(FineStickerEntity.DocsBean entity, String msg) {
+
+ }
+
+ public interface IFaceUnityInter {
+ void onPause();
+
+ void onStart();
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/Authpack.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/Authpack.java
new file mode 100644
index 000000000..ca9d6ef70
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/Authpack.java
@@ -0,0 +1,1259 @@
+package com.yunbao.faceunity.utils;
+
+import java.security.MessageDigest;
+
+public class Authpack {
+ public static int sha1_32(byte[] buf){int ret=0;try{byte[] digest=MessageDigest.getInstance("SHA1").digest(buf);return ((int)(digest[0]&0xff)<<24)+((int)(digest[1]&0xff)<<16)+((int)(digest[2]&0xff)<<8)+((int)(digest[3]&0xff)<<0);}catch(Exception e){}return ret;}
+ public static byte[] A(){
+ byte[] buf=new byte[1247];
+ int i=0;
+ for(i=-128;i<-112;i++){ buf[0]=(byte)i; if(sha1_32(buf)==-1412499448){break;} }
+ for(i=115;i<128;i++){ buf[1]=(byte)i; if(sha1_32(buf)==-1698625626){break;} }
+ for(i=-9;i<1;i++){ buf[2]=(byte)i; if(sha1_32(buf)==-147940925){break;} }
+ for(i=-59;i<-49;i++){ buf[3]=(byte)i; if(sha1_32(buf)==1264200790){break;} }
+ for(i=-42;i<-25;i++){ buf[4]=(byte)i; if(sha1_32(buf)==1697566769){break;} }
+ for(i=111;i<128;i++){ buf[5]=(byte)i; if(sha1_32(buf)==122233839){break;} }
+ for(i=-98;i<-76;i++){ buf[6]=(byte)i; if(sha1_32(buf)==1405264427){break;} }
+ for(i=0;i<18;i++){ buf[7]=(byte)i; if(sha1_32(buf)==161323028){break;} }
+ for(i=24;i<42;i++){ buf[8]=(byte)i; if(sha1_32(buf)==-1675062338){break;} }
+ for(i=117;i<128;i++){ buf[9]=(byte)i; if(sha1_32(buf)==-131929334){break;} }
+ for(i=-18;i<7;i++){ buf[10]=(byte)i; if(sha1_32(buf)==1810231483){break;} }
+ for(i=-12;i<1;i++){ buf[11]=(byte)i; if(sha1_32(buf)==536750049){break;} }
+ for(i=-104;i<-83;i++){ buf[12]=(byte)i; if(sha1_32(buf)==-772102224){break;} }
+ for(i=-60;i<-42;i++){ buf[13]=(byte)i; if(sha1_32(buf)==-1163472125){break;} }
+ for(i=-117;i<-105;i++){ buf[14]=(byte)i; if(sha1_32(buf)==-97232876){break;} }
+ for(i=43;i<61;i++){ buf[15]=(byte)i; if(sha1_32(buf)==-974974571){break;} }
+ for(i=-128;i<-105;i++){ buf[16]=(byte)i; if(sha1_32(buf)==938583498){break;} }
+ for(i=-78;i<-47;i++){ buf[17]=(byte)i; if(sha1_32(buf)==149414679){break;} }
+ for(i=-44;i<-19;i++){ buf[18]=(byte)i; if(sha1_32(buf)==1562799730){break;} }
+ for(i=-108;i<-99;i++){ buf[19]=(byte)i; if(sha1_32(buf)==-716069345){break;} }
+ for(i=-85;i<-79;i++){ buf[20]=(byte)i; if(sha1_32(buf)==-2034197675){break;} }
+ for(i=81;i<95;i++){ buf[21]=(byte)i; if(sha1_32(buf)==-1470068282){break;} }
+ for(i=32;i<58;i++){ buf[22]=(byte)i; if(sha1_32(buf)==1612712273){break;} }
+ for(i=-55;i<-49;i++){ buf[23]=(byte)i; if(sha1_32(buf)==-1952211726){break;} }
+ for(i=-56;i<-46;i++){ buf[24]=(byte)i; if(sha1_32(buf)==-1018965288){break;} }
+ for(i=-93;i<-79;i++){ buf[25]=(byte)i; if(sha1_32(buf)==69718128){break;} }
+ for(i=42;i<60;i++){ buf[26]=(byte)i; if(sha1_32(buf)==-268157222){break;} }
+ for(i=-29;i<-12;i++){ buf[27]=(byte)i; if(sha1_32(buf)==1584136471){break;} }
+ for(i=5;i<16;i++){ buf[28]=(byte)i; if(sha1_32(buf)==-923857467){break;} }
+ for(i=94;i<115;i++){ buf[29]=(byte)i; if(sha1_32(buf)==107458704){break;} }
+ for(i=110;i<121;i++){ buf[30]=(byte)i; if(sha1_32(buf)==-2090884940){break;} }
+ for(i=97;i<123;i++){ buf[31]=(byte)i; if(sha1_32(buf)==842320789){break;} }
+ for(i=-60;i<-44;i++){ buf[32]=(byte)i; if(sha1_32(buf)==1171152656){break;} }
+ for(i=25;i<38;i++){ buf[33]=(byte)i; if(sha1_32(buf)==-1262055942){break;} }
+ for(i=-21;i<-5;i++){ buf[34]=(byte)i; if(sha1_32(buf)==-1513729912){break;} }
+ for(i=42;i<48;i++){ buf[35]=(byte)i; if(sha1_32(buf)==-577232115){break;} }
+ for(i=-56;i<-34;i++){ buf[36]=(byte)i; if(sha1_32(buf)==800272954){break;} }
+ for(i=-45;i<-27;i++){ buf[37]=(byte)i; if(sha1_32(buf)==-380833634){break;} }
+ for(i=5;i<14;i++){ buf[38]=(byte)i; if(sha1_32(buf)==-1426585505){break;} }
+ for(i=55;i<77;i++){ buf[39]=(byte)i; if(sha1_32(buf)==-535281532){break;} }
+ for(i=73;i<90;i++){ buf[40]=(byte)i; if(sha1_32(buf)==-1262520372){break;} }
+ for(i=9;i<28;i++){ buf[41]=(byte)i; if(sha1_32(buf)==-1332611969){break;} }
+ for(i=-118;i<-89;i++){ buf[42]=(byte)i; if(sha1_32(buf)==634967331){break;} }
+ for(i=-55;i<-44;i++){ buf[43]=(byte)i; if(sha1_32(buf)==-858200931){break;} }
+ for(i=-28;i<-19;i++){ buf[44]=(byte)i; if(sha1_32(buf)==-1436330132){break;} }
+ for(i=-128;i<-111;i++){ buf[45]=(byte)i; if(sha1_32(buf)==2023414779){break;} }
+ for(i=88;i<105;i++){ buf[46]=(byte)i; if(sha1_32(buf)==433475509){break;} }
+ for(i=-103;i<-83;i++){ buf[47]=(byte)i; if(sha1_32(buf)==-621155751){break;} }
+ for(i=-6;i<14;i++){ buf[48]=(byte)i; if(sha1_32(buf)==1314308691){break;} }
+ for(i=49;i<67;i++){ buf[49]=(byte)i; if(sha1_32(buf)==-1861746386){break;} }
+ for(i=52;i<77;i++){ buf[50]=(byte)i; if(sha1_32(buf)==1094009215){break;} }
+ for(i=-28;i<-22;i++){ buf[51]=(byte)i; if(sha1_32(buf)==1488594964){break;} }
+ for(i=-25;i<-13;i++){ buf[52]=(byte)i; if(sha1_32(buf)==-406758515){break;} }
+ for(i=40;i<53;i++){ buf[53]=(byte)i; if(sha1_32(buf)==-84669502){break;} }
+ for(i=60;i<87;i++){ buf[54]=(byte)i; if(sha1_32(buf)==1807959476){break;} }
+ for(i=-72;i<-56;i++){ buf[55]=(byte)i; if(sha1_32(buf)==-1883226178){break;} }
+ for(i=64;i<76;i++){ buf[56]=(byte)i; if(sha1_32(buf)==-24668801){break;} }
+ for(i=-21;i<-5;i++){ buf[57]=(byte)i; if(sha1_32(buf)==1746578807){break;} }
+ for(i=-22;i<-6;i++){ buf[58]=(byte)i; if(sha1_32(buf)==1611225785){break;} }
+ for(i=-53;i<-39;i++){ buf[59]=(byte)i; if(sha1_32(buf)==-999806378){break;} }
+ for(i=-8;i<-3;i++){ buf[60]=(byte)i; if(sha1_32(buf)==-1414416900){break;} }
+ for(i=11;i<32;i++){ buf[61]=(byte)i; if(sha1_32(buf)==-1040321994){break;} }
+ for(i=-62;i<-36;i++){ buf[62]=(byte)i; if(sha1_32(buf)==-1239002453){break;} }
+ for(i=-115;i<-101;i++){ buf[63]=(byte)i; if(sha1_32(buf)==-907107210){break;} }
+ for(i=-77;i<-60;i++){ buf[64]=(byte)i; if(sha1_32(buf)==1519384337){break;} }
+ for(i=-63;i<-38;i++){ buf[65]=(byte)i; if(sha1_32(buf)==1109368055){break;} }
+ for(i=36;i<43;i++){ buf[66]=(byte)i; if(sha1_32(buf)==-1924664184){break;} }
+ for(i=84;i<102;i++){ buf[67]=(byte)i; if(sha1_32(buf)==1391598924){break;} }
+ for(i=66;i<83;i++){ buf[68]=(byte)i; if(sha1_32(buf)==-799377408){break;} }
+ for(i=-37;i<-32;i++){ buf[69]=(byte)i; if(sha1_32(buf)==-24333892){break;} }
+ for(i=5;i<22;i++){ buf[70]=(byte)i; if(sha1_32(buf)==-1535818358){break;} }
+ for(i=-78;i<-55;i++){ buf[71]=(byte)i; if(sha1_32(buf)==691291821){break;} }
+ for(i=-34;i<-26;i++){ buf[72]=(byte)i; if(sha1_32(buf)==991957106){break;} }
+ for(i=96;i<112;i++){ buf[73]=(byte)i; if(sha1_32(buf)==-1876049701){break;} }
+ for(i=-128;i<-115;i++){ buf[74]=(byte)i; if(sha1_32(buf)==503361804){break;} }
+ for(i=-113;i<-100;i++){ buf[75]=(byte)i; if(sha1_32(buf)==95257085){break;} }
+ for(i=-95;i<-92;i++){ buf[76]=(byte)i; if(sha1_32(buf)==-1347375184){break;} }
+ for(i=-16;i<0;i++){ buf[77]=(byte)i; if(sha1_32(buf)==-1894647524){break;} }
+ for(i=-40;i<-35;i++){ buf[78]=(byte)i; if(sha1_32(buf)==-1872567850){break;} }
+ for(i=72;i<88;i++){ buf[79]=(byte)i; if(sha1_32(buf)==257120497){break;} }
+ for(i=90;i<107;i++){ buf[80]=(byte)i; if(sha1_32(buf)==-492744382){break;} }
+ for(i=105;i<126;i++){ buf[81]=(byte)i; if(sha1_32(buf)==-1675481457){break;} }
+ for(i=-14;i<6;i++){ buf[82]=(byte)i; if(sha1_32(buf)==-93549984){break;} }
+ for(i=30;i<34;i++){ buf[83]=(byte)i; if(sha1_32(buf)==204518874){break;} }
+ for(i=-38;i<-21;i++){ buf[84]=(byte)i; if(sha1_32(buf)==1921938458){break;} }
+ for(i=-1;i<19;i++){ buf[85]=(byte)i; if(sha1_32(buf)==-1030724951){break;} }
+ for(i=-72;i<-66;i++){ buf[86]=(byte)i; if(sha1_32(buf)==-1184901460){break;} }
+ for(i=-112;i<-94;i++){ buf[87]=(byte)i; if(sha1_32(buf)==-1850773765){break;} }
+ for(i=-127;i<-109;i++){ buf[88]=(byte)i; if(sha1_32(buf)==31175595){break;} }
+ for(i=-25;i<-4;i++){ buf[89]=(byte)i; if(sha1_32(buf)==1348238946){break;} }
+ for(i=-128;i<-113;i++){ buf[90]=(byte)i; if(sha1_32(buf)==1964722027){break;} }
+ for(i=-15;i<2;i++){ buf[91]=(byte)i; if(sha1_32(buf)==-886994052){break;} }
+ for(i=-48;i<-43;i++){ buf[92]=(byte)i; if(sha1_32(buf)==1222870455){break;} }
+ for(i=-49;i<-23;i++){ buf[93]=(byte)i; if(sha1_32(buf)==858686984){break;} }
+ for(i=88;i<93;i++){ buf[94]=(byte)i; if(sha1_32(buf)==932581436){break;} }
+ for(i=2;i<17;i++){ buf[95]=(byte)i; if(sha1_32(buf)==2106490313){break;} }
+ for(i=106;i<128;i++){ buf[96]=(byte)i; if(sha1_32(buf)==-219101145){break;} }
+ for(i=12;i<30;i++){ buf[97]=(byte)i; if(sha1_32(buf)==-1321536496){break;} }
+ for(i=58;i<82;i++){ buf[98]=(byte)i; if(sha1_32(buf)==2087006130){break;} }
+ for(i=81;i<99;i++){ buf[99]=(byte)i; if(sha1_32(buf)==256936830){break;} }
+ for(i=113;i<126;i++){ buf[100]=(byte)i; if(sha1_32(buf)==1888247753){break;} }
+ for(i=61;i<86;i++){ buf[101]=(byte)i; if(sha1_32(buf)==-1448325049){break;} }
+ for(i=74;i<85;i++){ buf[102]=(byte)i; if(sha1_32(buf)==-433489063){break;} }
+ for(i=-61;i<-43;i++){ buf[103]=(byte)i; if(sha1_32(buf)==1361684187){break;} }
+ for(i=42;i<50;i++){ buf[104]=(byte)i; if(sha1_32(buf)==-1605865820){break;} }
+ for(i=-128;i<-124;i++){ buf[105]=(byte)i; if(sha1_32(buf)==-1725725127){break;} }
+ for(i=88;i<91;i++){ buf[106]=(byte)i; if(sha1_32(buf)==-1831143070){break;} }
+ for(i=25;i<35;i++){ buf[107]=(byte)i; if(sha1_32(buf)==2127049197){break;} }
+ for(i=-58;i<-45;i++){ buf[108]=(byte)i; if(sha1_32(buf)==1033855386){break;} }
+ for(i=25;i<36;i++){ buf[109]=(byte)i; if(sha1_32(buf)==1101891401){break;} }
+ for(i=-87;i<-77;i++){ buf[110]=(byte)i; if(sha1_32(buf)==-1060828279){break;} }
+ for(i=-93;i<-81;i++){ buf[111]=(byte)i; if(sha1_32(buf)==-318880590){break;} }
+ for(i=-2;i<8;i++){ buf[112]=(byte)i; if(sha1_32(buf)==1445326750){break;} }
+ for(i=-38;i<-18;i++){ buf[113]=(byte)i; if(sha1_32(buf)==-1195367210){break;} }
+ for(i=-114;i<-105;i++){ buf[114]=(byte)i; if(sha1_32(buf)==169563563){break;} }
+ for(i=109;i<121;i++){ buf[115]=(byte)i; if(sha1_32(buf)==1031241834){break;} }
+ for(i=70;i<81;i++){ buf[116]=(byte)i; if(sha1_32(buf)==-239126190){break;} }
+ for(i=-128;i<-115;i++){ buf[117]=(byte)i; if(sha1_32(buf)==974906213){break;} }
+ for(i=7;i<11;i++){ buf[118]=(byte)i; if(sha1_32(buf)==1496850618){break;} }
+ for(i=23;i<33;i++){ buf[119]=(byte)i; if(sha1_32(buf)==-1458286737){break;} }
+ for(i=-49;i<-36;i++){ buf[120]=(byte)i; if(sha1_32(buf)==-1473069717){break;} }
+ for(i=111;i<126;i++){ buf[121]=(byte)i; if(sha1_32(buf)==109690542){break;} }
+ for(i=-45;i<-23;i++){ buf[122]=(byte)i; if(sha1_32(buf)==-1058925538){break;} }
+ for(i=-111;i<-109;i++){ buf[123]=(byte)i; if(sha1_32(buf)==1199505338){break;} }
+ for(i=110;i<124;i++){ buf[124]=(byte)i; if(sha1_32(buf)==-1940211235){break;} }
+ for(i=-84;i<-57;i++){ buf[125]=(byte)i; if(sha1_32(buf)==-432978110){break;} }
+ for(i=-125;i<-116;i++){ buf[126]=(byte)i; if(sha1_32(buf)==-153329399){break;} }
+ for(i=13;i<30;i++){ buf[127]=(byte)i; if(sha1_32(buf)==1467074174){break;} }
+ for(i=-45;i<-29;i++){ buf[128]=(byte)i; if(sha1_32(buf)==-278697719){break;} }
+ for(i=-35;i<-12;i++){ buf[129]=(byte)i; if(sha1_32(buf)==1656754734){break;} }
+ for(i=-91;i<-69;i++){ buf[130]=(byte)i; if(sha1_32(buf)==-1323416232){break;} }
+ for(i=15;i<24;i++){ buf[131]=(byte)i; if(sha1_32(buf)==-1096397902){break;} }
+ for(i=-30;i<-11;i++){ buf[132]=(byte)i; if(sha1_32(buf)==-1286678485){break;} }
+ for(i=69;i<93;i++){ buf[133]=(byte)i; if(sha1_32(buf)==-1973746010){break;} }
+ for(i=42;i<55;i++){ buf[134]=(byte)i; if(sha1_32(buf)==-1296679301){break;} }
+ for(i=-16;i<-5;i++){ buf[135]=(byte)i; if(sha1_32(buf)==-1925750709){break;} }
+ for(i=31;i<36;i++){ buf[136]=(byte)i; if(sha1_32(buf)==899082178){break;} }
+ for(i=85;i<107;i++){ buf[137]=(byte)i; if(sha1_32(buf)==-435643029){break;} }
+ for(i=29;i<48;i++){ buf[138]=(byte)i; if(sha1_32(buf)==626619198){break;} }
+ for(i=-100;i<-96;i++){ buf[139]=(byte)i; if(sha1_32(buf)==58968932){break;} }
+ for(i=0;i<16;i++){ buf[140]=(byte)i; if(sha1_32(buf)==-1930712169){break;} }
+ for(i=8;i<25;i++){ buf[141]=(byte)i; if(sha1_32(buf)==-758683808){break;} }
+ for(i=110;i<113;i++){ buf[142]=(byte)i; if(sha1_32(buf)==-379398155){break;} }
+ for(i=77;i<99;i++){ buf[143]=(byte)i; if(sha1_32(buf)==437903521){break;} }
+ for(i=-128;i<-108;i++){ buf[144]=(byte)i; if(sha1_32(buf)==1797662536){break;} }
+ for(i=-113;i<-99;i++){ buf[145]=(byte)i; if(sha1_32(buf)==-1937279529){break;} }
+ for(i=-128;i<-117;i++){ buf[146]=(byte)i; if(sha1_32(buf)==-690261932){break;} }
+ for(i=74;i<88;i++){ buf[147]=(byte)i; if(sha1_32(buf)==666184370){break;} }
+ for(i=-49;i<-29;i++){ buf[148]=(byte)i; if(sha1_32(buf)==1989337912){break;} }
+ for(i=16;i<20;i++){ buf[149]=(byte)i; if(sha1_32(buf)==-248737939){break;} }
+ for(i=-121;i<-91;i++){ buf[150]=(byte)i; if(sha1_32(buf)==-2081144298){break;} }
+ for(i=-18;i<2;i++){ buf[151]=(byte)i; if(sha1_32(buf)==-1492738742){break;} }
+ for(i=31;i<55;i++){ buf[152]=(byte)i; if(sha1_32(buf)==1119239042){break;} }
+ for(i=92;i<95;i++){ buf[153]=(byte)i; if(sha1_32(buf)==2008794865){break;} }
+ for(i=20;i<34;i++){ buf[154]=(byte)i; if(sha1_32(buf)==1166286225){break;} }
+ for(i=-39;i<-21;i++){ buf[155]=(byte)i; if(sha1_32(buf)==-666739568){break;} }
+ for(i=92;i<109;i++){ buf[156]=(byte)i; if(sha1_32(buf)==-349799930){break;} }
+ for(i=-127;i<-117;i++){ buf[157]=(byte)i; if(sha1_32(buf)==2068002740){break;} }
+ for(i=90;i<105;i++){ buf[158]=(byte)i; if(sha1_32(buf)==1207735081){break;} }
+ for(i=112;i<128;i++){ buf[159]=(byte)i; if(sha1_32(buf)==-1590726377){break;} }
+ for(i=51;i<73;i++){ buf[160]=(byte)i; if(sha1_32(buf)==-1607476798){break;} }
+ for(i=14;i<21;i++){ buf[161]=(byte)i; if(sha1_32(buf)==-1928011671){break;} }
+ for(i=-66;i<-56;i++){ buf[162]=(byte)i; if(sha1_32(buf)==-304125475){break;} }
+ for(i=-104;i<-85;i++){ buf[163]=(byte)i; if(sha1_32(buf)==1824768089){break;} }
+ for(i=-121;i<-112;i++){ buf[164]=(byte)i; if(sha1_32(buf)==-91175453){break;} }
+ for(i=-100;i<-78;i++){ buf[165]=(byte)i; if(sha1_32(buf)==-711980811){break;} }
+ for(i=-78;i<-59;i++){ buf[166]=(byte)i; if(sha1_32(buf)==-1672897177){break;} }
+ for(i=-60;i<-49;i++){ buf[167]=(byte)i; if(sha1_32(buf)==1248334242){break;} }
+ for(i=-61;i<-36;i++){ buf[168]=(byte)i; if(sha1_32(buf)==2120400947){break;} }
+ for(i=-24;i<-15;i++){ buf[169]=(byte)i; if(sha1_32(buf)==-612828350){break;} }
+ for(i=26;i<42;i++){ buf[170]=(byte)i; if(sha1_32(buf)==-156999410){break;} }
+ for(i=-104;i<-82;i++){ buf[171]=(byte)i; if(sha1_32(buf)==978132079){break;} }
+ for(i=81;i<98;i++){ buf[172]=(byte)i; if(sha1_32(buf)==-288546842){break;} }
+ for(i=-79;i<-58;i++){ buf[173]=(byte)i; if(sha1_32(buf)==-419024619){break;} }
+ for(i=-80;i<-63;i++){ buf[174]=(byte)i; if(sha1_32(buf)==-1561430151){break;} }
+ for(i=-111;i<-93;i++){ buf[175]=(byte)i; if(sha1_32(buf)==-1528552391){break;} }
+ for(i=24;i<35;i++){ buf[176]=(byte)i; if(sha1_32(buf)==-564977503){break;} }
+ for(i=-65;i<-42;i++){ buf[177]=(byte)i; if(sha1_32(buf)==-1938509234){break;} }
+ for(i=-45;i<-29;i++){ buf[178]=(byte)i; if(sha1_32(buf)==1071273463){break;} }
+ for(i=-45;i<-32;i++){ buf[179]=(byte)i; if(sha1_32(buf)==67459){break;} }
+ for(i=96;i<108;i++){ buf[180]=(byte)i; if(sha1_32(buf)==-432051415){break;} }
+ for(i=-96;i<-86;i++){ buf[181]=(byte)i; if(sha1_32(buf)==1250796013){break;} }
+ for(i=59;i<84;i++){ buf[182]=(byte)i; if(sha1_32(buf)==1902093660){break;} }
+ for(i=-113;i<-98;i++){ buf[183]=(byte)i; if(sha1_32(buf)==-1768527220){break;} }
+ for(i=-61;i<-37;i++){ buf[184]=(byte)i; if(sha1_32(buf)==1913069923){break;} }
+ for(i=-14;i<0;i++){ buf[185]=(byte)i; if(sha1_32(buf)==-1439622388){break;} }
+ for(i=34;i<47;i++){ buf[186]=(byte)i; if(sha1_32(buf)==2122621928){break;} }
+ for(i=64;i<82;i++){ buf[187]=(byte)i; if(sha1_32(buf)==-1515565699){break;} }
+ for(i=-110;i<-95;i++){ buf[188]=(byte)i; if(sha1_32(buf)==-512538076){break;} }
+ for(i=87;i<111;i++){ buf[189]=(byte)i; if(sha1_32(buf)==2016601852){break;} }
+ for(i=-52;i<-34;i++){ buf[190]=(byte)i; if(sha1_32(buf)==1672395526){break;} }
+ for(i=76;i<98;i++){ buf[191]=(byte)i; if(sha1_32(buf)==1585801214){break;} }
+ for(i=-105;i<-86;i++){ buf[192]=(byte)i; if(sha1_32(buf)==-1849191234){break;} }
+ for(i=51;i<73;i++){ buf[193]=(byte)i; if(sha1_32(buf)==1411033217){break;} }
+ for(i=54;i<59;i++){ buf[194]=(byte)i; if(sha1_32(buf)==-871256954){break;} }
+ for(i=108;i<128;i++){ buf[195]=(byte)i; if(sha1_32(buf)==-1164651543){break;} }
+ for(i=-92;i<-75;i++){ buf[196]=(byte)i; if(sha1_32(buf)==1116207870){break;} }
+ for(i=21;i<41;i++){ buf[197]=(byte)i; if(sha1_32(buf)==423946517){break;} }
+ for(i=-30;i<-16;i++){ buf[198]=(byte)i; if(sha1_32(buf)==-947803117){break;} }
+ for(i=-128;i<-121;i++){ buf[199]=(byte)i; if(sha1_32(buf)==1580627496){break;} }
+ for(i=14;i<30;i++){ buf[200]=(byte)i; if(sha1_32(buf)==1558808886){break;} }
+ for(i=-10;i<14;i++){ buf[201]=(byte)i; if(sha1_32(buf)==-2126097587){break;} }
+ for(i=-110;i<-93;i++){ buf[202]=(byte)i; if(sha1_32(buf)==-1986801831){break;} }
+ for(i=26;i<40;i++){ buf[203]=(byte)i; if(sha1_32(buf)==-919266358){break;} }
+ for(i=-68;i<-59;i++){ buf[204]=(byte)i; if(sha1_32(buf)==452661707){break;} }
+ for(i=82;i<99;i++){ buf[205]=(byte)i; if(sha1_32(buf)==-925948851){break;} }
+ for(i=-114;i<-99;i++){ buf[206]=(byte)i; if(sha1_32(buf)==-1028354707){break;} }
+ for(i=88;i<101;i++){ buf[207]=(byte)i; if(sha1_32(buf)==-412063505){break;} }
+ for(i=-56;i<-37;i++){ buf[208]=(byte)i; if(sha1_32(buf)==-1972418711){break;} }
+ for(i=118;i<128;i++){ buf[209]=(byte)i; if(sha1_32(buf)==1123008594){break;} }
+ for(i=-26;i<-13;i++){ buf[210]=(byte)i; if(sha1_32(buf)==-1760617026){break;} }
+ for(i=-116;i<-104;i++){ buf[211]=(byte)i; if(sha1_32(buf)==-1718145480){break;} }
+ for(i=91;i<95;i++){ buf[212]=(byte)i; if(sha1_32(buf)==1387298611){break;} }
+ for(i=-35;i<-22;i++){ buf[213]=(byte)i; if(sha1_32(buf)==556446295){break;} }
+ for(i=-94;i<-85;i++){ buf[214]=(byte)i; if(sha1_32(buf)==-837185142){break;} }
+ for(i=77;i<98;i++){ buf[215]=(byte)i; if(sha1_32(buf)==-277643950){break;} }
+ for(i=99;i<115;i++){ buf[216]=(byte)i; if(sha1_32(buf)==-466185733){break;} }
+ for(i=-117;i<-93;i++){ buf[217]=(byte)i; if(sha1_32(buf)==1354090725){break;} }
+ for(i=39;i<53;i++){ buf[218]=(byte)i; if(sha1_32(buf)==-1854734879){break;} }
+ for(i=91;i<112;i++){ buf[219]=(byte)i; if(sha1_32(buf)==928357020){break;} }
+ for(i=-51;i<-27;i++){ buf[220]=(byte)i; if(sha1_32(buf)==-742927067){break;} }
+ for(i=38;i<46;i++){ buf[221]=(byte)i; if(sha1_32(buf)==-1429798175){break;} }
+ for(i=-128;i<-111;i++){ buf[222]=(byte)i; if(sha1_32(buf)==-877344282){break;} }
+ for(i=-69;i<-48;i++){ buf[223]=(byte)i; if(sha1_32(buf)==-1564147440){break;} }
+ for(i=78;i<101;i++){ buf[224]=(byte)i; if(sha1_32(buf)==678979988){break;} }
+ for(i=80;i<99;i++){ buf[225]=(byte)i; if(sha1_32(buf)==1324359560){break;} }
+ for(i=-75;i<-68;i++){ buf[226]=(byte)i; if(sha1_32(buf)==682854216){break;} }
+ for(i=95;i<115;i++){ buf[227]=(byte)i; if(sha1_32(buf)==-741283195){break;} }
+ for(i=-35;i<-14;i++){ buf[228]=(byte)i; if(sha1_32(buf)==1604862991){break;} }
+ for(i=69;i<89;i++){ buf[229]=(byte)i; if(sha1_32(buf)==-841993601){break;} }
+ for(i=81;i<90;i++){ buf[230]=(byte)i; if(sha1_32(buf)==159203675){break;} }
+ for(i=81;i<103;i++){ buf[231]=(byte)i; if(sha1_32(buf)==912242604){break;} }
+ for(i=83;i<105;i++){ buf[232]=(byte)i; if(sha1_32(buf)==-474265443){break;} }
+ for(i=54;i<64;i++){ buf[233]=(byte)i; if(sha1_32(buf)==2066392912){break;} }
+ for(i=124;i<128;i++){ buf[234]=(byte)i; if(sha1_32(buf)==-1017656305){break;} }
+ for(i=39;i<49;i++){ buf[235]=(byte)i; if(sha1_32(buf)==-1382015062){break;} }
+ for(i=112;i<128;i++){ buf[236]=(byte)i; if(sha1_32(buf)==284848878){break;} }
+ for(i=76;i<89;i++){ buf[237]=(byte)i; if(sha1_32(buf)==-1839977352){break;} }
+ for(i=104;i<119;i++){ buf[238]=(byte)i; if(sha1_32(buf)==650017843){break;} }
+ for(i=-37;i<-10;i++){ buf[239]=(byte)i; if(sha1_32(buf)==-1356486780){break;} }
+ for(i=-64;i<-39;i++){ buf[240]=(byte)i; if(sha1_32(buf)==-1351340717){break;} }
+ for(i=-98;i<-86;i++){ buf[241]=(byte)i; if(sha1_32(buf)==-1298866375){break;} }
+ for(i=-45;i<-30;i++){ buf[242]=(byte)i; if(sha1_32(buf)==-37536291){break;} }
+ for(i=-78;i<-60;i++){ buf[243]=(byte)i; if(sha1_32(buf)==-1910705733){break;} }
+ for(i=-91;i<-75;i++){ buf[244]=(byte)i; if(sha1_32(buf)==454818767){break;} }
+ for(i=92;i<101;i++){ buf[245]=(byte)i; if(sha1_32(buf)==-1761086398){break;} }
+ for(i=-26;i<-19;i++){ buf[246]=(byte)i; if(sha1_32(buf)==564353539){break;} }
+ for(i=61;i<88;i++){ buf[247]=(byte)i; if(sha1_32(buf)==-670906912){break;} }
+ for(i=-94;i<-77;i++){ buf[248]=(byte)i; if(sha1_32(buf)==-38748490){break;} }
+ for(i=-48;i<-19;i++){ buf[249]=(byte)i; if(sha1_32(buf)==-661594362){break;} }
+ for(i=95;i<124;i++){ buf[250]=(byte)i; if(sha1_32(buf)==-1620105028){break;} }
+ for(i=-59;i<-30;i++){ buf[251]=(byte)i; if(sha1_32(buf)==400131275){break;} }
+ for(i=-70;i<-53;i++){ buf[252]=(byte)i; if(sha1_32(buf)==1187113784){break;} }
+ for(i=48;i<63;i++){ buf[253]=(byte)i; if(sha1_32(buf)==-147782723){break;} }
+ for(i=52;i<80;i++){ buf[254]=(byte)i; if(sha1_32(buf)==611605929){break;} }
+ for(i=30;i<52;i++){ buf[255]=(byte)i; if(sha1_32(buf)==-1096372686){break;} }
+ for(i=6;i<26;i++){ buf[256]=(byte)i; if(sha1_32(buf)==980503281){break;} }
+ for(i=87;i<103;i++){ buf[257]=(byte)i; if(sha1_32(buf)==1650554610){break;} }
+ for(i=-20;i<-2;i++){ buf[258]=(byte)i; if(sha1_32(buf)==1851394002){break;} }
+ for(i=64;i<79;i++){ buf[259]=(byte)i; if(sha1_32(buf)==-607642789){break;} }
+ for(i=101;i<127;i++){ buf[260]=(byte)i; if(sha1_32(buf)==918466977){break;} }
+ for(i=78;i<96;i++){ buf[261]=(byte)i; if(sha1_32(buf)==-392880429){break;} }
+ for(i=115;i<128;i++){ buf[262]=(byte)i; if(sha1_32(buf)==1496144953){break;} }
+ for(i=0;i<27;i++){ buf[263]=(byte)i; if(sha1_32(buf)==-2053042576){break;} }
+ for(i=-128;i<-116;i++){ buf[264]=(byte)i; if(sha1_32(buf)==1971218718){break;} }
+ for(i=85;i<103;i++){ buf[265]=(byte)i; if(sha1_32(buf)==479713740){break;} }
+ for(i=100;i<112;i++){ buf[266]=(byte)i; if(sha1_32(buf)==1160941662){break;} }
+ for(i=118;i<128;i++){ buf[267]=(byte)i; if(sha1_32(buf)==1186931412){break;} }
+ for(i=-125;i<-96;i++){ buf[268]=(byte)i; if(sha1_32(buf)==-1469979439){break;} }
+ for(i=111;i<128;i++){ buf[269]=(byte)i; if(sha1_32(buf)==2034191812){break;} }
+ for(i=-40;i<-22;i++){ buf[270]=(byte)i; if(sha1_32(buf)==878910379){break;} }
+ for(i=72;i<79;i++){ buf[271]=(byte)i; if(sha1_32(buf)==-2075385547){break;} }
+ for(i=-87;i<-70;i++){ buf[272]=(byte)i; if(sha1_32(buf)==-21104088){break;} }
+ for(i=-124;i<-118;i++){ buf[273]=(byte)i; if(sha1_32(buf)==1129042336){break;} }
+ for(i=-11;i<15;i++){ buf[274]=(byte)i; if(sha1_32(buf)==1129042336){break;} }
+ for(i=-128;i<-106;i++){ buf[275]=(byte)i; if(sha1_32(buf)==2140387235){break;} }
+ for(i=-18;i<-5;i++){ buf[276]=(byte)i; if(sha1_32(buf)==1114446476){break;} }
+ for(i=7;i<32;i++){ buf[277]=(byte)i; if(sha1_32(buf)==1259466697){break;} }
+ for(i=66;i<90;i++){ buf[278]=(byte)i; if(sha1_32(buf)==-374459314){break;} }
+ for(i=-84;i<-63;i++){ buf[279]=(byte)i; if(sha1_32(buf)==-857582370){break;} }
+ for(i=-22;i<-4;i++){ buf[280]=(byte)i; if(sha1_32(buf)==1125289313){break;} }
+ for(i=-27;i<-5;i++){ buf[281]=(byte)i; if(sha1_32(buf)==-893056009){break;} }
+ for(i=-94;i<-74;i++){ buf[282]=(byte)i; if(sha1_32(buf)==-953033781){break;} }
+ for(i=45;i<69;i++){ buf[283]=(byte)i; if(sha1_32(buf)==-16136772){break;} }
+ for(i=-6;i<10;i++){ buf[284]=(byte)i; if(sha1_32(buf)==-1128589861){break;} }
+ for(i=1;i<14;i++){ buf[285]=(byte)i; if(sha1_32(buf)==1314361443){break;} }
+ for(i=-118;i<-116;i++){ buf[286]=(byte)i; if(sha1_32(buf)==-34086001){break;} }
+ for(i=-91;i<-85;i++){ buf[287]=(byte)i; if(sha1_32(buf)==-234580872){break;} }
+ for(i=30;i<41;i++){ buf[288]=(byte)i; if(sha1_32(buf)==1072812379){break;} }
+ for(i=-52;i<-38;i++){ buf[289]=(byte)i; if(sha1_32(buf)==953890063){break;} }
+ for(i=54;i<56;i++){ buf[290]=(byte)i; if(sha1_32(buf)==790255249){break;} }
+ for(i=48;i<67;i++){ buf[291]=(byte)i; if(sha1_32(buf)==-484875277){break;} }
+ for(i=0;i<20;i++){ buf[292]=(byte)i; if(sha1_32(buf)==-1711265318){break;} }
+ for(i=-97;i<-76;i++){ buf[293]=(byte)i; if(sha1_32(buf)==1393695998){break;} }
+ for(i=22;i<35;i++){ buf[294]=(byte)i; if(sha1_32(buf)==663277501){break;} }
+ for(i=-99;i<-79;i++){ buf[295]=(byte)i; if(sha1_32(buf)==-356185660){break;} }
+ for(i=-34;i<-20;i++){ buf[296]=(byte)i; if(sha1_32(buf)==1537118817){break;} }
+ for(i=97;i<115;i++){ buf[297]=(byte)i; if(sha1_32(buf)==-1092541999){break;} }
+ for(i=27;i<46;i++){ buf[298]=(byte)i; if(sha1_32(buf)==-1593594824){break;} }
+ for(i=-100;i<-97;i++){ buf[299]=(byte)i; if(sha1_32(buf)==850051461){break;} }
+ for(i=-6;i<22;i++){ buf[300]=(byte)i; if(sha1_32(buf)==2047324253){break;} }
+ for(i=-124;i<-107;i++){ buf[301]=(byte)i; if(sha1_32(buf)==1571322573){break;} }
+ for(i=-25;i<-3;i++){ buf[302]=(byte)i; if(sha1_32(buf)==-1027274720){break;} }
+ for(i=-19;i<7;i++){ buf[303]=(byte)i; if(sha1_32(buf)==592117518){break;} }
+ for(i=52;i<71;i++){ buf[304]=(byte)i; if(sha1_32(buf)==-68393443){break;} }
+ for(i=-13;i<8;i++){ buf[305]=(byte)i; if(sha1_32(buf)==-707101844){break;} }
+ for(i=-44;i<-17;i++){ buf[306]=(byte)i; if(sha1_32(buf)==1312586964){break;} }
+ for(i=4;i<21;i++){ buf[307]=(byte)i; if(sha1_32(buf)==523784224){break;} }
+ for(i=-63;i<-34;i++){ buf[308]=(byte)i; if(sha1_32(buf)==1476249222){break;} }
+ for(i=-20;i<11;i++){ buf[309]=(byte)i; if(sha1_32(buf)==-1860149091){break;} }
+ for(i=-114;i<-106;i++){ buf[310]=(byte)i; if(sha1_32(buf)==-613198311){break;} }
+ for(i=35;i<43;i++){ buf[311]=(byte)i; if(sha1_32(buf)==1707516305){break;} }
+ for(i=-24;i<-8;i++){ buf[312]=(byte)i; if(sha1_32(buf)==-468268738){break;} }
+ for(i=94;i<124;i++){ buf[313]=(byte)i; if(sha1_32(buf)==-1064857912){break;} }
+ for(i=57;i<67;i++){ buf[314]=(byte)i; if(sha1_32(buf)==838406798){break;} }
+ for(i=108;i<112;i++){ buf[315]=(byte)i; if(sha1_32(buf)==-543194426){break;} }
+ for(i=-91;i<-69;i++){ buf[316]=(byte)i; if(sha1_32(buf)==-367524160){break;} }
+ for(i=59;i<86;i++){ buf[317]=(byte)i; if(sha1_32(buf)==-1838535645){break;} }
+ for(i=22;i<32;i++){ buf[318]=(byte)i; if(sha1_32(buf)==1402831790){break;} }
+ for(i=-53;i<-40;i++){ buf[319]=(byte)i; if(sha1_32(buf)==490308331){break;} }
+ for(i=-42;i<-28;i++){ buf[320]=(byte)i; if(sha1_32(buf)==-1722100779){break;} }
+ for(i=-91;i<-81;i++){ buf[321]=(byte)i; if(sha1_32(buf)==1804548091){break;} }
+ for(i=66;i<86;i++){ buf[322]=(byte)i; if(sha1_32(buf)==451640460){break;} }
+ for(i=96;i<111;i++){ buf[323]=(byte)i; if(sha1_32(buf)==1889778150){break;} }
+ for(i=2;i<10;i++){ buf[324]=(byte)i; if(sha1_32(buf)==-1255141772){break;} }
+ for(i=62;i<88;i++){ buf[325]=(byte)i; if(sha1_32(buf)==-4376205){break;} }
+ for(i=106;i<123;i++){ buf[326]=(byte)i; if(sha1_32(buf)==638193107){break;} }
+ for(i=-47;i<-28;i++){ buf[327]=(byte)i; if(sha1_32(buf)==-1357190723){break;} }
+ for(i=107;i<128;i++){ buf[328]=(byte)i; if(sha1_32(buf)==-2139012486){break;} }
+ for(i=-95;i<-85;i++){ buf[329]=(byte)i; if(sha1_32(buf)==-546955760){break;} }
+ for(i=15;i<40;i++){ buf[330]=(byte)i; if(sha1_32(buf)==-1230577714){break;} }
+ for(i=-25;i<-11;i++){ buf[331]=(byte)i; if(sha1_32(buf)==842891665){break;} }
+ for(i=-90;i<-76;i++){ buf[332]=(byte)i; if(sha1_32(buf)==1205678965){break;} }
+ for(i=14;i<22;i++){ buf[333]=(byte)i; if(sha1_32(buf)==594891528){break;} }
+ for(i=-103;i<-89;i++){ buf[334]=(byte)i; if(sha1_32(buf)==1507230389){break;} }
+ for(i=-49;i<-28;i++){ buf[335]=(byte)i; if(sha1_32(buf)==2069176244){break;} }
+ for(i=-20;i<-2;i++){ buf[336]=(byte)i; if(sha1_32(buf)==-1199359738){break;} }
+ for(i=-34;i<-22;i++){ buf[337]=(byte)i; if(sha1_32(buf)==1655752607){break;} }
+ for(i=117;i<128;i++){ buf[338]=(byte)i; if(sha1_32(buf)==1068622523){break;} }
+ for(i=-128;i<-115;i++){ buf[339]=(byte)i; if(sha1_32(buf)==1237466334){break;} }
+ for(i=95;i<108;i++){ buf[340]=(byte)i; if(sha1_32(buf)==-2070524204){break;} }
+ for(i=-88;i<-66;i++){ buf[341]=(byte)i; if(sha1_32(buf)==1838242902){break;} }
+ for(i=-59;i<-37;i++){ buf[342]=(byte)i; if(sha1_32(buf)==-416540109){break;} }
+ for(i=-75;i<-51;i++){ buf[343]=(byte)i; if(sha1_32(buf)==1214569366){break;} }
+ for(i=-8;i<-2;i++){ buf[344]=(byte)i; if(sha1_32(buf)==-402529029){break;} }
+ for(i=-125;i<-117;i++){ buf[345]=(byte)i; if(sha1_32(buf)==-1706321781){break;} }
+ for(i=96;i<113;i++){ buf[346]=(byte)i; if(sha1_32(buf)==1227580521){break;} }
+ for(i=41;i<71;i++){ buf[347]=(byte)i; if(sha1_32(buf)==-680660883){break;} }
+ for(i=-49;i<-36;i++){ buf[348]=(byte)i; if(sha1_32(buf)==-246913339){break;} }
+ for(i=-40;i<-19;i++){ buf[349]=(byte)i; if(sha1_32(buf)==1807131122){break;} }
+ for(i=-128;i<-123;i++){ buf[350]=(byte)i; if(sha1_32(buf)==-1656076852){break;} }
+ for(i=-82;i<-68;i++){ buf[351]=(byte)i; if(sha1_32(buf)==1035782779){break;} }
+ for(i=-84;i<-62;i++){ buf[352]=(byte)i; if(sha1_32(buf)==1011885645){break;} }
+ for(i=52;i<77;i++){ buf[353]=(byte)i; if(sha1_32(buf)==-1878805351){break;} }
+ for(i=20;i<41;i++){ buf[354]=(byte)i; if(sha1_32(buf)==294664665){break;} }
+ for(i=14;i<33;i++){ buf[355]=(byte)i; if(sha1_32(buf)==1265208828){break;} }
+ for(i=26;i<49;i++){ buf[356]=(byte)i; if(sha1_32(buf)==-132924184){break;} }
+ for(i=-76;i<-75;i++){ buf[357]=(byte)i; if(sha1_32(buf)==145661272){break;} }
+ for(i=68;i<92;i++){ buf[358]=(byte)i; if(sha1_32(buf)==-82346936){break;} }
+ for(i=1;i<13;i++){ buf[359]=(byte)i; if(sha1_32(buf)==1000567120){break;} }
+ for(i=59;i<73;i++){ buf[360]=(byte)i; if(sha1_32(buf)==1533956752){break;} }
+ for(i=-71;i<-65;i++){ buf[361]=(byte)i; if(sha1_32(buf)==495558616){break;} }
+ for(i=-35;i<-20;i++){ buf[362]=(byte)i; if(sha1_32(buf)==-1715818295){break;} }
+ for(i=-34;i<-18;i++){ buf[363]=(byte)i; if(sha1_32(buf)==14073959){break;} }
+ for(i=-28;i<-13;i++){ buf[364]=(byte)i; if(sha1_32(buf)==1573883290){break;} }
+ for(i=-105;i<-104;i++){ buf[365]=(byte)i; if(sha1_32(buf)==-585970445){break;} }
+ for(i=-86;i<-66;i++){ buf[366]=(byte)i; if(sha1_32(buf)==-987338395){break;} }
+ for(i=-64;i<-50;i++){ buf[367]=(byte)i; if(sha1_32(buf)==2034795330){break;} }
+ for(i=102;i<106;i++){ buf[368]=(byte)i; if(sha1_32(buf)==-1613474416){break;} }
+ for(i=-27;i<-20;i++){ buf[369]=(byte)i; if(sha1_32(buf)==-1779628086){break;} }
+ for(i=103;i<123;i++){ buf[370]=(byte)i; if(sha1_32(buf)==1976932965){break;} }
+ for(i=79;i<94;i++){ buf[371]=(byte)i; if(sha1_32(buf)==-1948719853){break;} }
+ for(i=119;i<128;i++){ buf[372]=(byte)i; if(sha1_32(buf)==-1374405194){break;} }
+ for(i=-76;i<-69;i++){ buf[373]=(byte)i; if(sha1_32(buf)==-862636111){break;} }
+ for(i=78;i<84;i++){ buf[374]=(byte)i; if(sha1_32(buf)==-812855648){break;} }
+ for(i=-75;i<-56;i++){ buf[375]=(byte)i; if(sha1_32(buf)==-1996037292){break;} }
+ for(i=-26;i<-16;i++){ buf[376]=(byte)i; if(sha1_32(buf)==-1840939286){break;} }
+ for(i=44;i<53;i++){ buf[377]=(byte)i; if(sha1_32(buf)==567575584){break;} }
+ for(i=31;i<42;i++){ buf[378]=(byte)i; if(sha1_32(buf)==-1190477973){break;} }
+ for(i=115;i<128;i++){ buf[379]=(byte)i; if(sha1_32(buf)==1345234706){break;} }
+ for(i=97;i<123;i++){ buf[380]=(byte)i; if(sha1_32(buf)==20186104){break;} }
+ for(i=-27;i<2;i++){ buf[381]=(byte)i; if(sha1_32(buf)==-1486946345){break;} }
+ for(i=23;i<40;i++){ buf[382]=(byte)i; if(sha1_32(buf)==1612521806){break;} }
+ for(i=115;i<128;i++){ buf[383]=(byte)i; if(sha1_32(buf)==-184882490){break;} }
+ for(i=41;i<55;i++){ buf[384]=(byte)i; if(sha1_32(buf)==-2108670024){break;} }
+ for(i=65;i<86;i++){ buf[385]=(byte)i; if(sha1_32(buf)==-556868906){break;} }
+ for(i=-75;i<-72;i++){ buf[386]=(byte)i; if(sha1_32(buf)==-1010308642){break;} }
+ for(i=-50;i<-31;i++){ buf[387]=(byte)i; if(sha1_32(buf)==-1247439366){break;} }
+ for(i=-91;i<-85;i++){ buf[388]=(byte)i; if(sha1_32(buf)==-1924989854){break;} }
+ for(i=63;i<72;i++){ buf[389]=(byte)i; if(sha1_32(buf)==37003972){break;} }
+ for(i=91;i<116;i++){ buf[390]=(byte)i; if(sha1_32(buf)==885398110){break;} }
+ for(i=30;i<51;i++){ buf[391]=(byte)i; if(sha1_32(buf)==-948115201){break;} }
+ for(i=75;i<86;i++){ buf[392]=(byte)i; if(sha1_32(buf)==-364901515){break;} }
+ for(i=-93;i<-89;i++){ buf[393]=(byte)i; if(sha1_32(buf)==-395854831){break;} }
+ for(i=-119;i<-112;i++){ buf[394]=(byte)i; if(sha1_32(buf)==-135206100){break;} }
+ for(i=53;i<70;i++){ buf[395]=(byte)i; if(sha1_32(buf)==-764078604){break;} }
+ for(i=-17;i<-10;i++){ buf[396]=(byte)i; if(sha1_32(buf)==-607392502){break;} }
+ for(i=58;i<65;i++){ buf[397]=(byte)i; if(sha1_32(buf)==3819551){break;} }
+ for(i=-104;i<-89;i++){ buf[398]=(byte)i; if(sha1_32(buf)==-1574945729){break;} }
+ for(i=-66;i<-48;i++){ buf[399]=(byte)i; if(sha1_32(buf)==-7224264){break;} }
+ for(i=17;i<48;i++){ buf[400]=(byte)i; if(sha1_32(buf)==-358087190){break;} }
+ for(i=78;i<102;i++){ buf[401]=(byte)i; if(sha1_32(buf)==259054917){break;} }
+ for(i=14;i<34;i++){ buf[402]=(byte)i; if(sha1_32(buf)==-190221058){break;} }
+ for(i=-108;i<-93;i++){ buf[403]=(byte)i; if(sha1_32(buf)==-330111073){break;} }
+ for(i=-110;i<-102;i++){ buf[404]=(byte)i; if(sha1_32(buf)==430433900){break;} }
+ for(i=81;i<101;i++){ buf[405]=(byte)i; if(sha1_32(buf)==516727364){break;} }
+ for(i=92;i<118;i++){ buf[406]=(byte)i; if(sha1_32(buf)==-1633996953){break;} }
+ for(i=-58;i<-52;i++){ buf[407]=(byte)i; if(sha1_32(buf)==-591957143){break;} }
+ for(i=108;i<127;i++){ buf[408]=(byte)i; if(sha1_32(buf)==-351660749){break;} }
+ for(i=42;i<54;i++){ buf[409]=(byte)i; if(sha1_32(buf)==14209859){break;} }
+ for(i=120;i<128;i++){ buf[410]=(byte)i; if(sha1_32(buf)==-893902894){break;} }
+ for(i=-16;i<-6;i++){ buf[411]=(byte)i; if(sha1_32(buf)==-1385763406){break;} }
+ for(i=-72;i<-56;i++){ buf[412]=(byte)i; if(sha1_32(buf)==-1531911325){break;} }
+ for(i=27;i<41;i++){ buf[413]=(byte)i; if(sha1_32(buf)==204479397){break;} }
+ for(i=-7;i<8;i++){ buf[414]=(byte)i; if(sha1_32(buf)==204479397){break;} }
+ for(i=11;i<27;i++){ buf[415]=(byte)i; if(sha1_32(buf)==-556942100){break;} }
+ for(i=-38;i<-13;i++){ buf[416]=(byte)i; if(sha1_32(buf)==-1774924369){break;} }
+ for(i=98;i<116;i++){ buf[417]=(byte)i; if(sha1_32(buf)==-1167683531){break;} }
+ for(i=-31;i<-25;i++){ buf[418]=(byte)i; if(sha1_32(buf)==1950353857){break;} }
+ for(i=-48;i<-31;i++){ buf[419]=(byte)i; if(sha1_32(buf)==-1888548962){break;} }
+ for(i=-113;i<-98;i++){ buf[420]=(byte)i; if(sha1_32(buf)==1823066411){break;} }
+ for(i=-17;i<3;i++){ buf[421]=(byte)i; if(sha1_32(buf)==-1748400920){break;} }
+ for(i=89;i<108;i++){ buf[422]=(byte)i; if(sha1_32(buf)==407986305){break;} }
+ for(i=60;i<78;i++){ buf[423]=(byte)i; if(sha1_32(buf)==-1334283255){break;} }
+ for(i=-41;i<-21;i++){ buf[424]=(byte)i; if(sha1_32(buf)==-432799587){break;} }
+ for(i=125;i<128;i++){ buf[425]=(byte)i; if(sha1_32(buf)==-195901776){break;} }
+ for(i=12;i<25;i++){ buf[426]=(byte)i; if(sha1_32(buf)==685200940){break;} }
+ for(i=106;i<121;i++){ buf[427]=(byte)i; if(sha1_32(buf)==-1996608483){break;} }
+ for(i=-62;i<-50;i++){ buf[428]=(byte)i; if(sha1_32(buf)==-1455535287){break;} }
+ for(i=30;i<46;i++){ buf[429]=(byte)i; if(sha1_32(buf)==-869288527){break;} }
+ for(i=-120;i<-105;i++){ buf[430]=(byte)i; if(sha1_32(buf)==-669664662){break;} }
+ for(i=-56;i<-39;i++){ buf[431]=(byte)i; if(sha1_32(buf)==1903595001){break;} }
+ for(i=-76;i<-52;i++){ buf[432]=(byte)i; if(sha1_32(buf)==1013662947){break;} }
+ for(i=-99;i<-88;i++){ buf[433]=(byte)i; if(sha1_32(buf)==-917300652){break;} }
+ for(i=-19;i<2;i++){ buf[434]=(byte)i; if(sha1_32(buf)==879356283){break;} }
+ for(i=-56;i<-34;i++){ buf[435]=(byte)i; if(sha1_32(buf)==1978720203){break;} }
+ for(i=69;i<90;i++){ buf[436]=(byte)i; if(sha1_32(buf)==-2139968951){break;} }
+ for(i=-29;i<-25;i++){ buf[437]=(byte)i; if(sha1_32(buf)==-104954557){break;} }
+ for(i=72;i<82;i++){ buf[438]=(byte)i; if(sha1_32(buf)==2079551621){break;} }
+ for(i=-116;i<-93;i++){ buf[439]=(byte)i; if(sha1_32(buf)==1475363846){break;} }
+ for(i=-93;i<-74;i++){ buf[440]=(byte)i; if(sha1_32(buf)==-311724539){break;} }
+ for(i=-103;i<-88;i++){ buf[441]=(byte)i; if(sha1_32(buf)==685431772){break;} }
+ for(i=-95;i<-83;i++){ buf[442]=(byte)i; if(sha1_32(buf)==1530063202){break;} }
+ for(i=72;i<92;i++){ buf[443]=(byte)i; if(sha1_32(buf)==-798233024){break;} }
+ for(i=75;i<89;i++){ buf[444]=(byte)i; if(sha1_32(buf)==-1591338462){break;} }
+ for(i=70;i<87;i++){ buf[445]=(byte)i; if(sha1_32(buf)==-738032218){break;} }
+ for(i=20;i<47;i++){ buf[446]=(byte)i; if(sha1_32(buf)==688542031){break;} }
+ for(i=111;i<127;i++){ buf[447]=(byte)i; if(sha1_32(buf)==-959709611){break;} }
+ for(i=-54;i<-39;i++){ buf[448]=(byte)i; if(sha1_32(buf)==-1801285731){break;} }
+ for(i=37;i<51;i++){ buf[449]=(byte)i; if(sha1_32(buf)==991130777){break;} }
+ for(i=-73;i<-62;i++){ buf[450]=(byte)i; if(sha1_32(buf)==-1834496692){break;} }
+ for(i=39;i<44;i++){ buf[451]=(byte)i; if(sha1_32(buf)==1557188495){break;} }
+ for(i=-28;i<-12;i++){ buf[452]=(byte)i; if(sha1_32(buf)==-915894670){break;} }
+ for(i=68;i<98;i++){ buf[453]=(byte)i; if(sha1_32(buf)==-1793673395){break;} }
+ for(i=-125;i<-108;i++){ buf[454]=(byte)i; if(sha1_32(buf)==-79286146){break;} }
+ for(i=-46;i<-31;i++){ buf[455]=(byte)i; if(sha1_32(buf)==1977560074){break;} }
+ for(i=-91;i<-75;i++){ buf[456]=(byte)i; if(sha1_32(buf)==566371059){break;} }
+ for(i=-128;i<-120;i++){ buf[457]=(byte)i; if(sha1_32(buf)==1975325294){break;} }
+ for(i=-50;i<-27;i++){ buf[458]=(byte)i; if(sha1_32(buf)==2072241636){break;} }
+ for(i=54;i<69;i++){ buf[459]=(byte)i; if(sha1_32(buf)==139216427){break;} }
+ for(i=-62;i<-41;i++){ buf[460]=(byte)i; if(sha1_32(buf)==-215286768){break;} }
+ for(i=-118;i<-96;i++){ buf[461]=(byte)i; if(sha1_32(buf)==-1943298232){break;} }
+ for(i=111;i<127;i++){ buf[462]=(byte)i; if(sha1_32(buf)==642839792){break;} }
+ for(i=-108;i<-91;i++){ buf[463]=(byte)i; if(sha1_32(buf)==200964557){break;} }
+ for(i=70;i<96;i++){ buf[464]=(byte)i; if(sha1_32(buf)==2079965472){break;} }
+ for(i=-97;i<-83;i++){ buf[465]=(byte)i; if(sha1_32(buf)==181953489){break;} }
+ for(i=108;i<128;i++){ buf[466]=(byte)i; if(sha1_32(buf)==1001994411){break;} }
+ for(i=-127;i<-106;i++){ buf[467]=(byte)i; if(sha1_32(buf)==-330855638){break;} }
+ for(i=-40;i<-22;i++){ buf[468]=(byte)i; if(sha1_32(buf)==876756893){break;} }
+ for(i=-25;i<-2;i++){ buf[469]=(byte)i; if(sha1_32(buf)==201341209){break;} }
+ for(i=-8;i<7;i++){ buf[470]=(byte)i; if(sha1_32(buf)==2027818034){break;} }
+ for(i=115;i<127;i++){ buf[471]=(byte)i; if(sha1_32(buf)==1768691508){break;} }
+ for(i=31;i<43;i++){ buf[472]=(byte)i; if(sha1_32(buf)==955441519){break;} }
+ for(i=8;i<30;i++){ buf[473]=(byte)i; if(sha1_32(buf)==-2135985032){break;} }
+ for(i=-128;i<-125;i++){ buf[474]=(byte)i; if(sha1_32(buf)==1935969704){break;} }
+ for(i=-128;i<-110;i++){ buf[475]=(byte)i; if(sha1_32(buf)==1856686773){break;} }
+ for(i=17;i<22;i++){ buf[476]=(byte)i; if(sha1_32(buf)==-221402101){break;} }
+ for(i=63;i<70;i++){ buf[477]=(byte)i; if(sha1_32(buf)==2043086470){break;} }
+ for(i=84;i<106;i++){ buf[478]=(byte)i; if(sha1_32(buf)==-2132640603){break;} }
+ for(i=31;i<59;i++){ buf[479]=(byte)i; if(sha1_32(buf)==-1981482354){break;} }
+ for(i=-63;i<-51;i++){ buf[480]=(byte)i; if(sha1_32(buf)==-678103827){break;} }
+ for(i=-17;i<-4;i++){ buf[481]=(byte)i; if(sha1_32(buf)==-940476198){break;} }
+ for(i=-79;i<-64;i++){ buf[482]=(byte)i; if(sha1_32(buf)==-1566196481){break;} }
+ for(i=53;i<73;i++){ buf[483]=(byte)i; if(sha1_32(buf)==1358965502){break;} }
+ for(i=58;i<80;i++){ buf[484]=(byte)i; if(sha1_32(buf)==726991258){break;} }
+ for(i=32;i<41;i++){ buf[485]=(byte)i; if(sha1_32(buf)==-1649545190){break;} }
+ for(i=-53;i<-49;i++){ buf[486]=(byte)i; if(sha1_32(buf)==-526462117){break;} }
+ for(i=-113;i<-89;i++){ buf[487]=(byte)i; if(sha1_32(buf)==233187184){break;} }
+ for(i=45;i<55;i++){ buf[488]=(byte)i; if(sha1_32(buf)==785872432){break;} }
+ for(i=-79;i<-59;i++){ buf[489]=(byte)i; if(sha1_32(buf)==-1111336332){break;} }
+ for(i=24;i<29;i++){ buf[490]=(byte)i; if(sha1_32(buf)==-696537105){break;} }
+ for(i=-56;i<-36;i++){ buf[491]=(byte)i; if(sha1_32(buf)==-1685485274){break;} }
+ for(i=-81;i<-56;i++){ buf[492]=(byte)i; if(sha1_32(buf)==581276579){break;} }
+ for(i=109;i<113;i++){ buf[493]=(byte)i; if(sha1_32(buf)==2092531200){break;} }
+ for(i=68;i<78;i++){ buf[494]=(byte)i; if(sha1_32(buf)==-519802178){break;} }
+ for(i=45;i<75;i++){ buf[495]=(byte)i; if(sha1_32(buf)==2106646901){break;} }
+ for(i=-45;i<-31;i++){ buf[496]=(byte)i; if(sha1_32(buf)==1099608972){break;} }
+ for(i=-80;i<-59;i++){ buf[497]=(byte)i; if(sha1_32(buf)==409560157){break;} }
+ for(i=20;i<30;i++){ buf[498]=(byte)i; if(sha1_32(buf)==119462599){break;} }
+ for(i=-22;i<-12;i++){ buf[499]=(byte)i; if(sha1_32(buf)==349050918){break;} }
+ for(i=-120;i<-89;i++){ buf[500]=(byte)i; if(sha1_32(buf)==366549341){break;} }
+ for(i=-62;i<-34;i++){ buf[501]=(byte)i; if(sha1_32(buf)==-643676172){break;} }
+ for(i=-49;i<-33;i++){ buf[502]=(byte)i; if(sha1_32(buf)==-1313219791){break;} }
+ for(i=108;i<118;i++){ buf[503]=(byte)i; if(sha1_32(buf)==1904832630){break;} }
+ for(i=-112;i<-105;i++){ buf[504]=(byte)i; if(sha1_32(buf)==-1982591212){break;} }
+ for(i=74;i<99;i++){ buf[505]=(byte)i; if(sha1_32(buf)==1054996718){break;} }
+ for(i=-128;i<-115;i++){ buf[506]=(byte)i; if(sha1_32(buf)==-1913102251){break;} }
+ for(i=12;i<30;i++){ buf[507]=(byte)i; if(sha1_32(buf)==1191915664){break;} }
+ for(i=-54;i<-34;i++){ buf[508]=(byte)i; if(sha1_32(buf)==-579115866){break;} }
+ for(i=-113;i<-94;i++){ buf[509]=(byte)i; if(sha1_32(buf)==-174275785){break;} }
+ for(i=53;i<71;i++){ buf[510]=(byte)i; if(sha1_32(buf)==-195790777){break;} }
+ for(i=23;i<45;i++){ buf[511]=(byte)i; if(sha1_32(buf)==-1976910445){break;} }
+ for(i=-29;i<-12;i++){ buf[512]=(byte)i; if(sha1_32(buf)==-1112423069){break;} }
+ for(i=-128;i<-106;i++){ buf[513]=(byte)i; if(sha1_32(buf)==177142569){break;} }
+ for(i=-58;i<-45;i++){ buf[514]=(byte)i; if(sha1_32(buf)==-162519970){break;} }
+ for(i=-13;i<3;i++){ buf[515]=(byte)i; if(sha1_32(buf)==-386282464){break;} }
+ for(i=6;i<28;i++){ buf[516]=(byte)i; if(sha1_32(buf)==-1389176822){break;} }
+ for(i=67;i<70;i++){ buf[517]=(byte)i; if(sha1_32(buf)==1578604388){break;} }
+ for(i=-91;i<-79;i++){ buf[518]=(byte)i; if(sha1_32(buf)==-878082180){break;} }
+ for(i=18;i<47;i++){ buf[519]=(byte)i; if(sha1_32(buf)==-1274559119){break;} }
+ for(i=-124;i<-104;i++){ buf[520]=(byte)i; if(sha1_32(buf)==879721935){break;} }
+ for(i=29;i<39;i++){ buf[521]=(byte)i; if(sha1_32(buf)==-200251756){break;} }
+ for(i=-67;i<-58;i++){ buf[522]=(byte)i; if(sha1_32(buf)==-1232383084){break;} }
+ for(i=33;i<60;i++){ buf[523]=(byte)i; if(sha1_32(buf)==1221899561){break;} }
+ for(i=36;i<39;i++){ buf[524]=(byte)i; if(sha1_32(buf)==-1010830123){break;} }
+ for(i=13;i<28;i++){ buf[525]=(byte)i; if(sha1_32(buf)==451484026){break;} }
+ for(i=33;i<59;i++){ buf[526]=(byte)i; if(sha1_32(buf)==-1788018102){break;} }
+ for(i=-128;i<-113;i++){ buf[527]=(byte)i; if(sha1_32(buf)==-186855432){break;} }
+ for(i=-85;i<-75;i++){ buf[528]=(byte)i; if(sha1_32(buf)==1532942072){break;} }
+ for(i=-12;i<-2;i++){ buf[529]=(byte)i; if(sha1_32(buf)==-1845757566){break;} }
+ for(i=83;i<111;i++){ buf[530]=(byte)i; if(sha1_32(buf)==-930913013){break;} }
+ for(i=12;i<37;i++){ buf[531]=(byte)i; if(sha1_32(buf)==1342751644){break;} }
+ for(i=-63;i<-52;i++){ buf[532]=(byte)i; if(sha1_32(buf)==-1639150657){break;} }
+ for(i=-49;i<-26;i++){ buf[533]=(byte)i; if(sha1_32(buf)==608801705){break;} }
+ for(i=-15;i<5;i++){ buf[534]=(byte)i; if(sha1_32(buf)==1793985409){break;} }
+ for(i=83;i<86;i++){ buf[535]=(byte)i; if(sha1_32(buf)==-1033927222){break;} }
+ for(i=-123;i<-105;i++){ buf[536]=(byte)i; if(sha1_32(buf)==878173522){break;} }
+ for(i=96;i<108;i++){ buf[537]=(byte)i; if(sha1_32(buf)==1253842455){break;} }
+ for(i=-13;i<-1;i++){ buf[538]=(byte)i; if(sha1_32(buf)==795046903){break;} }
+ for(i=-77;i<-60;i++){ buf[539]=(byte)i; if(sha1_32(buf)==1156857137){break;} }
+ for(i=-2;i<22;i++){ buf[540]=(byte)i; if(sha1_32(buf)==1263230460){break;} }
+ for(i=109;i<124;i++){ buf[541]=(byte)i; if(sha1_32(buf)==-897403851){break;} }
+ for(i=-113;i<-99;i++){ buf[542]=(byte)i; if(sha1_32(buf)==223658738){break;} }
+ for(i=0;i<24;i++){ buf[543]=(byte)i; if(sha1_32(buf)==-1417952676){break;} }
+ for(i=33;i<38;i++){ buf[544]=(byte)i; if(sha1_32(buf)==-676589163){break;} }
+ for(i=86;i<106;i++){ buf[545]=(byte)i; if(sha1_32(buf)==-2020927457){break;} }
+ for(i=108;i<125;i++){ buf[546]=(byte)i; if(sha1_32(buf)==1251617083){break;} }
+ for(i=9;i<26;i++){ buf[547]=(byte)i; if(sha1_32(buf)==-20408469){break;} }
+ for(i=113;i<127;i++){ buf[548]=(byte)i; if(sha1_32(buf)==159898027){break;} }
+ for(i=-24;i<1;i++){ buf[549]=(byte)i; if(sha1_32(buf)==-1329782894){break;} }
+ for(i=-50;i<-37;i++){ buf[550]=(byte)i; if(sha1_32(buf)==898898013){break;} }
+ for(i=-79;i<-60;i++){ buf[551]=(byte)i; if(sha1_32(buf)==-755503574){break;} }
+ for(i=-10;i<0;i++){ buf[552]=(byte)i; if(sha1_32(buf)==-467204347){break;} }
+ for(i=63;i<86;i++){ buf[553]=(byte)i; if(sha1_32(buf)==573059061){break;} }
+ for(i=-48;i<-25;i++){ buf[554]=(byte)i; if(sha1_32(buf)==745373843){break;} }
+ for(i=112;i<127;i++){ buf[555]=(byte)i; if(sha1_32(buf)==1609365126){break;} }
+ for(i=-84;i<-71;i++){ buf[556]=(byte)i; if(sha1_32(buf)==1418453518){break;} }
+ for(i=23;i<32;i++){ buf[557]=(byte)i; if(sha1_32(buf)==857198589){break;} }
+ for(i=0;i<9;i++){ buf[558]=(byte)i; if(sha1_32(buf)==-1348459811){break;} }
+ for(i=62;i<80;i++){ buf[559]=(byte)i; if(sha1_32(buf)==660480683){break;} }
+ for(i=19;i<31;i++){ buf[560]=(byte)i; if(sha1_32(buf)==-896772715){break;} }
+ for(i=-84;i<-58;i++){ buf[561]=(byte)i; if(sha1_32(buf)==1977031129){break;} }
+ for(i=56;i<64;i++){ buf[562]=(byte)i; if(sha1_32(buf)==-1194284687){break;} }
+ for(i=18;i<43;i++){ buf[563]=(byte)i; if(sha1_32(buf)==2110770295){break;} }
+ for(i=112;i<125;i++){ buf[564]=(byte)i; if(sha1_32(buf)==-233491039){break;} }
+ for(i=-14;i<-3;i++){ buf[565]=(byte)i; if(sha1_32(buf)==-1898952953){break;} }
+ for(i=34;i<60;i++){ buf[566]=(byte)i; if(sha1_32(buf)==1743404484){break;} }
+ for(i=-52;i<-39;i++){ buf[567]=(byte)i; if(sha1_32(buf)==-435045967){break;} }
+ for(i=15;i<29;i++){ buf[568]=(byte)i; if(sha1_32(buf)==-2066594378){break;} }
+ for(i=15;i<30;i++){ buf[569]=(byte)i; if(sha1_32(buf)==626388871){break;} }
+ for(i=7;i<26;i++){ buf[570]=(byte)i; if(sha1_32(buf)==-1737815161){break;} }
+ for(i=102;i<127;i++){ buf[571]=(byte)i; if(sha1_32(buf)==-1486282144){break;} }
+ for(i=-7;i<20;i++){ buf[572]=(byte)i; if(sha1_32(buf)==998774394){break;} }
+ for(i=73;i<84;i++){ buf[573]=(byte)i; if(sha1_32(buf)==1327927363){break;} }
+ for(i=-59;i<-37;i++){ buf[574]=(byte)i; if(sha1_32(buf)==-443839776){break;} }
+ for(i=17;i<30;i++){ buf[575]=(byte)i; if(sha1_32(buf)==-128069953){break;} }
+ for(i=3;i<32;i++){ buf[576]=(byte)i; if(sha1_32(buf)==1316757945){break;} }
+ for(i=-2;i<8;i++){ buf[577]=(byte)i; if(sha1_32(buf)==-218484016){break;} }
+ for(i=-93;i<-70;i++){ buf[578]=(byte)i; if(sha1_32(buf)==-487746524){break;} }
+ for(i=50;i<68;i++){ buf[579]=(byte)i; if(sha1_32(buf)==291576577){break;} }
+ for(i=87;i<103;i++){ buf[580]=(byte)i; if(sha1_32(buf)==665830287){break;} }
+ for(i=-30;i<-24;i++){ buf[581]=(byte)i; if(sha1_32(buf)==-2416344){break;} }
+ for(i=-91;i<-90;i++){ buf[582]=(byte)i; if(sha1_32(buf)==-1550206709){break;} }
+ for(i=-107;i<-96;i++){ buf[583]=(byte)i; if(sha1_32(buf)==-1568181116){break;} }
+ for(i=-106;i<-99;i++){ buf[584]=(byte)i; if(sha1_32(buf)==379182954){break;} }
+ for(i=-100;i<-87;i++){ buf[585]=(byte)i; if(sha1_32(buf)==788739428){break;} }
+ for(i=-59;i<-56;i++){ buf[586]=(byte)i; if(sha1_32(buf)==-657128371){break;} }
+ for(i=17;i<26;i++){ buf[587]=(byte)i; if(sha1_32(buf)==154775182){break;} }
+ for(i=75;i<89;i++){ buf[588]=(byte)i; if(sha1_32(buf)==-370386978){break;} }
+ for(i=-45;i<-25;i++){ buf[589]=(byte)i; if(sha1_32(buf)==961210721){break;} }
+ for(i=83;i<113;i++){ buf[590]=(byte)i; if(sha1_32(buf)==-340399097){break;} }
+ for(i=24;i<30;i++){ buf[591]=(byte)i; if(sha1_32(buf)==25869226){break;} }
+ for(i=66;i<70;i++){ buf[592]=(byte)i; if(sha1_32(buf)==-249787740){break;} }
+ for(i=-93;i<-84;i++){ buf[593]=(byte)i; if(sha1_32(buf)==1347143724){break;} }
+ for(i=60;i<80;i++){ buf[594]=(byte)i; if(sha1_32(buf)==1079578087){break;} }
+ for(i=-39;i<-22;i++){ buf[595]=(byte)i; if(sha1_32(buf)==355067710){break;} }
+ for(i=-40;i<-21;i++){ buf[596]=(byte)i; if(sha1_32(buf)==-1834389777){break;} }
+ for(i=-125;i<-117;i++){ buf[597]=(byte)i; if(sha1_32(buf)==-1029900079){break;} }
+ for(i=-22;i<2;i++){ buf[598]=(byte)i; if(sha1_32(buf)==-179364529){break;} }
+ for(i=19;i<24;i++){ buf[599]=(byte)i; if(sha1_32(buf)==-1846645041){break;} }
+ for(i=-47;i<-30;i++){ buf[600]=(byte)i; if(sha1_32(buf)==549022614){break;} }
+ for(i=-110;i<-94;i++){ buf[601]=(byte)i; if(sha1_32(buf)==-848673440){break;} }
+ for(i=21;i<35;i++){ buf[602]=(byte)i; if(sha1_32(buf)==-944105573){break;} }
+ for(i=-128;i<-114;i++){ buf[603]=(byte)i; if(sha1_32(buf)==1313690893){break;} }
+ for(i=83;i<105;i++){ buf[604]=(byte)i; if(sha1_32(buf)==-157284586){break;} }
+ for(i=24;i<46;i++){ buf[605]=(byte)i; if(sha1_32(buf)==1733449225){break;} }
+ for(i=84;i<96;i++){ buf[606]=(byte)i; if(sha1_32(buf)==1197590025){break;} }
+ for(i=-64;i<-50;i++){ buf[607]=(byte)i; if(sha1_32(buf)==340267423){break;} }
+ for(i=-34;i<-27;i++){ buf[608]=(byte)i; if(sha1_32(buf)==-1173554552){break;} }
+ for(i=-9;i<19;i++){ buf[609]=(byte)i; if(sha1_32(buf)==2075513322){break;} }
+ for(i=-17;i<-4;i++){ buf[610]=(byte)i; if(sha1_32(buf)==2034416648){break;} }
+ for(i=-128;i<-122;i++){ buf[611]=(byte)i; if(sha1_32(buf)==541524852){break;} }
+ for(i=-88;i<-71;i++){ buf[612]=(byte)i; if(sha1_32(buf)==-1754369782){break;} }
+ for(i=-101;i<-82;i++){ buf[613]=(byte)i; if(sha1_32(buf)==-1803041651){break;} }
+ for(i=-75;i<-51;i++){ buf[614]=(byte)i; if(sha1_32(buf)==-943348773){break;} }
+ for(i=63;i<89;i++){ buf[615]=(byte)i; if(sha1_32(buf)==-2071407874){break;} }
+ for(i=-30;i<-8;i++){ buf[616]=(byte)i; if(sha1_32(buf)==1808867299){break;} }
+ for(i=-125;i<-119;i++){ buf[617]=(byte)i; if(sha1_32(buf)==-1440041197){break;} }
+ for(i=94;i<109;i++){ buf[618]=(byte)i; if(sha1_32(buf)==-1200532320){break;} }
+ for(i=49;i<72;i++){ buf[619]=(byte)i; if(sha1_32(buf)==-1385932669){break;} }
+ for(i=-59;i<-41;i++){ buf[620]=(byte)i; if(sha1_32(buf)==1540516078){break;} }
+ for(i=-113;i<-95;i++){ buf[621]=(byte)i; if(sha1_32(buf)==1855214844){break;} }
+ for(i=104;i<122;i++){ buf[622]=(byte)i; if(sha1_32(buf)==214455782){break;} }
+ for(i=-14;i<4;i++){ buf[623]=(byte)i; if(sha1_32(buf)==29116112){break;} }
+ for(i=-63;i<-50;i++){ buf[624]=(byte)i; if(sha1_32(buf)==-1652556241){break;} }
+ for(i=-88;i<-76;i++){ buf[625]=(byte)i; if(sha1_32(buf)==713711696){break;} }
+ for(i=-106;i<-90;i++){ buf[626]=(byte)i; if(sha1_32(buf)==213795053){break;} }
+ for(i=-128;i<-110;i++){ buf[627]=(byte)i; if(sha1_32(buf)==397966535){break;} }
+ for(i=49;i<63;i++){ buf[628]=(byte)i; if(sha1_32(buf)==-2019158569){break;} }
+ for(i=-120;i<-103;i++){ buf[629]=(byte)i; if(sha1_32(buf)==-144726280){break;} }
+ for(i=-105;i<-93;i++){ buf[630]=(byte)i; if(sha1_32(buf)==67499038){break;} }
+ for(i=95;i<102;i++){ buf[631]=(byte)i; if(sha1_32(buf)==-1397398566){break;} }
+ for(i=-110;i<-89;i++){ buf[632]=(byte)i; if(sha1_32(buf)==2025626571){break;} }
+ for(i=69;i<75;i++){ buf[633]=(byte)i; if(sha1_32(buf)==298497630){break;} }
+ for(i=107;i<115;i++){ buf[634]=(byte)i; if(sha1_32(buf)==1138203879){break;} }
+ for(i=-59;i<-31;i++){ buf[635]=(byte)i; if(sha1_32(buf)==1880019610){break;} }
+ for(i=-48;i<-22;i++){ buf[636]=(byte)i; if(sha1_32(buf)==-1092898254){break;} }
+ for(i=-92;i<-78;i++){ buf[637]=(byte)i; if(sha1_32(buf)==128732646){break;} }
+ for(i=-86;i<-64;i++){ buf[638]=(byte)i; if(sha1_32(buf)==528312898){break;} }
+ for(i=-111;i<-94;i++){ buf[639]=(byte)i; if(sha1_32(buf)==585638073){break;} }
+ for(i=-99;i<-94;i++){ buf[640]=(byte)i; if(sha1_32(buf)==-1855330627){break;} }
+ for(i=-57;i<-49;i++){ buf[641]=(byte)i; if(sha1_32(buf)==-96631636){break;} }
+ for(i=-22;i<6;i++){ buf[642]=(byte)i; if(sha1_32(buf)==2055388486){break;} }
+ for(i=31;i<34;i++){ buf[643]=(byte)i; if(sha1_32(buf)==-1717856187){break;} }
+ for(i=51;i<75;i++){ buf[644]=(byte)i; if(sha1_32(buf)==-1321616820){break;} }
+ for(i=41;i<62;i++){ buf[645]=(byte)i; if(sha1_32(buf)==380600492){break;} }
+ for(i=-32;i<-11;i++){ buf[646]=(byte)i; if(sha1_32(buf)==463784007){break;} }
+ for(i=-37;i<-13;i++){ buf[647]=(byte)i; if(sha1_32(buf)==414626999){break;} }
+ for(i=88;i<105;i++){ buf[648]=(byte)i; if(sha1_32(buf)==1751619324){break;} }
+ for(i=53;i<73;i++){ buf[649]=(byte)i; if(sha1_32(buf)==1088829749){break;} }
+ for(i=-47;i<-27;i++){ buf[650]=(byte)i; if(sha1_32(buf)==1632511163){break;} }
+ for(i=-13;i<8;i++){ buf[651]=(byte)i; if(sha1_32(buf)==485770442){break;} }
+ for(i=91;i<109;i++){ buf[652]=(byte)i; if(sha1_32(buf)==2052991226){break;} }
+ for(i=-28;i<-22;i++){ buf[653]=(byte)i; if(sha1_32(buf)==-1725682924){break;} }
+ for(i=-99;i<-97;i++){ buf[654]=(byte)i; if(sha1_32(buf)==1094930849){break;} }
+ for(i=83;i<84;i++){ buf[655]=(byte)i; if(sha1_32(buf)==-1393139995){break;} }
+ for(i=119;i<128;i++){ buf[656]=(byte)i; if(sha1_32(buf)==1566906247){break;} }
+ for(i=-21;i<3;i++){ buf[657]=(byte)i; if(sha1_32(buf)==1535623841){break;} }
+ for(i=-88;i<-60;i++){ buf[658]=(byte)i; if(sha1_32(buf)==1120344049){break;} }
+ for(i=96;i<112;i++){ buf[659]=(byte)i; if(sha1_32(buf)==1425629217){break;} }
+ for(i=-26;i<-9;i++){ buf[660]=(byte)i; if(sha1_32(buf)==-530421514){break;} }
+ for(i=-111;i<-89;i++){ buf[661]=(byte)i; if(sha1_32(buf)==-1520028357){break;} }
+ for(i=32;i<44;i++){ buf[662]=(byte)i; if(sha1_32(buf)==-781447009){break;} }
+ for(i=46;i<64;i++){ buf[663]=(byte)i; if(sha1_32(buf)==142207321){break;} }
+ for(i=-56;i<-51;i++){ buf[664]=(byte)i; if(sha1_32(buf)==-341740233){break;} }
+ for(i=-118;i<-97;i++){ buf[665]=(byte)i; if(sha1_32(buf)==1567028047){break;} }
+ for(i=-95;i<-85;i++){ buf[666]=(byte)i; if(sha1_32(buf)==-2145899528){break;} }
+ for(i=52;i<70;i++){ buf[667]=(byte)i; if(sha1_32(buf)==-1416943909){break;} }
+ for(i=-101;i<-77;i++){ buf[668]=(byte)i; if(sha1_32(buf)==-1698718607){break;} }
+ for(i=-101;i<-83;i++){ buf[669]=(byte)i; if(sha1_32(buf)==-918243681){break;} }
+ for(i=20;i<36;i++){ buf[670]=(byte)i; if(sha1_32(buf)==1645167050){break;} }
+ for(i=5;i<30;i++){ buf[671]=(byte)i; if(sha1_32(buf)==1548138058){break;} }
+ for(i=29;i<38;i++){ buf[672]=(byte)i; if(sha1_32(buf)==1756202878){break;} }
+ for(i=-111;i<-104;i++){ buf[673]=(byte)i; if(sha1_32(buf)==2009909019){break;} }
+ for(i=-2;i<24;i++){ buf[674]=(byte)i; if(sha1_32(buf)==-1203980403){break;} }
+ for(i=-74;i<-50;i++){ buf[675]=(byte)i; if(sha1_32(buf)==1162551501){break;} }
+ for(i=-21;i<-3;i++){ buf[676]=(byte)i; if(sha1_32(buf)==-909848995){break;} }
+ for(i=-103;i<-91;i++){ buf[677]=(byte)i; if(sha1_32(buf)==-295014814){break;} }
+ for(i=-82;i<-66;i++){ buf[678]=(byte)i; if(sha1_32(buf)==1787004307){break;} }
+ for(i=-45;i<-41;i++){ buf[679]=(byte)i; if(sha1_32(buf)==-329608339){break;} }
+ for(i=35;i<47;i++){ buf[680]=(byte)i; if(sha1_32(buf)==139506064){break;} }
+ for(i=104;i<123;i++){ buf[681]=(byte)i; if(sha1_32(buf)==1307856062){break;} }
+ for(i=-61;i<-40;i++){ buf[682]=(byte)i; if(sha1_32(buf)==-1482623048){break;} }
+ for(i=-127;i<-103;i++){ buf[683]=(byte)i; if(sha1_32(buf)==-2127291098){break;} }
+ for(i=-49;i<-40;i++){ buf[684]=(byte)i; if(sha1_32(buf)==2090869989){break;} }
+ for(i=-72;i<-67;i++){ buf[685]=(byte)i; if(sha1_32(buf)==113503783){break;} }
+ for(i=94;i<101;i++){ buf[686]=(byte)i; if(sha1_32(buf)==1339263195){break;} }
+ for(i=78;i<100;i++){ buf[687]=(byte)i; if(sha1_32(buf)==725052528){break;} }
+ for(i=66;i<86;i++){ buf[688]=(byte)i; if(sha1_32(buf)==1359805586){break;} }
+ for(i=106;i<124;i++){ buf[689]=(byte)i; if(sha1_32(buf)==1351459575){break;} }
+ for(i=22;i<34;i++){ buf[690]=(byte)i; if(sha1_32(buf)==444152056){break;} }
+ for(i=118;i<128;i++){ buf[691]=(byte)i; if(sha1_32(buf)==247015488){break;} }
+ for(i=32;i<49;i++){ buf[692]=(byte)i; if(sha1_32(buf)==-856203820){break;} }
+ for(i=-112;i<-86;i++){ buf[693]=(byte)i; if(sha1_32(buf)==-1830125956){break;} }
+ for(i=62;i<79;i++){ buf[694]=(byte)i; if(sha1_32(buf)==907528162){break;} }
+ for(i=-50;i<-26;i++){ buf[695]=(byte)i; if(sha1_32(buf)==-294535997){break;} }
+ for(i=64;i<77;i++){ buf[696]=(byte)i; if(sha1_32(buf)==-1904719800){break;} }
+ for(i=77;i<82;i++){ buf[697]=(byte)i; if(sha1_32(buf)==447685988){break;} }
+ for(i=96;i<125;i++){ buf[698]=(byte)i; if(sha1_32(buf)==-364991408){break;} }
+ for(i=-76;i<-51;i++){ buf[699]=(byte)i; if(sha1_32(buf)==-423035843){break;} }
+ for(i=0;i<20;i++){ buf[700]=(byte)i; if(sha1_32(buf)==1512523132){break;} }
+ for(i=18;i<42;i++){ buf[701]=(byte)i; if(sha1_32(buf)==-1458206428){break;} }
+ for(i=-121;i<-113;i++){ buf[702]=(byte)i; if(sha1_32(buf)==1609129112){break;} }
+ for(i=63;i<79;i++){ buf[703]=(byte)i; if(sha1_32(buf)==-1178789951){break;} }
+ for(i=-12;i<12;i++){ buf[704]=(byte)i; if(sha1_32(buf)==849948816){break;} }
+ for(i=77;i<85;i++){ buf[705]=(byte)i; if(sha1_32(buf)==-645764798){break;} }
+ for(i=79;i<96;i++){ buf[706]=(byte)i; if(sha1_32(buf)==46713417){break;} }
+ for(i=-16;i<-1;i++){ buf[707]=(byte)i; if(sha1_32(buf)==1373343269){break;} }
+ for(i=70;i<84;i++){ buf[708]=(byte)i; if(sha1_32(buf)==-1783328425){break;} }
+ for(i=-90;i<-75;i++){ buf[709]=(byte)i; if(sha1_32(buf)==1744787182){break;} }
+ for(i=-3;i<2;i++){ buf[710]=(byte)i; if(sha1_32(buf)==-434075287){break;} }
+ for(i=-19;i<-1;i++){ buf[711]=(byte)i; if(sha1_32(buf)==-743677482){break;} }
+ for(i=-44;i<-36;i++){ buf[712]=(byte)i; if(sha1_32(buf)==-1434721798){break;} }
+ for(i=35;i<47;i++){ buf[713]=(byte)i; if(sha1_32(buf)==1372636841){break;} }
+ for(i=33;i<56;i++){ buf[714]=(byte)i; if(sha1_32(buf)==134908921){break;} }
+ for(i=0;i<25;i++){ buf[715]=(byte)i; if(sha1_32(buf)==-1425117377){break;} }
+ for(i=-71;i<-45;i++){ buf[716]=(byte)i; if(sha1_32(buf)==37612467){break;} }
+ for(i=0;i<14;i++){ buf[717]=(byte)i; if(sha1_32(buf)==1092914423){break;} }
+ for(i=17;i<38;i++){ buf[718]=(byte)i; if(sha1_32(buf)==2081624597){break;} }
+ for(i=-18;i<1;i++){ buf[719]=(byte)i; if(sha1_32(buf)==-1795372506){break;} }
+ for(i=-12;i<7;i++){ buf[720]=(byte)i; if(sha1_32(buf)==181328001){break;} }
+ for(i=43;i<69;i++){ buf[721]=(byte)i; if(sha1_32(buf)==-227755996){break;} }
+ for(i=70;i<84;i++){ buf[722]=(byte)i; if(sha1_32(buf)==-781525293){break;} }
+ for(i=-119;i<-103;i++){ buf[723]=(byte)i; if(sha1_32(buf)==975446784){break;} }
+ for(i=-126;i<-122;i++){ buf[724]=(byte)i; if(sha1_32(buf)==-1692713104){break;} }
+ for(i=52;i<71;i++){ buf[725]=(byte)i; if(sha1_32(buf)==1964228484){break;} }
+ for(i=65;i<80;i++){ buf[726]=(byte)i; if(sha1_32(buf)==1728532594){break;} }
+ for(i=-21;i<4;i++){ buf[727]=(byte)i; if(sha1_32(buf)==1402146204){break;} }
+ for(i=11;i<22;i++){ buf[728]=(byte)i; if(sha1_32(buf)==1406220389){break;} }
+ for(i=-107;i<-80;i++){ buf[729]=(byte)i; if(sha1_32(buf)==266524750){break;} }
+ for(i=-27;i<-10;i++){ buf[730]=(byte)i; if(sha1_32(buf)==1815375407){break;} }
+ for(i=-120;i<-101;i++){ buf[731]=(byte)i; if(sha1_32(buf)==-517233939){break;} }
+ for(i=23;i<50;i++){ buf[732]=(byte)i; if(sha1_32(buf)==1974064725){break;} }
+ for(i=-58;i<-34;i++){ buf[733]=(byte)i; if(sha1_32(buf)==1707858413){break;} }
+ for(i=-33;i<-11;i++){ buf[734]=(byte)i; if(sha1_32(buf)==-726286900){break;} }
+ for(i=-105;i<-89;i++){ buf[735]=(byte)i; if(sha1_32(buf)==-1415892267){break;} }
+ for(i=68;i<84;i++){ buf[736]=(byte)i; if(sha1_32(buf)==1153087083){break;} }
+ for(i=-111;i<-102;i++){ buf[737]=(byte)i; if(sha1_32(buf)==-568815529){break;} }
+ for(i=58;i<79;i++){ buf[738]=(byte)i; if(sha1_32(buf)==1948618631){break;} }
+ for(i=-122;i<-92;i++){ buf[739]=(byte)i; if(sha1_32(buf)==386316160){break;} }
+ for(i=68;i<93;i++){ buf[740]=(byte)i; if(sha1_32(buf)==1433232993){break;} }
+ for(i=88;i<97;i++){ buf[741]=(byte)i; if(sha1_32(buf)==-1724592476){break;} }
+ for(i=-124;i<-101;i++){ buf[742]=(byte)i; if(sha1_32(buf)==-1885518718){break;} }
+ for(i=100;i<110;i++){ buf[743]=(byte)i; if(sha1_32(buf)==1171160744){break;} }
+ for(i=93;i<105;i++){ buf[744]=(byte)i; if(sha1_32(buf)==-1259700173){break;} }
+ for(i=-36;i<-24;i++){ buf[745]=(byte)i; if(sha1_32(buf)==2037650482){break;} }
+ for(i=-99;i<-83;i++){ buf[746]=(byte)i; if(sha1_32(buf)==1452249615){break;} }
+ for(i=-76;i<-68;i++){ buf[747]=(byte)i; if(sha1_32(buf)==1627442399){break;} }
+ for(i=-27;i<-11;i++){ buf[748]=(byte)i; if(sha1_32(buf)==-1725678750){break;} }
+ for(i=-51;i<-37;i++){ buf[749]=(byte)i; if(sha1_32(buf)==1038605706){break;} }
+ for(i=17;i<34;i++){ buf[750]=(byte)i; if(sha1_32(buf)==-1744394376){break;} }
+ for(i=-90;i<-81;i++){ buf[751]=(byte)i; if(sha1_32(buf)==-629030670){break;} }
+ for(i=81;i<99;i++){ buf[752]=(byte)i; if(sha1_32(buf)==-2014142248){break;} }
+ for(i=-127;i<-110;i++){ buf[753]=(byte)i; if(sha1_32(buf)==137715319){break;} }
+ for(i=-85;i<-59;i++){ buf[754]=(byte)i; if(sha1_32(buf)==948637425){break;} }
+ for(i=-116;i<-92;i++){ buf[755]=(byte)i; if(sha1_32(buf)==-1459448241){break;} }
+ for(i=8;i<34;i++){ buf[756]=(byte)i; if(sha1_32(buf)==-1349587498){break;} }
+ for(i=-13;i<-7;i++){ buf[757]=(byte)i; if(sha1_32(buf)==-1395406027){break;} }
+ for(i=-44;i<-17;i++){ buf[758]=(byte)i; if(sha1_32(buf)==1628658181){break;} }
+ for(i=107;i<120;i++){ buf[759]=(byte)i; if(sha1_32(buf)==-1367819193){break;} }
+ for(i=68;i<79;i++){ buf[760]=(byte)i; if(sha1_32(buf)==-1395283302){break;} }
+ for(i=46;i<61;i++){ buf[761]=(byte)i; if(sha1_32(buf)==1447952921){break;} }
+ for(i=-128;i<-124;i++){ buf[762]=(byte)i; if(sha1_32(buf)==391955570){break;} }
+ for(i=-35;i<-30;i++){ buf[763]=(byte)i; if(sha1_32(buf)==2092920607){break;} }
+ for(i=41;i<45;i++){ buf[764]=(byte)i; if(sha1_32(buf)==-691311509){break;} }
+ for(i=3;i<25;i++){ buf[765]=(byte)i; if(sha1_32(buf)==-1915228705){break;} }
+ for(i=-126;i<-119;i++){ buf[766]=(byte)i; if(sha1_32(buf)==868110865){break;} }
+ for(i=-128;i<-123;i++){ buf[767]=(byte)i; if(sha1_32(buf)==1655955767){break;} }
+ for(i=107;i<123;i++){ buf[768]=(byte)i; if(sha1_32(buf)==-453272344){break;} }
+ for(i=-17;i<-5;i++){ buf[769]=(byte)i; if(sha1_32(buf)==524375216){break;} }
+ for(i=-107;i<-77;i++){ buf[770]=(byte)i; if(sha1_32(buf)==2147243710){break;} }
+ for(i=-109;i<-102;i++){ buf[771]=(byte)i; if(sha1_32(buf)==177243785){break;} }
+ for(i=18;i<27;i++){ buf[772]=(byte)i; if(sha1_32(buf)==631382949){break;} }
+ for(i=8;i<19;i++){ buf[773]=(byte)i; if(sha1_32(buf)==1459113285){break;} }
+ for(i=4;i<18;i++){ buf[774]=(byte)i; if(sha1_32(buf)==2124474776){break;} }
+ for(i=-72;i<-55;i++){ buf[775]=(byte)i; if(sha1_32(buf)==643361482){break;} }
+ for(i=4;i<29;i++){ buf[776]=(byte)i; if(sha1_32(buf)==1197999722){break;} }
+ for(i=-16;i<0;i++){ buf[777]=(byte)i; if(sha1_32(buf)==1322838811){break;} }
+ for(i=-124;i<-102;i++){ buf[778]=(byte)i; if(sha1_32(buf)==1119825531){break;} }
+ for(i=-10;i<1;i++){ buf[779]=(byte)i; if(sha1_32(buf)==-1226604475){break;} }
+ for(i=105;i<109;i++){ buf[780]=(byte)i; if(sha1_32(buf)==-2055887862){break;} }
+ for(i=110;i<128;i++){ buf[781]=(byte)i; if(sha1_32(buf)==916357329){break;} }
+ for(i=-96;i<-80;i++){ buf[782]=(byte)i; if(sha1_32(buf)==-2021627297){break;} }
+ for(i=87;i<108;i++){ buf[783]=(byte)i; if(sha1_32(buf)==1734351796){break;} }
+ for(i=-57;i<-42;i++){ buf[784]=(byte)i; if(sha1_32(buf)==-1675303911){break;} }
+ for(i=106;i<118;i++){ buf[785]=(byte)i; if(sha1_32(buf)==763770855){break;} }
+ for(i=56;i<79;i++){ buf[786]=(byte)i; if(sha1_32(buf)==611411035){break;} }
+ for(i=-8;i<14;i++){ buf[787]=(byte)i; if(sha1_32(buf)==1297596589){break;} }
+ for(i=71;i<93;i++){ buf[788]=(byte)i; if(sha1_32(buf)==1199592792){break;} }
+ for(i=-77;i<-74;i++){ buf[789]=(byte)i; if(sha1_32(buf)==1860439248){break;} }
+ for(i=-32;i<-25;i++){ buf[790]=(byte)i; if(sha1_32(buf)==-1656914953){break;} }
+ for(i=-109;i<-93;i++){ buf[791]=(byte)i; if(sha1_32(buf)==-1045801392){break;} }
+ for(i=38;i<54;i++){ buf[792]=(byte)i; if(sha1_32(buf)==-939142239){break;} }
+ for(i=67;i<80;i++){ buf[793]=(byte)i; if(sha1_32(buf)==-592220772){break;} }
+ for(i=-84;i<-63;i++){ buf[794]=(byte)i; if(sha1_32(buf)==244963476){break;} }
+ for(i=64;i<88;i++){ buf[795]=(byte)i; if(sha1_32(buf)==1357131852){break;} }
+ for(i=26;i<37;i++){ buf[796]=(byte)i; if(sha1_32(buf)==292385884){break;} }
+ for(i=10;i<37;i++){ buf[797]=(byte)i; if(sha1_32(buf)==1173077528){break;} }
+ for(i=7;i<22;i++){ buf[798]=(byte)i; if(sha1_32(buf)==-1860280511){break;} }
+ for(i=74;i<87;i++){ buf[799]=(byte)i; if(sha1_32(buf)==1234551752){break;} }
+ for(i=-37;i<-33;i++){ buf[800]=(byte)i; if(sha1_32(buf)==1565011624){break;} }
+ for(i=-51;i<-44;i++){ buf[801]=(byte)i; if(sha1_32(buf)==115230938){break;} }
+ for(i=-11;i<8;i++){ buf[802]=(byte)i; if(sha1_32(buf)==78940504){break;} }
+ for(i=-128;i<-121;i++){ buf[803]=(byte)i; if(sha1_32(buf)==-523858117){break;} }
+ for(i=-67;i<-56;i++){ buf[804]=(byte)i; if(sha1_32(buf)==-2140679137){break;} }
+ for(i=15;i<36;i++){ buf[805]=(byte)i; if(sha1_32(buf)==-110009593){break;} }
+ for(i=-49;i<-39;i++){ buf[806]=(byte)i; if(sha1_32(buf)==1008324720){break;} }
+ for(i=-34;i<-8;i++){ buf[807]=(byte)i; if(sha1_32(buf)==1546593636){break;} }
+ for(i=76;i<84;i++){ buf[808]=(byte)i; if(sha1_32(buf)==-1565419260){break;} }
+ for(i=62;i<91;i++){ buf[809]=(byte)i; if(sha1_32(buf)==-2011395222){break;} }
+ for(i=68;i<85;i++){ buf[810]=(byte)i; if(sha1_32(buf)==-1185260134){break;} }
+ for(i=89;i<119;i++){ buf[811]=(byte)i; if(sha1_32(buf)==-1096284501){break;} }
+ for(i=-17;i<-4;i++){ buf[812]=(byte)i; if(sha1_32(buf)==-977362776){break;} }
+ for(i=-97;i<-78;i++){ buf[813]=(byte)i; if(sha1_32(buf)==1193530857){break;} }
+ for(i=99;i<113;i++){ buf[814]=(byte)i; if(sha1_32(buf)==237535532){break;} }
+ for(i=-115;i<-101;i++){ buf[815]=(byte)i; if(sha1_32(buf)==378184262){break;} }
+ for(i=-108;i<-101;i++){ buf[816]=(byte)i; if(sha1_32(buf)==80880527){break;} }
+ for(i=-3;i<13;i++){ buf[817]=(byte)i; if(sha1_32(buf)==-2028580865){break;} }
+ for(i=-37;i<-6;i++){ buf[818]=(byte)i; if(sha1_32(buf)==839528856){break;} }
+ for(i=-61;i<-40;i++){ buf[819]=(byte)i; if(sha1_32(buf)==-1626457839){break;} }
+ for(i=72;i<91;i++){ buf[820]=(byte)i; if(sha1_32(buf)==-1264199659){break;} }
+ for(i=107;i<111;i++){ buf[821]=(byte)i; if(sha1_32(buf)==527784336){break;} }
+ for(i=74;i<89;i++){ buf[822]=(byte)i; if(sha1_32(buf)==-1941335903){break;} }
+ for(i=21;i<50;i++){ buf[823]=(byte)i; if(sha1_32(buf)==602884404){break;} }
+ for(i=-10;i<7;i++){ buf[824]=(byte)i; if(sha1_32(buf)==-1368476295){break;} }
+ for(i=9;i<30;i++){ buf[825]=(byte)i; if(sha1_32(buf)==-1411529436){break;} }
+ for(i=13;i<29;i++){ buf[826]=(byte)i; if(sha1_32(buf)==1826296206){break;} }
+ for(i=108;i<119;i++){ buf[827]=(byte)i; if(sha1_32(buf)==-1917011736){break;} }
+ for(i=-43;i<-34;i++){ buf[828]=(byte)i; if(sha1_32(buf)==1698504771){break;} }
+ for(i=95;i<115;i++){ buf[829]=(byte)i; if(sha1_32(buf)==-1911819336){break;} }
+ for(i=-128;i<-120;i++){ buf[830]=(byte)i; if(sha1_32(buf)==1481552570){break;} }
+ for(i=45;i<60;i++){ buf[831]=(byte)i; if(sha1_32(buf)==135432920){break;} }
+ for(i=-120;i<-107;i++){ buf[832]=(byte)i; if(sha1_32(buf)==37682142){break;} }
+ for(i=-58;i<-53;i++){ buf[833]=(byte)i; if(sha1_32(buf)==113136438){break;} }
+ for(i=124;i<128;i++){ buf[834]=(byte)i; if(sha1_32(buf)==2108209209){break;} }
+ for(i=-128;i<-115;i++){ buf[835]=(byte)i; if(sha1_32(buf)==94743830){break;} }
+ for(i=-106;i<-90;i++){ buf[836]=(byte)i; if(sha1_32(buf)==144965547){break;} }
+ for(i=-1;i<18;i++){ buf[837]=(byte)i; if(sha1_32(buf)==-381643767){break;} }
+ for(i=-109;i<-85;i++){ buf[838]=(byte)i; if(sha1_32(buf)==1836297432){break;} }
+ for(i=25;i<44;i++){ buf[839]=(byte)i; if(sha1_32(buf)==-688107090){break;} }
+ for(i=-63;i<-43;i++){ buf[840]=(byte)i; if(sha1_32(buf)==-1857910988){break;} }
+ for(i=17;i<39;i++){ buf[841]=(byte)i; if(sha1_32(buf)==898973180){break;} }
+ for(i=-83;i<-75;i++){ buf[842]=(byte)i; if(sha1_32(buf)==-1643597747){break;} }
+ for(i=-72;i<-71;i++){ buf[843]=(byte)i; if(sha1_32(buf)==-400873898){break;} }
+ for(i=-7;i<19;i++){ buf[844]=(byte)i; if(sha1_32(buf)==-824262170){break;} }
+ for(i=-54;i<-35;i++){ buf[845]=(byte)i; if(sha1_32(buf)==-1831373087){break;} }
+ for(i=54;i<58;i++){ buf[846]=(byte)i; if(sha1_32(buf)==1560952458){break;} }
+ for(i=107;i<126;i++){ buf[847]=(byte)i; if(sha1_32(buf)==-673802350){break;} }
+ for(i=-76;i<-58;i++){ buf[848]=(byte)i; if(sha1_32(buf)==1797025068){break;} }
+ for(i=32;i<48;i++){ buf[849]=(byte)i; if(sha1_32(buf)==1043212102){break;} }
+ for(i=-115;i<-100;i++){ buf[850]=(byte)i; if(sha1_32(buf)==-136639024){break;} }
+ for(i=-91;i<-72;i++){ buf[851]=(byte)i; if(sha1_32(buf)==-815024478){break;} }
+ for(i=-59;i<-55;i++){ buf[852]=(byte)i; if(sha1_32(buf)==1501104040){break;} }
+ for(i=66;i<90;i++){ buf[853]=(byte)i; if(sha1_32(buf)==-1452597053){break;} }
+ for(i=35;i<43;i++){ buf[854]=(byte)i; if(sha1_32(buf)==-26113895){break;} }
+ for(i=-31;i<-18;i++){ buf[855]=(byte)i; if(sha1_32(buf)==1565679204){break;} }
+ for(i=120;i<127;i++){ buf[856]=(byte)i; if(sha1_32(buf)==-1789784920){break;} }
+ for(i=94;i<123;i++){ buf[857]=(byte)i; if(sha1_32(buf)==-1966581445){break;} }
+ for(i=-91;i<-73;i++){ buf[858]=(byte)i; if(sha1_32(buf)==571812384){break;} }
+ for(i=-46;i<-28;i++){ buf[859]=(byte)i; if(sha1_32(buf)==2097262707){break;} }
+ for(i=-126;i<-118;i++){ buf[860]=(byte)i; if(sha1_32(buf)==1707908826){break;} }
+ for(i=-128;i<-113;i++){ buf[861]=(byte)i; if(sha1_32(buf)==-1223965175){break;} }
+ for(i=-66;i<-55;i++){ buf[862]=(byte)i; if(sha1_32(buf)==2070331132){break;} }
+ for(i=-57;i<-31;i++){ buf[863]=(byte)i; if(sha1_32(buf)==1289222810){break;} }
+ for(i=20;i<28;i++){ buf[864]=(byte)i; if(sha1_32(buf)==505406414){break;} }
+ for(i=75;i<81;i++){ buf[865]=(byte)i; if(sha1_32(buf)==1845609766){break;} }
+ for(i=-89;i<-67;i++){ buf[866]=(byte)i; if(sha1_32(buf)==340884892){break;} }
+ for(i=-43;i<-26;i++){ buf[867]=(byte)i; if(sha1_32(buf)==994872654){break;} }
+ for(i=72;i<88;i++){ buf[868]=(byte)i; if(sha1_32(buf)==-1310315465){break;} }
+ for(i=-94;i<-71;i++){ buf[869]=(byte)i; if(sha1_32(buf)==-688640202){break;} }
+ for(i=-71;i<-41;i++){ buf[870]=(byte)i; if(sha1_32(buf)==-1315526506){break;} }
+ for(i=83;i<97;i++){ buf[871]=(byte)i; if(sha1_32(buf)==-1783387293){break;} }
+ for(i=73;i<98;i++){ buf[872]=(byte)i; if(sha1_32(buf)==1716894680){break;} }
+ for(i=-36;i<-26;i++){ buf[873]=(byte)i; if(sha1_32(buf)==-74449483){break;} }
+ for(i=72;i<91;i++){ buf[874]=(byte)i; if(sha1_32(buf)==-997270936){break;} }
+ for(i=-23;i<-4;i++){ buf[875]=(byte)i; if(sha1_32(buf)==369043212){break;} }
+ for(i=115;i<128;i++){ buf[876]=(byte)i; if(sha1_32(buf)==-1034014640){break;} }
+ for(i=76;i<83;i++){ buf[877]=(byte)i; if(sha1_32(buf)==-1176867968){break;} }
+ for(i=99;i<118;i++){ buf[878]=(byte)i; if(sha1_32(buf)==430298914){break;} }
+ for(i=-94;i<-75;i++){ buf[879]=(byte)i; if(sha1_32(buf)==-856936018){break;} }
+ for(i=-87;i<-75;i++){ buf[880]=(byte)i; if(sha1_32(buf)==-1419165926){break;} }
+ for(i=-15;i<9;i++){ buf[881]=(byte)i; if(sha1_32(buf)==307916524){break;} }
+ for(i=-102;i<-82;i++){ buf[882]=(byte)i; if(sha1_32(buf)==-1961619850){break;} }
+ for(i=114;i<115;i++){ buf[883]=(byte)i; if(sha1_32(buf)==1527837708){break;} }
+ for(i=91;i<99;i++){ buf[884]=(byte)i; if(sha1_32(buf)==815291783){break;} }
+ for(i=43;i<57;i++){ buf[885]=(byte)i; if(sha1_32(buf)==2034585130){break;} }
+ for(i=-18;i<-1;i++){ buf[886]=(byte)i; if(sha1_32(buf)==1584079423){break;} }
+ for(i=-20;i<-15;i++){ buf[887]=(byte)i; if(sha1_32(buf)==-1311221307){break;} }
+ for(i=33;i<53;i++){ buf[888]=(byte)i; if(sha1_32(buf)==719490539){break;} }
+ for(i=-57;i<-34;i++){ buf[889]=(byte)i; if(sha1_32(buf)==821954952){break;} }
+ for(i=-110;i<-91;i++){ buf[890]=(byte)i; if(sha1_32(buf)==481095898){break;} }
+ for(i=105;i<122;i++){ buf[891]=(byte)i; if(sha1_32(buf)==1658391745){break;} }
+ for(i=-121;i<-100;i++){ buf[892]=(byte)i; if(sha1_32(buf)==-1722293060){break;} }
+ for(i=-55;i<-28;i++){ buf[893]=(byte)i; if(sha1_32(buf)==-238178538){break;} }
+ for(i=-110;i<-96;i++){ buf[894]=(byte)i; if(sha1_32(buf)==885027063){break;} }
+ for(i=121;i<128;i++){ buf[895]=(byte)i; if(sha1_32(buf)==-466905847){break;} }
+ for(i=33;i<59;i++){ buf[896]=(byte)i; if(sha1_32(buf)==-1164207810){break;} }
+ for(i=-28;i<-11;i++){ buf[897]=(byte)i; if(sha1_32(buf)==1610640951){break;} }
+ for(i=43;i<60;i++){ buf[898]=(byte)i; if(sha1_32(buf)==-1278323121){break;} }
+ for(i=-99;i<-72;i++){ buf[899]=(byte)i; if(sha1_32(buf)==1182403858){break;} }
+ for(i=18;i<41;i++){ buf[900]=(byte)i; if(sha1_32(buf)==-526139178){break;} }
+ for(i=-60;i<-32;i++){ buf[901]=(byte)i; if(sha1_32(buf)==107416853){break;} }
+ for(i=81;i<96;i++){ buf[902]=(byte)i; if(sha1_32(buf)==-1695600896){break;} }
+ for(i=42;i<67;i++){ buf[903]=(byte)i; if(sha1_32(buf)==874171659){break;} }
+ for(i=29;i<42;i++){ buf[904]=(byte)i; if(sha1_32(buf)==1267019492){break;} }
+ for(i=-56;i<-46;i++){ buf[905]=(byte)i; if(sha1_32(buf)==927927371){break;} }
+ for(i=75;i<90;i++){ buf[906]=(byte)i; if(sha1_32(buf)==-1547541297){break;} }
+ for(i=-128;i<-110;i++){ buf[907]=(byte)i; if(sha1_32(buf)==471498148){break;} }
+ for(i=-58;i<-41;i++){ buf[908]=(byte)i; if(sha1_32(buf)==-1733192963){break;} }
+ for(i=-32;i<-14;i++){ buf[909]=(byte)i; if(sha1_32(buf)==-856388965){break;} }
+ for(i=88;i<106;i++){ buf[910]=(byte)i; if(sha1_32(buf)==-283238127){break;} }
+ for(i=121;i<128;i++){ buf[911]=(byte)i; if(sha1_32(buf)==126326673){break;} }
+ for(i=80;i<84;i++){ buf[912]=(byte)i; if(sha1_32(buf)==573167804){break;} }
+ for(i=8;i<27;i++){ buf[913]=(byte)i; if(sha1_32(buf)==-2102595088){break;} }
+ for(i=54;i<72;i++){ buf[914]=(byte)i; if(sha1_32(buf)==67404945){break;} }
+ for(i=-69;i<-53;i++){ buf[915]=(byte)i; if(sha1_32(buf)==-195164181){break;} }
+ for(i=-112;i<-84;i++){ buf[916]=(byte)i; if(sha1_32(buf)==1470777006){break;} }
+ for(i=99;i<112;i++){ buf[917]=(byte)i; if(sha1_32(buf)==416494345){break;} }
+ for(i=12;i<24;i++){ buf[918]=(byte)i; if(sha1_32(buf)==-1326679142){break;} }
+ for(i=-54;i<-30;i++){ buf[919]=(byte)i; if(sha1_32(buf)==977857457){break;} }
+ for(i=-128;i<-106;i++){ buf[920]=(byte)i; if(sha1_32(buf)==-358303429){break;} }
+ for(i=112;i<122;i++){ buf[921]=(byte)i; if(sha1_32(buf)==-431636437){break;} }
+ for(i=-86;i<-67;i++){ buf[922]=(byte)i; if(sha1_32(buf)==-1525867658){break;} }
+ for(i=17;i<29;i++){ buf[923]=(byte)i; if(sha1_32(buf)==1207429464){break;} }
+ for(i=30;i<54;i++){ buf[924]=(byte)i; if(sha1_32(buf)==-120169350){break;} }
+ for(i=93;i<111;i++){ buf[925]=(byte)i; if(sha1_32(buf)==-75466538){break;} }
+ for(i=-65;i<-54;i++){ buf[926]=(byte)i; if(sha1_32(buf)==1968532059){break;} }
+ for(i=99;i<104;i++){ buf[927]=(byte)i; if(sha1_32(buf)==932565443){break;} }
+ for(i=-11;i<-4;i++){ buf[928]=(byte)i; if(sha1_32(buf)==-1306672013){break;} }
+ for(i=-38;i<-16;i++){ buf[929]=(byte)i; if(sha1_32(buf)==1557137948){break;} }
+ for(i=109;i<128;i++){ buf[930]=(byte)i; if(sha1_32(buf)==284642009){break;} }
+ for(i=65;i<77;i++){ buf[931]=(byte)i; if(sha1_32(buf)==620148436){break;} }
+ for(i=-21;i<-4;i++){ buf[932]=(byte)i; if(sha1_32(buf)==-1408703341){break;} }
+ for(i=-107;i<-98;i++){ buf[933]=(byte)i; if(sha1_32(buf)==1759232259){break;} }
+ for(i=-128;i<-114;i++){ buf[934]=(byte)i; if(sha1_32(buf)==-1025753306){break;} }
+ for(i=124;i<128;i++){ buf[935]=(byte)i; if(sha1_32(buf)==-1326510494){break;} }
+ for(i=76;i<99;i++){ buf[936]=(byte)i; if(sha1_32(buf)==-1548984632){break;} }
+ for(i=-74;i<-58;i++){ buf[937]=(byte)i; if(sha1_32(buf)==86513613){break;} }
+ for(i=-18;i<-10;i++){ buf[938]=(byte)i; if(sha1_32(buf)==1845128628){break;} }
+ for(i=107;i<114;i++){ buf[939]=(byte)i; if(sha1_32(buf)==1792840457){break;} }
+ for(i=-39;i<-22;i++){ buf[940]=(byte)i; if(sha1_32(buf)==-1384029540){break;} }
+ for(i=66;i<83;i++){ buf[941]=(byte)i; if(sha1_32(buf)==1529895549){break;} }
+ for(i=-17;i<3;i++){ buf[942]=(byte)i; if(sha1_32(buf)==1554809948){break;} }
+ for(i=-75;i<-60;i++){ buf[943]=(byte)i; if(sha1_32(buf)==-758152888){break;} }
+ for(i=-68;i<-47;i++){ buf[944]=(byte)i; if(sha1_32(buf)==-1558136225){break;} }
+ for(i=-127;i<-101;i++){ buf[945]=(byte)i; if(sha1_32(buf)==1379384914){break;} }
+ for(i=75;i<90;i++){ buf[946]=(byte)i; if(sha1_32(buf)==1428043592){break;} }
+ for(i=-86;i<-77;i++){ buf[947]=(byte)i; if(sha1_32(buf)==1002029205){break;} }
+ for(i=36;i<50;i++){ buf[948]=(byte)i; if(sha1_32(buf)==-96562092){break;} }
+ for(i=104;i<118;i++){ buf[949]=(byte)i; if(sha1_32(buf)==-734735239){break;} }
+ for(i=43;i<49;i++){ buf[950]=(byte)i; if(sha1_32(buf)==-145077417){break;} }
+ for(i=-47;i<-19;i++){ buf[951]=(byte)i; if(sha1_32(buf)==-529812607){break;} }
+ for(i=12;i<17;i++){ buf[952]=(byte)i; if(sha1_32(buf)==2052314312){break;} }
+ for(i=50;i<55;i++){ buf[953]=(byte)i; if(sha1_32(buf)==153575512){break;} }
+ for(i=-54;i<-49;i++){ buf[954]=(byte)i; if(sha1_32(buf)==-952155822){break;} }
+ for(i=87;i<99;i++){ buf[955]=(byte)i; if(sha1_32(buf)==646110206){break;} }
+ for(i=-62;i<-55;i++){ buf[956]=(byte)i; if(sha1_32(buf)==-784506359){break;} }
+ for(i=-78;i<-53;i++){ buf[957]=(byte)i; if(sha1_32(buf)==339985000){break;} }
+ for(i=50;i<76;i++){ buf[958]=(byte)i; if(sha1_32(buf)==1620647443){break;} }
+ for(i=-80;i<-72;i++){ buf[959]=(byte)i; if(sha1_32(buf)==1031355063){break;} }
+ for(i=-36;i<-28;i++){ buf[960]=(byte)i; if(sha1_32(buf)==144558401){break;} }
+ for(i=101;i<111;i++){ buf[961]=(byte)i; if(sha1_32(buf)==1704436470){break;} }
+ for(i=45;i<63;i++){ buf[962]=(byte)i; if(sha1_32(buf)==1247075308){break;} }
+ for(i=-102;i<-99;i++){ buf[963]=(byte)i; if(sha1_32(buf)==1832818279){break;} }
+ for(i=82;i<92;i++){ buf[964]=(byte)i; if(sha1_32(buf)==1944066577){break;} }
+ for(i=70;i<81;i++){ buf[965]=(byte)i; if(sha1_32(buf)==1714062522){break;} }
+ for(i=-89;i<-68;i++){ buf[966]=(byte)i; if(sha1_32(buf)==-1725759160){break;} }
+ for(i=47;i<72;i++){ buf[967]=(byte)i; if(sha1_32(buf)==-748965551){break;} }
+ for(i=-122;i<-110;i++){ buf[968]=(byte)i; if(sha1_32(buf)==-143294668){break;} }
+ for(i=69;i<80;i++){ buf[969]=(byte)i; if(sha1_32(buf)==1378462731){break;} }
+ for(i=99;i<103;i++){ buf[970]=(byte)i; if(sha1_32(buf)==425588224){break;} }
+ for(i=-46;i<-32;i++){ buf[971]=(byte)i; if(sha1_32(buf)==-371641798){break;} }
+ for(i=62;i<80;i++){ buf[972]=(byte)i; if(sha1_32(buf)==-1183611133){break;} }
+ for(i=0;i<14;i++){ buf[973]=(byte)i; if(sha1_32(buf)==-84554665){break;} }
+ for(i=-116;i<-106;i++){ buf[974]=(byte)i; if(sha1_32(buf)==-1005930890){break;} }
+ for(i=70;i<93;i++){ buf[975]=(byte)i; if(sha1_32(buf)==-1245000117){break;} }
+ for(i=125;i<128;i++){ buf[976]=(byte)i; if(sha1_32(buf)==1906392477){break;} }
+ for(i=-99;i<-78;i++){ buf[977]=(byte)i; if(sha1_32(buf)==-1802505862){break;} }
+ for(i=-51;i<-49;i++){ buf[978]=(byte)i; if(sha1_32(buf)==-1659219411){break;} }
+ for(i=-5;i<9;i++){ buf[979]=(byte)i; if(sha1_32(buf)==-1659219411){break;} }
+ for(i=94;i<107;i++){ buf[980]=(byte)i; if(sha1_32(buf)==-2146343400){break;} }
+ for(i=35;i<51;i++){ buf[981]=(byte)i; if(sha1_32(buf)==1156626595){break;} }
+ for(i=-25;i<-3;i++){ buf[982]=(byte)i; if(sha1_32(buf)==1753977474){break;} }
+ for(i=80;i<101;i++){ buf[983]=(byte)i; if(sha1_32(buf)==-1772589788){break;} }
+ for(i=-106;i<-81;i++){ buf[984]=(byte)i; if(sha1_32(buf)==267991478){break;} }
+ for(i=-9;i<2;i++){ buf[985]=(byte)i; if(sha1_32(buf)==1104181110){break;} }
+ for(i=-98;i<-75;i++){ buf[986]=(byte)i; if(sha1_32(buf)==-1936861577){break;} }
+ for(i=70;i<80;i++){ buf[987]=(byte)i; if(sha1_32(buf)==-324977635){break;} }
+ for(i=-106;i<-97;i++){ buf[988]=(byte)i; if(sha1_32(buf)==1811841842){break;} }
+ for(i=-71;i<-61;i++){ buf[989]=(byte)i; if(sha1_32(buf)==642379136){break;} }
+ for(i=5;i<22;i++){ buf[990]=(byte)i; if(sha1_32(buf)==-441309002){break;} }
+ for(i=103;i<122;i++){ buf[991]=(byte)i; if(sha1_32(buf)==704723753){break;} }
+ for(i=36;i<63;i++){ buf[992]=(byte)i; if(sha1_32(buf)==-1920187613){break;} }
+ for(i=96;i<108;i++){ buf[993]=(byte)i; if(sha1_32(buf)==541448516){break;} }
+ for(i=78;i<97;i++){ buf[994]=(byte)i; if(sha1_32(buf)==-1349269229){break;} }
+ for(i=97;i<103;i++){ buf[995]=(byte)i; if(sha1_32(buf)==2048129999){break;} }
+ for(i=86;i<102;i++){ buf[996]=(byte)i; if(sha1_32(buf)==-2063243727){break;} }
+ for(i=100;i<119;i++){ buf[997]=(byte)i; if(sha1_32(buf)==396532166){break;} }
+ for(i=39;i<68;i++){ buf[998]=(byte)i; if(sha1_32(buf)==2059014792){break;} }
+ for(i=-18;i<-11;i++){ buf[999]=(byte)i; if(sha1_32(buf)==134609310){break;} }
+ for(i=4;i<8;i++){ buf[1000]=(byte)i; if(sha1_32(buf)==-1208767529){break;} }
+ for(i=-6;i<11;i++){ buf[1001]=(byte)i; if(sha1_32(buf)==1313557674){break;} }
+ for(i=34;i<57;i++){ buf[1002]=(byte)i; if(sha1_32(buf)==-841596712){break;} }
+ for(i=-64;i<-38;i++){ buf[1003]=(byte)i; if(sha1_32(buf)==-856106210){break;} }
+ for(i=30;i<51;i++){ buf[1004]=(byte)i; if(sha1_32(buf)==-1181012455){break;} }
+ for(i=112;i<128;i++){ buf[1005]=(byte)i; if(sha1_32(buf)==-1413033970){break;} }
+ for(i=14;i<23;i++){ buf[1006]=(byte)i; if(sha1_32(buf)==1184974230){break;} }
+ for(i=-54;i<-38;i++){ buf[1007]=(byte)i; if(sha1_32(buf)==-1238765615){break;} }
+ for(i=-78;i<-59;i++){ buf[1008]=(byte)i; if(sha1_32(buf)==29449057){break;} }
+ for(i=-93;i<-88;i++){ buf[1009]=(byte)i; if(sha1_32(buf)==-771371306){break;} }
+ for(i=-35;i<-16;i++){ buf[1010]=(byte)i; if(sha1_32(buf)==1113069500){break;} }
+ for(i=16;i<33;i++){ buf[1011]=(byte)i; if(sha1_32(buf)==-261511860){break;} }
+ for(i=-91;i<-84;i++){ buf[1012]=(byte)i; if(sha1_32(buf)==720978659){break;} }
+ for(i=-81;i<-67;i++){ buf[1013]=(byte)i; if(sha1_32(buf)==-500384335){break;} }
+ for(i=81;i<104;i++){ buf[1014]=(byte)i; if(sha1_32(buf)==-711305360){break;} }
+ for(i=-25;i<-15;i++){ buf[1015]=(byte)i; if(sha1_32(buf)==1725266424){break;} }
+ for(i=-9;i<10;i++){ buf[1016]=(byte)i; if(sha1_32(buf)==1480784421){break;} }
+ for(i=15;i<33;i++){ buf[1017]=(byte)i; if(sha1_32(buf)==-1103891438){break;} }
+ for(i=32;i<38;i++){ buf[1018]=(byte)i; if(sha1_32(buf)==1960825682){break;} }
+ for(i=-90;i<-64;i++){ buf[1019]=(byte)i; if(sha1_32(buf)==20498666){break;} }
+ for(i=54;i<73;i++){ buf[1020]=(byte)i; if(sha1_32(buf)==-2048604743){break;} }
+ for(i=48;i<67;i++){ buf[1021]=(byte)i; if(sha1_32(buf)==-1357516245){break;} }
+ for(i=78;i<99;i++){ buf[1022]=(byte)i; if(sha1_32(buf)==367358800){break;} }
+ for(i=-60;i<-41;i++){ buf[1023]=(byte)i; if(sha1_32(buf)==1953367031){break;} }
+ for(i=6;i<12;i++){ buf[1024]=(byte)i; if(sha1_32(buf)==-2010779026){break;} }
+ for(i=3;i<15;i++){ buf[1025]=(byte)i; if(sha1_32(buf)==284327842){break;} }
+ for(i=-13;i<15;i++){ buf[1026]=(byte)i; if(sha1_32(buf)==140114380){break;} }
+ for(i=-56;i<-50;i++){ buf[1027]=(byte)i; if(sha1_32(buf)==1692185449){break;} }
+ for(i=-128;i<-118;i++){ buf[1028]=(byte)i; if(sha1_32(buf)==-124128310){break;} }
+ for(i=38;i<54;i++){ buf[1029]=(byte)i; if(sha1_32(buf)==1450405700){break;} }
+ for(i=-29;i<-25;i++){ buf[1030]=(byte)i; if(sha1_32(buf)==940398738){break;} }
+ for(i=25;i<43;i++){ buf[1031]=(byte)i; if(sha1_32(buf)==-1110966174){break;} }
+ for(i=11;i<32;i++){ buf[1032]=(byte)i; if(sha1_32(buf)==-1313013260){break;} }
+ for(i=-114;i<-90;i++){ buf[1033]=(byte)i; if(sha1_32(buf)==1993807763){break;} }
+ for(i=27;i<37;i++){ buf[1034]=(byte)i; if(sha1_32(buf)==-1116439613){break;} }
+ for(i=39;i<55;i++){ buf[1035]=(byte)i; if(sha1_32(buf)==-810119510){break;} }
+ for(i=-79;i<-73;i++){ buf[1036]=(byte)i; if(sha1_32(buf)==239678987){break;} }
+ for(i=-75;i<-49;i++){ buf[1037]=(byte)i; if(sha1_32(buf)==2071242170){break;} }
+ for(i=82;i<91;i++){ buf[1038]=(byte)i; if(sha1_32(buf)==1904371303){break;} }
+ for(i=-115;i<-86;i++){ buf[1039]=(byte)i; if(sha1_32(buf)==883245962){break;} }
+ for(i=17;i<33;i++){ buf[1040]=(byte)i; if(sha1_32(buf)==2028389477){break;} }
+ for(i=-2;i<27;i++){ buf[1041]=(byte)i; if(sha1_32(buf)==-780205294){break;} }
+ for(i=-100;i<-81;i++){ buf[1042]=(byte)i; if(sha1_32(buf)==-1876009194){break;} }
+ for(i=-46;i<-27;i++){ buf[1043]=(byte)i; if(sha1_32(buf)==1086704606){break;} }
+ for(i=-44;i<-26;i++){ buf[1044]=(byte)i; if(sha1_32(buf)==-897879903){break;} }
+ for(i=25;i<45;i++){ buf[1045]=(byte)i; if(sha1_32(buf)==-1535146325){break;} }
+ for(i=98;i<110;i++){ buf[1046]=(byte)i; if(sha1_32(buf)==-2520714){break;} }
+ for(i=70;i<90;i++){ buf[1047]=(byte)i; if(sha1_32(buf)==1178031679){break;} }
+ for(i=53;i<72;i++){ buf[1048]=(byte)i; if(sha1_32(buf)==-168415495){break;} }
+ for(i=-114;i<-100;i++){ buf[1049]=(byte)i; if(sha1_32(buf)==-602617373){break;} }
+ for(i=3;i<8;i++){ buf[1050]=(byte)i; if(sha1_32(buf)==1724436035){break;} }
+ for(i=9;i<35;i++){ buf[1051]=(byte)i; if(sha1_32(buf)==693898549){break;} }
+ for(i=93;i<105;i++){ buf[1052]=(byte)i; if(sha1_32(buf)==-1007431740){break;} }
+ for(i=-84;i<-77;i++){ buf[1053]=(byte)i; if(sha1_32(buf)==-383292312){break;} }
+ for(i=55;i<84;i++){ buf[1054]=(byte)i; if(sha1_32(buf)==-2133547926){break;} }
+ for(i=94;i<116;i++){ buf[1055]=(byte)i; if(sha1_32(buf)==1437563134){break;} }
+ for(i=-18;i<2;i++){ buf[1056]=(byte)i; if(sha1_32(buf)==-1179841729){break;} }
+ for(i=-110;i<-91;i++){ buf[1057]=(byte)i; if(sha1_32(buf)==1412779384){break;} }
+ for(i=-58;i<-54;i++){ buf[1058]=(byte)i; if(sha1_32(buf)==-1881162203){break;} }
+ for(i=90;i<111;i++){ buf[1059]=(byte)i; if(sha1_32(buf)==-132187819){break;} }
+ for(i=85;i<93;i++){ buf[1060]=(byte)i; if(sha1_32(buf)==275676521){break;} }
+ for(i=35;i<56;i++){ buf[1061]=(byte)i; if(sha1_32(buf)==-766839842){break;} }
+ for(i=-92;i<-75;i++){ buf[1062]=(byte)i; if(sha1_32(buf)==639330734){break;} }
+ for(i=47;i<56;i++){ buf[1063]=(byte)i; if(sha1_32(buf)==-1339754457){break;} }
+ for(i=60;i<89;i++){ buf[1064]=(byte)i; if(sha1_32(buf)==-1178675486){break;} }
+ for(i=25;i<44;i++){ buf[1065]=(byte)i; if(sha1_32(buf)==-1361524986){break;} }
+ for(i=-72;i<-58;i++){ buf[1066]=(byte)i; if(sha1_32(buf)==903251803){break;} }
+ for(i=-59;i<-54;i++){ buf[1067]=(byte)i; if(sha1_32(buf)==1822439342){break;} }
+ for(i=76;i<101;i++){ buf[1068]=(byte)i; if(sha1_32(buf)==1852987489){break;} }
+ for(i=4;i<6;i++){ buf[1069]=(byte)i; if(sha1_32(buf)==488884552){break;} }
+ for(i=-57;i<-42;i++){ buf[1070]=(byte)i; if(sha1_32(buf)==-1526741004){break;} }
+ for(i=-23;i<-18;i++){ buf[1071]=(byte)i; if(sha1_32(buf)==768438125){break;} }
+ for(i=-27;i<-13;i++){ buf[1072]=(byte)i; if(sha1_32(buf)==1598112504){break;} }
+ for(i=-1;i<25;i++){ buf[1073]=(byte)i; if(sha1_32(buf)==-315067595){break;} }
+ for(i=-106;i<-95;i++){ buf[1074]=(byte)i; if(sha1_32(buf)==-1285640383){break;} }
+ for(i=104;i<116;i++){ buf[1075]=(byte)i; if(sha1_32(buf)==-1596754678){break;} }
+ for(i=90;i<105;i++){ buf[1076]=(byte)i; if(sha1_32(buf)==-7646790){break;} }
+ for(i=-41;i<-29;i++){ buf[1077]=(byte)i; if(sha1_32(buf)==-1168004879){break;} }
+ for(i=-86;i<-70;i++){ buf[1078]=(byte)i; if(sha1_32(buf)==1050868807){break;} }
+ for(i=-28;i<-1;i++){ buf[1079]=(byte)i; if(sha1_32(buf)==-914704599){break;} }
+ for(i=61;i<80;i++){ buf[1080]=(byte)i; if(sha1_32(buf)==1003969863){break;} }
+ for(i=35;i<50;i++){ buf[1081]=(byte)i; if(sha1_32(buf)==1546712866){break;} }
+ for(i=3;i<22;i++){ buf[1082]=(byte)i; if(sha1_32(buf)==-217542488){break;} }
+ for(i=34;i<46;i++){ buf[1083]=(byte)i; if(sha1_32(buf)==2020794014){break;} }
+ for(i=-33;i<-23;i++){ buf[1084]=(byte)i; if(sha1_32(buf)==-548982562){break;} }
+ for(i=33;i<43;i++){ buf[1085]=(byte)i; if(sha1_32(buf)==-55211800){break;} }
+ for(i=24;i<42;i++){ buf[1086]=(byte)i; if(sha1_32(buf)==-2080056533){break;} }
+ for(i=74;i<92;i++){ buf[1087]=(byte)i; if(sha1_32(buf)==1144786979){break;} }
+ for(i=-42;i<-26;i++){ buf[1088]=(byte)i; if(sha1_32(buf)==479601119){break;} }
+ for(i=11;i<34;i++){ buf[1089]=(byte)i; if(sha1_32(buf)==1120316542){break;} }
+ for(i=-75;i<-53;i++){ buf[1090]=(byte)i; if(sha1_32(buf)==837660676){break;} }
+ for(i=100;i<116;i++){ buf[1091]=(byte)i; if(sha1_32(buf)==-492648414){break;} }
+ for(i=-128;i<-108;i++){ buf[1092]=(byte)i; if(sha1_32(buf)==1659297975){break;} }
+ for(i=110;i<128;i++){ buf[1093]=(byte)i; if(sha1_32(buf)==1159350264){break;} }
+ for(i=-50;i<-47;i++){ buf[1094]=(byte)i; if(sha1_32(buf)==-1466271861){break;} }
+ for(i=79;i<102;i++){ buf[1095]=(byte)i; if(sha1_32(buf)==1812831354){break;} }
+ for(i=54;i<70;i++){ buf[1096]=(byte)i; if(sha1_32(buf)==17198510){break;} }
+ for(i=28;i<43;i++){ buf[1097]=(byte)i; if(sha1_32(buf)==-1188659218){break;} }
+ for(i=55;i<77;i++){ buf[1098]=(byte)i; if(sha1_32(buf)==-1983566871){break;} }
+ for(i=52;i<61;i++){ buf[1099]=(byte)i; if(sha1_32(buf)==1705508536){break;} }
+ for(i=-128;i<-118;i++){ buf[1100]=(byte)i; if(sha1_32(buf)==1622682726){break;} }
+ for(i=25;i<35;i++){ buf[1101]=(byte)i; if(sha1_32(buf)==-496087965){break;} }
+ for(i=-24;i<-16;i++){ buf[1102]=(byte)i; if(sha1_32(buf)==28983475){break;} }
+ for(i=-50;i<-42;i++){ buf[1103]=(byte)i; if(sha1_32(buf)==-167999423){break;} }
+ for(i=-60;i<-50;i++){ buf[1104]=(byte)i; if(sha1_32(buf)==-1818400423){break;} }
+ for(i=-84;i<-72;i++){ buf[1105]=(byte)i; if(sha1_32(buf)==-929007144){break;} }
+ for(i=-4;i<15;i++){ buf[1106]=(byte)i; if(sha1_32(buf)==-1403825961){break;} }
+ for(i=32;i<51;i++){ buf[1107]=(byte)i; if(sha1_32(buf)==-329397345){break;} }
+ for(i=-107;i<-92;i++){ buf[1108]=(byte)i; if(sha1_32(buf)==1611167336){break;} }
+ for(i=-13;i<7;i++){ buf[1109]=(byte)i; if(sha1_32(buf)==124607498){break;} }
+ for(i=-61;i<-34;i++){ buf[1110]=(byte)i; if(sha1_32(buf)==1463882986){break;} }
+ for(i=122;i<128;i++){ buf[1111]=(byte)i; if(sha1_32(buf)==-310252680){break;} }
+ for(i=-112;i<-100;i++){ buf[1112]=(byte)i; if(sha1_32(buf)==1961548815){break;} }
+ for(i=-102;i<-78;i++){ buf[1113]=(byte)i; if(sha1_32(buf)==144025429){break;} }
+ for(i=-118;i<-96;i++){ buf[1114]=(byte)i; if(sha1_32(buf)==653372603){break;} }
+ for(i=27;i<47;i++){ buf[1115]=(byte)i; if(sha1_32(buf)==-962834407){break;} }
+ for(i=-128;i<-111;i++){ buf[1116]=(byte)i; if(sha1_32(buf)==558685208){break;} }
+ for(i=27;i<52;i++){ buf[1117]=(byte)i; if(sha1_32(buf)==1878935292){break;} }
+ for(i=15;i<39;i++){ buf[1118]=(byte)i; if(sha1_32(buf)==705920388){break;} }
+ for(i=-105;i<-86;i++){ buf[1119]=(byte)i; if(sha1_32(buf)==-1142735278){break;} }
+ for(i=15;i<37;i++){ buf[1120]=(byte)i; if(sha1_32(buf)==-1252157501){break;} }
+ for(i=86;i<105;i++){ buf[1121]=(byte)i; if(sha1_32(buf)==1680321681){break;} }
+ for(i=55;i<74;i++){ buf[1122]=(byte)i; if(sha1_32(buf)==1954548368){break;} }
+ for(i=94;i<114;i++){ buf[1123]=(byte)i; if(sha1_32(buf)==-81329140){break;} }
+ for(i=43;i<58;i++){ buf[1124]=(byte)i; if(sha1_32(buf)==1823771724){break;} }
+ for(i=-4;i<10;i++){ buf[1125]=(byte)i; if(sha1_32(buf)==1121172861){break;} }
+ for(i=-46;i<-16;i++){ buf[1126]=(byte)i; if(sha1_32(buf)==1363081856){break;} }
+ for(i=-83;i<-72;i++){ buf[1127]=(byte)i; if(sha1_32(buf)==1825695681){break;} }
+ for(i=-17;i<6;i++){ buf[1128]=(byte)i; if(sha1_32(buf)==-1465585569){break;} }
+ for(i=70;i<92;i++){ buf[1129]=(byte)i; if(sha1_32(buf)==583667788){break;} }
+ for(i=-34;i<-21;i++){ buf[1130]=(byte)i; if(sha1_32(buf)==1381834554){break;} }
+ for(i=-76;i<-58;i++){ buf[1131]=(byte)i; if(sha1_32(buf)==1200053598){break;} }
+ for(i=72;i<86;i++){ buf[1132]=(byte)i; if(sha1_32(buf)==1068922464){break;} }
+ for(i=51;i<74;i++){ buf[1133]=(byte)i; if(sha1_32(buf)==-310365412){break;} }
+ for(i=77;i<91;i++){ buf[1134]=(byte)i; if(sha1_32(buf)==-713644632){break;} }
+ for(i=96;i<120;i++){ buf[1135]=(byte)i; if(sha1_32(buf)==-256526925){break;} }
+ for(i=-47;i<-33;i++){ buf[1136]=(byte)i; if(sha1_32(buf)==-265203639){break;} }
+ for(i=68;i<80;i++){ buf[1137]=(byte)i; if(sha1_32(buf)==1563467392){break;} }
+ for(i=-54;i<-37;i++){ buf[1138]=(byte)i; if(sha1_32(buf)==977129512){break;} }
+ for(i=50;i<74;i++){ buf[1139]=(byte)i; if(sha1_32(buf)==1439581789){break;} }
+ for(i=-67;i<-48;i++){ buf[1140]=(byte)i; if(sha1_32(buf)==636432058){break;} }
+ for(i=-56;i<-34;i++){ buf[1141]=(byte)i; if(sha1_32(buf)==744697460){break;} }
+ for(i=88;i<109;i++){ buf[1142]=(byte)i; if(sha1_32(buf)==-2017069391){break;} }
+ for(i=-76;i<-61;i++){ buf[1143]=(byte)i; if(sha1_32(buf)==1959780175){break;} }
+ for(i=6;i<21;i++){ buf[1144]=(byte)i; if(sha1_32(buf)==1436574439){break;} }
+ for(i=119;i<128;i++){ buf[1145]=(byte)i; if(sha1_32(buf)==-1001156931){break;} }
+ for(i=-9;i<10;i++){ buf[1146]=(byte)i; if(sha1_32(buf)==-1799952403){break;} }
+ for(i=95;i<104;i++){ buf[1147]=(byte)i; if(sha1_32(buf)==772054824){break;} }
+ for(i=94;i<105;i++){ buf[1148]=(byte)i; if(sha1_32(buf)==-1672494985){break;} }
+ for(i=-70;i<-53;i++){ buf[1149]=(byte)i; if(sha1_32(buf)==-1183571825){break;} }
+ for(i=-23;i<-7;i++){ buf[1150]=(byte)i; if(sha1_32(buf)==-733512329){break;} }
+ for(i=-21;i<-12;i++){ buf[1151]=(byte)i; if(sha1_32(buf)==2075190235){break;} }
+ for(i=-56;i<-49;i++){ buf[1152]=(byte)i; if(sha1_32(buf)==559440948){break;} }
+ for(i=-3;i<7;i++){ buf[1153]=(byte)i; if(sha1_32(buf)==970942501){break;} }
+ for(i=-128;i<-124;i++){ buf[1154]=(byte)i; if(sha1_32(buf)==1814883948){break;} }
+ for(i=-21;i<-7;i++){ buf[1155]=(byte)i; if(sha1_32(buf)==1385060363){break;} }
+ for(i=-103;i<-96;i++){ buf[1156]=(byte)i; if(sha1_32(buf)==1826960520){break;} }
+ for(i=-28;i<-7;i++){ buf[1157]=(byte)i; if(sha1_32(buf)==1109574936){break;} }
+ for(i=-18;i<-11;i++){ buf[1158]=(byte)i; if(sha1_32(buf)==768259716){break;} }
+ for(i=-18;i<-2;i++){ buf[1159]=(byte)i; if(sha1_32(buf)==675163084){break;} }
+ for(i=17;i<30;i++){ buf[1160]=(byte)i; if(sha1_32(buf)==-377695170){break;} }
+ for(i=-17;i<1;i++){ buf[1161]=(byte)i; if(sha1_32(buf)==-1113401053){break;} }
+ for(i=37;i<63;i++){ buf[1162]=(byte)i; if(sha1_32(buf)==1910440018){break;} }
+ for(i=79;i<102;i++){ buf[1163]=(byte)i; if(sha1_32(buf)==1198354966){break;} }
+ for(i=-112;i<-89;i++){ buf[1164]=(byte)i; if(sha1_32(buf)==-378345942){break;} }
+ for(i=-11;i<5;i++){ buf[1165]=(byte)i; if(sha1_32(buf)==2139530799){break;} }
+ for(i=97;i<119;i++){ buf[1166]=(byte)i; if(sha1_32(buf)==1538087189){break;} }
+ for(i=32;i<41;i++){ buf[1167]=(byte)i; if(sha1_32(buf)==672992213){break;} }
+ for(i=-110;i<-95;i++){ buf[1168]=(byte)i; if(sha1_32(buf)==-493534266){break;} }
+ for(i=-123;i<-117;i++){ buf[1169]=(byte)i; if(sha1_32(buf)==-930100102){break;} }
+ for(i=-99;i<-84;i++){ buf[1170]=(byte)i; if(sha1_32(buf)==2099239829){break;} }
+ for(i=-95;i<-86;i++){ buf[1171]=(byte)i; if(sha1_32(buf)==-1259080821){break;} }
+ for(i=-46;i<-20;i++){ buf[1172]=(byte)i; if(sha1_32(buf)==1187804181){break;} }
+ for(i=-128;i<-127;i++){ buf[1173]=(byte)i; if(sha1_32(buf)==24646088){break;} }
+ for(i=38;i<62;i++){ buf[1174]=(byte)i; if(sha1_32(buf)==-611366063){break;} }
+ for(i=91;i<98;i++){ buf[1175]=(byte)i; if(sha1_32(buf)==1394377096){break;} }
+ for(i=33;i<37;i++){ buf[1176]=(byte)i; if(sha1_32(buf)==-563632807){break;} }
+ for(i=-46;i<-23;i++){ buf[1177]=(byte)i; if(sha1_32(buf)==580968163){break;} }
+ for(i=-35;i<-14;i++){ buf[1178]=(byte)i; if(sha1_32(buf)==447151774){break;} }
+ for(i=98;i<116;i++){ buf[1179]=(byte)i; if(sha1_32(buf)==1201242262){break;} }
+ for(i=112;i<128;i++){ buf[1180]=(byte)i; if(sha1_32(buf)==-1814134789){break;} }
+ for(i=-96;i<-67;i++){ buf[1181]=(byte)i; if(sha1_32(buf)==280631127){break;} }
+ for(i=-16;i<-3;i++){ buf[1182]=(byte)i; if(sha1_32(buf)==-1016234569){break;} }
+ for(i=-6;i<12;i++){ buf[1183]=(byte)i; if(sha1_32(buf)==-1142962663){break;} }
+ for(i=92;i<113;i++){ buf[1184]=(byte)i; if(sha1_32(buf)==1833934811){break;} }
+ for(i=-41;i<-26;i++){ buf[1185]=(byte)i; if(sha1_32(buf)==303155673){break;} }
+ for(i=-73;i<-59;i++){ buf[1186]=(byte)i; if(sha1_32(buf)==954067543){break;} }
+ for(i=28;i<41;i++){ buf[1187]=(byte)i; if(sha1_32(buf)==847034673){break;} }
+ for(i=34;i<48;i++){ buf[1188]=(byte)i; if(sha1_32(buf)==468768694){break;} }
+ for(i=115;i<128;i++){ buf[1189]=(byte)i; if(sha1_32(buf)==-1906299000){break;} }
+ for(i=20;i<43;i++){ buf[1190]=(byte)i; if(sha1_32(buf)==116195261){break;} }
+ for(i=-19;i<6;i++){ buf[1191]=(byte)i; if(sha1_32(buf)==956676034){break;} }
+ for(i=-79;i<-71;i++){ buf[1192]=(byte)i; if(sha1_32(buf)==-768255979){break;} }
+ for(i=24;i<42;i++){ buf[1193]=(byte)i; if(sha1_32(buf)==-834689880){break;} }
+ for(i=15;i<35;i++){ buf[1194]=(byte)i; if(sha1_32(buf)==-1005776799){break;} }
+ for(i=-89;i<-80;i++){ buf[1195]=(byte)i; if(sha1_32(buf)==-1597248345){break;} }
+ for(i=18;i<34;i++){ buf[1196]=(byte)i; if(sha1_32(buf)==436268321){break;} }
+ for(i=96;i<111;i++){ buf[1197]=(byte)i; if(sha1_32(buf)==-2098911670){break;} }
+ for(i=-6;i<-2;i++){ buf[1198]=(byte)i; if(sha1_32(buf)==1522004565){break;} }
+ for(i=45;i<65;i++){ buf[1199]=(byte)i; if(sha1_32(buf)==-268362963){break;} }
+ for(i=-68;i<-48;i++){ buf[1200]=(byte)i; if(sha1_32(buf)==256296908){break;} }
+ for(i=-111;i<-93;i++){ buf[1201]=(byte)i; if(sha1_32(buf)==-1460615846){break;} }
+ for(i=-116;i<-98;i++){ buf[1202]=(byte)i; if(sha1_32(buf)==1426505688){break;} }
+ for(i=10;i<24;i++){ buf[1203]=(byte)i; if(sha1_32(buf)==347882593){break;} }
+ for(i=-98;i<-79;i++){ buf[1204]=(byte)i; if(sha1_32(buf)==616904075){break;} }
+ for(i=2;i<22;i++){ buf[1205]=(byte)i; if(sha1_32(buf)==-817318881){break;} }
+ for(i=120;i<128;i++){ buf[1206]=(byte)i; if(sha1_32(buf)==486907567){break;} }
+ for(i=-66;i<-52;i++){ buf[1207]=(byte)i; if(sha1_32(buf)==493991007){break;} }
+ for(i=120;i<128;i++){ buf[1208]=(byte)i; if(sha1_32(buf)==-2062391101){break;} }
+ for(i=79;i<93;i++){ buf[1209]=(byte)i; if(sha1_32(buf)==1348983173){break;} }
+ for(i=-113;i<-85;i++){ buf[1210]=(byte)i; if(sha1_32(buf)==885296629){break;} }
+ for(i=62;i<87;i++){ buf[1211]=(byte)i; if(sha1_32(buf)==863029118){break;} }
+ for(i=109;i<114;i++){ buf[1212]=(byte)i; if(sha1_32(buf)==-545638418){break;} }
+ for(i=83;i<91;i++){ buf[1213]=(byte)i; if(sha1_32(buf)==-1954833862){break;} }
+ for(i=41;i<43;i++){ buf[1214]=(byte)i; if(sha1_32(buf)==1803112571){break;} }
+ for(i=-104;i<-84;i++){ buf[1215]=(byte)i; if(sha1_32(buf)==-779764530){break;} }
+ for(i=-8;i<1;i++){ buf[1216]=(byte)i; if(sha1_32(buf)==432262664){break;} }
+ for(i=-13;i<8;i++){ buf[1217]=(byte)i; if(sha1_32(buf)==432262664){break;} }
+ for(i=54;i<66;i++){ buf[1218]=(byte)i; if(sha1_32(buf)==1399293764){break;} }
+ for(i=-41;i<-21;i++){ buf[1219]=(byte)i; if(sha1_32(buf)==1632796764){break;} }
+ for(i=-12;i<3;i++){ buf[1220]=(byte)i; if(sha1_32(buf)==1632796764){break;} }
+ for(i=104;i<127;i++){ buf[1221]=(byte)i; if(sha1_32(buf)==-1431651495){break;} }
+ for(i=-13;i<-2;i++){ buf[1222]=(byte)i; if(sha1_32(buf)==-474103663){break;} }
+ for(i=-77;i<-74;i++){ buf[1223]=(byte)i; if(sha1_32(buf)==1986314914){break;} }
+ for(i=7;i<18;i++){ buf[1224]=(byte)i; if(sha1_32(buf)==-2042746239){break;} }
+ for(i=97;i<105;i++){ buf[1225]=(byte)i; if(sha1_32(buf)==-2018140441){break;} }
+ for(i=102;i<122;i++){ buf[1226]=(byte)i; if(sha1_32(buf)==-1921373391){break;} }
+ for(i=-105;i<-100;i++){ buf[1227]=(byte)i; if(sha1_32(buf)==-957629312){break;} }
+ for(i=77;i<100;i++){ buf[1228]=(byte)i; if(sha1_32(buf)==-245319436){break;} }
+ for(i=15;i<20;i++){ buf[1229]=(byte)i; if(sha1_32(buf)==538308770){break;} }
+ for(i=82;i<107;i++){ buf[1230]=(byte)i; if(sha1_32(buf)==990290973){break;} }
+ for(i=-105;i<-86;i++){ buf[1231]=(byte)i; if(sha1_32(buf)==530521490){break;} }
+ for(i=-67;i<-44;i++){ buf[1232]=(byte)i; if(sha1_32(buf)==-805582523){break;} }
+ for(i=51;i<65;i++){ buf[1233]=(byte)i; if(sha1_32(buf)==-1070885278){break;} }
+ for(i=117;i<128;i++){ buf[1234]=(byte)i; if(sha1_32(buf)==633776446){break;} }
+ for(i=39;i<46;i++){ buf[1235]=(byte)i; if(sha1_32(buf)==1112424496){break;} }
+ for(i=-96;i<-87;i++){ buf[1236]=(byte)i; if(sha1_32(buf)==110190055){break;} }
+ for(i=116;i<128;i++){ buf[1237]=(byte)i; if(sha1_32(buf)==-836788621){break;} }
+ for(i=71;i<86;i++){ buf[1238]=(byte)i; if(sha1_32(buf)==-321550370){break;} }
+ for(i=63;i<84;i++){ buf[1239]=(byte)i; if(sha1_32(buf)==1959636437){break;} }
+ for(i=28;i<51;i++){ buf[1240]=(byte)i; if(sha1_32(buf)==-1431486352){break;} }
+ for(i=-8;i<19;i++){ buf[1241]=(byte)i; if(sha1_32(buf)==-1528323064){break;} }
+ for(i=88;i<101;i++){ buf[1242]=(byte)i; if(sha1_32(buf)==1470835884){break;} }
+ for(i=-89;i<-76;i++){ buf[1243]=(byte)i; if(sha1_32(buf)==-552074251){break;} }
+ for(i=111;i<118;i++){ buf[1244]=(byte)i; if(sha1_32(buf)==1936907243){break;} }
+ for(i=62;i<83;i++){ buf[1245]=(byte)i; if(sha1_32(buf)==380472647){break;} }
+ for(i=72;i<88;i++){ buf[1246]=(byte)i; if(sha1_32(buf)==-467342091){break;} }
+ return buf;
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/CPUInfoUtil.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/CPUInfoUtil.java
new file mode 100644
index 000000000..a059a1541
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/CPUInfoUtil.java
@@ -0,0 +1,172 @@
+package com.yunbao.faceunity.utils;
+
+import android.content.Context;
+import android.os.Build;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.RandomAccessFile;
+
+/**
+ * cpu使用率获取工具类
+ * Created by lirui on 2017/8/2.
+ */
+
+public class CPUInfoUtil {
+ private long lastTotalCpu = 0;
+ private long lastProcessCpu = 0;
+
+ private final String PackageName;
+
+ private volatile boolean isRunningCPU = false;
+ private volatile double cpuRate = 0;
+
+ public CPUInfoUtil(Context context) {
+ if (Build.VERSION.SDK_INT >= 26) {
+ final String pn = context.getPackageName();
+ if (pn.length() <= 16) {
+ PackageName = pn;
+ } else {
+ PackageName = pn.substring(0, 15) + "+";
+ }
+// Log.e(TAG, "CSVUtils PackageName " + PackageName);
+ isRunningCPU = true;
+ CPUInfoThread cpuinfothread = new CPUInfoThread();
+ cpuinfothread.start();
+ } else {
+ PackageName = null;
+ }
+ }
+
+ public double getProcessCpuUsed() {
+ if (Build.VERSION.SDK_INT >= 26) {
+ return cpuRate;
+ } else {
+ double pcpu = 0;
+ double tmp = 1.0;
+ long nowTotalCpu = getTotalCpu();
+ long nowProcessCpu = getMyProcessCpu();
+ if (nowTotalCpu != 0 && (nowTotalCpu - lastTotalCpu) != 0) {
+// Log.e(TAG, "cpu used nowProcessCpu " + nowProcessCpu + " lastProcessCpu " + lastProcessCpu + " nowTotalCpu " + nowTotalCpu + " lastTotalCpu " + lastTotalCpu);
+ pcpu = 100 * (tmp * (nowProcessCpu - lastProcessCpu) / (nowTotalCpu - lastTotalCpu));
+ }
+ lastProcessCpu = nowProcessCpu;
+ lastTotalCpu = nowTotalCpu;
+ return pcpu < 0 ? 0 : pcpu;
+ }
+ }
+
+ public void close() {
+ if (Build.VERSION.SDK_INT >= 26) {
+ isRunningCPU = false;
+ }
+ }
+
+ private long getTotalCpu() {
+ String[] cpuInfos = null;
+ try {
+ RandomAccessFile reader = null;
+ reader = new RandomAccessFile("/proc/stat", "r");
+ String load = reader.readLine();
+ reader.close();
+ cpuInfos = load.split(" ");
+ } catch (IOException e) {
+ e.printStackTrace();
+ return 0;
+ }
+ long totalCpu = 0;
+ try {
+ totalCpu = Long.parseLong(cpuInfos[2])
+ + Long.parseLong(cpuInfos[3]) + Long.parseLong(cpuInfos[4])
+ + Long.parseLong(cpuInfos[6]) + Long.parseLong(cpuInfos[5])
+ + Long.parseLong(cpuInfos[7]) + Long.parseLong(cpuInfos[8]);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ e.printStackTrace();
+ return 0;
+ }
+ return totalCpu;
+ }
+
+ private long getMyProcessCpu() {
+ String[] cpuInfos = null;
+ try {
+ int pid = android.os.Process.myPid();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(
+ new FileInputStream("/proc/" + pid + "/stat")), 1000);
+ String load = reader.readLine();
+ reader.close();
+ cpuInfos = load.split(" ");
+ } catch (IOException e) {
+ e.printStackTrace();
+ return 0;
+ }
+ long appCpuTime = 0;
+ try {
+ appCpuTime = Long.parseLong(cpuInfos[13])
+ + Long.parseLong(cpuInfos[14]) + Long.parseLong(cpuInfos[15])
+ + Long.parseLong(cpuInfos[16]);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ e.printStackTrace();
+ return 0;
+ }
+ return appCpuTime;
+ }
+
+ class CPUInfoThread extends Thread {
+
+ private double allCPU = 0;
+
+ @Override
+ public void run() {
+ String line = null;
+ InputStream is = null;
+ try {
+
+ Runtime runtime = Runtime.getRuntime();
+ Process proc = runtime.exec("top -d 1");
+ is = proc.getInputStream();
+
+ // 换成BufferedReader
+ BufferedReader buf = new BufferedReader(new InputStreamReader(is));
+ do {
+ line = buf.readLine();
+ if (allCPU == 0 && line.contains("user") && line.contains("nice") && line.contains("sys") && line.contains("idle") && line.contains("iow") && line.contains("irq") && line.contains("sirq") && line.contains("host")) {
+ if (line.indexOf("%cpu ") > 0)
+ allCPU = Double.parseDouble(line.split("%cpu ")[0]);
+ if (allCPU == 0) {
+ String[] s = line.split("%,");
+ for (String st : s) {
+ String[] sts = st.split(" ");
+ if (sts.length > 0)
+ allCPU += Double.parseDouble(sts[sts.length - 1]);
+ }
+ }
+ }
+ // 读取到相应pkgName跳出循环(或者未找到)
+ if (line == null || line.endsWith(PackageName)) {
+// Log.e(TAG, "cpu line : " + line);
+ String str[] = line.split(" ");
+ int t = 0;
+ for (int i = str.length - 1; i > 0; i--) {
+ if (!str[i].isEmpty() && ++t == 4) {
+// Log.e(TAG, "cpu : " + str[i] + " allCPU " + allCPU);
+ cpuRate = 100 * Double.parseDouble(str[i]) / allCPU;
+ }
+ }
+ continue;
+ }
+ } while (isRunningCPU);
+
+ if (is != null) {
+ buf.close();
+ is.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/CSVUtils.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/CSVUtils.java
new file mode 100644
index 000000000..e30df5b0a
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/CSVUtils.java
@@ -0,0 +1,168 @@
+package com.yunbao.faceunity.utils;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * Created by tujh on 2017/11/2.
+ */
+public class CSVUtils {
+ public static final String TAG = CSVUtils.class.getSimpleName();
+ /* 每 100 帧统计一次 */
+ public static final int FRAME_STEP = 100;
+
+ public static final String COMMA = ",";
+
+ private OutputStreamWriter mStreamWriter;
+
+ private ActivityManager mActivityManager;
+
+ private Handler mHandler;
+
+ private CPUInfoUtil mCPUInfoUtil;
+
+ private int mFrameRate;
+ private volatile double mCpuUsed;
+ private volatile double mAverageFps;
+ private volatile double mAverageRenderTime;
+ private volatile double mMemory;
+ private long mSumRenderTimeInNano;
+ private volatile long mTimestamp;
+
+ public CSVUtils(Context context) {
+ mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ mCPUInfoUtil = new CPUInfoUtil(context);
+
+ HandlerThread handlerThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
+ handlerThread.start();
+ mHandler = new Handler(handlerThread.getLooper());
+ }
+
+ public void initHeader(String folderName, StringBuilder headerInfo) {
+ Log.d(TAG, "initHeader() called with: folderName = [" + folderName + "], headerInfo = [" + headerInfo + "]");
+ StringBuilder stringBuilder = new StringBuilder().append("时间").append(COMMA)
+ .append("帧率").append(COMMA)
+ .append("渲染耗时").append(COMMA)
+ .append("CPU").append(COMMA)
+ .append("内存").append(COMMA);
+ if (headerInfo != null) {
+ stringBuilder.append(headerInfo);
+ }
+ stringBuilder.append("\n");
+
+ File file = new File(folderName);
+ File parentFile = file.getParentFile();
+ if (!parentFile.exists()) {
+ parentFile.mkdirs();
+ }
+ try {
+ if (!file.exists()) {
+ file.createNewFile();
+ }
+ mStreamWriter = new OutputStreamWriter(new FileOutputStream(file, false), "GBK");
+ } catch (IOException e) {
+ Log.e(TAG, "CSVUtils: ", e);
+ }
+ flush(stringBuilder);
+ mTimestamp = System.currentTimeMillis();
+ }
+
+ public void writeCsv(final StringBuilder extraInfo, long renderTimeInNano) {
+ if (mStreamWriter == null) {
+ return;
+ }
+
+ mSumRenderTimeInNano += renderTimeInNano;
+ if (mFrameRate % FRAME_STEP == FRAME_STEP - 1) {
+ mTimestamp = System.currentTimeMillis();
+ mAverageFps = FPSUtil.fpsAVG(FRAME_STEP);
+ mAverageRenderTime = (double) mSumRenderTimeInNano / FRAME_STEP / 1_000_000;
+ mSumRenderTimeInNano = 0;
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mCpuUsed = mCPUInfoUtil.getProcessCpuUsed();
+ mMemory = MemoryInfoUtil.getMemory(mActivityManager.getProcessMemoryInfo(new int[]{Process.myPid()}));
+ String strCPU = String.format(Locale.getDefault(), "%.2f", mCpuUsed);
+ String strMemory = String.format(Locale.getDefault(), "%.2f", mMemory);
+
+ StringBuilder stringBuilder = new StringBuilder();
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS", Locale.getDefault());
+
+ stringBuilder.append(dateFormat.format(new Date(mTimestamp))).append(COMMA)
+ .append(String.format(Locale.getDefault(), "%.2f", mAverageFps)).append(COMMA)
+ .append(String.format(Locale.getDefault(), "%.2f", mAverageRenderTime)).append(COMMA)
+ .append(strCPU).append(COMMA)
+ .append(strMemory).append(COMMA);
+
+ if (extraInfo != null) {
+ stringBuilder.append(extraInfo);
+ }
+ stringBuilder.append("\n");
+ flush(stringBuilder);
+ }
+ });
+ }
+ mFrameRate++;
+ }
+
+ private void flush(StringBuilder stringBuilder) {
+ if (mStreamWriter == null) {
+ return;
+ }
+ try {
+ mStreamWriter.write(stringBuilder.toString());
+ mStreamWriter.flush();
+ } catch (IOException e) {
+ Log.e(TAG, "flush: ", e);
+ }
+ }
+
+ public void close() {
+ Log.d(TAG, "close: ");
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mStreamWriter != null) {
+ try {
+ mStreamWriter.close();
+ mStreamWriter = null;
+ } catch (IOException e) {
+ Log.e(TAG, "close: ", e);
+ }
+ }
+ }
+ });
+ mHandler.getLooper().quitSafely();
+ mHandler = null;
+ mCPUInfoUtil.close();
+ }
+
+ public double getCpuUsed() {
+ return mCpuUsed;
+ }
+
+ public double getMemory() {
+ return mMemory;
+ }
+
+ public double getAverageRenderTime() {
+ return mAverageRenderTime;
+ }
+
+ public double getAverageFps() {
+ return mAverageFps;
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FPSUtil.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FPSUtil.java
new file mode 100644
index 000000000..4a20acafb
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FPSUtil.java
@@ -0,0 +1,63 @@
+package com.yunbao.faceunity.utils;
+
+/**
+ * FPS工具类
+ * Created by tujh on 2018/5/24.
+ */
+public class FPSUtil {
+ private static final int NANO_IN_ONE_MILLI_SECOND = 1000000;
+ private static final int NANO_IN_ONE_SECOND = 1000 * NANO_IN_ONE_MILLI_SECOND;
+
+ private static long sLastFrameTimeStamp = 0;
+
+ /**
+ * 每帧都计算
+ *
+ * @return
+ */
+ public static double fps() {
+ long tmp = System.nanoTime();
+ double fps = ((double) NANO_IN_ONE_SECOND) / (tmp - sLastFrameTimeStamp);
+ sLastFrameTimeStamp = tmp;
+// Log.e(TAG, "FPS : " + fps);
+ return fps;
+ }
+
+ private static long mStartTime = 0;
+
+ /**
+ * 平均值
+ *
+ * @return
+ */
+ public static double fpsAVG(int time) {
+ long tmp = System.nanoTime();
+ double fps = ((double) NANO_IN_ONE_SECOND) * time / (tmp - mStartTime);
+ mStartTime = tmp;
+// Log.e(TAG, "FPS : " + fps);
+ return fps;
+ }
+
+ private long mLimitMinTime = 33333333;
+ private long mLimitStartTime;
+ private int mLimitFrameRate;
+
+ public void setLimitMinTime(long limitMinTime) {
+ mLimitMinTime = limitMinTime;
+ }
+
+ public void limit() {
+ try {
+ if (mLimitFrameRate == 0 || mLimitFrameRate > 600000) {
+ mLimitStartTime = System.nanoTime();
+ mLimitFrameRate = 0;
+ }
+ long sleepTime = mLimitMinTime * mLimitFrameRate++ - (System.nanoTime() - mLimitStartTime);
+ if (sleepTime > 0) {
+ Thread.sleep(sleepTime / NANO_IN_ONE_MILLI_SECOND, (int) (sleepTime % NANO_IN_ONE_MILLI_SECOND));
+ }
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FURenderer.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FURenderer.java
new file mode 100644
index 000000000..19ff7db23
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FURenderer.java
@@ -0,0 +1,326 @@
+package com.yunbao.faceunity.utils;
+
+import android.content.Context;
+import android.hardware.Camera;
+
+import com.faceunity.core.callback.OperateCallback;
+import com.faceunity.core.entity.FURenderInputData;
+import com.faceunity.core.entity.FURenderOutputData;
+import com.faceunity.core.enumeration.CameraFacingEnum;
+import com.faceunity.core.enumeration.FUAIProcessorEnum;
+import com.faceunity.core.enumeration.FUAITypeEnum;
+import com.faceunity.core.faceunity.FURenderConfig;
+import com.faceunity.core.faceunity.FURenderKit;
+import com.faceunity.core.faceunity.FURenderManager;
+import com.faceunity.core.utils.CameraUtils;
+import com.faceunity.core.utils.FULogger;
+import com.yunbao.faceunity.listener.FURendererListener;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+
+/**
+ * DESC:
+ * Created on 2021/4/26
+ */
+public class FURenderer extends IFURenderer {
+
+ private static final String TAG = "FURenderer";
+
+ public volatile static FURenderer INSTANCE;
+
+ public static FURenderer getInstance() {
+ if (INSTANCE == null) {
+ synchronized (FURenderer.class) {
+ if (INSTANCE == null) {
+ INSTANCE = new FURenderer();
+ INSTANCE.mFURenderKit = FURenderKit.getInstance();
+ }
+ }
+ }
+ return INSTANCE;
+ }
+
+ /**
+ * 状态回调监听
+ */
+ private FURendererListener mFURendererListener;
+
+
+ /* 特效FURenderKit*/
+ private FURenderKit mFURenderKit;
+
+ /* AI道具*/
+ public static String BUNDLE_AI_FACE = "model" + File.separator + "ai_face_processor_lite.bundle";
+ public static String BUNDLE_AI_HUMAN = "model" + File.separator + "ai_human_processor.bundle";
+
+ /* GL 线程 ID */
+ private Long mGlThreadId = 0L;
+ /* 任务队列 */
+ private ArrayList mEventQueue = new ArrayList<>(16);
+ private final Object queueLock = new Object();
+ /* 渲染开关标识 */
+ private volatile boolean mRendererSwitch = false;
+ /* 清除队列标识 */
+ private volatile boolean mClearQueue = false;
+
+ /* 相机角度-朝向映射 */
+ private HashMap cameraOrientationMap = new HashMap<>();
+
+ /*检测类型*/
+ private FUAIProcessorEnum aIProcess = FUAIProcessorEnum.FACE_PROCESSOR;
+ /*检测标识*/
+ private int aIProcessTrackStatus = -1;
+
+ public String getVersion() {
+ return mFURenderKit.getVersion();
+ }
+
+ /**
+ * 初始化鉴权
+ *
+ * @param context
+ */
+ @Override
+ public void setup(Context context) {
+ FURenderManager.setKitDebug(FULogger.LogLevel.INFO);
+ FURenderManager.setCoreDebug(FULogger.LogLevel.INFO);
+ FURenderManager.registerFURender(context, Authpack.A(), new OperateCallback() {
+ @Override
+ public void onSuccess(int i, @NotNull String s) {
+ if (i == FURenderConfig.OPERATE_SUCCESS_AUTH) {
+ mFURenderKit.getFUAIController().loadAIProcessor(BUNDLE_AI_FACE, FUAITypeEnum.FUAITYPE_FACEPROCESSOR);
+ mFURenderKit.getFUAIController().loadAIProcessor(BUNDLE_AI_HUMAN, FUAITypeEnum.FUAITYPE_HUMAN_PROCESSOR);
+ mFURenderKit.setReadBackSync(true);
+ int cameraFrontOrientation = CameraUtils.INSTANCE.getCameraOrientation(Camera.CameraInfo.CAMERA_FACING_FRONT);
+ int cameraBackOrientation = CameraUtils.INSTANCE.getCameraOrientation(Camera.CameraInfo.CAMERA_FACING_BACK);
+ cameraOrientationMap.put(cameraFrontOrientation, CameraFacingEnum.CAMERA_FRONT);
+ cameraOrientationMap.put(cameraBackOrientation, CameraFacingEnum.CAMERA_BACK);
+ }
+ }
+
+ @Override
+ public void onFail(int i, @NotNull String s) {
+ }
+ });
+ }
+
+ /**
+ * 开启合成状态
+ */
+ @Override
+ public void prepareRenderer(FURendererListener mFURendererListener) {
+ this.mFURendererListener = mFURendererListener;
+ mRendererSwitch = true;
+ mClearQueue = false;
+ queueEvent(() -> mGlThreadId = Thread.currentThread().getId());
+ mFURendererListener.onPrepare();
+ }
+
+
+ /**
+ * 双输入接口,输入 buffer 和 texture,必须在具有 GL 环境的线程调用
+ * 由于省去数据拷贝,性能相对最优,优先推荐使用。
+ * 缺点是无法保证 buffer 和纹理对齐,可能出现点位和效果对不上的情况。
+ *
+ * @param img NV21 buffer
+ * @param texId 纹理 ID
+ * @param width 宽
+ * @param height 高
+ * @return
+ */
+ @Override
+ public int onDrawFrameDualInput(byte[] img, int texId, int width, int height) {
+ prepareDrawFrame();
+ if (!mRendererSwitch) {
+ return texId;
+ }
+ FURenderInputData inputData = new FURenderInputData(width, height);
+ if (img != null) {
+ inputData.setImageBuffer(new FURenderInputData.FUImageBuffer(inputBufferType, img));
+ }
+ if (texId != -1) {
+ inputData.setTexture(new FURenderInputData.FUTexture(inputTextureType, texId));
+ }
+ FURenderInputData.FURenderConfig config = inputData.getRenderConfig();
+ config.setExternalInputType(externalInputType);
+ config.setInputOrientation(inputOrientation);
+ config.setDeviceOrientation(deviceOrientation);
+ config.setInputBufferMatrix(inputBufferMatrix);
+ config.setInputTextureMatrix(inputTextureMatrix);
+ config.setOutputMatrix(outputMatrix);
+ config.setCameraFacing(cameraFacing);
+ FURenderOutputData outputData = mFURenderKit.renderWithInput(inputData);
+ if (outputData.getTexture() != null && outputData.getTexture().getTexId() > 0) {
+ return outputData.getTexture().getTexId();
+ }
+ return texId;
+ }
+
+ public FURenderOutputData onDrawFrameInputWithReturn(byte[] img, int width, int height) {
+ prepareDrawFrame();
+ if (!mRendererSwitch) {
+ return null ;
+ }
+ FURenderInputData inputData = new FURenderInputData(width, height);
+ if (img != null) {
+ inputData.setImageBuffer(new FURenderInputData.FUImageBuffer(inputBufferType, img));
+ }
+ FURenderInputData.FURenderConfig config = inputData.getRenderConfig();
+ config.setExternalInputType(externalInputType);
+ config.setInputOrientation(inputOrientation);
+ config.setDeviceOrientation(deviceOrientation);
+ config.setInputBufferMatrix(inputBufferMatrix);
+ config.setInputTextureMatrix(inputTextureMatrix);
+ config.setCameraFacing(cameraFacing);
+ config.setNeedBufferReturn(true);
+ config.setOutputMatrix(outputMatrix);
+ return mFURenderKit.renderWithInput(inputData);
+ }
+
+ /**
+ * 类似 GLSurfaceView 的 queueEvent 机制,把任务抛到 GL 线程执行。
+ *
+ * @param runnable
+ */
+ @Override
+ public void queueEvent(Runnable runnable) {
+ if (runnable == null) {
+ return;
+ }
+ if (mGlThreadId == Thread.currentThread().getId()) {
+ runnable.run();
+ } else {
+ synchronized (queueLock) {
+ mEventQueue.add(runnable);
+ }
+ }
+ }
+
+
+ /**
+ * 释放资源
+ */
+ @Override
+ public void release() {
+ mRendererSwitch = false;
+ mClearQueue = true;
+ mGlThreadId = 0L;
+ synchronized (queueLock) {
+ mEventQueue.clear();
+ mClearQueue = false;
+ mFURenderKit.release();
+ aIProcessTrackStatus = -1;
+ if (mFURendererListener != null) {
+ mFURendererListener.onRelease();
+ mFURendererListener = null;
+ }
+ }
+ }
+
+
+ /**
+ * 渲染前置执行
+ *
+ * @return
+ */
+ private void prepareDrawFrame() {
+ benchmarkFPS();
+
+ // 执行任务队列中的任务
+ synchronized (queueLock) {
+ while (!mEventQueue.isEmpty() && !mClearQueue) {
+ mEventQueue.remove(0).run();
+ }
+ }
+ // AI检测
+ trackStatus();
+ }
+
+ /**
+ * 设置检测类型
+ *
+ * @param type
+ */
+ @Override
+ public void setAIProcessTrackType(FUAIProcessorEnum type) {
+ aIProcess = type;
+ aIProcessTrackStatus = -1;
+ }
+
+ /**
+ * 设置FPS检测
+ *
+ * @param enable
+ */
+ @Override
+ public void setMarkFPSEnable(boolean enable) {
+ mIsRunBenchmark = enable;
+ }
+
+ @Override
+ public void setInputOrientation(int inputOrientation) {
+ if (cameraOrientationMap.containsKey(inputOrientation)) {
+ setCameraFacing(cameraOrientationMap.get(inputOrientation));
+ }
+ super.setInputOrientation(inputOrientation);
+ }
+
+ /**
+ * AI识别数目检测
+ */
+ private void trackStatus() {
+ int trackCount;
+ if (aIProcess == FUAIProcessorEnum.HAND_GESTURE_PROCESSOR) {
+ trackCount = mFURenderKit.getFUAIController().handProcessorGetNumResults();
+ } else if (aIProcess == FUAIProcessorEnum.HUMAN_PROCESSOR) {
+ trackCount = mFURenderKit.getFUAIController().humanProcessorGetNumResults();
+ } else {
+ trackCount = mFURenderKit.getFUAIController().isTracking();
+ }
+ if (trackCount != aIProcessTrackStatus) {
+ aIProcessTrackStatus = trackCount;
+ } else {
+ return;
+ }
+ if (mFURendererListener != null) {
+ mFURendererListener.onTrackStatusChanged(aIProcess, trackCount);
+ }
+ }
+ //endregion AI识别
+
+ //------------------------------FPS 渲染时长回调相关定义------------------------------------
+
+ private static final int NANO_IN_ONE_MILLI_SECOND = 1_000_000;
+ private static final int NANO_IN_ONE_SECOND = 1_000_000_000;
+ private static final int FRAME_COUNT = 20;
+ private boolean mIsRunBenchmark = false;
+ private int mCurrentFrameCount;
+ private long mLastFrameTimestamp;
+ private long mSumCallTime;
+ private long mCallStartTime;
+
+ private void benchmarkFPS() {
+ if (!mIsRunBenchmark) {
+ return;
+ }
+ if (++mCurrentFrameCount == FRAME_COUNT) {
+ long tmp = System.nanoTime();
+ double fps = (double) NANO_IN_ONE_SECOND / ((double) (tmp - mLastFrameTimestamp) / FRAME_COUNT);
+ double renderTime = (double) mSumCallTime / FRAME_COUNT / NANO_IN_ONE_MILLI_SECOND;
+ mLastFrameTimestamp = tmp;
+ mSumCallTime = 0;
+ mCurrentFrameCount = 0;
+
+ if (mFURendererListener != null) {
+ mFURendererListener.onFpsChanged(fps, renderTime);
+ }
+ }
+ }
+
+
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FUUtils.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FUUtils.java
new file mode 100644
index 000000000..2194ea131
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FUUtils.java
@@ -0,0 +1,271 @@
+package com.yunbao.faceunity.utils;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import com.faceunity.core.model.facebeauty.FaceBeauty;
+import com.google.gson.Gson;
+import com.yunbao.faceunity.data.FaceBeautyData;
+import com.yunbao.faceunity.entity.FaceBeautyFilterBean;
+
+import java.util.ArrayList;
+
+/**
+ * 将FaceBeauty FaceBeautyData 互相设置
+ */
+public class FUUtils {
+ private static final String SP_NAME = "FaceBeauty";
+ private static final String SP_KEY_NAME = "faceBeautyData";
+
+ /**
+ * 将FaceBeauty 转 FaceBeautyData
+ *
+ * @param faceBeautyData
+ */
+ public static void buildFaceBeautyData(FaceBeautyData faceBeautyData, FaceBeauty faceBeauty, ArrayList filterList, int styleTypeIndex) {
+ if (faceBeauty != null) {
+ /* 美肤 */
+ /* 磨皮类型 */
+ faceBeautyData.blurType = faceBeauty.getBlurType();
+ /* 磨皮程度 */
+ faceBeautyData.blurIntensity = faceBeauty.getBlurIntensity();
+ /* 美白程度 */
+ faceBeautyData.colorIntensity = faceBeauty.getColorIntensity();
+ /* 红润程度 */
+ faceBeautyData.redIntensity = faceBeauty.getRedIntensity();
+ /* 锐化程度 */
+ faceBeautyData.sharpenIntensity = faceBeauty.getSharpenIntensity();
+ /* 亮眼程度 */
+ faceBeautyData.eyeBrightIntensity = faceBeauty.getEyeBrightIntensity();
+ /* 美牙程度 */
+ faceBeautyData.toothIntensity = faceBeauty.getToothIntensity();
+ /* 去黑眼圈强度*/
+ faceBeautyData.removePouchIntensity = faceBeauty.getRemovePouchIntensity();
+ /* 去法令纹强度*/
+ faceBeautyData.removeLawPatternIntensity = faceBeauty.getRemoveLawPatternIntensity();
+
+ /* 美型 */
+ /* 瘦脸程度 */
+ faceBeautyData.cheekThinningIntensity = faceBeauty.getCheekThinningIntensity();
+ /* V脸程度 */
+ faceBeautyData.cheekVIntensity = faceBeauty.getCheekVIntensity();
+ /* 窄脸程度 */
+ faceBeautyData.cheekNarrowIntensity = faceBeauty.getCheekNarrowIntensity();
+ /* 短脸程度 */
+ faceBeautyData.cheekShortIntensity = faceBeauty.getCheekShortIntensity();
+ /* 小脸程度 */
+ faceBeautyData.cheekSmallIntensity = faceBeauty.getCheekSmallIntensity();
+ /* 瘦颧骨 */
+ faceBeautyData.cheekBonesIntensity = faceBeauty.getCheekBonesIntensity();
+ /* 瘦下颌骨 */
+ faceBeautyData.lowerJawIntensity = faceBeauty.getLowerJawIntensity();
+ /* 大眼程度 */
+ faceBeautyData.eyeEnlargingIntensity = faceBeauty.getEyeEnlargingIntensity();
+ /* 圆眼程度 */
+ faceBeautyData.eyeCircleIntensity = faceBeauty.getEyeCircleIntensity();
+ /* 下巴调整程度 */
+ faceBeautyData.chinIntensity = faceBeauty.getChinIntensity();
+ /* 额头调整程度 */
+ faceBeautyData.forHeadIntensity = faceBeauty.getForHeadIntensity();
+ /* 瘦鼻程度 */
+ faceBeautyData.noseIntensity = faceBeauty.getNoseIntensity();
+ /* 嘴巴调整程度 */
+ faceBeautyData.mouthIntensity = faceBeauty.getMouthIntensity();
+ /* 开眼角强度 */
+ faceBeautyData.canthusIntensity = faceBeauty.getCanthusIntensity();
+ /* 眼睛间距 */
+ faceBeautyData.eyeSpaceIntensity = faceBeauty.getEyeSpaceIntensity();
+ /* 眼睛角度 */
+ faceBeautyData.eyeRotateIntensity = faceBeauty.getEyeRotateIntensity();
+ /* 鼻子长度 */
+ faceBeautyData.longNoseIntensity = faceBeauty.getLongNoseIntensity();
+ /* 调节人中 */
+ faceBeautyData.philtrumIntensity = faceBeauty.getPhiltrumIntensity();
+ /* 微笑嘴角强度 */
+ faceBeautyData.smileIntensity = faceBeauty.getSmileIntensity();
+ /* 眉毛上下 */
+ faceBeautyData.browHeightIntensity = faceBeauty.getBrowHeightIntensity();
+ /* 眉毛间距 */
+ faceBeautyData.browSpaceIntensity = faceBeauty.getBrowSpaceIntensity();
+
+ /* 滤镜相关 */
+ if (filterList != null) {
+ for (FaceBeautyFilterBean filterBean:filterList) {
+ faceBeautyData.filterMap.put(filterBean.getKey(),filterBean.getIntensity());
+ }
+ }
+ /* 滤镜名称 */
+ faceBeautyData.filterName = faceBeauty.getFilterName();
+ /* 滤镜强度 */
+ faceBeautyData.filterIntensity = faceBeauty.getFilterIntensity();
+
+ /* 是否开启风格 */
+ faceBeautyData.styleTypeIndex = styleTypeIndex;
+ }
+ }
+
+ /**
+ * 将FaceBeauty 转 FaceBeautyData
+ *
+ * @param faceBeautyData
+ */
+ public static boolean setFaceBeauty(FaceBeautyData faceBeautyData ,FaceBeauty faceBeauty) {
+ if (faceBeautyData == null) {
+ return false;
+ }
+ if (faceBeauty != null) {
+ /* 如果用户开启了风格推荐 */
+ //下面是否则
+ /* 美肤 */
+ /* 磨皮类型 */
+ if (faceBeautyData.blurType != faceBeauty.getBlurType())
+ faceBeauty.setBlurType(faceBeautyData.blurType);
+ /* 磨皮程度 */
+ if (faceBeautyData.blurIntensity != faceBeauty.getBlurIntensity())
+ faceBeauty.setBlurIntensity(faceBeautyData.blurIntensity);
+ /* 美白程度 */
+ if (faceBeautyData.colorIntensity != faceBeauty.getColorIntensity())
+ faceBeauty.setColorIntensity(faceBeautyData.colorIntensity);
+ /* 红润程度 */
+ if (faceBeautyData.redIntensity != faceBeauty.getRedIntensity())
+ faceBeauty.setRedIntensity(faceBeautyData.redIntensity);
+ /* 锐化程度 */
+ if (faceBeautyData.sharpenIntensity != faceBeauty.getSharpenIntensity())
+ faceBeauty.setSharpenIntensity(faceBeautyData.sharpenIntensity);
+ /* 亮眼程度 */
+ if (faceBeautyData.eyeBrightIntensity != faceBeauty.getEyeBrightIntensity())
+ faceBeauty.setEyeBrightIntensity(faceBeautyData.eyeBrightIntensity);
+ /* 美牙程度 */
+ if (faceBeautyData.toothIntensity != faceBeauty.getToothIntensity())
+ faceBeauty.setToothIntensity(faceBeautyData.toothIntensity);
+ /* 去黑眼圈强度*/
+ if (faceBeautyData.removePouchIntensity != faceBeauty.getRemovePouchIntensity())
+ faceBeauty.setRemovePouchIntensity(faceBeautyData.removePouchIntensity);
+ /* 去法令纹强度*/
+ if (faceBeautyData.removeLawPatternIntensity != faceBeauty.getRemoveLawPatternIntensity())
+ faceBeauty.setRemoveLawPatternIntensity(faceBeautyData.removeLawPatternIntensity);
+
+ /* 美型 */
+ /* 瘦脸程度 */
+ if (faceBeautyData.cheekThinningIntensity != faceBeauty.getCheekThinningIntensity())
+ faceBeauty.setCheekThinningIntensity(faceBeautyData.cheekThinningIntensity);
+ /* V脸程度 */
+ if (faceBeautyData.cheekVIntensity != faceBeauty.getCheekVIntensity())
+ faceBeauty.setCheekVIntensity(faceBeautyData.cheekVIntensity);
+ /* 窄脸程度 */
+ /* V脸程度 */
+ if (faceBeautyData.cheekNarrowIntensity != faceBeauty.getCheekNarrowIntensity())
+ faceBeauty.setCheekNarrowIntensity(faceBeautyData.cheekNarrowIntensity);
+ /* 短脸程度 */
+ if (faceBeautyData.cheekShortIntensity != faceBeauty.getCheekShortIntensity())
+ faceBeauty.setCheekShortIntensity(faceBeautyData.cheekShortIntensity);
+ /* 小脸程度 */
+ if (faceBeautyData.cheekSmallIntensity != faceBeauty.getCheekSmallIntensity())
+ faceBeauty.setCheekSmallIntensity(faceBeautyData.cheekSmallIntensity);
+ /* 瘦颧骨 */
+ if (faceBeautyData.cheekBonesIntensity != faceBeauty.getCheekBonesIntensity())
+ faceBeauty.setCheekBonesIntensity(faceBeautyData.cheekBonesIntensity);
+ /* 瘦下颌骨 */
+ if (faceBeautyData.lowerJawIntensity != faceBeauty.getLowerJawIntensity())
+ faceBeauty.setLowerJawIntensity(faceBeautyData.lowerJawIntensity);
+ /* 大眼程度 */
+ if (faceBeautyData.eyeEnlargingIntensity != faceBeauty.getEyeEnlargingIntensity())
+ faceBeauty.setEyeEnlargingIntensity(faceBeautyData.eyeEnlargingIntensity);
+ /* 圆眼程度 */
+ if (faceBeautyData.eyeCircleIntensity != faceBeauty.getEyeCircleIntensity())
+ faceBeauty.setEyeCircleIntensity(faceBeautyData.eyeCircleIntensity);
+ /* 下巴调整程度 */
+ if (faceBeautyData.chinIntensity != faceBeauty.getChinIntensity())
+ faceBeauty.setChinIntensity(faceBeautyData.chinIntensity);
+ /* 额头调整程度 */
+ if (faceBeautyData.forHeadIntensity != faceBeauty.getForHeadIntensity())
+ faceBeauty.setForHeadIntensity(faceBeautyData.forHeadIntensity);
+ /* 瘦鼻程度 */
+ if (faceBeautyData.noseIntensity != faceBeauty.getNoseIntensity())
+ faceBeauty.setNoseIntensity(faceBeautyData.noseIntensity);
+ /* 嘴巴调整程度 */
+ if (faceBeautyData.mouthIntensity != faceBeauty.getMouthIntensity())
+ faceBeauty.setMouthIntensity(faceBeautyData.mouthIntensity);
+ /* 开眼角强度 */
+ if (faceBeautyData.canthusIntensity != faceBeauty.getCanthusIntensity())
+ faceBeauty.setCanthusIntensity(faceBeautyData.canthusIntensity);
+ /* 眼睛间距 */
+ if (faceBeautyData.eyeSpaceIntensity != faceBeauty.getEyeSpaceIntensity())
+ faceBeauty.setEyeSpaceIntensity(faceBeautyData.eyeSpaceIntensity);
+ /* 眼睛角度 */
+ if (faceBeautyData.eyeRotateIntensity != faceBeauty.getEyeRotateIntensity())
+ faceBeauty.setEyeRotateIntensity(faceBeautyData.eyeRotateIntensity);
+ /* 鼻子长度 */
+ if (faceBeautyData.longNoseIntensity != faceBeauty.getLongNoseIntensity())
+ faceBeauty.setLongNoseIntensity(faceBeautyData.longNoseIntensity);
+ /* 调节人中 */
+ if (faceBeautyData.philtrumIntensity != faceBeauty.getPhiltrumIntensity())
+ faceBeauty.setPhiltrumIntensity(faceBeautyData.philtrumIntensity);
+ /* 微笑嘴角强度 */
+ if (faceBeautyData.smileIntensity != faceBeauty.getSmileIntensity())
+ faceBeauty.setSmileIntensity(faceBeautyData.smileIntensity);
+ /* 眉毛上下 */
+ if (faceBeautyData.browHeightIntensity != faceBeauty.getBrowHeightIntensity())
+ faceBeauty.setBrowHeightIntensity(faceBeautyData.browHeightIntensity);
+ /* 眉毛间距 */
+ if (faceBeautyData.browSpaceIntensity != faceBeauty.getBrowSpaceIntensity())
+ faceBeauty.setBrowSpaceIntensity(faceBeautyData.browSpaceIntensity);
+
+ /* 滤镜相关 */
+ /* 滤镜名称 */
+ if (!faceBeautyData.filterName.equals(faceBeauty.getFilterName()))
+ faceBeauty.setFilterName(faceBeautyData.filterName);
+ if (faceBeautyData.filterIntensity != faceBeauty.getFilterIntensity())
+ faceBeauty.setFilterIntensity(faceBeautyData.filterIntensity);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * 将FaceBeautyData保存到文件
+ *
+ * @param faceBeautyData
+ */
+ public static void saveFaceBeautyData2File(FaceBeautyData faceBeautyData, FaceBeauty faceBeauty, ArrayList filterList, int styleTypeIndex) {
+ buildFaceBeautyData(faceBeautyData,faceBeauty,filterList,styleTypeIndex);
+ saveFaceBeautyData2File(faceBeautyData);
+ }
+
+ /**
+ * 将FaceBeautyData保存到文件
+ *
+ * @param faceBeautyData
+ */
+ public static void saveFaceBeautyData2File(FaceBeautyData faceBeautyData) {
+ Gson gson = new Gson();
+ String faceBeautyString = gson.toJson(faceBeautyData);
+ saveToSp(SP_KEY_NAME, faceBeautyString);
+ }
+
+ /**
+ * 获取bean对象
+ *
+ * @return
+ */
+ public static FaceBeautyData loadFaceBeautyData() {
+ String faceBeautyData = loadFormSp(SP_KEY_NAME);
+ if (faceBeautyData != null && !faceBeautyData.isEmpty()) {
+ Gson gson = new Gson();
+ FaceBeautyData faceBeautyDataBean = gson.fromJson(faceBeautyData, FaceBeautyData.class);
+ return faceBeautyDataBean;
+ }
+ return null;
+ }
+
+ private static void saveToSp(String key, String value) {
+ SharedPreferences sp = FaceUnityData.mApplication.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
+ sp.edit().putString(key, value).apply();
+ }
+
+ private static String loadFormSp(String key) {
+ SharedPreferences sp = FaceUnityData.mApplication.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
+ return sp.getString(key, "");
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FaceSPUtils.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FaceSPUtils.java
new file mode 100644
index 000000000..4eb1ab1d4
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FaceSPUtils.java
@@ -0,0 +1,46 @@
+package com.yunbao.faceunity.utils;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import com.yunbao.common.CommonAppContext;
+
+import java.util.Map;
+
+public class FaceSPUtils {
+ private static FaceSPUtils utils;
+ private SharedPreferences mSharedPreferences;
+ private FaceSPUtils(){
+ mSharedPreferences = CommonAppContext.sInstance.getSharedPreferences("FaceUnityConfig", Context.MODE_PRIVATE);
+ }
+ public static FaceSPUtils getInstance(){
+ if(utils==null){
+ utils=new FaceSPUtils();
+ }
+ return utils;
+ }
+ public void saveString(String key,String value){
+ mSharedPreferences.edit().putString(key,value).apply();
+ }
+ public String getString(String key){
+ return mSharedPreferences.getString(key,null);
+ }
+ public boolean saveBool(String key,boolean value){
+ return mSharedPreferences.getBoolean(key,false);
+ }
+
+ public void del(String key) {
+ mSharedPreferences.edit().remove(key).apply();
+ }
+
+ public void delStart(String key){
+ for (String _key : mSharedPreferences.getAll().keySet()) {
+ if(_key.startsWith(key)){
+ mSharedPreferences.edit().remove(key).apply();
+ }
+ }
+ }
+ public Map getAll(){
+ return mSharedPreferences.getAll();
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FaceUnityConfig.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FaceUnityConfig.java
new file mode 100644
index 000000000..7ca84f925
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FaceUnityConfig.java
@@ -0,0 +1,127 @@
+package com.yunbao.faceunity.utils;
+
+import android.app.Application;
+import android.os.Environment;
+
+
+import java.io.File;
+
+/**
+ * 一些配置参数
+ * DESC:
+ * Created on 2021/3/1
+ */
+public class FaceUnityConfig {
+
+ /************************** 算法Model ******************************/
+ // 人脸识别
+ public static String BUNDLE_AI_FACE = "model" + File.separator + "ai_face_processor_lite.bundle";
+ // 手势
+ public static String BUNDLE_AI_HAND = "model" + File.separator + "ai_hand_processor.bundle";
+
+ //获取人体bundle
+ public static String getAIHumanBundle() {
+ if (FaceUnityConfig.DEVICE_LEVEL > FuDeviceUtils.DEVICE_LEVEL_MID)
+ return BUNDLE_AI_HUMAN_GPU;
+ else
+ return BUNDLE_AI_HUMAN;
+ }
+
+ // 人体
+ public static String BUNDLE_AI_HUMAN = "model" + File.separator + "ai_human_processor.bundle";
+ // 人体
+ public static String BUNDLE_AI_HUMAN_GPU = "model" + File.separator + "ai_human_processor_gpu.bundle";
+ // 头发
+ public static String BUNDLE_AI_HAIR_SEG = "model" + File.separator + "ai_hairseg.bundle";
+ // 舌头
+ public static String BUNDLE_AI_TONGUE = "graphics" + File.separator + "tongue.bundle";
+
+ /************************** 业务道具存储 ******************************/
+ // 美颜
+ public static String BUNDLE_FACE_BEAUTIFICATION = "graphics" + File.separator + "face_beautification.bundle";
+
+ // 美妆
+ public static String BUNDLE_FACE_MAKEUP = "graphics" + File.separator + "face_makeup.bundle";
+ // 美妆根目录
+ private static String MAKEUP_RESOURCE_DIR = "makeup" + File.separator;
+ //美妆单项颜色组合文件
+ public static String MAKEUP_RESOURCE_COLOR_SETUP_JSON = MAKEUP_RESOURCE_DIR + "color_setup.json";
+ // 美妆参数配置文件目录
+ public static String MAKEUP_RESOURCE_JSON_DIR = MAKEUP_RESOURCE_DIR + "config_json" + File.separator;
+ //美妆组合妆句柄文件目录
+ public static String MAKEUP_RESOURCE_COMBINATION_BUNDLE_DIR = MAKEUP_RESOURCE_DIR + "combination_bundle" + File.separator;//
+ //美妆妆容单项句柄文件目录
+ public static String MAKEUP_RESOURCE_ITEM_BUNDLE_DIR = MAKEUP_RESOURCE_DIR + "item_bundle" + File.separator;
+
+ // 美体
+ public static String BUNDLE_BODY_BEAUTY = "graphics" + File.separator + "body_slim.bundle";
+
+ //动漫滤镜
+ public static String BUNDLE_ANIMATION_FILTER = "graphics" + File.separator + "fuzzytoonfilter.bundle";
+
+ // 美发正常色
+ public static String BUNDLE_HAIR_NORMAL = "hair_seg" + File.separator + "hair_normal.bundle";
+ // 美发渐变色
+ public static String BUNDLE_HAIR_GRADIENT = "hair_seg" + File.separator + "hair_gradient.bundle";
+ // 轻美妆
+ public static String BUNDLE_LIGHT_MAKEUP = "light_makeup" + File.separator + "light_makeup.bundle";
+
+ // 海报换脸
+ public static String BUNDLE_POSTER_CHANGE_FACE = "change_face" + File.separator + "change_face.bundle";
+
+ // 绿幕抠像
+ public static String BUNDLE_BG_SEG_GREEN = "bg_seg_green" + File.separator + "green_screen.bundle";
+
+ // 3D抗锯齿
+ public static String BUNDLE_ANTI_ALIASING = "graphics" + File.separator + "fxaa.bundle";
+
+ // 人像分割
+ public static String BUNDLE_BG_SEG_CUSTOM = "effect" + File.separator + "segment" + File.separator + "bg_segment.bundle";
+
+ //mask bundle
+ public static String BUNDLE_LANDMARKS = "effect" + File.separator + "landmarks.bundle";
+
+ //设备等级默认为中级
+ public static int DEVICE_LEVEL = FuDeviceUtils.DEVICE_LEVEL_HIGH;
+
+ //人脸置信度 标准
+ public static float FACE_CONFIDENCE_SCORE = 0.95f;
+
+ //测试使用 -> 是否开启人脸点位,目前仅在美颜,美妆 情况下使用
+ public static boolean IS_OPEN_LAND_MARK = false;
+
+ //设备名称
+ public static String DEVICE_NAME = "";
+
+ //是否开启日志重定向到文件
+ public static boolean OPEN_FILE_LOG = false;
+ //TAG
+ public static final String APP_NAME = "KotlinFaceUnityDemo";
+ //文件夹路径
+ public static String OPEN_FILE_PATH = Environment.getExternalStoragePublicDirectory("") + File.separator + "FaceUnity" + File.separator + APP_NAME + File.separator;
+ //文件夹名称
+ public static String OPEN_FILE_NAME = "openFile.txt";
+ //文件大小
+ public static int OPEN_FILE_MAX_SIZE = 100 * 1024 * 1024;
+ //文件数量
+ public static int OPEN_FILES = 100;
+
+ //timeProfile是否开启
+ public static boolean OPEN_TIME_PROFILE_LOG = false;
+ //timeProfile文件夹路径
+ public static String OPEN_TIME_PROFILE_PATH = Environment.getExternalStoragePublicDirectory("") + File.separator + "FaceUnity" + File.separator + APP_NAME + File.separator;
+
+ //是否开启美颜序列化到磁盘
+ public static boolean OPEN_FACE_BEAUTY_TO_FILE = true;
+
+ //获取缓存路径
+ public static String cacheFilePath(Application application){
+ return application.getCacheDir() + File.separator + "attribute";
+ }
+
+ //绿幕背景切换的时候跳过的帧数
+ public static final int BG_GREEN_FILTER_FRAME = 1;
+
+ //测试用是否展示效果还原按钮
+ public static final boolean IS_SHOW_RESET_BUTTON = false;
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FaceUnityData.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FaceUnityData.java
new file mode 100644
index 000000000..bb9cc8a03
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FaceUnityData.java
@@ -0,0 +1,7 @@
+package com.yunbao.faceunity.utils;
+
+import android.content.Context;
+
+public class FaceUnityData {
+ public static Context mApplication;
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FileUtils.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FileUtils.java
new file mode 100644
index 000000000..c868cbf1b
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FileUtils.java
@@ -0,0 +1,607 @@
+package com.yunbao.faceunity.utils;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.ImageFormat;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.YuvImage;
+import android.media.ExifInterface;
+import android.media.MediaMetadataRetriever;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.provider.MediaStore;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.regex.Pattern;
+
+/**
+ * DESC:
+ * Created on 2021/3/12
+ */
+public class FileUtils {
+ public static final String DCIM_FILE_PATH = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath();
+ public static final String photoFilePath;
+ public static final String videoFilePath;
+ public static final String exportVideoDir;
+
+ static {
+ if (Build.FINGERPRINT.contains("Flyme")
+ || Pattern.compile("Flyme", Pattern.CASE_INSENSITIVE).matcher(Build.DISPLAY).find()
+ || Build.MANUFACTURER.contains("Meizu")
+ || Build.MANUFACTURER.contains("MeiZu")) {
+ photoFilePath = DCIM_FILE_PATH + File.separator + "Camera" + File.separator;
+ videoFilePath = DCIM_FILE_PATH + File.separator + "Video" + File.separator;
+ } else if (Build.FINGERPRINT.contains("vivo")
+ || Pattern.compile("vivo", Pattern.CASE_INSENSITIVE).matcher(Build.DISPLAY).find()
+ || Build.MANUFACTURER.contains("vivo")
+ || Build.MANUFACTURER.contains("Vivo")) {
+ photoFilePath = videoFilePath = Environment.getExternalStoragePublicDirectory("") + File.separator + "相机" + File.separator;
+ } else {
+ photoFilePath = videoFilePath = DCIM_FILE_PATH + File.separator + "Camera" + File.separator;
+ }
+ exportVideoDir = DCIM_FILE_PATH + File.separator + "FaceUnity" + File.separator;
+ createFileDir(photoFilePath);
+ createFileDir(videoFilePath);
+ createFileDir(exportVideoDir);
+ }
+
+ public static final String IMAGE_FORMAT_JPG = ".jpg";
+ public static final String IMAGE_FORMAT_JPEG = ".jpeg";
+ public static final String IMAGE_FORMAT_PNG = ".png";
+ public static final String VIDEO_FORMAT_MP4 = ".mp4";
+
+
+ /**
+ * 创建文件夹
+ *
+ * @param path
+ */
+ public static void createFileDir(String path) {
+ File dir = new File(path);
+ if (!dir.exists()) {
+ dir.mkdirs();
+ }
+ }
+
+ /**
+ * 应用外部文件目录
+ *
+ * @return
+ */
+ public static File getExternalFileDir(Context context) {
+ File fileDir = context.getFilesDir();
+ return fileDir;
+ }
+
+ /**
+ * 应用下缓存文件目录
+ *
+ * @return
+ */
+ public static File getCacheFileDir(Context context) {
+ File fileDir = context.getCacheDir();
+ if (fileDir == null) {
+ fileDir = context.getFilesDir();
+ }
+ return fileDir;
+ }
+
+ /**
+ * 获取当前时间日期
+ *
+ * @return
+ */
+ public static String getDateTimeString() {
+ GregorianCalendar now = new GregorianCalendar();
+ return new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.US).format(now.getTime());
+ }
+
+
+ /**
+ * 获取视频缓存文件
+ *
+ * @param context Context
+ * @return File
+ */
+ public static File getCacheVideoFile(Context context) {
+ File fileDir = new File(getExternalFileDir(context).getPath() + File.separator + "video");
+ if (!fileDir.exists()) {
+ fileDir.mkdirs();
+ }
+ File file = new File(fileDir, getCurrentVideoFileName());
+ if (file.exists()) {
+ file.delete();
+ }
+ return file;
+ }
+
+ /**
+ * 构造视频文件名称
+ *
+ * @return
+ */
+ public static String getCurrentVideoFileName() {
+ return getDateTimeString() + VIDEO_FORMAT_MP4;
+ }
+
+
+ /**
+ * 构造图片文件名称
+ *
+ * @return
+ */
+ public static String getCurrentPhotoFileName() {
+ return getDateTimeString() + IMAGE_FORMAT_JPG;
+ }
+
+
+ /**
+ * Bitmap保存到本地
+ *
+ * @param context Context
+ * @param bitmap Bitmap
+ * @return String?
+ */
+ public static String addBitmapToExternal(Context context, Bitmap bitmap) {
+ if (bitmap == null) return null;
+ File fileDir = new File(getExternalFileDir(context).getPath() + File.separator + "photo");
+ if (!fileDir.exists()) {
+ fileDir.mkdirs();
+ }
+ File file = new File(fileDir, getCurrentPhotoFileName());
+ if (file.exists()) {
+ file.delete();
+ }
+ try {
+ FileOutputStream fos = new FileOutputStream(file);
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
+ fos.flush();
+ fos.close();
+ return file.getAbsolutePath();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+
+ /**
+ * 将Bitmap文件保存到相册
+ *
+ * @param bitmap Bitmap
+ */
+ public static String addBitmapToAlbum(Context context, Bitmap bitmap) {
+ if (bitmap == null) return null;
+ FileOutputStream fos = null;
+ File dcimFile;
+
+ File fileDir = new File(exportVideoDir);
+ if (fileDir.exists()) {
+ dcimFile = new File(exportVideoDir, getCurrentPhotoFileName());
+ } else {
+ dcimFile = new File(photoFilePath, getCurrentPhotoFileName());
+ }
+ if (dcimFile.exists()) {
+ dcimFile.delete();
+ }
+ try {
+ fos = new FileOutputStream(dcimFile);
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
+ fos.flush();
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ } finally {
+ if (fos != null) {
+ try {
+ fos.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(dcimFile)));
+ return dcimFile.getAbsolutePath();
+ }
+
+
+ /**
+ * 将视频文件保存到相册
+ *
+ * @param videoFile File
+ * @return Uri?
+ */
+
+ public static String addVideoToAlbum(Context context, File videoFile) {
+ if (videoFile == null) return null;
+ File fileDir = new File(exportVideoDir);
+ File dcimFile;
+ if (fileDir.exists()) {
+ dcimFile = new File(exportVideoDir, getCurrentVideoFileName());
+ } else {
+ dcimFile = new File(videoFilePath, getCurrentVideoFileName());
+ }
+ if (dcimFile.exists()) {
+ dcimFile.delete();
+ }
+ BufferedInputStream bis = null;
+ BufferedOutputStream bos = null;
+ try {
+ bis = new BufferedInputStream(new FileInputStream(videoFile));
+ bos = new BufferedOutputStream(new FileOutputStream(dcimFile));
+ byte[] bytes = new byte[1024 * 10];
+ int length;
+ while ((length = bis.read(bytes)) != -1) {
+ bos.write(bytes, 0, length);
+ }
+ bos.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ } finally {
+ if (bis != null) {
+ try {
+ bis.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if (bos != null) {
+ try {
+ bos.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(dcimFile)));
+ return dcimFile.getAbsolutePath();
+ }
+
+
+ /**
+ * 将Assets文件拷贝到应用作用域存储
+ *
+ * @param context Context
+ * @param assetsPath String
+ * @param fileName String
+ */
+ public static String copyAssetsToExternalFilesDir(Context context, String assetsPath, String fileName) {
+ File fileDir = new File(getExternalFileDir(context).getPath() + File.separator + "assets");
+ if (!fileDir.exists()) {
+ fileDir.mkdirs();
+ }
+ File file = new File(fileDir, fileName);
+ if (file.exists()) {
+ return file.getAbsolutePath();
+ }
+ try {
+ InputStream inputStream = context.getAssets().open(assetsPath);
+ FileOutputStream fos = new FileOutputStream(file);
+ BufferedInputStream bis = new BufferedInputStream(inputStream);
+ BufferedOutputStream bos = new BufferedOutputStream(fos);
+ byte[] byteArray = new byte[1024];
+ int bytes = bis.read(byteArray);
+ while (bytes > 0) {
+ bos.write(byteArray, 0, bytes);
+ bos.flush();
+ bytes = bis.read(byteArray);
+ }
+ bos.close();
+ fos.close();
+ return file.getAbsolutePath();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static String copyAssetsFile(Context context, String assetsPath, String fileName, String saveFileDir) {
+ File fileDir = new File(saveFileDir);
+ if (!fileDir.exists()) {
+ fileDir.mkdirs();
+ }
+ File file = new File(fileDir, fileName);
+ if (file.exists()) {
+ return file.getAbsolutePath();
+ }
+ try {
+ InputStream inputStream = context.getAssets().open(assetsPath);
+ FileOutputStream fos = new FileOutputStream(file);
+ BufferedInputStream bis = new BufferedInputStream(inputStream);
+ BufferedOutputStream bos = new BufferedOutputStream(fos);
+ byte[] byteArray = new byte[1024];
+ int bytes = bis.read(byteArray);
+ while (bytes > 0) {
+ bos.write(byteArray, 0, bytes);
+ bos.flush();
+ bytes = bis.read(byteArray);
+ }
+ bos.close();
+ fos.close();
+ return file.getAbsolutePath();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * 获取Uri文件绝对路径
+ *
+ * @param context: Context
+ * @param uri Uri
+ * @return String
+ */
+ public static String getFilePathByUri(Context context, Uri uri) {
+ if (uri == null) return null;
+ return Uri2PathUtil.getRealPathFromUri(context, uri);
+ }
+
+ private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
+ Cursor cursor = null;
+ String[] projection = new String[]{MediaStore.Images.Media.DATA};
+ try {
+ cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
+ if (cursor != null && cursor.moveToFirst()) {
+ int index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
+ return cursor.getString(index);
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * load本地图片
+ *
+ * @param path
+ * @param screenWidth
+ * @return
+ */
+ public static Bitmap loadBitmapFromExternal(String path, int screenWidth) {
+ BitmapFactory.Options opt = new BitmapFactory.Options();
+ opt.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(path, opt);
+ int picWidth = opt.outWidth;
+ int picHeight = opt.outHeight;
+ opt.inSampleSize = 1;
+ // 根据屏的大小和图片大小计算出缩放比例
+ if (picWidth > picHeight) {
+ if (picHeight > screenWidth) {
+ opt.inSampleSize = picHeight / screenWidth;
+ }
+ } else {
+ if (picWidth > screenWidth) {
+ opt.inSampleSize = picWidth / screenWidth;
+ }
+ }
+ opt.inJustDecodeBounds = false;
+ Bitmap bitmap = BitmapFactory.decodeFile(path, opt);
+ int orientation = getPhotoOrientation(path);
+ bitmap = rotateBitmap(bitmap, orientation);
+ return bitmap;
+ }
+
+ /**
+ * load本地图片
+ *
+ * @param path String
+ * @param screenWidth Int
+ * @param screenHeight Int
+ * @return Bitmap
+ */
+ public static Bitmap loadBitmapFromExternal(String path, int screenWidth, int screenHeight) {
+ BitmapFactory.Options opt = new BitmapFactory.Options();
+ opt.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(path, opt);
+ int picWidth = opt.outWidth;
+ int picHeight = opt.outHeight;
+ int inSampleSize = 1;
+ // 根据屏的大小和图片大小计算出缩放比例
+ if (picHeight > screenHeight || picWidth > screenWidth) {
+ int halfHeight = picHeight / 2;
+ int halfWidth = picWidth / 2;
+ while (halfHeight / inSampleSize >= screenHeight && halfWidth / inSampleSize >= screenWidth) {
+ inSampleSize *= 2;
+ }
+ }
+ opt.inSampleSize = inSampleSize;
+ opt.inJustDecodeBounds = false;
+ Bitmap bitmap = BitmapFactory.decodeFile(path, opt);
+ int orientation = getPhotoOrientation(path);
+ bitmap = rotateBitmap(bitmap, orientation);
+ return bitmap;
+ }
+
+
+ /**
+ * 旋转 Bitmap
+ *
+ * @param bitmap
+ * @param orientation
+ * @return
+ */
+ private static Bitmap rotateBitmap(Bitmap bitmap, int orientation) {
+ if (orientation == 90 || orientation == 180 || orientation == 270) {
+ Matrix matrix = new Matrix();
+ matrix.postRotate((float) orientation);
+ bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
+ }
+ return bitmap;
+ }
+
+
+ /**
+ * 获取图片的方向
+ *
+ * @param path
+ * @return
+ */
+ public static int getPhotoOrientation(String path) {
+ int orientation = 0;
+ int tagOrientation = 0;
+ try {
+ tagOrientation = new ExifInterface(path).getAttributeInt(ExifInterface.TAG_ORIENTATION, -1);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ switch (tagOrientation) {
+ case ExifInterface.ORIENTATION_ROTATE_90:
+ orientation = 90;
+ break;
+
+ case ExifInterface.ORIENTATION_ROTATE_180:
+ orientation = 180;
+ break;
+
+ case ExifInterface.ORIENTATION_ROTATE_270:
+ orientation = 270;
+ break;
+ }
+ return orientation;
+ }
+
+
+ /**
+ * 选中图片
+ *
+ * @param activity Activity
+ */
+ public static void pickImageFile(Activity activity, int requestCode) {
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setType("image/*");
+ activity.startActivityForResult(intent, requestCode);
+ }
+
+ /**
+ * 选中视频
+ *
+ * @param activity Activity
+ * 回调可参考下方
+ */
+ public static void pickVideoFile(Activity activity, int requestCode) {
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setType("video/*");
+ activity.startActivityForResult(intent, requestCode);
+ }
+
+ /**
+ * 根据路径获取InputStream
+ *
+ * @param context Context
+ * @param path String
+ * @return InputStream?
+ */
+ public static InputStream readInputStreamByPath(Context context, String path) {
+ if (path == null || path.trim().length() == 0)
+ return null;
+ InputStream inputStream = null;
+ try {
+ inputStream = context.getAssets().open(path);
+ } catch (IOException e1) {
+ try {
+ inputStream = new FileInputStream(path);
+ } catch (IOException e2) {
+ e2.printStackTrace();
+ }
+ }
+ return inputStream;
+ }
+
+ /**
+ * 相机byte转bitmap
+ *
+ * @param buffer
+ * @param width
+ * @param height
+ * @return
+ */
+ public static Bitmap bytes2Bitmap(byte[] buffer, int width, int height) {
+ YuvImage yuvimage = new YuvImage(buffer, ImageFormat.NV21, width, height, null);//20、20分别是图的宽度与高度
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ yuvimage.compressToJpeg(new Rect(0, 0, width, height), 80, baos);//80--JPG图片的质量[0-100],100最高
+ byte[] jdata = baos.toByteArray();
+ return BitmapFactory.decodeByteArray(baos.toByteArray(), 0, jdata.length);
+ }
+
+ /**
+ * 遍历一个文件夹获取改文件夹下所有文件名
+ *
+ * @param path
+ * @return
+ */
+ public static ArrayList getFileList(String path) {
+ ArrayList fileList = new ArrayList<>();
+ File dir = new File(path);
+ // 该文件目录下文件全部放入数组
+ File[] files = dir.listFiles();
+ if (files != null) {
+ for (int i = 0; i < files.length; i++) {
+ // 判断是文件还是文件夹
+ if (files[i].isDirectory()) {
+ // 获取文件绝对路径
+ getFileList(files[i].getAbsolutePath());
+ // 判断文件名是否以.jpg结尾
+ } else {
+ fileList.add(files[i].getName());
+ }
+ }
+ }
+ return fileList;
+ }
+
+ /**
+ * 校验文件是否是图片
+ *
+ * @param path String
+ * @return Boolean
+ */
+ public static Boolean checkIsImage(String path) {
+ String name = new File(path).getName().toLowerCase();
+ return (name.endsWith(IMAGE_FORMAT_PNG) || name.endsWith(IMAGE_FORMAT_JPG)
+ || name.endsWith(IMAGE_FORMAT_JPEG));
+ }
+
+ /**
+ * 校验文件是否是视频
+ *
+ * @param path String
+ * @return Boolean
+ */
+ public static Boolean checkIsVideo(Context context, String path) {
+ MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+ try {
+ retriever.setDataSource(context, Uri.fromFile(new File(path)));
+ String hasVideo = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO);
+ return "yes".equals(hasVideo);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FuDeviceUtils.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FuDeviceUtils.java
new file mode 100644
index 000000000..79a58785e
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FuDeviceUtils.java
@@ -0,0 +1,720 @@
+package com.yunbao.faceunity.utils;
+
+import static android.content.Context.MODE_PRIVATE;
+
+import android.annotation.TargetApi;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.opengl.GLES20;
+import android.os.Build;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.faceunity.core.faceunity.OffLineRenderHandler;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 工具类
+ */
+public class FuDeviceUtils {
+
+ public static final String TAG = "FuDeviceUtils";
+ public static final String DEVICE_LEVEL = "device_level";
+
+ public static final int DEVICE_LEVEL_HIGH = 2;
+ public static final int DEVICE_LEVEL_MID = 1;
+ public static final int DEVICE_LEVEL_LOW = 0;
+
+ /**
+ * The default return value of any method in this class when an
+ * error occurs or when processing fails (Currently set to -1). Use this to check if
+ * the information about the device in question was successfully obtained.
+ */
+ public static final int DEVICEINFO_UNKNOWN = -1;
+
+ private static final FileFilter CPU_FILTER = new FileFilter() {
+ @Override
+ public boolean accept(File pathname) {
+ String path = pathname.getName();
+ //regex is slow, so checking char by char.
+ if (path.startsWith("cpu")) {
+ for (int i = 3; i < path.length(); i++) {
+ if (!Character.isDigit(path.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+ };
+
+
+ /**
+ * Calculates the total RAM of the device through Android API or /proc/meminfo.
+ *
+ * @param c - Context object for current running activity.
+ * @return Total RAM that the device has, or DEVICEINFO_UNKNOWN = -1 in the event of an error.
+ */
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ public static long getTotalMemory(Context c) {
+ // memInfo.totalMem not supported in pre-Jelly Bean APIs.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
+ ActivityManager am = (ActivityManager) c.getSystemService(Context.ACTIVITY_SERVICE);
+ am.getMemoryInfo(memInfo);
+ if (memInfo != null) {
+ return memInfo.totalMem;
+ } else {
+ return DEVICEINFO_UNKNOWN;
+ }
+ } else {
+ long totalMem = DEVICEINFO_UNKNOWN;
+ try {
+ FileInputStream stream = new FileInputStream("/proc/meminfo");
+ try {
+ totalMem = parseFileForValue("MemTotal", stream);
+ totalMem *= 1024;
+ } finally {
+ stream.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return totalMem;
+ }
+ }
+
+ /**
+ * Method for reading the clock speed of a CPU core on the device. Will read from either
+ * {@code /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq} or {@code /proc/cpuinfo}.
+ *
+ * @return Clock speed of a core on the device, or -1 in the event of an error.
+ */
+ public static int getCPUMaxFreqKHz() {
+ int maxFreq = DEVICEINFO_UNKNOWN;
+ try {
+ for (int i = 0; i < getNumberOfCPUCores(); i++) {
+ String filename =
+ "/sys/devices/system/cpu/cpu" + i + "/cpufreq/cpuinfo_max_freq";
+ File cpuInfoMaxFreqFile = new File(filename);
+ if (cpuInfoMaxFreqFile.exists() && cpuInfoMaxFreqFile.canRead()) {
+ byte[] buffer = new byte[128];
+ FileInputStream stream = new FileInputStream(cpuInfoMaxFreqFile);
+ try {
+ stream.read(buffer);
+ int endIndex = 0;
+ //Trim the first number out of the byte buffer.
+ while (Character.isDigit(buffer[endIndex]) && endIndex < buffer.length) {
+ endIndex++;
+ }
+ String str = new String(buffer, 0, endIndex);
+ Integer freqBound = Integer.parseInt(str);
+ if (freqBound > maxFreq) {
+ maxFreq = freqBound;
+ }
+ } catch (NumberFormatException e) {
+ //Fall through and use /proc/cpuinfo.
+ } finally {
+ stream.close();
+ }
+ }
+ }
+ if (maxFreq == DEVICEINFO_UNKNOWN) {
+ FileInputStream stream = new FileInputStream("/proc/cpuinfo");
+ try {
+ int freqBound = parseFileForValue("cpu MHz", stream);
+ freqBound *= 1024; //MHz -> kHz
+ if (freqBound > maxFreq) maxFreq = freqBound;
+ } finally {
+ stream.close();
+ }
+ }
+ } catch (IOException e) {
+ maxFreq = DEVICEINFO_UNKNOWN; //Fall through and return unknown.
+ }
+ return maxFreq;
+ }
+
+ /**
+ * Reads the number of CPU cores from the first available information from
+ * {@code /sys/devices/system/cpu/possible}, {@code /sys/devices/system/cpu/present},
+ * then {@code /sys/devices/system/cpu/}.
+ *
+ * @return Number of CPU cores in the phone, or DEVICEINFO_UKNOWN = -1 in the event of an error.
+ */
+ public static int getNumberOfCPUCores() {
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
+ // Gingerbread doesn't support giving a single application access to both cores, but a
+ // handful of devices (Atrix 4G and Droid X2 for example) were released with a dual-core
+ // chipset and Gingerbread; that can let an app in the background run without impacting
+ // the foreground application. But for our purposes, it makes them single core.
+ return 1;
+ }
+ int cores;
+ try {
+ cores = getCoresFromFileInfo("/sys/devices/system/cpu/possible");
+ if (cores == DEVICEINFO_UNKNOWN) {
+ cores = getCoresFromFileInfo("/sys/devices/system/cpu/present");
+ }
+ if (cores == DEVICEINFO_UNKNOWN) {
+ cores = new File("/sys/devices/system/cpu/").listFiles(CPU_FILTER).length;
+ }
+ } catch (SecurityException e) {
+ cores = DEVICEINFO_UNKNOWN;
+ } catch (NullPointerException e) {
+ cores = DEVICEINFO_UNKNOWN;
+ }
+ return cores;
+ }
+
+ /**
+ * Tries to read file contents from the file location to determine the number of cores on device.
+ *
+ * @param fileLocation The location of the file with CPU information
+ * @return Number of CPU cores in the phone, or DEVICEINFO_UKNOWN = -1 in the event of an error.
+ */
+ private static int getCoresFromFileInfo(String fileLocation) {
+ InputStream is = null;
+ try {
+ is = new FileInputStream(fileLocation);
+ BufferedReader buf = new BufferedReader(new InputStreamReader(is));
+ String fileContents = buf.readLine();
+ buf.close();
+ return getCoresFromFileString(fileContents);
+ } catch (IOException e) {
+ return DEVICEINFO_UNKNOWN;
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ // Do nothing.
+ }
+ }
+ }
+ }
+
+ /**
+ * Converts from a CPU core information format to number of cores.
+ *
+ * @param str The CPU core information string, in the format of "0-N"
+ * @return The number of cores represented by this string
+ */
+ private static int getCoresFromFileString(String str) {
+ if (str == null || !str.matches("0-[\\d]+$")) {
+ return DEVICEINFO_UNKNOWN;
+ }
+ return Integer.valueOf(str.substring(2)) + 1;
+ }
+
+ /**
+ * Helper method for reading values from system files, using a minimised buffer.
+ *
+ * @param textToMatch - Text in the system files to read for.
+ * @param stream - FileInputStream of the system file being read from.
+ * @return A numerical value following textToMatch in specified the system file.
+ * -1 in the event of a failure.
+ */
+ private static int parseFileForValue(String textToMatch, FileInputStream stream) {
+ byte[] buffer = new byte[1024];
+ try {
+ int length = stream.read(buffer);
+ for (int i = 0; i < length; i++) {
+ if (buffer[i] == '\n' || i == 0) {
+ if (buffer[i] == '\n') i++;
+ for (int j = i; j < length; j++) {
+ int textIndex = j - i;
+ //Text doesn't match query at some point.
+ if (buffer[j] != textToMatch.charAt(textIndex)) {
+ break;
+ }
+ //Text matches query here.
+ if (textIndex == textToMatch.length() - 1) {
+ return extractValue(buffer, j);
+ }
+ }
+ }
+ }
+ } catch (IOException e) {
+ //Ignore any exceptions and fall through to return unknown value.
+ } catch (NumberFormatException e) {
+ }
+ return DEVICEINFO_UNKNOWN;
+ }
+
+ /**
+ * Helper method used by {@link #parseFileForValue(String, FileInputStream) parseFileForValue}. Parses
+ * the next available number after the match in the file being read and returns it as an integer.
+ *
+ * @param index - The index in the buffer array to begin looking.
+ * @return The next number on that line in the buffer, returned as an int. Returns
+ * DEVICEINFO_UNKNOWN = -1 in the event that no more numbers exist on the same line.
+ */
+ private static int extractValue(byte[] buffer, int index) {
+ while (index < buffer.length && buffer[index] != '\n') {
+ if (Character.isDigit(buffer[index])) {
+ int start = index;
+ index++;
+ while (index < buffer.length && Character.isDigit(buffer[index])) {
+ index++;
+ }
+ String str = new String(buffer, 0, start, index - start);
+ return Integer.parseInt(str);
+ }
+ index++;
+ }
+ return DEVICEINFO_UNKNOWN;
+ }
+
+ /**
+ * 获取当前剩余内存(ram)
+ *
+ * @param context
+ * @return
+ */
+ public static long getAvailMemory(Context context) {
+ ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
+ am.getMemoryInfo(mi);
+ return mi.availMem;
+ }
+
+ /**
+ * 获取厂商信息
+ *
+ * @return
+ */
+ public static String getBrand() {
+ return Build.BRAND;
+ }
+
+ /**
+ * 获取手机机型
+ *
+ * @return
+ */
+ public static String getModel() {
+ return Build.MODEL;
+ }
+
+ /**
+ * 获取硬件信息(cpu型号)
+ *
+ * @return
+ */
+ public static String getHardWare() {
+ try {
+ FileReader fr = new FileReader("/proc/cpuinfo");
+ BufferedReader br = new BufferedReader(fr);
+ String text;
+ String last = "";
+ while ((text = br.readLine()) != null) {
+ last = text;
+ }
+ //一般机型的cpu型号都会在cpuinfo文件的最后一行
+ if (last.contains("Hardware")) {
+ String[] hardWare = last.split(":\\s+", 2);
+ return hardWare[1];
+ }
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return Build.HARDWARE;
+ }
+
+ /**
+ * Level judgement based on current memory and CPU.
+ *
+ * @param context - Context object.
+ * @return
+ */
+ public static int judgeDeviceLevel(Context context) {
+ //加一个sp读取 和 设置
+ int cacheDeviceLevel = getCacheDeviceLevel(context);
+ if (cacheDeviceLevel > -1) {
+ return cacheDeviceLevel;
+ }
+
+ int level;
+ //有一些设备不符合下述的判断规则,则走一个机型判断模式
+ int specialDevice = judgeDeviceLevelInDeviceName();
+ if (specialDevice >= 0) return specialDevice;
+
+ int ramLevel = judgeMemory(context);
+ int cpuLevel = judgeCPU();
+ if (ramLevel == 0 || ramLevel == 1 || cpuLevel == 0) {
+ level = DEVICE_LEVEL_LOW;
+ } else {
+ if (cpuLevel > 1) {
+ level = DEVICE_LEVEL_HIGH;
+ } else {
+ level = DEVICE_LEVEL_MID;
+ }
+ }
+ Log.d(TAG,"DeviceLevel: " + level);
+ saveCacheDeviceLevel(context,level);
+ return level;
+ }
+
+ /**
+ * Level judgement based on current GPU.
+ * 需要GL环境
+ * @return
+ */
+ public static int judgeDeviceLevelGPU(Context context) {
+ int cacheDeviceLevel = getCacheDeviceLevel(context);
+ if (cacheDeviceLevel > -1) {
+ return cacheDeviceLevel;
+ }
+
+ OffLineRenderHandler.getInstance().onResume();
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ //加一个sp读取 和 设置
+ final int[] level = {-1};
+ //有一些设备不符合下述的判断规则,则走一个机型判断模式
+ OffLineRenderHandler.getInstance().queueEvent(() -> {
+ try {
+ //高低端名单
+ int specialDevice = judgeDeviceLevelInDeviceName();
+ level[0] = specialDevice;
+ if (specialDevice >= 0) return;
+
+ String glRenderer = GLES20.glGetString(GLES20.GL_RENDERER); //GPU 渲染器
+ String glVendor = GLES20.glGetString(GLES20.GL_VENDOR); //GPU 供应商
+ int GPUVersion;
+ if ("Qualcomm".equals(glVendor)) {
+ //高通
+ if (glRenderer != null && glRenderer.startsWith("Adreno")) {
+ //截取后面的数字
+ String GPUVersionStr = glRenderer.substring(glRenderer.lastIndexOf(" ") + 1);
+ try {
+ GPUVersion = Integer.parseInt(GPUVersionStr);
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ //可能是后面还包含了非数字的东西,那么截取三位
+ String GPUVersionStrNew = GPUVersionStr.substring(0,3);
+ GPUVersion = Integer.parseInt(GPUVersionStrNew);
+ }
+
+ if (GPUVersion >= 512) {
+ level[0] = DEVICE_LEVEL_HIGH;
+ } else {
+ level[0] = DEVICE_LEVEL_MID;
+ }
+ countDownLatch.countDown();
+ }
+ } else if ("ARM".equals(glVendor)) {
+ //ARM
+ if (glRenderer != null && glRenderer.startsWith("Mali")) {
+ //截取-后面的东西
+ String GPUVersionStr = glRenderer.substring(glRenderer.lastIndexOf("-") + 1);
+ String strStart = GPUVersionStr.substring(0, 1);
+ String strEnd = GPUVersionStr.substring(1);
+ try {
+ GPUVersion = Integer.parseInt(strEnd);
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ //可能是后面还包含了非数字的东西,那么截取三位
+ String strEndNew = strEnd.substring(0,2);
+ GPUVersion = Integer.parseInt(strEndNew);
+ }
+
+ if ("G".equals(strStart)) {
+ if (GPUVersion >= 51) {
+ level[0] = DEVICE_LEVEL_HIGH;
+ } else {
+ level[0] = DEVICE_LEVEL_MID;
+ }
+ } else if ("T".equals(strStart)) {
+ if (GPUVersion > 880) {
+ level[0] = DEVICE_LEVEL_HIGH;
+ } else {
+ level[0] = DEVICE_LEVEL_MID;
+ }
+ }
+ countDownLatch.countDown();
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ level[0] = -1;
+ countDownLatch.countDown();
+ }
+ });
+
+ try {
+ countDownLatch.await(200,TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ OffLineRenderHandler.getInstance().onPause();
+
+ //存储设备等级
+ saveCacheDeviceLevel(context,level[0]);
+ Log.d(TAG,"DeviceLevel: " + level);
+ return level[0];
+ }
+
+ /**
+ * -1 不是特定的高低端机型
+ * @return
+ */
+ private static int judgeDeviceLevelInDeviceName() {
+ String currentDeviceName = getDeviceName();
+ for (String deviceName:upscaleDevice) {
+ if (deviceName.equals(currentDeviceName)) {
+ return DEVICE_LEVEL_HIGH;
+ }
+ }
+
+ for (String deviceName:middleDevice) {
+ if (deviceName.equals(currentDeviceName)) {
+ return DEVICE_LEVEL_MID;
+ }
+ }
+
+ for (String deviceName:lowDevice) {
+ if (deviceName.equals(currentDeviceName)) {
+ return DEVICE_LEVEL_LOW;
+ }
+ }
+ return -1;
+ }
+
+ public static final String[] upscaleDevice = {"vivo X6S A","MHA-AL00","VKY-AL00","V1838A"};
+ public static final String[] lowDevice = {};
+ public static final String[] middleDevice = {"OPPO R11s","PAR-AL00","MI 8 Lite","ONEPLUS A6000","PRO 6","PRO 7 Plus"};
+
+ /**
+ * 评定内存的等级.
+ *
+ * @return
+ */
+ private static int judgeMemory(Context context) {
+ long ramMB = getTotalMemory(context) / (1024 * 1024);
+ int level = -1;
+ if (ramMB <= 2000) { //2G或以下的最低档
+ level = 0;
+ } else if (ramMB <= 3000) { //2-3G
+ level = 1;
+ } else if (ramMB <= 4000) { //4G档 2018主流中端机
+ level = 2;
+ } else if (ramMB <= 6000) { //6G档 高端机
+ level = 3;
+ } else { //6G以上 旗舰机配置
+ level = 4;
+ }
+ return level;
+ }
+
+ /**
+ * 评定CPU等级.(按频率和厂商型号综合判断)
+ *
+ * @return
+ */
+ private static int judgeCPU() {
+ int level = 0;
+ String cpuName = getHardWare();
+ int freqMHz = getCPUMaxFreqKHz() / 1024;
+
+ //一个不符合下述规律的高级白名单
+ //如果可以获取到CPU型号名称 -> 根据不同的名称走不同判定策略
+ if (!TextUtils.isEmpty(cpuName)) {
+ if (cpuName.contains("qcom") || cpuName.contains("Qualcomm")) { //高通骁龙
+ return judgeQualcommCPU(cpuName, freqMHz);
+ } else if (cpuName.contains("hi") || cpuName.contains("kirin")) { //海思麒麟
+ return judgeSkinCPU(cpuName, freqMHz);
+ } else if (cpuName.contains("MT")) {//联发科
+ return judgeMTCPU(cpuName, freqMHz);
+ }
+ }
+
+ //cpu型号无法获取的普通规则
+ if (freqMHz <= 1600) { //1.5G 低端
+ level = 0;
+ } else if (freqMHz <= 1950) { //2GHz 低中端
+ level = 1;
+ } else if (freqMHz <= 2500) { //2.2 2.3g 中高端
+ level = 2;
+ } else { //高端
+ level = 3;
+ }
+ return level;
+ }
+
+ /**
+ * 联发科芯片等级判定
+ *
+ * @return
+ */
+ private static int judgeMTCPU(String cpuName, int freqMHz) {
+ //P60之前的全是低端机 MT6771V/C
+ int level = 0;
+ int mtCPUVersion = getMTCPUVersion(cpuName);
+ if (mtCPUVersion == -1) {
+ //读取不出version 按照一个比较严格的方式来筛选出高端机
+ if (freqMHz <= 1600) { //1.5G 低端
+ level = 0;
+ } else if (freqMHz <= 2200) { //2GHz 低中端
+ level = 1;
+ } else if (freqMHz <= 2700) { //2.2 2.3g 中高端
+ level = 2;
+ } else { //高端
+ level = 3;
+ }
+ } else if (mtCPUVersion < 6771) {
+ //均为中低端机
+ if (freqMHz <= 1600) { //1.5G 低端
+ level = 0;
+ } else { //2GHz 中端
+ level = 1;
+ }
+ } else {
+ if (freqMHz <= 1600) { //1.5G 低端
+ level = 0;
+ } else if (freqMHz <= 1900) { //2GHz 低中端
+ level = 1;
+ } else if (freqMHz <= 2500) { //2.2 2.3g 中高端
+ level = 2;
+ } else { //高端
+ level = 3;
+ }
+ }
+
+ return level;
+ }
+
+ /**
+ * 通过联发科CPU型号定义 -> 获取cpu version
+ *
+ * @param cpuName
+ * @return
+ */
+ private static int getMTCPUVersion(String cpuName) {
+ //截取MT后面的四位数字
+ int cpuVersion = -1;
+ if (cpuName.length() > 5) {
+ String cpuVersionStr = cpuName.substring(2, 6);
+ try {
+ cpuVersion = Integer.valueOf(cpuVersionStr);
+ } catch (NumberFormatException exception) {
+ exception.printStackTrace();
+ }
+ }
+
+ return cpuVersion;
+ }
+
+ /**
+ * 高通骁龙芯片等级判定
+ *
+ * @return
+ */
+ private static int judgeQualcommCPU(String cpuName, int freqMHz) {
+ int level = 0;
+ //xxxx inc MSM8937 比较老的芯片
+ //7 8 xxx inc SDM710
+ if (cpuName.contains("MSM")) {
+ //老芯片
+ if (freqMHz <= 1600) { //1.5G 低端
+ level = 0;
+ } else { //2GHz 低中端
+ level = 1;
+ }
+ } else {
+ //新的芯片
+ if (freqMHz <= 1600) { //1.5G 低端
+ level = 0;
+ } else if (freqMHz <= 2000) { //2GHz 低中端
+ level = 1;
+ } else if (freqMHz <= 2500) { //2.2 2.3g 中高端
+ level = 2;
+ } else { //高端
+ level = 3;
+ }
+ }
+
+ return level;
+ }
+
+ /**
+ * 麒麟芯片等级判定
+ *
+ * @param freqMHz
+ * @return
+ */
+ private static int judgeSkinCPU(String cpuName, int freqMHz) {
+ //型号 -> kirin710之后 & 最高核心频率
+ int level = 0;
+ if (cpuName.startsWith("hi")) {
+ //这个是海思的芯片中低端
+ if (freqMHz <= 1600) { //1.5G 低端
+ level = 0;
+ } else if (freqMHz <= 2000) { //2GHz 低中端
+ level = 1;
+ }
+ } else {
+ //这个是海思麒麟的芯片
+ if (freqMHz <= 1600) { //1.5G 低端
+ level = 0;
+ } else if (freqMHz <= 2000) { //2GHz 低中端
+ level = 1;
+ } else if (freqMHz <= 2500) { //2.2 2.3g 中高端
+ level = 2;
+ } else { //高端
+ level = 3;
+ }
+ }
+
+ return level;
+ }
+
+ public static final String Nexus_6P = "Nexus 6P";
+
+ /**
+ * 获取设备名
+ *
+ * @return
+ */
+ public static String getDeviceName() {
+ String deviceName = "";
+ if (Build.MODEL != null) deviceName = Build.MODEL;
+ Log.d(TAG,"deviceName: " + deviceName);
+ return deviceName;
+ }
+
+ /**
+ * 缓存设备等级
+ *
+ * @param level
+ */
+ public static void saveCacheDeviceLevel(Context context,int level) {
+ SharedPreferences sp = context.getSharedPreferences(DEVICE_LEVEL, MODE_PRIVATE);
+ sp.edit().putInt(DEVICE_LEVEL, level).apply();
+ }
+
+ /**
+ * 获取设备等级
+ *
+ * @return
+ */
+ public static int getCacheDeviceLevel(Context context) {
+ SharedPreferences sp = context.getSharedPreferences(DEVICE_LEVEL, MODE_PRIVATE);
+ return sp.getInt(DEVICE_LEVEL, -1);
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/IFURenderer.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/IFURenderer.java
new file mode 100644
index 000000000..e6be37267
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/IFURenderer.java
@@ -0,0 +1,249 @@
+package com.yunbao.faceunity.utils;
+
+
+import android.content.Context;
+
+import com.faceunity.core.enumeration.CameraFacingEnum;
+import com.faceunity.core.enumeration.FUAIProcessorEnum;
+import com.faceunity.core.enumeration.FUExternalInputEnum;
+import com.faceunity.core.enumeration.FUInputBufferEnum;
+import com.faceunity.core.enumeration.FUInputTextureEnum;
+import com.faceunity.core.enumeration.FUTransformMatrixEnum;
+import com.yunbao.faceunity.listener.FURendererListener;
+
+/**
+ * DESC:
+ * Created on 2021/4/26
+ */
+abstract class IFURenderer {
+
+ /**
+ * 渲染属性
+ */
+ protected FUExternalInputEnum externalInputType = FUExternalInputEnum.EXTERNAL_INPUT_TYPE_CAMERA;//数据源类型
+ protected FUInputTextureEnum inputTextureType = FUInputTextureEnum.FU_ADM_FLAG_EXTERNAL_OES_TEXTURE;//纹理类型
+ protected FUInputBufferEnum inputBufferType = FUInputBufferEnum.FU_FORMAT_NV21_BUFFER;//数据类型
+ protected int inputOrientation = 270;//数据源朝向
+ protected int deviceOrientation = 90;//手机设备朝向
+ protected CameraFacingEnum cameraFacing = CameraFacingEnum.CAMERA_FRONT; //数据源为相机时候->前后置相机
+ protected FUTransformMatrixEnum inputTextureMatrix = FUTransformMatrixEnum.CCROT0_FLIPVERTICAL;//纹理旋转类型
+ protected FUTransformMatrixEnum inputBufferMatrix = FUTransformMatrixEnum.CCROT0_FLIPVERTICAL;//图象旋转类型
+ protected FUTransformMatrixEnum outputMatrix = FUTransformMatrixEnum.CCROT0;//图象旋转类型
+
+
+ /**
+ * 初始化鉴权
+ */
+ public abstract void setup(Context context);
+
+
+ /**
+ * 双输入接口,输入 buffer 和 texture,必须在具有 GL 环境的线程调用
+ * 由于省去数据拷贝,性能相对最优,优先推荐使用。
+ * 缺点是无法保证 buffer 和纹理对齐,可能出现点位和效果对不上的情况。
+ *
+ * @param img NV21 buffer
+ * @param texId 纹理 ID
+ * @param width 宽
+ * @param height 高
+ * @return
+ */
+ public abstract int onDrawFrameDualInput(byte[] img, int texId, int width, int height);
+
+ /**
+ * 类似 GLSurfaceView 的 queueEvent 机制,把任务抛到 GL 线程执行。
+ *
+ * @param runnable
+ */
+ public abstract void queueEvent(Runnable runnable);
+
+
+ /**
+ * 设置检测类型
+ *
+ * @param type
+ */
+ public abstract void setAIProcessTrackType(FUAIProcessorEnum type);
+
+ /**
+ * 资源释放
+ */
+ public abstract void release();
+
+ /**
+ * 开启合成状态
+ */
+ public abstract void prepareRenderer(FURendererListener mFURendererListener);
+
+
+ /**
+ * 设置FPS检测
+ *
+ * @param enable
+ */
+ public abstract void setMarkFPSEnable(boolean enable);
+
+
+
+ /**
+ * 获取输入源类型
+ *
+ * @return
+ */
+ public FUExternalInputEnum getExternalInputType() {
+ return externalInputType;
+ }
+
+ /**
+ * 设置输入源类型
+ *
+ * @param externalInputType
+ */
+ public void setExternalInputType(FUExternalInputEnum externalInputType) {
+ this.externalInputType = externalInputType;
+ }
+
+ /**
+ * 获取输入纹理类型
+ *
+ * @return
+ */
+ public FUInputTextureEnum getInputTextureType() {
+ return inputTextureType;
+ }
+
+ /**
+ * 设置输入纹理类型
+ *
+ * @param inputTextureType
+ */
+ public void setInputTextureType(FUInputTextureEnum inputTextureType) {
+ this.inputTextureType = inputTextureType;
+ }
+
+ /**
+ * 获取输入Buffer类型
+ *
+ * @return
+ */
+ public FUInputBufferEnum getInputBufferType() {
+ return inputBufferType;
+ }
+
+ /**
+ * 设置输入Buffer类型
+ *
+ * @param inputBufferType
+ */
+ public void setInputBufferType(FUInputBufferEnum inputBufferType) {
+ this.inputBufferType = inputBufferType;
+ }
+
+ /**
+ * 获取输入数据朝向
+ *
+ * @return
+ */
+ public int getInputOrientation() {
+ return inputOrientation;
+ }
+
+ /**
+ * 设置输入数据朝向
+ *
+ * @param inputOrientation
+ */
+ public void setInputOrientation(int inputOrientation) {
+ this.inputOrientation = inputOrientation;
+ }
+
+ /**
+ * 获取设备类型
+ *
+ * @return
+ */
+ public int getDeviceOrientation() {
+ return deviceOrientation;
+ }
+
+ /**
+ * 设置设备类型
+ *
+ * @param deviceOrientation
+ */
+ public void setDeviceOrientation(int deviceOrientation) {
+ this.deviceOrientation = deviceOrientation;
+ }
+
+ /**
+ * 获取设备朝向
+ *
+ * @return
+ */
+ public CameraFacingEnum getCameraFacing() {
+ return cameraFacing;
+ }
+
+ /**
+ * 设置设备朝向
+ *
+ * @param cameraFacing
+ */
+ public void setCameraFacing(CameraFacingEnum cameraFacing) {
+ this.cameraFacing = cameraFacing;
+ }
+
+ /**
+ * 获取输入纹理旋转类型
+ *
+ * @return
+ */
+ public FUTransformMatrixEnum getInputTextureMatrix() {
+ return inputTextureMatrix;
+ }
+
+ /**
+ * 设置输入纹理旋转类型
+ *
+ * @param inputTextureMatrix
+ */
+ public void setInputTextureMatrix(FUTransformMatrixEnum inputTextureMatrix) {
+ this.inputTextureMatrix = inputTextureMatrix;
+ }
+
+ /**
+ * 获取输入数据旋转类型
+ *
+ * @return
+ */
+ public FUTransformMatrixEnum getInputBufferMatrix() {
+ return inputBufferMatrix;
+ }
+
+ /**
+ * 设置输入数据旋转类型
+ *
+ * @param inputBufferMatrix
+ */
+ public void setInputBufferMatrix(FUTransformMatrixEnum inputBufferMatrix) {
+ this.inputBufferMatrix = inputBufferMatrix;
+ }
+
+ /**
+ * 获取输出数据旋转类型
+ *
+ * @return
+ */
+ public FUTransformMatrixEnum getOutputMatrix() {
+ return outputMatrix;
+ }
+
+ /**
+ * 设置输出数据旋转类型
+ *
+ * @param outputMatrix
+ */
+ public void setOutputMatrix(FUTransformMatrixEnum outputMatrix) {
+ this.outputMatrix = outputMatrix;
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/MemoryInfoUtil.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/MemoryInfoUtil.java
new file mode 100644
index 000000000..6b5f95c3f
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/MemoryInfoUtil.java
@@ -0,0 +1,90 @@
+package com.yunbao.faceunity.utils;
+
+import android.os.Build;
+import android.os.Debug;
+
+import androidx.annotation.RequiresApi;
+
+import java.lang.reflect.Field;
+
+/**
+ * 内存使用率获取工具类
+ * Created by tujh on 2018/5/24.
+ */
+public abstract class MemoryInfoUtil {
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ public static double getMemory(Debug.MemoryInfo[] memoryInfos) {
+ try {
+ if (Build.VERSION.SDK_INT >= 27) {
+ return compute(memoryInfos);
+ }
+
+ Field otherStats_Field = memoryInfos[0].getClass().getDeclaredField("otherStats");
+ otherStats_Field.setAccessible(true);
+ int[] otherStats = (int[]) otherStats_Field.get(memoryInfos[0]);
+
+ Field NUM_CATEGORIES_Field = memoryInfos[0].getClass().getDeclaredField("NUM_CATEGORIES");
+ Field offsetPrivateClean_Field = memoryInfos[0].getClass().getDeclaredField("offsetPrivateClean");
+ Field offsetPrivateDirty_Field = memoryInfos[0].getClass().getDeclaredField("offsetPrivateDirty");
+ final int NUM_CATEGORIES = (int) NUM_CATEGORIES_Field.get(memoryInfos[0]);
+ final int offsetPrivateClean = (int) offsetPrivateClean_Field.get(memoryInfos[0]);
+ final int offsetPrivateDirty = (int) offsetPrivateDirty_Field.get(memoryInfos[0]);
+
+ int javaHeap = memoryInfos[0].dalvikPrivateDirty
+ + otherStats[12 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[12 * NUM_CATEGORIES + offsetPrivateDirty];
+ int nativeHeap = memoryInfos[0].nativePrivateDirty;
+ int code = otherStats[6 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[6 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[7 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[7 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[8 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[8 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[9 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[9 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[10 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[10 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[11 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[11 * NUM_CATEGORIES + offsetPrivateDirty];
+ int stack = otherStats[NUM_CATEGORIES + offsetPrivateDirty];
+ int graphics = otherStats[4 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[4 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[14 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[14 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[15 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[15 * NUM_CATEGORIES + offsetPrivateDirty];
+ int other = otherStats[0 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[0 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[2 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[2 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[3 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[3 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[5 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[5 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[13 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[13 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[16 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[16 * NUM_CATEGORIES + offsetPrivateDirty];
+ int all = javaHeap + nativeHeap + code + stack + graphics + other;
+// Log.d("MemoryAll", "javaHeap=" + javaHeap
+// + "\nnativeHeap=" + nativeHeap + "\ncode=" + code + "\nstack=" + stack
+// + "\ngraphics=" + graphics + "\nother=" + other);
+// Log.e(TAG, "memory " + memoryStr
+// + " java-heap " + String.format("%.2f", ((double) javaHeap) / 1024)
+// + " native-heap " + String.format("%.2f", ((double) nativeHeap) / 1024)
+// + " code " + String.format("%.2f", ((double) code) / 1024)
+// + " stack " + String.format("%.2f", ((double) stack) / 1024)
+// + " graphics " + String.format("%.2f", ((double) graphics) / 1024)
+// + " other " + String.format("%.2f", ((double) other) / 1024)
+// + " pps " + String.format("%.2f", ((double) memoryInfos[0].getTotalPss()) / 1024)
+// );
+ return ((double) all) / 1024;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return 0;
+ }
+
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ private static double compute(Debug.MemoryInfo[] memInfo) {
+ String java_mem = memInfo[0].getMemoryStat("summary.java-heap");
+ String native_mem = memInfo[0].getMemoryStat("summary.native-heap");
+ String graphics_mem = memInfo[0].getMemoryStat("summary.graphics");
+ String stack_mem = memInfo[0].getMemoryStat("summary.stack");
+ String code_mem = memInfo[0].getMemoryStat("summary.code");
+ String others_mem = memInfo[0].getMemoryStat("summary.system");
+ int all = Integer.parseInt(java_mem)
+ + Integer.parseInt(native_mem)
+ + Integer.parseInt(graphics_mem)
+ + Integer.parseInt(stack_mem)
+ + Integer.parseInt(code_mem)
+ + Integer.parseInt(others_mem);
+ return ((double) all) / 1024;
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/SeekBarUtils.kt b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/SeekBarUtils.kt
new file mode 100644
index 000000000..cadecf261
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/SeekBarUtils.kt
@@ -0,0 +1,37 @@
+package com.yunbao.faceunity.utils
+
+import android.view.View
+import com.yunbao.faceunity.seekbar.DiscreteSeekBar
+
+class SeekBarUtils {
+ companion object {
+ /**
+ * 设置滚动条数值
+ * @param value Double 结果值
+ * @param stand Double 标准值
+ * @param range Double 范围区间
+ */
+ fun seekToSeekBar(bar: DiscreteSeekBar, value: Double, stand: Double, range: Double) {
+ if (stand == 0.5) {
+ bar.min = -50
+ bar.max = 50
+ bar.progress = (value * 100 / range - 50).toInt()
+ } else {
+ bar.min = 0
+ bar.max = 100
+ bar.progress = (value * 100 / range).toInt()
+ }
+ bar.visibility = View.VISIBLE
+ }
+
+ /**
+ * 转换成滚动条的值
+ */
+ fun seekToValue(range:Double,value:Int,seekBarMin:Int):Double{
+ val valueF: Double = 1.0 * (value - seekBarMin) / 100
+ return range*valueF;
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/Uri2PathUtil.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/Uri2PathUtil.java
new file mode 100644
index 000000000..01af25574
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/Uri2PathUtil.java
@@ -0,0 +1,161 @@
+package com.yunbao.faceunity.utils;
+
+import android.annotation.TargetApi;
+import android.content.ContentUris;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.provider.DocumentsContract;
+import android.provider.MediaStore;
+
+import androidx.loader.content.CursorLoader;
+
+public class Uri2PathUtil {
+
+ //复杂版处理 (适配多种API)
+ public static String getRealPathFromUri(Context context, Uri uri) {
+ int sdkVersion = Build.VERSION.SDK_INT;
+ if (sdkVersion < 11) return getRealPathFromUri_BelowApi11(context, uri);
+ if (sdkVersion < 19) return getRealPathFromUri_Api11To18(context, uri);
+ else return getRealPathFromUri_AboveApi19(context, uri);
+ }
+
+ /**
+ * 适配api19以上,根据uri获取图片的绝对路径
+ */
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ private static String getRealPathFromUri_AboveApi19(Context context, Uri uri) {
+ if (DocumentsContract.isDocumentUri(context, uri)) {
+ if (isExternalStorageDocument(uri)) {
+ final String docId = DocumentsContract.getDocumentId(uri);
+ final String[] split = docId.split(":");
+ final String type = split[0];
+ if ("primary".equalsIgnoreCase(type)) {
+ return Environment.getExternalStorageDirectory() + "/" + split[1];
+ }
+ } else if (isDownloadsDocument(uri)) {
+ final String id = DocumentsContract.getDocumentId(uri);
+ final Uri contentUri = ContentUris.withAppendedId(
+ Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
+
+ return getDataColumn(context, contentUri, null, null);
+ } else if (isMediaDocument(uri)) {
+ final String docId = DocumentsContract.getDocumentId(uri);
+ final String[] split = docId.split(":");
+ final String type = split[0];
+
+ Uri contentUri;
+ if ("image".equals(type)) {
+ contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+ } else if ("video".equals(type)) {
+ contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
+ } else if ("audio".equals(type)) {
+ contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+ } else {
+ contentUri = MediaStore.Files.getContentUri("external");
+ }
+
+ final String selection = "_id=?";
+ final String[] selectionArgs = new String[]{split[1]};
+ return getDataColumn(context, contentUri, selection, selectionArgs);
+ }
+
+
+ } else if ("content".equalsIgnoreCase(uri.getScheme())) {
+ return getDataColumn(context, uri, null, null);
+ } else if ("file".equalsIgnoreCase(uri.getScheme())) {
+ return uri.getPath();
+ }
+
+ return null;
+ }
+
+ /**
+ * 适配api11-api18,根据uri获取图片的绝对路径
+ */
+ private static String getRealPathFromUri_Api11To18(Context context, Uri uri) {
+ String filePath = null;
+ String[] projection = {MediaStore.Images.Media.DATA};
+ //这个有两个包不知道是哪个。。。。不过这个复杂版一般用不到
+ CursorLoader loader = new CursorLoader(context, uri, projection, null, null, null);
+ Cursor cursor = loader.loadInBackground();
+
+ if (cursor != null) {
+ cursor.moveToFirst();
+ filePath = cursor.getString(cursor.getColumnIndex(projection[0]));
+ cursor.close();
+ }
+ return filePath;
+ }
+
+ /**
+ * 适配api11以下(不包括api11),根据uri获取图片的绝对路径
+ */
+ private static String getRealPathFromUri_BelowApi11(Context context, Uri uri) {
+ String filePath = null;
+ String[] projection = {MediaStore.Images.Media.DATA};
+ Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
+ if (cursor != null && cursor.moveToFirst()) {
+ filePath = cursor.getString(cursor.getColumnIndex(projection[0]));
+ cursor.close();
+ }
+ return filePath;
+ }
+
+ /**
+ * Get the value of the data column for this Uri. This is useful for
+ * MediaStore Uris, and other file-based ContentProviders.
+ *
+ * @param context The context.
+ * @param uri The Uri to query.
+ * @param selection (Optional) Filter used in the query.
+ * @param selectionArgs (Optional) Selection arguments used in the query.
+ * @return The value of the _data column, which is typically a file path.
+ */
+ public static String getDataColumn(Context context, Uri uri, String selection,
+ String[] selectionArgs) {
+ Cursor cursor = null;
+ String column = MediaStore.MediaColumns.DATA;
+ String[] projection = {column};
+ try {
+ cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
+ null);
+ if (cursor != null && cursor.moveToFirst()) {
+ int column_index = cursor.getColumnIndexOrThrow(column);
+ return cursor.getString(column_index);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ if (cursor != null)
+ cursor.close();
+ }
+ return null;
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is ExternalStorageProvider.
+ */
+ public static boolean isExternalStorageDocument(Uri uri) {
+ return "com.android.externalstorage.documents".equals(uri.getAuthority());
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is DownloadsProvider.
+ */
+ public static boolean isDownloadsDocument(Uri uri) {
+ return "com.android.providers.downloads.documents".equals(uri.getAuthority());
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is MediaProvider.
+ */
+ public static boolean isMediaDocument(Uri uri) {
+ return "com.android.providers.media.documents".equals(uri.getAuthority());
+ }
+}
\ No newline at end of file
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/ZipUtils.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/ZipUtils.java
new file mode 100644
index 000000000..22cbfa3af
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/ZipUtils.java
@@ -0,0 +1,251 @@
+package com.yunbao.faceunity.utils;
+
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * @author: lijuan
+ * @description: 解压ZIP文件
+ * @date: 2017-04-11
+ * @time: 09:22
+ */
+public class ZipUtils {
+ public static final String TAG = "ZIP";
+
+ /**
+ * 解压zip到指定的路径
+ *
+ * @param zipFileString ZIP的名称
+ * @param outPathString 要解压缩路径
+ * @throws Exception
+ */
+ public static void unZipFolder(String zipFileString, String outPathString) throws Exception {
+ ZipInputStream inZip = new ZipInputStream(new FileInputStream(zipFileString));
+ ZipEntry zipEntry;
+ String szName = "";
+ while ((zipEntry = inZip.getNextEntry()) != null) {
+ szName = zipEntry.getName();
+ if (zipEntry.isDirectory()) {
+ //获取部件的文件夹名
+ szName = szName.substring(0, szName.length() - 1);
+ File folder = new File(outPathString + File.separator + szName);
+ folder.mkdirs();
+ } else {
+ Log.i(TAG, outPathString + File.separator + szName);
+ File file = new File(outPathString + File.separator + szName);
+ if (!file.exists()) {
+ Log.i(TAG, "Create the file:" + outPathString + File.separator + szName);
+ file.getParentFile().mkdirs();
+ file.createNewFile();
+ }
+ // 获取文件的输出流
+ FileOutputStream out = new FileOutputStream(file);
+ int len;
+ byte[] buffer = new byte[1024];
+ // 读取(字节)字节到缓冲区
+ while ((len = inZip.read(buffer)) != -1) {
+ // 从缓冲区(0)位置写入(字节)字节
+ out.write(buffer, 0, len);
+ out.flush();
+ }
+ out.close();
+ }
+ }
+ inZip.close();
+ }
+
+ /**
+ * 解压zip到指定的路径
+ *
+ * @param zipFileString ZIP的名称
+ * @param outPathString 要解压缩路径
+ * @throws Exception
+ */
+ public static ArrayList unZipFolderWithFileName(String zipFileString, String outPathString) throws Exception {
+ //记录所有文件名
+ ArrayList list = new ArrayList<>();
+ ZipInputStream inZip = new ZipInputStream(new FileInputStream(zipFileString));
+ ZipEntry zipEntry;
+ String szName = "";
+ while ((zipEntry = inZip.getNextEntry()) != null) {
+ szName = zipEntry.getName();
+ if (zipEntry.isDirectory()) {
+ //获取部件的文件夹名
+ szName = szName.substring(0, szName.length() - 1);
+ File folder = new File(outPathString + File.separator + szName);
+ folder.mkdirs();
+ } else {
+ Log.i(TAG, outPathString + File.separator + szName);
+ list.add(szName);
+ File file = new File(outPathString + File.separator + szName);
+ if (!file.exists()) {
+ Log.i(TAG, "Create the file:" + outPathString + File.separator + szName);
+ file.getParentFile().mkdirs();
+ file.createNewFile();
+ }
+ // 获取文件的输出流
+ FileOutputStream out = new FileOutputStream(file);
+ int len;
+ byte[] buffer = new byte[1024];
+ // 读取(字节)字节到缓冲区
+ while ((len = inZip.read(buffer)) != -1) {
+ // 从缓冲区(0)位置写入(字节)字节
+ out.write(buffer, 0, len);
+ out.flush();
+ }
+ out.close();
+ }
+ }
+ inZip.close();
+ return list;
+ }
+
+ public static void unZipFolder(String zipFileString, String outPathString, String szName) throws Exception {
+ ZipInputStream inZip = new ZipInputStream(new FileInputStream(zipFileString));
+ ZipEntry zipEntry;
+ while ((zipEntry = inZip.getNextEntry()) != null) {
+ //szName = zipEntry.getName();
+ if (zipEntry.isDirectory()) {
+ //获取部件的文件夹名
+ szName = szName.substring(0, szName.length() - 1);
+ File folder = new File(outPathString + File.separator + szName);
+ folder.mkdirs();
+ } else {
+ Log.i(TAG, outPathString + File.separator + szName);
+ File file = new File(outPathString + File.separator + szName);
+ if (!file.exists()) {
+ Log.i(TAG, "Create the file:" + outPathString + File.separator + szName);
+ file.getParentFile().mkdirs();
+ file.createNewFile();
+ }
+ // 获取文件的输出流
+ FileOutputStream out = new FileOutputStream(file);
+ int len;
+ byte[] buffer = new byte[1024];
+ // 读取(字节)字节到缓冲区
+ while ((len = inZip.read(buffer)) != -1) {
+ // 从缓冲区(0)位置写入(字节)字节
+ out.write(buffer, 0, len);
+ out.flush();
+ }
+ out.close();
+ }
+ }
+ inZip.close();
+ }
+
+ /**
+ * 压缩文件和文件夹
+ *
+ * @param srcFileString 要压缩的文件或文件夹
+ * @param zipFileString 解压完成的Zip路径
+ * @throws Exception
+ */
+ public static void zipFolder(String srcFileString, String zipFileString) throws Exception {
+ //创建ZIP
+ ZipOutputStream outZip = new ZipOutputStream(new FileOutputStream(zipFileString));
+ //创建文件
+ File file = new File(srcFileString);
+ //压缩
+ zipFiles(file.getParent() + File.separator, file.getName(), outZip);
+ //完成和关闭
+ outZip.finish();
+ outZip.close();
+ }
+
+ /**
+ * 压缩文件
+ *
+ * @param folderString
+ * @param fileString
+ * @param zipOutputSteam
+ * @throws Exception
+ */
+ private static void zipFiles(String folderString, String fileString, ZipOutputStream zipOutputSteam) throws Exception {
+ if (zipOutputSteam == null)
+ return;
+ File file = new File(folderString + fileString);
+ if (file.isFile()) {
+ ZipEntry zipEntry = new ZipEntry(fileString);
+ FileInputStream inputStream = new FileInputStream(file);
+ zipOutputSteam.putNextEntry(zipEntry);
+ int len;
+ byte[] buffer = new byte[4096];
+ while ((len = inputStream.read(buffer)) != -1) {
+ zipOutputSteam.write(buffer, 0, len);
+ }
+ zipOutputSteam.closeEntry();
+ } else {
+ //文件夹
+ String fileList[] = file.list();
+ //没有子文件和压缩
+ if (fileList.length <= 0) {
+ ZipEntry zipEntry = new ZipEntry(fileString + File.separator);
+ zipOutputSteam.putNextEntry(zipEntry);
+ zipOutputSteam.closeEntry();
+ }
+ //子文件和递归
+ for (int i = 0; i < fileList.length; i++) {
+ zipFiles(folderString, fileString + File.separator + fileList[i], zipOutputSteam);
+ }
+ }
+ }
+
+ /**
+ * 返回zip的文件输入流
+ *
+ * @param zipFileString zip的名称
+ * @param fileString ZIP的文件名
+ * @return InputStream
+ * @throws Exception
+ */
+ public static InputStream upZip(String zipFileString, String fileString) throws Exception {
+ ZipFile zipFile = new ZipFile(zipFileString);
+ ZipEntry zipEntry = zipFile.getEntry(fileString);
+ return zipFile.getInputStream(zipEntry);
+ }
+
+ /**
+ * 返回ZIP中的文件列表(文件和文件夹)
+ *
+ * @param zipFileString ZIP的名称
+ * @param bContainFolder 是否包含文件夹
+ * @param bContainFile 是否包含文件
+ * @return
+ * @throws Exception
+ */
+ public static List getFileList(String zipFileString, boolean bContainFolder, boolean bContainFile) throws Exception {
+ List fileList = new ArrayList();
+ ZipInputStream inZip = new ZipInputStream(new FileInputStream(zipFileString));
+ ZipEntry zipEntry;
+ String szName = "";
+ while ((zipEntry = inZip.getNextEntry()) != null) {
+ szName = zipEntry.getName();
+ if (zipEntry.isDirectory()) {
+ // 获取部件的文件夹名
+ szName = szName.substring(0, szName.length() - 1);
+ File folder = new File(szName);
+ if (bContainFolder) {
+ fileList.add(folder);
+ }
+ } else {
+ File file = new File(szName);
+ if (bContainFile) {
+ fileList.add(file);
+ }
+ }
+ }
+ inZip.close();
+ return fileList;
+ }
+}
\ No newline at end of file
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/net/GsonConverter.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/net/GsonConverter.java
new file mode 100644
index 000000000..8a14aab2f
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/net/GsonConverter.java
@@ -0,0 +1,86 @@
+package com.yunbao.faceunity.utils.net;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+import com.google.gson.reflect.TypeToken;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * GSON 转换器
+ *
+ * @author Richie on 2018.12.22
+ */
+public final class GsonConverter {
+ private final static Gson GSON = new Gson();
+
+ private GsonConverter() {
+ }
+
+ /**
+ * json to bean
+ *
+ * @param json must be JSONObject
+ * @param classOfT
+ * @param
+ * @return
+ */
+ public static T jsonToBean(String json, Class classOfT) {
+ return GSON.fromJson(json, classOfT);
+ }
+
+ /**
+ * json to bean list, without generic erase problem, recommend
+ *
+ * @param json must be JSONArray
+ * @param classOfT
+ * @param
+ * @return
+ */
+ public static List jsonToList(String json, Class classOfT) {
+ JsonArray array = JsonParser.parseString(json).getAsJsonArray();
+ List list = new ArrayList<>(array.size());
+ for (JsonElement elem : array) {
+ T t = GSON.fromJson(elem, classOfT);
+ list.add(t);
+ }
+ return list;
+ }
+
+ /**
+ * json to map
+ *
+ * @param json must be JSONObject
+ * @return
+ */
+ public static Map jsonToMap(String json) {
+ return GSON.fromJson(json, new TypeToken