Working with high-quality images in sqlite3

1

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

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;
    }
}
    
asked by anonymous 19.10.2016 / 20:22

1 answer

4

First, since we're good developers, we're too lazy : let's not invent the wheel! Dealing with images on Android is not simple, so we'll use a library to make our life easier. The benefits will be many. And since we can not trust everything about open source project out there, we'll soon use one from Google itself and include a reference to Glide (a quick introduction to it a>).

To add Glide to your project include its dependency on the Gradle project 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 the complexity of loading images (and resizing them too) on the android, including fade effects and everything. But it can also be used to process the bitmap, resize, etc.

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

Let's rename getImage to getImagePath and return the imageUrl field instead of the image field. We also added a order by desc clause that returns the most recent times first and also a limit 1 clause that will only return the first found record, so the latest one, because it is in order. So we do not do the potentially very 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;
}

Now we change the reading part of the image in onCreate to:

//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);
}

Let's update our routine to select 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 may be in the Android Gallery (and be erased at any time), it may be an image of WhatsApp (which we delete at all times). Therefore, we will call the copyImageToAppFolder() that will copy the image to a location that is under the tutelage of our applied. I'm going to use a very interesting copy routine that I've created to suit virtually every scenario I could think of (the same as I use in professional apps code already published on Google Play). This routine knows how to decode the Uri's returned by Android by the document picker:

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 insertImg is no longer the old one, which received a Bitmap. It now receives a String with the path of the image.

public void insertImg(String path) {

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

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

The utility class for making copies of files in android is a bit 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 your program will work correctly.

    
20.10.2016 / 02:22