1
\$\begingroup\$

Gallery will be displayed in RecyclerView. User can select some images and delete it with one button.

It all works, but i think I have bad code, because I don't use some instruments (like fragment). I just don't understand what and where I need it exactly.

So I need help with readability (code style) and refactoring.

https://github.com/KirstenLy/GalleryApp

Some points about code:

MainActivity - Just logo and autoswitch to the next activity after two seconds.

GalleryActivity - Here i show the gallery and write code to take and save photo.

  • onCreate. I take a path to directory with images, and if it's ok, i create File[] files with images, then create ArrayList<String> imageNamesList which contains files names and put it into RecyclerView adapter to display.

    1. I use this library to animate my Recycler: https://github.com/wasabeef/recyclerview-animators. I used their methods and classes on 138 - 153 lines.
  • onActivityResult

    1. tempFile.length() == 0 i used it, because if user activate camera and then press "back", photo will be created in tempFile but it will be empty, and RecyclerView will display empty cell with border.
  • Methods

    1. deleteAllPhoto method. Method deleted all selected photo. I returned my own class DellAllPhotoResult and then work with it. Method count succsess and failed results.
    2. takeAPhoto method. I use this to write it: https://developer.android.com/training/camera/photobasics

RVAdapter - my own adapter, extends RecyclerView.Adapter. He take ArrayList<String> imageNamesList, and create file and cell for every name on the list.

  • onBindViewHolder. Here i work with cell content. I used Glide library to avoid OOM when 5+ images displayed.
    1. viewHolder.img.setOnClickListener. RecyclerView widget placed inside invisible layout named scaledImg. When user click on image, i make this layout visible and put user's image in it. This way i "scaled" image.

Functions - class with simple functions.

  1. prepareData. This method take file[] array and return Arraylist<String> with them names. Check on .jpg is about to avoid sutiation when user put file with another format in directory.

Another things are simple, in my opinion. But i can write explanation of any part, if someone need it.

MainActivity

public class MainActivity extends AppCompatActivity {

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

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                Intent intent = new Intent(MainActivity.this, GalleryActivity.class);
                startActivity(intent);
                finish();
            }
        };
        Handler handler = new Handler();
        handler.postDelayed(runnable, 2000);
    }
}

GalleryActivity

public class GalleryActivity extends AppCompatActivity {
    final int REQUEST_TAKE_PHOTO = 1;
    private File tempFile;

