Wednesday, April 8, 2020

My Quick Find Gallery (Part 3 - Storage Permission & Browse, Tag and Save Image)

5) Storage Read/Write Permission

AndroidMainfest.xml

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

SplashScreen.java

 private static final int MY_WRITE_EXTERNAL_PERMISSION_CODE = 1;
    public boolean storagePermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, MY_WRITE_EXTERNAL_PERMISSION_CODE);
            }
            else
                return true;
        }
        return false;
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == MY_WRITE_EXTERNAL_PERMISSION_CODE) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Intent intent = new Intent(SplashScreen.this, SearchActivity.class);
                startActivity(intent);
            }
            else {
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setMessage("You need to give permission to access storage in order to work this app.");
                builder.setPositiveButton("GIVE PERMISSION", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        dialogInterface.dismiss();
                        // Show permission request popup
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                            requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, MY_WRITE_EXTERNAL_PERMISSION_CODE);
                        }
                    }
                });
                builder.setNegativeButton("CLOSE", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        dialogInterface.dismiss();
                        finish();
                    }
                });
                builder.show();
            }
        }

    }

6) Browse Image from Gallery and Show Image in ImageView
Subtasks:
i) Set ImageView Property like that all different Scale of Images are perfectly fit into different type of phone screen.
activity_save_photo.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".SavePhoto">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="0dp"
        android:layout_marginStart="0dp"
        android:layout_marginTop="8dp"
        android:adjustViewBounds="true"
        android:scaleType="fitCenter"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
ii) Open chooser Intent which show only Image Files
SavePhoto.java
private int PICK_IMAGE_REQUEST = 1;
    public void choosePhotoFromGallery(){
//        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
//        intent.setType("image/*");
//        startActivityForResult(intent, PICK_IMAGE_REQUEST);
//        startActivityForResult(Intent.createChooser(intent, "Select Image"), PICK_IMAGE_REQUEST);

        Intent getIntent = new Intent(Intent.ACTION_GET_CONTENT);
        getIntent.setType("image/*");

        Intent pickIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        pickIntent.setType("image/*");

        Intent chooserIntent = Intent.createChooser(getIntent, "Select Image");
        chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] {pickIntent});

        startActivityForResult(chooserIntent, PICK_IMAGE_REQUEST);
    }
iii) Selected Image will be shown in ImageView
Subtasks 
a) Image will be get in form of URI from Image Chooser Intent.
b) We will find the path of image from Image URI
c) We will reduce the size of image but image quality will be almost same via ImageCompression class.
//iii)
SavePhoto.java
Uri selectedImageUri;
String picturePath;
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK && data != null && data.getData() != null) {
//iii)a)
            selectedImageUri = data.getData();
//iii)b)
            picturePath = getPath(this, selectedImageUri );
            if(!picturePath.matches( "Not found")) {
                 try {
//iii)c)
                    ImageCompression ic= new ImageCompression(this);
                    bitmap=ic.compressImage(picturePath);
                    selectedImage.setImageBitmap(bitmap);
                } catch (Exception e) {
                    Toast.makeText(this, e.toString() , Toast.LENGTH_LONG).show();
                }
            }
            else
                Toast.makeText(this, "Image Path not found., Toast.LENGTH_SHORT).show();
        }

}
//iii)b)
public static String getPath(Context context, Uri uri ) {
        String result = null;
        String[] proj = { MediaStore.Images.Media.DATA };
        Cursor cursor = context.getContentResolver( ).query( uri, proj, null, null, null );
        if(cursor != null){
            if ( cursor.moveToFirst( ) ) {
                int column_index = cursor.getColumnIndexOrThrow( proj[0] );
                result = cursor.getString( column_index );
            }
            cursor.close( );
        }
        if(result == null) {
            result = "Not found";
        }
        return result;
}
//iii)c)
ImageCompression.java
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.media.ExifInterface;
import android.os.AsyncTask;
import android.os.Environment;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * Created by HP-HP on 03-07-2015.
 */
public class ImageCompression extends AsyncTask<String, Void, String> {
    private Context context;
    private static final float maxHeight = 1280.0f;
    private static final float maxWidth = 1280.0f;

    public ImageCompression(Context context){
        this.context=context;
    }
    @Override
    protected String doInBackground(String... strings) {
        if(strings.length == 0 || strings[0] == null)
            return null;
        //return compressImage(strings[0]);
        return "";
    }
    protected void onPostExecute(String imagePath){
        // imagePath is path of new compressed image.
    }

