Working with high quality images in sqlite3

Asked

Viewed 373 times

1

In my application I make the selection of an image from the gallery, then I save it in the bank, but if the image has a high quality the app does not save, besides not saving it to work, has some way to save this image in the bank even if it is with a good quality or not let the user save that image and send a message to him ?

public class Horarios extends AppCompatActivity {

    private int REQUEST_CAMERA = 0, SELECT_FILE = 1;
    private Button btnSelect;
    private Button btnCamera;
    private String Chave;
    BancoDeDados db = new BancoDeDados(this);
    SQLiteDatabase banco;
    Bitmap bitmap;
    private ImageView imageView;
    private FloatingActionButton fab;



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


        btnSelect = (Button) findViewById(R.id.btnSelect);
        btnCamera = (Button) findViewById(R.id.btnCamera);
        imageView = (ImageView) findViewById(R.id.imageView);
        fab = (FloatingActionButton) findViewById(R.id.fabH);

        btnSelect.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Chave = "Selecionar";
                galleryIntent();


            }
        });

        btnCamera.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Chave = "Camera";
                cameraIntent();

            }
        });

        fab.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {

                insertImg(bitmap);
                Toast.makeText(Horarios.this, "Imagem Salva!", Toast.LENGTH_SHORT).show();

                finish();
            }
        });


        //carregar imagem

        byte[] imageFinal = getImage();

        if(imageFinal != null){

            try {

                imageView.setImageBitmap(BitmapFactory.decodeByteArray(imageFinal,0,imageFinal.length));

                imageView.invalidate();

            } catch(Exception e) {
                Toast.makeText(Horarios.this, "erro:" +e, Toast.LENGTH_LONG).show();
            }

        }


    }


    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case Utility.MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    if(Chave.equals("Camera"))
                        cameraIntent();
                    else if(Chave.equals("Selecionar"))
                        galleryIntent();
                }
                break;
        }

        Toast.makeText(Horarios.this, "testetando o codigo 111111", Toast.LENGTH_LONG).show();
    }

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

        if (resultCode == Activity.RESULT_OK) {
            if (requestCode == SELECT_FILE)
                onSelectFromGalleryResult(data);
            else if (requestCode == REQUEST_CAMERA)
                onCaptureImageResult(data);
        }

    }




    private void galleryIntent()
    {
        Intent intent = new Intent();
        intent.setType("image/*");
        intent.setAction(Intent.ACTION_GET_CONTENT);
        startActivityForResult(Intent.createChooser(intent, "Select File"),SELECT_FILE);
    }

    @SuppressWarnings("deprecation")
    private void onSelectFromGalleryResult(Intent data) {

        bitmap = null;
        if (data != null) {
            try {
                bitmap = MediaStore.Images.Media.getBitmap(getApplicationContext().getContentResolver(), data.getData());

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        imageView.setImageBitmap(bitmap);

        fab.setVisibility(View.VISIBLE);
    }




    private void cameraIntent()
    {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        startActivityForResult(intent, REQUEST_CAMERA);
    }

    private void onCaptureImageResult(Intent data) {
        bitmap = (Bitmap) data.getExtras().get("data");
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, bytes);

        File destination = new File(Environment.getExternalStorageDirectory(), System.currentTimeMillis() + ".jpg");
        FileOutputStream fo;

        try {
            destination.createNewFile();
            fo = new FileOutputStream(destination);
            fo.write(bytes.toByteArray());
            fo.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        imageView.setImageBitmap(bitmap);

        fab.setVisibility(View.VISIBLE);
    }



    public void insertImg(Bitmap img ) {

        byte[] data = getBitmapAsByteArray(img);

        banco = db.getWritableDatabase();
        ContentValues content = new ContentValues();
        content.put("imagem", data);

        banco.insert("Horarios", null, content);

    }
    public static byte[] getBitmapAsByteArray(Bitmap bitmap) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.PNG, 0, outputStream);
        return outputStream.toByteArray();
    }



    public byte[] getImage(){
        byte[] result = null;

        banco = db.getReadableDatabase();

        String qu = "select imagem from Horarios";
        Cursor cur = banco.rawQuery(qu, null);

        if (cur.moveToLast()){
            result = cur.getBlob(0);
            cur.close();
        }
        if (cur != null && !cur.isClosed()) {
            cur.close();
        }

        return result;
    }
}
  • Friend, without you publishing your code it will be impossible to answer this question correctly. I imagine the problem is memory consumption when loading the full bitmap without downsampling it (something automatically performed if you use an image library such as Glide). Post your code, or this question may even end up being closed by moderators! At least the part that reads the image and records it. Another thing: do not store image in the database, but rather on file and just save its path in the database.

  • so if you have the code or some link that helps me save only the way and can send me I thank you

  • the part of the camera is leaving the photo that is taken unreadable, more if I can arrange the part that selects maybe I’ll even take the feature of the camera

  • I have the solution to both problems. But I need to separate the code and format to post the answer. I think I can get it by tomorrow 'cause I’m still working.

  • I noticed you insert in Schedules the image bytes and the Activity you give a select in times and take the first one that comes, returning the byte array of the image you recorded there... That won’t guarantee you’ll ever get the last picture. This seems to me a problem in the code, but I will answer only about the recording part of the image.

  • our thank you, saved my life, I will wait then, sorry for the abuse but if you can help me with one more problem, in my application I have a kind of event system , where I save the date of the event , I would like to make a notification , searching I found some codes but they only execute the notification at the time, my intention is to appear even with the application in the background, if you do not have the code but have at least a link q help me already I am grateful

  • really not perceived the moveToLast is inside the if was to be out, but I’m not having problems this working properly, if you notice any more notice I ask you to let me know, I’m new in android development, I am developing my TCC and all I’ve learned so far was on the internet, I haven’t had the opportunity to pay for a course but I really liked it

  • On the other problems, for example, to take the images from the camera, you can open a new question! With a specific title. About the notions in the background, same thing: open another question and try to specify well what you can not do (otherwise it can be considered too wide!). If there was something missing in the answer, or there was some doubt, comment there that I update the same in the measure of my time :-)