    private void takeAPhoto(String PATH, int REQUEST_TAKE_PHOTO) {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
            try {
                tempFile = Functions.createImageFile(PATH);
            } catch (IOException ex) {
                Toast.makeText(getApplicationContext(), "Denied. Create file error.", Toast.LENGTH_LONG).show();
                return;
            }
            if (tempFile != null) {
                Uri photoURI = FileProvider.getUriForFile(getApplicationContext(),
                        "com.kirsten.testapps.fileprovider",
                        tempFile);
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
                startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
            }
        } else {
            Toast.makeText(getApplicationContext(), "Denied. Can't find a camera.", Toast.LENGTH_LONG).show();
        }
    }

    private DellAllPhotoResult deleteAllPhoto(RVAdapter adapter, ArrayList<String> imageNames, String PATH) {
        boolean[] isSelected = adapter.getIsSelected();
        boolean noErrorResult = true;
        int successDeletedFileCounter = 0;
        int failedDeletedFileCounter = 0;
        StringBuilder pathString = new StringBuilder();

        for (int i = 0; i < imageNames.size(); i++) {
            if (isSelected[i]) {
                tempFile = new File(pathString.
                        append(PATH).
                        append("/").
                        append(imageNames.get(i)).
                        toString());
                if (tempFile.delete() && !tempFile.exists()) {
                    successDeletedFileCounter++;
                } else {
                    failedDeletedFileCounter++;
                }
            }
        }
        if (failedDeletedFileCounter > 0) {
            noErrorResult = false;
        }
        return new DellAllPhotoResult(noErrorResult, successDeletedFileCounter, failedDeletedFileCounter);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        if (requestCode == REQUEST_TAKE_PHOTO && tempFile != null) {
            if (tempFile.length() == 0) {
                if (!tempFile.delete()) {
                    Toast.makeText(this, "Unknown error, app will be closed", Toast.LENGTH_LONG).show();
                    android.os.Process.killProcess(android.os.Process.myPid());
                }
            }
        }
        recreate();
    }

    @Override
    public void onBackPressed() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setMessage("Are you sure you want to exit?")
                .setCancelable(false)
                .setPositiveButton(R.string.yes_dialog, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        android.os.Process.killProcess(android.os.Process.myPid());
                    }
                })
                .setNegativeButton(R.string.no_dialog, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        dialog.cancel();
                    }
                });
        AlertDialog alert = builder.create();
        alert.show();
    }

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

        final String PATH;
        try {
            PATH = getExternalFilesDir(DIRECTORY_PICTURES).getPath();
        } catch (NullPointerException ex) {
            Toast.makeText(getApplicationContext(), "Unknown error. Can't get access to app directory.", Toast.LENGTH_LONG).show();
            return;
        }

        File directory = new File(PATH);
        File[] files = directory.listFiles();

        if (files == null){
            Toast.makeText(getApplicationContext(), "Unknown error. App directory is not exist.", Toast.LENGTH_LONG).show();
            return;
        }

        final RecyclerView recyclerView = findViewById(R.id.image_gallery);
        recyclerView.setItemAnimator(new SlideInLeftAnimator());
        RecyclerView.LayoutManager layoutManager = new GridLayoutManager(this, 2);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.getItemAnimator().setMoveDuration(1000);
        recyclerView.getItemAnimator().setRemoveDuration(1000);
        recyclerView.getItemAnimator().setAddDuration(1000);

        final ArrayList<String> imageNamesList = Functions.prepareData(files);
        final ImageView scaledImage = findViewById(R.id.scaled_image);
        final RVAdapter adapter = new RVAdapter(getApplicationContext(), imageNamesList, PATH, scaledImage);

        ScaleInAnimationAdapter alphaAdapter = new ScaleInAnimationAdapter(adapter);
        alphaAdapter.setFirstOnly(false);
        alphaAdapter.setDuration(500);
        recyclerView.setAdapter(alphaAdapter);

        final Button takePhotoButton = findViewById(R.id.take_photo_button);
        takePhotoButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                takeAPhoto(PATH, REQUEST_TAKE_PHOTO);
            }
        });

        final Button delAllPhotoButton = findViewById(R.id.delete_all_photo_button);
        delAllPhotoButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                boolean[] isSelected = adapter.getIsSelected();
                boolean isSomethingSelected = Functions.checkArray(isSelected);

                if (!isSomethingSelected) {
                    Toast.makeText(getApplicationContext(), "No one image selected", Toast.LENGTH_LONG).show();
                    return;
                }

                DellAllPhotoResult result = deleteAllPhoto(adapter, imageNamesList, PATH);

                if (result.isResult()) {
                    Toast.makeText(getApplicationContext(), new StringBuilder().
                            append("Done.").
                            append(result.getSuccessResults()).
                            append(" files deleted.").
                            toString(), Toast.LENGTH_LONG).show();
                    recreate();
                } else {
                    Toast.makeText(getApplicationContext(), new StringBuilder().
                            append("Done. ").
                            append("Unknown error.").
                            append(result.getSuccessResults()).
                            append(" files deleted.").
                            append(result.getFailedResults()).
                            append(" files are not.").toString(), Toast.LENGTH_LONG).show();
                    recreate();
                }
            }
        });
    }
}

RVAdapter

public class RVAdapter extends RecyclerView.Adapter<RVAdapter.ViewHolder> {
    private ArrayList<String> imageNameList;
    private Context context;
    private final String PATH;
    private ImageView scaledImg;
    private static boolean[] isSelected;
    private boolean isScaled = false;


    public RVAdapter(Context context, ArrayList<String> imageNameList, String path, ImageView scaledImg) {
        this.imageNameList = imageNameList;
        this.context = context;
        this.PATH = path;
        this.scaledImg = scaledImg;
        isSelected = new boolean[imageNameList.size()];
    }

