iielse / imageviewer

A simple and customizable Android full-screen image viewer 一个简单且可自定义的Android全屏图像浏览器
MIT License
2.23k stars 310 forks source link

p01_ImageCropprer运行崩溃 #21

Closed davidhuang24 closed 6 years ago

davidhuang24 commented 6 years ago

您的debug-apk运行崩溃,重新编译也崩溃。 1.单选图片确定后崩溃 java.lang.NullPointerException: Attempt to invoke virtual method 'int android.graphics.Bitmap.getWidth()' on a null object reference at ch.ielse.view.imagecropper.ImageCropper.cropInternal(ImageCropper.java:101) at ch.ielse.view.imagecropper.ImageCropper.access$000(ImageCropper.java:25) at ch.ielse.view.imagecropper.ImageCropper$1.run(ImageCropper.java:139) 2.选择拍照后崩溃 io.reactivex.exceptions.OnErrorNotImplementedException: file:///storage/emulated/0/Android/data/ch.ielse.demo.p01/cache/rxPicker/TEMP1595091369.jpg exposed beyond app through ClipData.Item.getUri() at io.reactivex.internal.functions.Functions$OnErrorMissingConsumer.accept(Functions.java:704) at io.reactivex.internal.functions.Functions$OnErrorMissingConsumer.accept(Functions.java:701) at io.reactivex.internal.observers.LambdaObserver.onError(LambdaObserver.java:74) at io.reactivex.observers.SerializedObserver.onError(SerializedObserver.java:155) at io.reactivex.internal.operators.observable.ObservableTakeUntil$TakeUntilObserver.onError(ObservableTakeUntil.java:73) at io.reactivex.subjects.PublishSubject.subscribeActual(PublishSubject.java:95) at io.reactivex.Observable.subscribe(Observable.java:10842) at io.reactivex.internal.operators.observable.ObservableTakeUntil.subscribeActual(ObservableTakeUntil.java:41) at io.reactivex.Observable.subscribe(Observable.java:10842) at io.reactivex.Observable.subscribe(Observable.java:10828) at io.reactivex.Observable.subscribe(Observable.java:10731) at ch.ielse.demo.p01.MainActivity.takeCamera(MainActivity.java:107) at ch.ielse.demo.p01.MainActivity.access$000(MainActivity.java:26) at ch.ielse.demo.p01.MainActivity$1$1.accept(MainActivity.java:78) at ch.ielse.demo.p01.MainActivity$1$1.accept(MainActivity.java:75) at io.reactivex.internal.observers.ConsumerSingleObserver.onSuccess(ConsumerSingleObserver.java:61) at io.reactivex.internal.operators.single.SingleTakeUntil$TakeUntilMainObserver.onSuccess(SingleTakeUntil.java:92) at io.reactivex.internal.operators.observable.ObservableAllSingle$AllObserver.onComplete(ObservableAllSingle.java:99) at io.reactivex.internal.operators.observable.ObservableFlatMap$MergeObserver.drainLoop(ObservableFlatMap.java:367) at io.reactivex.internal.operators.observable.ObservableFlatMap$MergeObserver.drain(ObservableFlatMap.java:323) at io.reactivex.internal.operators.observable.ObservableFlatMap$MergeObserver.onComplete(ObservableFlatMap.java:300) at io.reactivex.internal.operators.observable.ObservableFlatMap$MergeObserver.drainLoop(ObservableFlatMap.java:367) at io.reactivex.internal.operators.observable.ObservableFlatMap$MergeObserver.drain(ObservableFlatMap.java:323) at io.reactivex.internal.operators.observable.ObservableFlatMap$MergeObserver.onComplete(ObservableFlatMap.java:300) at io.reactivex.internal.operators.observable.ObservableFromArray$FromArrayDisposable.run(ObservableFromArray.java:110) at io.reactivex.internal.operators.observable.ObservableFromArray.subscribeActual(ObservableFromArray.java:36) at io.reactivex.Observable.subscribe(Observable.java:10842) at io.reactivex.internal.operators.observable.ObservableFlatMap.subscribeActual(ObservableFlatMap.java:55) at io.reactivex.Observable.subscribe(Observable.java:10842) at io.reactivex.internal.operators.observable.ObservableFlatMap.subscribeActual(ObservableFlatMap.java:55) at io.reactivex.Observable.subscribe(Observable.java:10842) at io.reactivex.internal.operators.observable.ObservableAllSingle.subscribeActual(ObservableAllSingle.java:34) at io.reactivex.Single.subscribe(Single.java:2703) at io.reactivex.internal.operators.single.SingleTakeUntil.subscribeActual(SingleTakeUntil.java:51) at io.reactivex.Single.subscribe(Single.java:2703) at io.reactivex.Single.subscribe(Single.java:2689) at io.reactivex.Single.subscribe(Single.java:2660) at ch.ielse.demo.p01.MainActivity.requestPermissions(MainActivity.java:155) at ch.ielse.demo.p01.MainActivity$1.onClick(MainActivity.java:74) at ch.ielse.demo.p01.SheetDialog$Builder$1.onClick(SheetDialog.java:142) at android.view.View.performClick(View.java:5730) at android.view.View$PerformClick.run(View.java:22806) at android.os.Handler.handleCallback(Handler.java:836) at android.os.Handler.dispatchMessage(Handler.java:103) at android.os.Looper.loop(Looper.java:203) at android.app.ActivityThread.main(ActivityThread.java:6519) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1113) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:974) Caused by: android.os.FileUriExposedException: file:///storage/emulated/0/Android/data/ch.ielse.demo.p01/cache/rxPicker/TEMP1595091369.jpg exposed beyond app through ClipData.Item.getUri() at android.os.StrictMode.onFileUriExposed(StrictMode.java:1847) at android.net.Uri.checkFileUriExposed(Uri.java:2346) at android.content.ClipData.prepareToLeaveProcess(ClipData.java:833) at android.content.Intent.prepareToLeaveProcess(Intent.java:8976) at android.content.Intent.prepareToLeaveProcess(Intent.java:8961) at android.app.Instrumentation.execStartActivity(Instrumentation.java:1647) at android.app.Activity.startActivityForResult(Activity.java:4888) at android.app.Activity.startActivityFromFragment(Activity.java:4874) at android.app.Activity$HostCallbacks.onStartActivityFromFragment(Activity.java:7176) at android.app.Fragment.startActivityForResult(Fragment.java:1150) at android.app.Fragment.startActivityForResult(Fragment.java:1139) at ch.ielse.view.imagecropper.RxImagePickerFragment.takeCamera(RxImagePickerFragment.java:170) at ch.ielse.view.imagecropper.RxImagePicker.takeCamera(RxImagePicker.java:59) at ch.ielse.demo.p01.MainActivity.takeCamera(MainActivity.java:104) ... 37 more