Show 4 more comments

1 answer

4


First of all, how good we are developers, we are also lazy: Let’s not invent the wheel! Dealing with images on Android is not simple, so we will use a library to make our life easier. The benefits will be many. And as we can not trust everything is project open source we will soon use one from Google itself and include in your project a reference to the Glide (a brief introduction to it).

To add Glide to your project include your dependency in the project’s Gradle file:

dependencies {
  compile 'com.github.bumptech.glide:glide:3.7.0'
  compile 'com.android.support:support-v4:19.1.0'
}

Glide is a library that removes virtually all complexity in uploading images (and resize them as well) on android, including effects fade and everything else. But it can also be used to process bitmap, resizing, etc.

It is not good practice, unless it is a business need, to include the images themselves, as a blob, in the database. The database gets huge, recovery operations are slow, you can’t use a direct image reading library (you have to load the byte array to memory first), etc. So we will copy the selected image and save it in another directory, storing only this url in the bank and nothing else (we will see this below)

We will rename getImage for getImagePath and return the imageurl field instead of the image field. We also added a clause order by desc which returns the most recent times first and also a clause limit 1 that will return only the first record found, then the most recent, as it is in order. So we do not do the potentially too slow moveToLast():

public byte[] getImagePath(){
    banco = db.getReadableDatabase();

    // mude o campoData por um campo com data e hora da inserção, ou
    // pelo id autoincremento (que é sequencial) para forçar a ordem cronológica     
    String qu = "select imagemUrl from Horarios order by campoData desc limit 1";
    Cursor cur = banco.rawQuery(qu, null);

    if (cur.moveToFirst()){
        result = cur.getString(0);
        cur.close();
    }
    if (cur != null && !cur.isClosed()) {
        cur.close();
    }
    // faltava fechar o banco
    banco.close();

    return result;
}

We have now modified the reading part of the image on onCreate for:

//carregar imagem
String path = getImagePath();
if(path != null) {
    Glide
        .with(this) // se for fragment use getActivity()
        .load(path)
        .centerCrop() // manterá as proporções se não forem equivalentes à image view
        .into(imageView);
}

We will update our routine of selecting the gallery image. It needs to copy the Uri returned by Android, which points to an image under which we have no control! This image can be in the Android Gallery (and be deleted at any time), It can be a picture of Whatsapp (we erase all the time). So we’ll call the copyImageToAppFolder() that will copy the image to a location that is under the guardianship of our applied. I’m going to use a very interesting copy routine that I created to suit virtually every scenario I could foresee (the same as I use in professional code apps already published in Google Play). This routine knows how to decode the Uri returned by Android by document selector:

