package co.smartreceipts.android.fragments;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.media.ExifInterface;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.squareup.picasso.Callback;
import com.squareup.picasso.MemoryPolicy;
import com.squareup.picasso.Picasso;
import java.io.File;
import javax.inject.Inject;
import co.smartreceipts.android.R;
import co.smartreceipts.android.activities.NavigationHandler;
import co.smartreceipts.android.analytics.Analytics;
import co.smartreceipts.android.analytics.events.Events;
import co.smartreceipts.android.imports.ActivityFileResultImporter;
import co.smartreceipts.android.imports.CameraInteractionController;
import co.smartreceipts.android.model.Receipt;
import co.smartreceipts.android.model.factory.ReceiptBuilderFactory;
import co.smartreceipts.android.ocr.OcrManager;
import co.smartreceipts.android.persistence.PersistenceManager;
import co.smartreceipts.android.persistence.database.controllers.impl.ReceiptTableController;
import co.smartreceipts.android.persistence.database.controllers.impl.StubTableEventsListener;
import co.smartreceipts.android.persistence.database.operations.DatabaseOperationMetadata;
import co.smartreceipts.android.persistence.database.operations.OperationFamilyType;
import co.smartreceipts.android.utils.cache.FragmentStateCache;
import co.smartreceipts.android.utils.log.Logger;
import dagger.android.support.AndroidSupportInjection;
import io.reactivex.disposables.CompositeDisposable;
import wb.android.flex.Flex;
import wb.android.image.ImageUtils;
import wb.android.storage.StorageManager;
import wb.android.ui.PinchToZoomImageView;
public class ReceiptImageFragment extends WBFragment {
// Save state
private static final String KEY_OUT_RECEIPT = "key_out_receipt";
private static final String KEY_OUT_URI = "key_out_uri";
@Inject
Flex flex;
@Inject
PersistenceManager persistenceManager;
@Inject
Analytics analytics;
@Inject
ReceiptTableController receiptTableController;
@Inject
OcrManager ocrManager;
@Inject
NavigationHandler navigationHandler;
@Inject
FragmentStateCache fragmentStateCache;
private PinchToZoomImageView imageView;
private LinearLayout footer;
private ProgressBar progress;
private Toolbar toolbar;
private Receipt receipt;
private ActivityFileResultImporter activityFileResultImporter;
private ImageUpdatedListener imageUpdatedListener;
private CompositeDisposable compositeDisposable;
private boolean isRotateOngoing;
private Uri imageUri;
public static ReceiptImageFragment newInstance() {
return new ReceiptImageFragment();
}
@Override
public void onAttach(Context context) {
AndroidSupportInjection.inject(this);
super.onAttach(context);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
receipt = getArguments().getParcelable(Receipt.PARCEL_KEY);
} else {
receipt = savedInstanceState.getParcelable(KEY_OUT_RECEIPT);
imageUri = savedInstanceState.getParcelable(KEY_OUT_URI);
}
isRotateOngoing = false;
activityFileResultImporter = new ActivityFileResultImporter(getActivity(), getFragmentManager(), receipt.getTrip(),
persistenceManager, analytics, ocrManager);
imageUpdatedListener = new ImageUpdatedListener();
setHasOptionsMenu(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.receipt_image_view, container, false);
imageView = (PinchToZoomImageView) rootView.findViewById(R.id.receiptimagefragment_imageview);
footer = (LinearLayout) rootView.findViewById(R.id.footer);
progress = (ProgressBar) rootView.findViewById(R.id.progress);
final LinearLayout rotateCCW = (LinearLayout) rootView.findViewById(R.id.rotate_ccw);
final LinearLayout retakePhoto = (LinearLayout) rootView.findViewById(R.id.retake_photo);
final LinearLayout rotateCW = (LinearLayout) rootView.findViewById(R.id.rotate_cw);
rotateCCW.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
analytics.record(Events.Receipts.ReceiptImageViewRotateCcw);
rotate(ExifInterface.ORIENTATION_ROTATE_270);
}
});
retakePhoto.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
analytics.record(Events.Receipts.ReceiptImageViewRetakePhoto);
imageUri = new CameraInteractionController(ReceiptImageFragment.this).retakePhoto(receipt);
}
});
rotateCW.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
analytics.record(Events.Receipts.ReceiptImageViewRotateCw);
rotate(ExifInterface.ORIENTATION_ROTATE_90);
}
});
return rootView;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
loadImage();
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
toolbar = (Toolbar) getActivity().findViewById(R.id.toolbar);
}
@Override
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
Logger.debug(this, "Result Code: " + resultCode);
if (receipt == null) {
receipt = getArguments().getParcelable(Receipt.PARCEL_KEY);
}
// Show the progress bar
progress.setVisibility(View.VISIBLE);
// Null out the last request
final Uri cachedImageSaveLocation = imageUri;
imageUri = null;
activityFileResultImporter.onActivityResult(requestCode, resultCode, data, cachedImageSaveLocation);
}
@Override
public void onResume() {
super.onResume();
compositeDisposable = new CompositeDisposable();
((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);
final ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setHomeButtonEnabled(true);
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setTitle(receipt.getName());
}
receiptTableController.subscribe(imageUpdatedListener);
compositeDisposable.add(activityFileResultImporter.getResultStream()
.subscribe(response -> {
final Receipt retakeReceipt = new ReceiptBuilderFactory(receipt).setFile(response.getFile()).build();
receiptTableController.update(receipt, retakeReceipt, new DatabaseOperationMetadata());
}, throwable -> {
Toast.makeText(getActivity(), getFlexString(R.string.IMG_SAVE_ERROR), Toast.LENGTH_SHORT).show();
progress.setVisibility(View.GONE);
activityFileResultImporter.dispose();
}, () -> {
progress.setVisibility(View.GONE);
activityFileResultImporter.dispose();
}));
}
@Override
public void onPause() {
compositeDisposable.dispose();
compositeDisposable = null;
receiptTableController.unsubscribe(imageUpdatedListener);
super.onPause();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Logger.debug(this, "onSaveInstanceState");
outState.putParcelable(KEY_OUT_RECEIPT, receipt);
outState.putParcelable(KEY_OUT_URI, imageUri);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
navigationHandler.navigateToReportInfoFragment(receipt.getTrip());
return true;
} else {
return super.onOptionsItemSelected(item);
}
}
@Override
public void onDestroy() {
fragmentStateCache.onDestroy(this);
super.onDestroy();
}
private void loadImage() {
Picasso.with(getContext()).load(receipt.getImage()).memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE).fit().centerInside().into(imageView, new Callback() {
@Override
public void onSuccess() {
progress.setVisibility(View.GONE);
imageView.setVisibility(View.VISIBLE);
footer.setVisibility(View.VISIBLE);
}
@Override
public void onError() {
progress.setVisibility(View.GONE);
if (getActivity() != null) {
Toast.makeText(getActivity(), getFlexString(R.string.IMG_OPEN_ERROR), Toast.LENGTH_SHORT).show();
}
}
});
}
private void rotate(int orientation) {
if (isRotateOngoing) {
return;
}
isRotateOngoing = true;
progress.setVisibility(View.VISIBLE);
(new ImageRotater(orientation, receipt.getImage())).execute();
}
private void onRotateComplete(boolean success) {
if (!success) {
Toast.makeText(getActivity(), "Image Rotate Failed", Toast.LENGTH_SHORT).show();
}
isRotateOngoing = false;
progress.setVisibility(View.GONE);
}
private class ImageUpdatedListener extends StubTableEventsListener<Receipt> {
@Override
public void onUpdateSuccess(@NonNull Receipt oldReceipt, @NonNull Receipt newReceipt, @NonNull DatabaseOperationMetadata databaseOperationMetadata) {
if (databaseOperationMetadata.getOperationFamilyType() != OperationFamilyType.Sync) {
if (oldReceipt.equals(receipt)) {
receipt = newReceipt;
loadImage();
}
}
}
@Override
public void onUpdateFailure(@NonNull Receipt oldReceipt, @Nullable Throwable e, @NonNull DatabaseOperationMetadata databaseOperationMetadata) {
if (databaseOperationMetadata.getOperationFamilyType() != OperationFamilyType.Sync) {
progress.setVisibility(View.GONE);
Toast.makeText(getActivity(), getFlexString(R.string.database_error), Toast.LENGTH_SHORT).show();
}
}
}
private class ImageRotater extends AsyncTask<Void, Void, Bitmap> {
private final int mOrientation;
private final File mImg;
public ImageRotater(int orientation, File img) {
mOrientation = orientation;
mImg = img;
}
@Override
protected Bitmap doInBackground(Void... params) {
try {
StorageManager storage = persistenceManager.getStorageManager();
File root = mImg.getParentFile();
String filename = mImg.getName();
Bitmap bitmap = storage.getBitmap(root, filename);
bitmap = ImageUtils.rotateBitmap(bitmap, mOrientation);
storage.writeBitmap(root, bitmap, filename, CompressFormat.JPEG, 85);
return bitmap;
} catch (Exception e) {
Logger.error(this, e);
return null;
}
}
@Override
protected void onPostExecute(Bitmap result) {
if (result == null) {
onRotateComplete(false);
} else {
imageView.setImageBitmap(result);
onRotateComplete(true);
}
}
}
private String getFlexString(int id) {
return getFlexString(flex, id);
}
}