diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5a5217267..ed11d0e1b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -117,7 +117,6 @@ currencies = new HashMap<>(); - String tempCameraPicturePath; - LoyaltyCard tempLoyaltyCard; - ActivityResultLauncher mPhotoTakerLauncher; + ActivityResultLauncher mPhotoTakerLauncher; ActivityResultLauncher mPhotoPickerLauncher; ActivityResultLauncher mCardIdAndBarCodeEditorLauncher; ActivityResultLauncher mCropperLauncher; - int mRequestedImage; + int mRequestedImage = 0; + int mCropperFinishedType = 0; UCrop.Options mCropperOptions; - + boolean mFrontImageUnsaved = false; + boolean mBackImageUnsaved = false; final private TaskHandler mTasks = new TaskHandler(); @@ -199,6 +206,8 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity { + ", updateLoyaltyCard=" + updateLoyaltyCard); } + + @Override public void onSaveInstanceState(@NonNull Bundle savedInstanceState) { super.onSaveInstanceState(savedInstanceState); @@ -206,7 +215,20 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity { savedInstanceState.putInt(STATE_TAB_INDEX, tabs.getSelectedTabPosition()); savedInstanceState.putParcelable(STATE_TEMP_CARD, tempLoyaltyCard); savedInstanceState.putInt(STATE_REQUESTED_IMAGE, mRequestedImage); - savedInstanceState.putString(STATE_TEMP_CAMERA_PICTURE_PATH, tempCameraPicturePath); + Object cardImageFrontObj = cardImageFront.getTag(); + if (mFrontImageUnsaved && (cardImageFrontObj instanceof Bitmap) && Utils.saveTempImage(this, (Bitmap)cardImageFrontObj, TEMP_UNSAVED_FRONT_IMAGE_NAME, TEMP_UNSAVED_IMAGE_FORMAT) != null){ + savedInstanceState.putInt(STATE_FRONT_IMAGE_UNSAVED, 1); + }else{ + savedInstanceState.putInt(STATE_FRONT_IMAGE_UNSAVED, 0); + } + Object cardImageBackObj = cardImageBack.getTag(); + if (mBackImageUnsaved && (cardImageBackObj instanceof Bitmap) && Utils.saveTempImage(this, (Bitmap)cardImageBackObj, TEMP_UNSAVED_BACK_IMAGE_NAME, TEMP_UNSAVED_IMAGE_FORMAT) != null){ + savedInstanceState.putInt(STATE_BACK_IMAGE_UNSAVED, 1); + }else{ + savedInstanceState.putInt(STATE_BACK_IMAGE_UNSAVED, 0); + } + savedInstanceState.putInt(STATE_UPDATE_LOYALTY_CARD, updateLoyaltyCard ? 1 : 0); + savedInstanceState.putInt(STATE_HAS_CHANGED, hasChanged? 1:0); } @Override @@ -216,7 +238,10 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity { tabs = findViewById(R.id.tabs); tabs.selectTab(tabs.getTabAt(savedInstanceState.getInt(STATE_TAB_INDEX))); mRequestedImage = savedInstanceState.getInt(STATE_REQUESTED_IMAGE); - tempCameraPicturePath = savedInstanceState.getString(STATE_TEMP_CAMERA_PICTURE_PATH); + mFrontImageUnsaved = savedInstanceState.getInt(STATE_FRONT_IMAGE_UNSAVED) == 1; + mBackImageUnsaved = savedInstanceState.getInt(STATE_BACK_IMAGE_UNSAVED) == 1; + updateLoyaltyCard = savedInstanceState.getInt(STATE_UPDATE_LOYALTY_CARD) == 1; + hasChanged = savedInstanceState.getInt(STATE_HAS_CHANGED) == 1; } @Override @@ -438,7 +463,6 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity { if (!lastValue.toString().equals(getString(R.string.setBarcodeId))) { barcodeIdField.setText(lastValue); } - ; AlertDialog.Builder builder = new AlertDialog.Builder(LoyaltyCardEditActivity.this); builder.setTitle(R.string.setBarcodeId); @@ -531,13 +555,14 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity { tabs.selectTab(tabs.getTabAt(0)); - mPhotoTakerLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { - if (result.getResultCode() == RESULT_OK) { - startCropper(tempCameraPicturePath); - } + mPhotoTakerLauncher = registerForActivityResult(new ActivityResultContracts.TakePicture(), result->{ + if (result) { + startCropper(getCacheDir() + "/" + TEMP_CAMERA_IMAGE_NAME); + } }); + // android 11: wanted to swap it to ActivityResultContracts.GetContent but then it shows a file browsers that shows image mime types, offering gallery in the file browser mPhotoPickerLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { if (result.getResultCode() == RESULT_OK) { Intent intent = result.getData(); @@ -548,7 +573,6 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity { Uri uri = intent.getData(); startCropperUri(uri); } - }); mCardIdAndBarCodeEditorLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { @@ -577,25 +601,20 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity { if (debugUri == null){ throw new RuntimeException("ucrop returned success but not destination uri!"); } - String cropOutputPath = debugUri.getPath(); - Log.d("cropper", "cropper has produced image at " + debugUri.toString()); - Bitmap bitmap = BitmapFactory.decodeFile(cropOutputPath); - + Log.d("cropper", "ucrop produced image at " + debugUri); + Bitmap bitmap = BitmapFactory.decodeFile(getCacheDir() + "/" + TEMP_CROP_IMAGE_NAME); if (bitmap != null) { bitmap = Utils.resizeBitmap(bitmap); - try { - bitmap = Utils.rotateBitmap(bitmap, new ExifInterface(cropOutputPath)); - } catch (IOException e) { - e.printStackTrace(); - } - - if (mRequestedImage == Utils.CARD_IMAGE_FROM_CAMERA_FRONT || mRequestedImage == Utils.CARD_IMAGE_FROM_FILE_FRONT) { + if (requestedFrontImage()) { + mFrontImageUnsaved = true; setCardImage(cardImageFront, bitmap); } else { + mBackImageUnsaved = true; setCardImage(cardImageBack, bitmap); } - + Log.d("cropper", "mRequestedImage: " + mRequestedImage); + mCropperFinishedType = mRequestedImage; hasChanged = true; } else { Toast.makeText(LoyaltyCardEditActivity.this, R.string.errorReadingImage, Toast.LENGTH_LONG).show(); @@ -615,7 +634,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity { } private void setCropperOptions(){ - mCropperOptions.setCompressionFormat(Bitmap.CompressFormat.JPEG); + mCropperOptions.setCompressionFormat(TEMP_CROP_IMAGE_FORMAT); mCropperOptions.setFreeStyleCropEnabled(true); mCropperOptions.setHideBottomControls(false); // needed when bottom controls are hidden @@ -641,6 +660,18 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity { extractIntentFields(intent); } + private boolean requestedFrontImage(){ + return mRequestedImage == Utils.CARD_IMAGE_FROM_CAMERA_FRONT || mRequestedImage == Utils.CARD_IMAGE_FROM_FILE_FRONT; + } + + private boolean croppedFrontImage(){ + return mCropperFinishedType == Utils.CARD_IMAGE_FROM_CAMERA_FRONT || mCropperFinishedType == Utils.CARD_IMAGE_FROM_FILE_FRONT; + } + + private boolean croppedBackImage(){ + return mCropperFinishedType == Utils.CARD_IMAGE_FROM_CAMERA_BACK || mCropperFinishedType == Utils.CARD_IMAGE_FROM_FILE_BACK; + } + @SuppressLint("DefaultLocale") @Override public void onResume() { @@ -650,6 +681,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity { onResuming = true; + if (tempLoyaltyCard == null) { if (updateLoyaltyCard) { tempLoyaltyCard = db.getLoyaltyCard(loyaltyCardId); @@ -659,9 +691,6 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity { finish(); return; } - setTitle(R.string.editCardTitle); - setCardImage(cardImageFront, Utils.retrieveCardImage(this, tempLoyaltyCard.id, true)); - setCardImage(cardImageBack, Utils.retrieveCardImage(this, tempLoyaltyCard.id, false)); } else if (importLoyaltyCardUri != null) { try { tempLoyaltyCard = importUriHelper.parse(importLoyaltyCardUri); @@ -670,14 +699,37 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity { finish(); return; } - setTitle(R.string.addCardTitle); } else { // New card, use default values tempLoyaltyCard = new LoyaltyCard(-1, "", "", null, new BigDecimal("0"), null, "", null, null, null, 0, Utils.getUnixTime(),100); - setTitle(R.string.addCardTitle); + } } + if(!initDone) { + if (updateLoyaltyCard) { + setTitle(R.string.editCardTitle); + if (!mFrontImageUnsaved && !croppedFrontImage()) { + setCardImage(cardImageFront, Utils.retrieveCardImage(this, tempLoyaltyCard.id, true)); + } + if (!mBackImageUnsaved && !croppedBackImage()) { + setCardImage(cardImageBack, Utils.retrieveCardImage(this, tempLoyaltyCard.id, false)); + } + }else{ + setTitle(R.string.addCardTitle); + } + if(mFrontImageUnsaved && !croppedFrontImage()){ + setCardImage(cardImageFront, Utils.loadTempImage(this, TEMP_UNSAVED_FRONT_IMAGE_NAME)); + } + if(mBackImageUnsaved && !croppedBackImage()){ + setCardImage(cardImageBack, Utils.loadTempImage(this, TEMP_UNSAVED_BACK_IMAGE_NAME)); + } + } + + mCropperFinishedType = 0; + + boolean hadChanges = hasChanged; + storeFieldEdit.setText(tempLoyaltyCard.store); noteFieldEdit.setText(tempLoyaltyCard.note); formatExpiryField(this, expiryField, tempLoyaltyCard.expiry); @@ -767,8 +819,8 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity { // Initialization has finished if (!initDone) { - hasChanged = false; initDone = true; + hasChanged = hadChanges; } generateOrHideBarcode(); @@ -908,26 +960,13 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity { confirmExitDialog.show(); } - private File createTempFile(String prefix, String suffix) throws IOException{ - String imageFileName = prefix + new Date().getTime(); - return File.createTempFile( - imageFileName, - suffix, - getExternalFilesDir(Environment.DIRECTORY_PICTURES) - ); - } + private void takePhotoForCard(int type) throws IOException { - Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - - File image = createTempFile("CATIMA_", ".jpg"); - - tempCameraPicturePath = image.getAbsolutePath(); - - Uri photoURI = FileProvider.getUriForFile(LoyaltyCardEditActivity.this, BuildConfig.APPLICATION_ID, image); - i.putExtra(MediaStore.EXTRA_OUTPUT, photoURI); + Uri photoURI = FileProvider.getUriForFile(LoyaltyCardEditActivity.this, BuildConfig.APPLICATION_ID, Utils.createTempFile(this, TEMP_CAMERA_IMAGE_NAME)); mRequestedImage = type; - mPhotoTakerLauncher.launch(i); + + mPhotoTakerLauncher.launch(photoURI); } class EditCardIdAndBarcode implements View.OnClickListener { @@ -1168,12 +1207,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity { public void startCropperUri(Uri sourceUri){ Log.d("cropper", "launching cropper with image " + sourceUri.getPath()); File cropOutput; - try { - cropOutput = createTempFile("UCROP_", ".jpg"); - }catch(java.io.IOException e){ - Log.e("image cropping", "failed creating temporarily file"); - return; - } + cropOutput = Utils.createTempFile(this, TEMP_CROP_IMAGE_NAME); Uri destUri = Uri.parse("file://" + cropOutput.getAbsolutePath()); Log.d("cropper", "asking cropper to output to " + destUri.toString()); diff --git a/app/src/main/java/protect/card_locker/Utils.java b/app/src/main/java/protect/card_locker/Utils.java index 0ab199492..eb889b479 100644 --- a/app/src/main/java/protect/card_locker/Utils.java +++ b/app/src/main/java/protect/card_locker/Utils.java @@ -25,6 +25,7 @@ import com.google.zxing.Result; import com.google.zxing.common.HybridBinarizer; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -410,10 +411,37 @@ public class Utils { Configuration config = inputContext.getResources().getConfiguration(); int currentNightMode = config.uiMode & Configuration.UI_MODE_NIGHT_MASK; return (currentNightMode == Configuration.UI_MODE_NIGHT_YES); - }else if (nightModeSetting == AppCompatDelegate.MODE_NIGHT_YES){ - return true; - }else{ - return false; + }else { + return nightModeSetting == AppCompatDelegate.MODE_NIGHT_YES; } } + + public static File createTempFile(Context context, String name){ + return new File(context.getCacheDir() + "/" + name); + } + + public static String saveTempImage(Context context, Bitmap in, String name, Bitmap.CompressFormat format){ + File image = createTempFile(context, name); + try (FileOutputStream out = new FileOutputStream(image)){ + in.compress(format, 100, out); + return image.getAbsolutePath(); + }catch(IOException e){ + Log.d("store temp image", "failed writing temp file for temporary image, name: " + name); + return null; + } + } + + public static Bitmap loadImage(String path){ + try{ + return BitmapFactory.decodeStream(new FileInputStream(path)); + }catch(IOException e){ + Log.d("load image", "failed loading image from " + path); + return null; + } + } + + public static Bitmap loadTempImage(Context context, String name){ + return loadImage(context.getCacheDir() + "/" + name); + } + } diff --git a/app/src/main/res/xml/file_provider_paths.xml b/app/src/main/res/xml/file_provider_paths.xml index 8bacf6557..3cd946b00 100644 --- a/app/src/main/res/xml/file_provider_paths.xml +++ b/app/src/main/res/xml/file_provider_paths.xml @@ -1,3 +1,3 @@ - + \ No newline at end of file