private void onSelectFromGalleryResult(Intent data) {
    if (data == null)
        return;
    Uri uri = data.getData();
    String imageUrl = copyImageToAppFolder(uri);
    if (imageUrl != null) {
        Glide
            .with(this)
            .load(imageUrl)
            .centerCrop() 
            .into(imageView);    
        insertImg(imageUrl);
    }
    fab.setVisibility(View.VISIBLE);
}

private String copyImageToAppFolder(uri) {
    String dir = getAppDir(); // implemente essa rotina
    String filename = "imagem_" + System.currentTimeMillis() + ".jpg";
    String destination = new File(dir, filename).getPath();
    Files.copy(this, uri, destination);
    return destination;
}

Note that the insertImg is no longer the old one, which received a Bitmap. It now receives a String with the image path.

public void insertImg(String path) {

    banco = db.getWritableDatabase();
    ContentValues content = new ContentValues();
    content.put("imagemUrl", path);

    banco.insert("Horarios", null, content);
}

The utility class to carry out copies of files on android is a little large but quite versatile:

import android.content.Context;
import android.net.Uri;

import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;

public final class Files {

    // Copy file from a Uri retrieved from ACTION_GET_CONTENT, using a ContentResolver
    public static boolean copy(Context context, Uri fromUri, String to) {
        try {
            FileInputStream inStream = (FileInputStream)context.getContentResolver().openInputStream(fromUri);
            return copy(inStream, to);
        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
            return false;
        }
    }
    public static boolean copy(FileDescriptor from, String to) {
        FileInputStream inStream = new FileInputStream(from);
        return copy(inStream, to);
    }
    public static boolean copy(FileInputStream from, String to) {
        FileOutputStream outputStream;
        try {
            outputStream = new FileOutputStream(to);
        } catch (IOException ex) {
            ex.printStackTrace();
            try { if (from != null) from.close(); }
            catch (IOException e) { e.printStackTrace(); }
            return false;
        }
        return copy(from, outputStream);
    }
    public static boolean copy(FileInputStream inStream, FileOutputStream outStream) {
        FileChannel inChannel = null;
        FileChannel outChannel = null;
        try{
            try {
                inChannel = inStream.getChannel();
                outChannel = outStream.getChannel();
                long bytesTransferred = 0;
                while(bytesTransferred < inChannel.size()){
                    bytesTransferred += inChannel.transferTo(
                            bytesTransferred, inChannel.size() - bytesTransferred, outChannel);
                }
                return true;
            }
            finally {
                if (inChannel != null) inChannel.close();
                if (outChannel != null) outChannel.close();
                if (inStream != null) inStream.close();
                if (outStream != null) outStream.close();
            }
        }
        catch (IOException ex){
            ex.printStackTrace();
            return false;
        }
    }
}

With these modifications I believe that your program works correctly.

  • I am implementing the code, in the part of selecting the image appeared the following error, String imageurl = data.getData(); the getData method returns a Uri not a string, has the getDataString method I change the method ?

  • I will change the code, as I wrote here and not in Androidstudio I ended up not seeing. The method Files.copy also accepts a Uri in the first parameter! To convert a Uri for string, call the toString() of the object Uri

  • I am testing by parts, and the part of selecting the image you select and the image does not appear in the imageView

  • @Weird vitorhugo... only if the imageurl is null could this happen. Do you have how to debug to see? Or put an alert if it is null? Is there any way to check that the imageView is not invisible, too? If the imageurl is correct, there is no error, yes! Another thing: I saw now that you just insert the image in the bank when you click on FAB, the way I am inserting whenever you select an image!

  • i commented the line of Insert only to make the test, put a Toast and informed the Uri (imageurl.toString()) and this appearing a path (content://com.android.providers.downloads.Documents/Document/2)

  • I understood what is happening. It’s the new way the android returns selected items. Let’s then display the image already copied. I’ll change the code now.

  • a detail do not know if this is when paste the code gave error in the path then gave alt+enter and it imported (import Static android.R.attr.path;)

  • I managed to implement almost everything except Files, which honestly I do not understand, you would not have a link showing how to implement the same ?

  • @Vitorhugo o o files is a class that I wrote myself from scratch. Create a Java file for this class and paste it. All the code there... Then just reuse

  • I did a test to see if it would fail, I deleted in the gallery the image that was being used in the app, and no error appeared and the image continued there

  • Loudenvier if you can help me on another matter http://answall.com/questions/160332/trabando-com-c%C3%A2mera-e-galeria

Show 7 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.