    public Bitmap compressImage(String imagePath) {
        Bitmap scaledBitmap = null;
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        Bitmap bmp = BitmapFactory.decodeFile(imagePath, options);
        int actualHeight = options.outHeight;
        int actualWidth = options.outWidth;
        float imgRatio = (float) actualWidth / (float) actualHeight;
        float maxRatio = maxWidth / maxHeight;

        if (actualHeight > maxHeight || actualWidth > maxWidth) {
            if (imgRatio < maxRatio) {
                imgRatio = maxHeight / actualHeight;
                actualWidth = (int) (imgRatio * actualWidth);
                actualHeight = (int) maxHeight;
            } else if (imgRatio > maxRatio) {
                imgRatio = maxWidth / actualWidth;
                actualHeight = (int) (imgRatio * actualHeight);
                actualWidth = (int) maxWidth;
            } else {
                actualHeight = (int) maxHeight;
                actualWidth = (int) maxWidth;
            }
        }
        options.inSampleSize = calculateInSampleSize(options, actualWidth, actualHeight);
        options.inJustDecodeBounds = false;
        options.inDither = false;
        options.inPurgeable = true;
        options.inInputShareable = true;
        options.inTempStorage = new byte[16 * 1024];
        try {
            bmp = BitmapFactory.decodeFile(imagePath, options);
        } catch (OutOfMemoryError exception) {
            exception.printStackTrace();
        }
        try {
            scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.RGB_565);
        } catch (OutOfMemoryError exception) {
            exception.printStackTrace();
        }
        float ratioX = actualWidth / (float) options.outWidth;
        float ratioY = actualHeight / (float) options.outHeight;
        float middleX = actualWidth / 2.0f;
        float middleY = actualHeight / 2.0f;
        Matrix scaleMatrix = new Matrix();
        scaleMatrix.setScale(ratioX, ratioY, middleX, middleY);
        Canvas canvas = new Canvas(scaledBitmap);
        canvas.setMatrix(scaleMatrix);
        canvas.drawBitmap(bmp, middleX - bmp.getWidth() / 2, middleY - bmp.getHeight() / 2, new Paint(Paint.FILTER_BITMAP_FLAG));
        if(bmp!=null){
            bmp.recycle();
        }
        ExifInterface exif;
        try {
            exif = new ExifInterface(imagePath);
            int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0);
            Matrix matrix = new Matrix();
            if (orientation == 6) {
                matrix.postRotate(90);
            } else if (orientation == 3) {
                matrix.postRotate(180);
            } else if (orientation == 8) {
                matrix.postRotate(270);
            }
            scaledBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return scaledBitmap;

//        ByteArrayOutputStream out = new ByteArrayOutputStream();
//        scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
//        return out.toByteArray();
//        FileOutputStream out = null;
//        String filepath = getFilename();
//        try {
//            out = new FileOutputStream(filepath);
//
//            //write the compressed bitmap at the destination specified by filename.
//            scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
//
//        } catch (FileNotFoundException e) {
//            e.printStackTrace();
//        }
//
//        return filepath;
    }
    public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {
            final int heightRatio = Math.round((float) height / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        }
        final float totalPixels = width * height;
        final float totalReqPixelsCap = reqWidth * reqHeight * 2;
        while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
            inSampleSize++;
        }
        return inSampleSize;
    }
    public String getFilename() {
        File mediaStorageDir = new File(Environment.getExternalStorageDirectory()
                + "/Android/data/"
                + context.getApplicationContext().getPackageName()
                + "/Files/Compressed");
        // Create the storage directory if it does not exist
        if (! mediaStorageDir.exists()){
            mediaStorageDir.mkdirs();
        }
        String mImageName="IMG_"+ String.valueOf(System.currentTimeMillis()) +".jpg";
        String uriString = (mediaStorageDir.getAbsolutePath() + "/"+ mImageName);;
        return uriString;
    }
}

7) Textbox in Action Bar, AutoCompleteList and Input Legal Character
menu_save.xml
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/action_tag_editText"
        android:title="Enter Tag"
        app:showAsAction="always"
        app:actionViewClass="android.widget.AutoCompleteTextView" />
    <item
        android:id="@+id/action_save"
        android:title="Save"
        app:showAsAction="always" />
</menu>

SavePhoto.java
private Menu menu;
AutoCompleteTextView tag_editText;
@Override
public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_save, menu);
        this.menu = menu;

