Implementing backup and restore feature of SQLite Database to comply with Scoped Storage Android 11

package com.abc.def;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
import java.util.Date;

import static android.os.Build.VERSION.SDK_INT;

public class BackupRestoreActivity extends AppCompatActivity {

    Button btnBackup, btnRestore;
    SQLiteHelper sqLiteHelper;
    File mediaStorageDir;
    public static final int REQUEST_WRITE_STORAGE = 1;
    TextView txtStatus;

    /// ---
    private static final int CREATE_BACKUP_REQUEST_CODE = 1000;
    private static final int PICK_RESTORE_FILE_REQUEST_CODE = 2000;



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

        sqLiteHelper = new SQLiteHelper(BackupRestoreActivity.this);
        btnBackup = (Button)findViewById(R.id.btnBackup);
        btnRestore = (Button)findViewById(R.id.btnRestore);
        txtStatus = (TextView)findViewById(R.id.txtStatus);

        

        btnBackup.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                // Inform picker that we would like to create a file
                Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);

                // Filter to only show results that can be "opened", such as a file
                intent.addCategory(Intent.CATEGORY_OPENABLE);

                // Set the mimetype of the file we're creating
                intent.setType("application/octet-stream");

                // Set a suggested title for the backup file with custom .dtt extension
                // (though the user can change it within the picker)
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH-mm");
                final String backupFileName = sdf.format(new Date()) + ".bak";
                intent.putExtra(Intent.EXTRA_TITLE, backupFileName);

                // Open the picker
                startActivityForResult(intent, CREATE_BACKUP_REQUEST_CODE);
               
            }
        });

        btnRestore.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // Inform picker that we would like to open a file
                Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);

                intent.addCategory(Intent.CATEGORY_OPENABLE);

               
                intent.setType("*/*");
                String[] mimeTypes = {"application/octet-stream"};
                intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);

                startActivityForResult(intent, PICK_RESTORE_FILE_REQUEST_CODE);
            }
        });

    }








    private static final int READ_REQUEST_CODE = 42;

    @Override
    public void onActivityResult(int requestCode, int resultCode,
                                 Intent resultData) {
        super.onActivityResult(requestCode,resultCode,resultData);
        // The ACTION_OPEN_DOCUMENT intent was sent with the request code
        // READ_REQUEST_CODE. If the request code seen here doesn't match, it's the
        // response to some other intent, and the code below shouldn't run at all.



        if (resultCode != Activity.RESULT_OK) {
            return;
        }

        // to create backup
        if (requestCode == CREATE_BACKUP_REQUEST_CODE) {
            // get URI of file created by picker
            Uri backupFileUri = resultData.getData();

            // Perform the actual backup procedure
            // backupDatabaseOnSeparateThread(backupFileUri);
            newexportDB(backupFileUri);
            String msg = "Backup has been saved!";
            Toast.makeText(this, msg , Toast.LENGTH_SHORT).show();
            txtStatus.setText(msg);
            Log.i("FILE SELECT", "Save backup in: " + backupFileUri.getPath().toString());
        }
        // to create backup ends

        // // to restore
        if (requestCode == PICK_RESTORE_FILE_REQUEST_CODE) {
            try {
                // Get URI representing the file chosen in the picker.
                Uri uri = resultData.getData();
                InputStream inputStream = getContentResolver().openInputStream(uri);

                // Perform the actual restore procedure
                newrestoreDB(inputStream);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                //showErrorSnackbarOnUiThread(e);
                //showLogError(LOG_TAG, e, "");
            }
        }

    }

    private void newrestoreDB(InputStream _inputStream){
        boolean exceptionOccurred = false;
        final long startTime = System.currentTimeMillis();
        try {
            String tempDir = getCacheDir().getPath().toString();
            File newfile = new File(tempDir + "/" + "newbackup.db");
            copyInputStreamToFile(_inputStream, newfile);

            restoreDatabaseFile(BackupRestoreActivity.this, newfile, sqLiteHelper.getDatabaseName());

            sqLiteHelper.getWritableDatabase().close();


        }catch (Exception e)
        {

        }
    }

    // Restore database file stored at tempDir/nameOfFileToRestore.
    // This method is inside my DBHelper class.
    public void restoreDatabaseFile(Context context, File newDbFile, String nameOfFileToRestore)
            throws IOException {
        // Close the SQLiteOpenHelper so it will commit the created empty database
        // to internal storage.
        sqLiteHelper.close();

        File currentDbFile = new File(context.getDatabasePath(nameOfFileToRestore).getPath());
        //File newDbFile = new File(tempDir + "/" + nameOfFileToRestore);
        if (newDbFile.exists()) {
            copyFile(newDbFile, currentDbFile, true);
            String msg = "Database has been restored!";
            Toast.makeText(this, msg , Toast.LENGTH_SHORT).show();
            txtStatus.setText(msg);
        }
    }

    // Copy an InputStream to a File.