    private void deleteImageFromListWithRefresh(final RVAdapter.ViewHolder viewHolder, int i) {
        imageNameList.remove(i);
        notifyItemRemoved(i);
        notifyItemRangeChanged(i, imageNameList.size());
        viewHolder.cellLayout.removeAllViews();
        viewHolder.cellLayout.refreshDrawableState();
    }

    public boolean[] getIsSelected() {
        return isSelected;
    }

    @Override
    public RVAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.cell_layout, viewGroup, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(final RVAdapter.ViewHolder viewHolder, final int i) {
        final File image = new File(new StringBuilder().
                append(PATH).
                append("/").
                append(imageNameList.get(i)).
                toString());
        Glide.with(context).load(image).into(viewHolder.img);

        viewHolder.filename.setText(imageNameList.get(i));
        viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!isSelected[i]) {
                    isSelected[i] = !isSelected[i];
                    viewHolder.cellLayout.setBackgroundColor(context.getResources().getColor(R.color.colorCellActive));
                    viewHolder.filename.setTextColor(Color.BLACK);
                } else {
                    isSelected[i] = !isSelected[i];
                    viewHolder.cellLayout.setBackgroundColor(Color.TRANSPARENT);
                    viewHolder.filename.setTextColor(Color.WHITE);
                }
            }
        });

        viewHolder.img.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!isScaled) {
                    scaledImg.setImageURI(Uri.fromFile(image));
                    scaledImg.setVisibility(View.VISIBLE);
                    isScaled = !isScaled;
                }
            }
        });


        scaledImg.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                scaledImg.setVisibility(View.INVISIBLE);
                isScaled = !isScaled;
            }
        });

        viewHolder.delButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                boolean isDeleted = image.delete();
                if (isDeleted) {
                    Toast.makeText(context, "Done.", Toast.LENGTH_LONG).show();
                    deleteImageFromListWithRefresh(viewHolder, i);
                } else if (image.exists()) {
                    Toast.makeText(context, "Unknown error. File was not deleted.", Toast.LENGTH_LONG).show();
                } else {
                    Toast.makeText(context, "Unknown error. Gallery refresh started...", Toast.LENGTH_LONG).show();
                    deleteImageFromListWithRefresh(viewHolder, i);
                }
            }
        });
    }

    @Override
    public int getItemCount() {
        return imageNameList.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder {
        private TextView filename;
        private ImageView img;
        private Button delButton;
        private LinearLayout cellLayout;

        ViewHolder(View view) {
            super(view);
            filename = view.findViewById(R.id.filename);
            img = view.findViewById(R.id.img);
            delButton = view.findViewById(R.id.delete_button);
            cellLayout = view.findViewById(R.id.cell);
        }
    }
}

Functions

public class Functions {

    public static boolean checkArray(boolean[] checked) {
        for (boolean b : checked) {
            if (b) {
                return true;
            }
        }
        return false;
    }

    public static ArrayList<String> prepareData(File[] files) {
        if (files == null) {
            return null;
        }
        ArrayList<String> imageList = new ArrayList<>();
        for (File file : files) {
            if (!file.getName().endsWith(".jpg")) {
                continue;
            }
            imageList.add(file.getName());
        }
        return imageList;
    }

    public static File createImageFile(String path) throws IOException {
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
        String imageFileName = "JPEG_" + timeStamp + "_";
        return File.createTempFile(imageFileName, ".jpg", new File(path));
    }
}

DelAllPhotoResult

public class DellAllPhotoResult {

    private final boolean result;
    private final int successResults;
    private final int failedResults;

    public DellAllPhotoResult(boolean result, int sucsessResults, int failedResults) {
        this.result = result;
        this.successResults = sucsessResults;
        this.failedResults = failedResults;
    }

    public boolean isResult() {
        return result;
    }

    public int getSuccessResults() {
        return successResults;
    }

    public int getFailedResults() {
        return failedResults;
    }
}
\$\endgroup\$
  • \$\begingroup\$ What's wrong with my question? \$\endgroup\$ – KirstenLy Nov 16 '18 at 12:10
  • \$\begingroup\$ It's stiil actual \$\endgroup\$ – KirstenLy Nov 18 '18 at 5:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.