tag_editText = (AutoCompleteTextView)menu.findItem(R.id.action_tag_editText).getActionView();
        tag_editText.getBackground().setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP);
        tag_editText.setHint("Enter tag here...");
        tag_editText.requestFocus();
 ArrayAdapter<String> adapter = new ArrayAdapter<String> (this,android.R.layout.simple_list_item_1,uniqueFileName);
        tag_editText.setAdapter(adapter);
        tag_editText.setDropDownBackgroundResource(R.color.White);
        tag_editText.setFilters(new InputFilter[] { filter });
        return true;
}
private String LEGAL_CHARACTERSET = " 1234567890().,'@#$&ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
private InputFilter filter = new InputFilter() {
        @Override
        public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
            if (source != null && !(LEGAL_CHARACTERSET.contains("" + source)) && source.length()==1){//source.length==1 because autocomplete selection was going to empty.
                return "";
            }
            return null;
        }
};

8) Save image in Storage. File name should be same as Tag Name. If Tag Name is repeating but File name can't be repeat therefor File name format should be Tag Name[count].jpeg
SavePhoto.java
@Override
public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
case R.id.action_save:
                String tag=tag_editText.getText().toString();
                if(tag.matches("")) {
                    Toast toast = Toast.makeText(this, "Please Enter Tag", Toast.LENGTH_LONG);
                    toast.setGravity(Gravity.CENTER, 0, 0);
                    toast.show();
                }
                else {
                    if (bitmap != null) {
                        if (saveToInternalStorage(bitmap, tag.trim())) {
                            Intent intent = new Intent();
                            intent.putExtra("savePhotoFileName", tag);
                            setResult(RESULT_OK, intent);
                            finish();
                        } else {
                            Toast toast = Toast.makeText(this, "File not saved", Toast.LENGTH_LONG);
                            toast.setGravity(Gravity.CENTER, 0, 0);
                            toast.show();
                        }
                    }  else {
                        Toast toast = Toast.makeText(this, "Please select image", Toast.LENGTH_LONG);
                        toast.setGravity(Gravity.CENTER, 0, 0);
                        toast.show();
                    }
                }
                break;
            default:
                break;
        }
        return true;

    }
private boolean saveToInternalStorage(Bitmap bitmapImage, String filename){
        File directory = new File(Environment.getExternalStorageDirectory().getPath(), getResources().getString(R.string.tag_image_directory_name));
        if (!directory.exists()) {
            directory.mkdirs();
        }
        File mypath=new File(directory,filename + ".jpeg");
        int i=2;
        while(mypath.exists()){
            mypath=new File(directory,filename + "["+ i + "].jpeg");
            i++;
        }
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(mypath);
            // Use the compress method on the BitMap object to write image to the OutputStream
            bitmapImage.compress(Bitmap.CompressFormat.JPEG, 85, fos);
            return true;
        } catch (Exception e) {
            Toast.makeText(this, e.toString(), Toast.LENGTH_SHORT).show();
            return false;
        } finally {
            try {
                fos.close();
            } catch (IOException e) {
                Toast.makeText(this, e.toString(), Toast.LENGTH_SHORT).show();
                return false;
            }
        }
}

9) Back Arrow in Action Bar
SavePhoto.java
@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_save_photo);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                onBackPressed();
                break;
           default:
                break;
        }
        return true;
}

10)i) Passing Variable from Parent Activity (SearchActivity) to Child Activity (SavePhoto)
SearchActivity.java
int SAVE_PHOTO_REQUEST_CODE=1;//it will use when i add photo and then comeback to this activity. Child activity passing value to parent activity.
@Override
public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_add_photo:
                Intent intent=new Intent(SearchActivity.this,SavePhoto.class);
                intent.putStringArrayListExtra("uniqueFileName",uniqueFileName);
                startActivityForResult(intent,SAVE_PHOTO_REQUEST_CODE);
                break;
    default:
                break;
        }
        return true;

}

SavePhoto.java
@Override
protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_save_photo);

      Intent intent = getIntent(); // get Intent which we set from Previous Activity
      uniqueFileName=intent.getStringArrayListExtra("uniqueFileName");

}
10)ii) Passing Variable from Child Activity (SavePhoto) to Parent Activity (SearchActivity)
Note: Child Activity should be open via startActivityForResult from Parent Activity
SavePhoto.java
Save Success Message as follows:
Intent intent = new Intent();
intent.putExtra("savePhotoFileName", tag);
setResult(RESULT_OK, intent);
finish();

SearchActivity.java
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == SAVE_PHOTO_REQUEST_CODE && resultCode == RESULT_OK) {
            String savePhotoFileName = data.getStringExtra("savePhotoFileName");
            search.setQuery(savePhotoFileName, true);
        }

}

No comments:

Post a Comment