//
    private void copyInputStreamToFile(InputStream in, File file) {
        OutputStream out = null;

        try {
            out = new FileOutputStream(file);
            byte[] buf = new byte[1024];
            int len;
            while((len=in.read(buf))>0){
                out.write(buf,0,len);
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            // Ensure that the InputStreams are closed even if there's an exception.
            try {
                if ( out != null ) {
                    out.close();
                }

                // If you want to close the "in" InputStream yourself then remove this
                // from here but ensure that you close it yourself eventually.
                in.close();
            }
            catch ( IOException e ) {
                e.printStackTrace();
            }
        }
    }

    private void newexportDB(Uri _backupfileuri){
        try {
            File tempDirectory = getCacheDir();


            FileChannel source = null;
            FileChannel destination = null;


            String databaseBackupFile = getApplicationContext().getDatabasePath(sqLiteHelper.getDatabaseName()).toString();// getFilesDir().getPath() + "databases/SQLiteDatabase.db" ; // "/data/data/com.zakasoft.cashreceipt/databases/SQLiteDatabase.db" ; //  this.getDatabasePath(sqLiteHelper.getDatabaseName()).getPath();
            File dbbackupfile = backUpDatabaseFile(databaseBackupFile, tempDirectory.getPath() + "/" + sqLiteHelper.getDatabaseName());


            Uri zipFileUri = Uri.fromFile(dbbackupfile);

            InputStream inputStream = getContentResolver().openInputStream(zipFileUri);
            OutputStream outputStream = getContentResolver().openOutputStream(_backupfileuri);

            // Instead of Files, we pass in streams derived from the URIs.
            // This is because we don't have File write access to the final directory.
            copyFile(inputStream,outputStream);


        } catch (final Exception e)
        {
        }

    }
    // Create a copy of the file at pathOfFileToBackUp and save it to destinationFilePath.
    // This method is inside my DBHelper class.
    public File backUpDatabaseFile(String pathOfFileToBackUp, String destinationFilePath)
            throws IOException {
        File currentDbFile = new File(pathOfFileToBackUp);
        File newDb = new File(destinationFilePath);
        if (currentDbFile.exists()) {
            copyFile(currentDbFile, newDb, false);
            return newDb;
        }
        return null;
    }

    public static boolean copyFile(InputStream input, OutputStream output) {
        try {
            byte[] buf = new byte[1024];
            int len;
            while ((len = input.read(buf)) > 0) {
                output.write(buf, 0, len);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            try {
                if (input != null)
                    input.close();
                if (output != null)
                    output.close();
            } catch (Exception e) {
            }
        }
        return true;
    }

    // Copy fromFile to toFile, using traditional file IO
    public static boolean copyFile(File fromFile, File toFile, boolean bDeleteOriginalFile)
            throws IOException {

        boolean bSuccess = true;
        FileInputStream inputStream = new FileInputStream(fromFile);
        FileOutputStream outputStream = new FileOutputStream(toFile);

        FileChannel fromChannel = null;
        FileChannel toChannel = null;
        try {
            fromChannel = inputStream.getChannel();
            toChannel = outputStream.getChannel();
            fromChannel.transferTo(0, fromChannel.size(), toChannel);
        } catch (Exception e) {
            bSuccess = false;
        } finally {
            try {
                if (fromChannel != null) {
                    fromChannel.close();
                }
            } finally {
                if (toChannel != null) {
                    toChannel.close();
                }
            }

            if (bDeleteOriginalFile) {
                fromFile.delete();
            }
        }
        return bSuccess;
    }
}