iielse commented 6 years ago

代码比较老, 没用兼容android 7 解决 Android N 7.0 上 报错:android.os.FileUriExposedException https://blog.csdn.net/yy1300326388/article/details/52787853

拷贝源码的朋友 可以尝试替换一下:

public class ImageCropper extends FrameLayout implements GestureDetector.OnGestureListener, View.OnClickListener, BaseActivity.BackPressedObserver {

    private final View iBack;
    private final View iSubmit;
    private final ImageView iSource;
    private final OverlayView vOverlay;
    private final TextView tTitle;
    private float initMinScale;
    private int mOutputWidth;
    private int mOutputHeight;
    private String mTag;

    boolean isInAnimation;
    boolean isOutputFixedSize;
    private boolean isMultiTouch = false;
    private float mLastScale;
    private float mLastTranslateX;
    private float mLastTranslateY;
    private float mLastFingersDistance;
    private float mLastFingersCenterX;
    private float mLastFingersCenterY;
    private final GestureDetector mGestureDetector;

    private Callback mCallback;
    private ViewPropertyAnimator animRestore;
    private Bitmap bmpSource;

    public ImageCropper(Context context) {
        this(context, null);
    }

    public ImageCropper(Context context, AttributeSet attrs) {
        super(context, attrs);
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        mGestureDetector = new GestureDetector(context, this);
        LayoutInflater.from(context).inflate(R.layout.image_cropper_layout, this);
        (iBack = findViewById(R.id.vBack)).setOnClickListener(this);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            iBack.setBackgroundResource(R.drawable.b_white_ripple);
        }
        (iSubmit = findViewById(R.id.vSubmit)).setOnClickListener(this);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            iSubmit.setBackgroundResource(R.drawable.b_white_ripple);
        }
        tTitle = (TextView) findViewById(R.id.t_title);
        iSource = (ImageView) findViewById(R.id.i_source);
        vOverlay = (OverlayView) findViewById(R.id.v_overlay);
        setBackgroundColor(Color.BLACK);
        setVisibility(View.INVISIBLE);
    }

    /**
     * @param outputFixedSize 是否按照vImageCropper.crop()方法入参outputWidth和outputHeight来输出,false表示仅只按其比例输出,true为绝对尺寸输出
     */
    public void setOutputFixedSize(boolean outputFixedSize) {
        isOutputFixedSize = outputFixedSize;
    }

    public void setTranslucentStatus(int statusBarHeight) {
        findViewById(R.id.v_fit).setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, statusBarHeight));
    }

    private void cropInternal(Uri sourceUri, int outputWidth, int outputHeight, boolean isCircleOverlay, String tag) throws Throwable {
        final int mWidth = getWidth();
        final int mHeight = getWidth();
        if (mWidth * mHeight == 0) return;

        setVisibility(View.VISIBLE);
        mTag = tag;

        mOutputWidth = outputWidth;
        mOutputHeight = outputHeight;

        vOverlay.reset(mOutputWidth, mOutputHeight, isCircleOverlay);

        bmpSource = obtainBmpSource(getContext(), sourceUri, mOutputWidth, mOutputHeight);

        if (1f * bmpSource.getWidth() / bmpSource.getHeight() > 1f * iSource.getWidth() / iSource.getHeight()) {
            int bitmapHeightAfterFitCenter = (int) (1f * bmpSource.getHeight() * iSource.getWidth() / bmpSource.getWidth());
            bmpSource = Bitmap.createScaledBitmap(bmpSource, iSource.getWidth(), bitmapHeightAfterFitCenter, true);
            float initMinScaleX = 1f * vOverlay.getOverlayWidth() / iSource.getWidth();
            float initMinScaleY = 1f * vOverlay.getOverlayHeight() / bitmapHeightAfterFitCenter;
            initMinScale = Math.max(initMinScaleX, initMinScaleY);
        } else {
            int bitmapWidthAfterFitCenter = (int) (1f * bmpSource.getWidth() * iSource.getHeight() / bmpSource.getHeight());
            bmpSource = Bitmap.createScaledBitmap(bmpSource, bitmapWidthAfterFitCenter, iSource.getHeight(), true);
            float initMinScaleX = 1f * vOverlay.getOverlayWidth() / bitmapWidthAfterFitCenter;
            float initMinScaleY = 1f * vOverlay.getOverlayHeight() / iSource.getHeight();
            initMinScale = Math.max(initMinScaleX, initMinScaleY);
        }

        final float defaultScale = initMinScale > 1 ? initMinScale : 1;
        iSource.setTranslationX(0);
        iSource.setTranslationY(0);
        iSource.setScaleX(defaultScale);
        iSource.setScaleY(defaultScale);
        iSource.setImageBitmap(bmpSource);
    }

    /**
     * 裁剪图片(输出的图片默认只有比例是按照入参outputWidth和outputHeight获得的,尺寸大小并没有按照指定的尺寸大小。<br>
     * 如果要求大小也为入参请调用{@link ImageCropper#setOutputFixedSize(boolean)} 设置值为true) <br>
     * 逻辑见{@link ImageCropper#onClick(View)} output = mOutputWidth * mOutputHeight != 0 ? Bitmap.createScaledBitmap(clip, mOutputWidth, mOutputHeight, true) : clip <br>
     * ps: createScaledBitmap (小图变大图会造成内容的失真) 55555555<br>
     *
     * @param sourceUri       原图片路径
     * @param outputWidth     输出宽度
     * @param outputHeight    输出高度
     * @param isCircleOverlay 遮罩蒙板是否为圆形,为圆形的条件时在isCircleOverlay为true的同时,outputWidth等于outputHeight才行
     * @param tag             若同一界面有多处裁剪功能,对此传递一个tag标志避免混淆
     */
    public void crop(final Uri sourceUri, final int outputWidth, final int outputHeight, final boolean isCircleOverlay, final String tag) {
        Const.INSTANCE.setStatusBarDarkTheme(false, (Activity) getContext());
        post(new Runnable() {
            @Override
            public void run() {
                try {
                    cropInternal(sourceUri, outputWidth, outputHeight, isCircleOverlay, tag);
                } catch (Throwable e) {
                    e.printStackTrace();
                    if (e instanceof OutOfMemoryError) {
                        Toast.makeText(getContext().getApplicationContext(), "裁剪图片-无法正确加载源数据[1099]", Toast.LENGTH_SHORT).show();
                        System.gc();
                    } else {
                        Toast.makeText(getContext().getApplicationContext(), "裁剪图片-无法正确加载源数据[1088]", Toast.LENGTH_SHORT).show();
                    }

                    animExit();
                }
            }
        });
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        final int action = event.getAction() & MotionEvent.ACTION_MASK;
        switch (action) {
            case MotionEvent.ACTION_UP:
                final float startScale = iSource.getScaleX();
                final float endScale;
                if (startScale < initMinScale) endScale = initMinScale;
                else endScale = startScale;

                final float edgeTranslateX = (bmpSource.getWidth() * endScale - vOverlay.getOverlayWidth()) / 2;
                final float startTranslateX = iSource.getTranslationX();
                final float endTranslateX;
                if (startTranslateX > edgeTranslateX) endTranslateX = edgeTranslateX;
                else if (startTranslateX < -edgeTranslateX) endTranslateX = -edgeTranslateX;
                else endTranslateX = startTranslateX;

                final float edgeTranslateY = (bmpSource.getHeight() * endScale - vOverlay.getOverlayHeight()) / 2;
                final float startTranslateY = iSource.getTranslationY();
                final float endTranslateY;
                if (startTranslateY > edgeTranslateY) endTranslateY = edgeTranslateY;
                else if (startTranslateY < -edgeTranslateY) endTranslateY = -edgeTranslateY;
                else endTranslateY = startTranslateY;

                if (endScale == startScale && endTranslateY == startTranslateY && endTranslateX == startTranslateX)
                    break;

                if (animRestore != null) animRestore.cancel();
                animRestore = iSource.animate().scaleX(endScale).scaleY(endScale).translationX(endTranslateX)
                        .translationY(endTranslateY);
                animRestore.start();

                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                if (!isMultiTouch) {
                    isMultiTouch = true;
                    mLastFingersDistance = 0;
                    mLastFingersCenterX = 0;
                    mLastFingersCenterY = 0;
                    mLastScale = iSource.getScaleX();
                    mLastTranslateX = iSource.getTranslationX();
                    mLastTranslateY = iSource.getTranslationY();
                }
                break;
            case MotionEvent.ACTION_POINTER_UP:
                if (event.getPointerCount() - 1 < 1 + 1) {
                    isMultiTouch = false;
                }
                break;
        }
        return mGestureDetector.onTouchEvent(event);
    }

    @Override
    public boolean onDown(MotionEvent e) {
        mLastScale = iSource.getScaleX();
        mLastTranslateX = iSource.getTranslationX();
        mLastTranslateY = iSource.getTranslationY();
        return true;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        try {
            if (!isMultiTouch) {
                iSource.setTranslationX(iSource.getTranslationX() - distanceX);
                iSource.setTranslationY(iSource.getTranslationY() - distanceY);
            } else {
                final float deltaX = e2.getX(1) - e2.getX(0);
                final float deltaY = e2.getY(1) - e2.getY(0);
                float distance = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY);
                if (mLastFingersDistance == 0) mLastFingersDistance = distance;

                float changedScale = (distance - mLastFingersDistance) / (getWidth() * 0.8f);
                float changedScaleValue = mLastScale + changedScale;

                if (changedScaleValue < initMinScale / 2) changedScaleValue = initMinScale / 2;
                iSource.setScaleX(changedScaleValue);
                iSource.setScaleY(changedScaleValue);

                float centerX = (e2.getX(1) + e2.getX(0)) / 2;
                float centerY = (e2.getY(1) + e2.getY(0)) / 2;
                if (mLastFingersCenterX == 0 && mLastFingersCenterY == 0) {
                    mLastFingersCenterX = centerX;
                    mLastFingersCenterY = centerY;
                }

                float changedCenterX = centerX - mLastFingersCenterX;
                iSource.setTranslationX(mLastTranslateX + changedCenterX);
                float changedCenterY = centerY - mLastFingersCenterY;
                iSource.setTranslationY(mLastTranslateY + changedCenterY);
            }
        } catch (IllegalArgumentException ex) {
            ex.printStackTrace();
        }
        return false;
    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        return false;
    }

    @Override
    public void onShowPress(MotionEvent e) {
    }

    @Override
    public void onLongPress(MotionEvent e) {
    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        return false;
    }

    @Override
    public void onClick(View v) {
        if (animRestore != null) animRestore.cancel();

        if (isInAnimation) return;

        if (v == iSubmit) {
            if (mCallback != null) {
                final float x = ((bmpSource.getWidth() * iSource.getScaleX() - vOverlay.getOverlayWidth()) / 2 - iSource.getTranslationX()) / iSource.getScaleX();
                final float width = vOverlay.getOverlayWidth() / iSource.getScaleX();
                final float y = ((bmpSource.getHeight() * iSource.getScaleY() - vOverlay.getOverlayHeight()) / 2 - iSource.getTranslationY()) / iSource.getScaleY();
                final float height = vOverlay.getOverlayHeight() / iSource.getScaleY();
                Bitmap clip = Bitmap.createBitmap(bmpSource, (int) x, (int) y, (int) width, (int) (height));
                Bitmap output = (isOutputFixedSize && mOutputWidth * mOutputHeight != 0) ? Bitmap.createScaledBitmap(clip, mOutputWidth, mOutputHeight, true) : clip;

                mCallback.onPictureCropOut(output, mTag);
                animExit();
            }
        } else if (v == iBack) {
            animExit();
        }
    }

    private void animExit() {
        animate().alpha(0).setListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
                isInAnimation = true;
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                isInAnimation = false;
                setVisibility(View.GONE);
                setAlpha(1f);

                final String currentActivity = ((Activity) getContext()).getClass().getSimpleName();
                EventBus.getDefault().post(new BusEvent(Bus.INSTANCE.getREFRESH_STATUS_BAR_THEME(), currentActivity));
            }
        }).start();
    }

    @Override
    public boolean handleBackPressed() {
        if (View.GONE != getVisibility()) {
            iBack.performClick();
            return true;
        }
        return false;
    }

    interface Callback {
        void onPictureCropOut(Bitmap bitmap, String tag);
    }

    public void setCallback(Callback cb) {
        mCallback = cb;
    }

    static class Helper {
        private static final int VIEW_IMAGE_CROPPER_ID = R.id.view_image_cropper;
        private final ViewGroup activityDecorView;
        private final ImageCropper mImageCropper;

        private Helper(Activity activity) {
            mImageCropper = new ImageCropper(activity);
            mImageCropper.setId(VIEW_IMAGE_CROPPER_ID);
            activityDecorView = (ViewGroup) activity.getWindow().getDecorView();
        }

        public static Helper with(Activity activity) {
            return new Helper(activity);
        }

        public Helper setCallback(Callback cb) {
            mImageCropper.setCallback(cb);
            return this;
        }

        public Helper setOutputFixedSize(boolean isOutputFixedSize) {
            mImageCropper.setOutputFixedSize(isOutputFixedSize);
            return this;
        }

        public Helper setTitle(String title) {
            mImageCropper.tTitle.setText(title);
            return this;
        }

        Helper setTranslucentStatusHeight(int translucentStatusHeight) {
            mImageCropper.setTranslucentStatus(translucentStatusHeight);
            return this;
        }

        public ImageCropper create() {
            removeExistingOverlayInView(activityDecorView);
            activityDecorView.addView(mImageCropper);
            Activity act = Const.INSTANCE.extractActivity(activityDecorView.getContext());
            if (act != null) {
                final String currentActivity = act.getClass().getSimpleName();
                EventBus.getDefault().post(new BusEvent(Bus.INSTANCE.getBACK_PRESSED_SUBSCRIBE(), currentActivity, mImageCropper));
            }
            return mImageCropper;
        }

        void removeExistingOverlayInView(ViewGroup parent) {
            for (int i = 0; i < parent.getChildCount(); i++) {
                View child = parent.getChildAt(i);
                if (child.getId() == VIEW_IMAGE_CROPPER_ID) {
                    parent.removeView(child);
                }
                if (child instanceof ViewGroup) {
                    removeExistingOverlayInView((ViewGroup) child);
                }
            }
        }
    }

    private static Bitmap obtainBmpSource(@NonNull Context context, @NonNull Uri uri, int requiredWidth, int requiredHeight) throws Exception {
        final ParcelFileDescriptor parcelFileDescriptor = context.getContentResolver().openFileDescriptor(uri, "r");
        final FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
//        options.inSampleSize = calculateInSampleSize(options, Const.INSTANCE.getScreenWidth(), Const.INSTANCE.getScreenWidth());
        options.inJustDecodeBounds = false;

        Bitmap decodeSampledBitmap = null;

        boolean success = false;
        while (!success) {
            try {
                decodeSampledBitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);

                success = true;
            } catch (OutOfMemoryError error) {
                error.printStackTrace();
                // Log.e("ImageCropper", "decodeSampledBitmap: BitmapFactory.decodeFileDescriptor: ", error);
                options.inSampleSize++;
            }
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            close(parcelFileDescriptor);
        }

        int exifOrientation = getExifOrientation(context, uri);
        int exifDegrees = exifToDegrees(exifOrientation);
        int exifTranslation = exifToTranslation(exifOrientation);

        Matrix matrix = new Matrix();
        if (exifDegrees != 0) {
            matrix.preRotate(exifDegrees);
        }
        if (exifTranslation != 1) {
            matrix.postScale(exifTranslation, 1);
        }
        if (!matrix.isIdentity()) {
            decodeSampledBitmap = transformBitmap(decodeSampledBitmap, matrix);
        }
        return decodeSampledBitmap;
    }

    private static Bitmap transformBitmap(@NonNull Bitmap bitmap, @NonNull Matrix transformMatrix) {
        try {
            Bitmap converted = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), transformMatrix, true);
            if (bitmap != converted) {
                bitmap.recycle();
                bitmap = converted;
            }
        } catch (OutOfMemoryError error) {
            error.printStackTrace();
            // Log.e("ImageCropper", "transformBitmap: ", error);
        }
        return bitmap;
    }

    private static int calculateInSampleSize(@NonNull BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {
            // Calculate the largest inSampleSize value that is a power of 2 and keeps both
            // height and width lower or equal to the requested height and width.
            while ((height / inSampleSize) > reqHeight || (width / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }
        }
        return inSampleSize;
    }

    private static int getExifOrientation(@NonNull Context context, @NonNull Uri imageUri) {
        int orientation = ExifInterface.ORIENTATION_UNDEFINED;
        try {
            InputStream stream = context.getContentResolver().openInputStream(imageUri);
            if (stream == null) {
                return orientation;
            }
            orientation = new ImageHeaderParser(stream).getOrientation();
            close(stream);
        } catch (IOException e) {
            e.printStackTrace();
            // Log.e("ImageCropper", "getExifOrientation: " + imageUri.toString(), e);
        }
        return orientation;
    }

    private static int exifToDegrees(int exifOrientation) {
        int rotation;
        switch (exifOrientation) {
            case ExifInterface.ORIENTATION_ROTATE_90:
            case ExifInterface.ORIENTATION_TRANSPOSE:
                rotation = 90;
                break;
            case ExifInterface.ORIENTATION_ROTATE_180:
            case ExifInterface.ORIENTATION_FLIP_VERTICAL:
                rotation = 180;
                break;
            case ExifInterface.ORIENTATION_ROTATE_270:
            case ExifInterface.ORIENTATION_TRANSVERSE:
                rotation = 270;
                break;
            default:
                rotation = 0;
        }
        return rotation;
    }

    private static int exifToTranslation(int exifOrientation) {
        int translation;
        switch (exifOrientation) {
            case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
            case ExifInterface.ORIENTATION_FLIP_VERTICAL:
            case ExifInterface.ORIENTATION_TRANSPOSE:
            case ExifInterface.ORIENTATION_TRANSVERSE:
                translation = -1;
                break;
            default:
                translation = 1;
        }
        return translation;
    }

    @SuppressWarnings("ConstantConditions")
    private static void close(@Nullable Closeable c) {
        if (c != null && c instanceof Closeable) { // java.lang.IncompatibleClassChangeError: interface not implemented
            try {
                c.close();
            } catch (IOException e) {
                // silence
            }
        }
    }
}
iielse commented 6 years ago

项目移除了这个工程代码