Pushkar
Pushkar

Reputation: 770

Downloading and restore of sqlite database stored in google drive app folder

I have a database backup stored in app folder in the drive. Below is the code I have written.

public void startRestore(View view)
    {
        int EXTERNAL_WRITE_PERMISSION = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);

        if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.M)
        {
            if(EXTERNAL_WRITE_PERMISSION != PackageManager.PERMISSION_GRANTED)
            {
                if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE))
                {
                    Snackbar.make(mLayout, "Write permission is required",
                            Snackbar.LENGTH_INDEFINITE).setAction("OK", new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            // Request the permission
                            ActivityCompat.requestPermissions(BackupActivity.this,
                                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                                    PERMISSION_REQUEST_STORAGE);
                        }
                    }).show();

                } else {
                    ActivityCompat.requestPermissions(this,
                            new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                            PERMISSION_REQUEST_STORAGE);
                }
            }
        }
        if (backupExists())
        {
            Log.d("RESTORE: ", "Started restore");

            final  String driveFileID = sharedPreferences.getString("dbBackupDriveFileID", "");
            final DriveFile driveFile = DriveId.decodeFromString(driveFileID).asDriveFile();

            Log.d("RESTORE_FileID: ", driveFileID);


            final Task<DriveContents> openFileTask = mDriveResourceClient.openFile(driveFile, DriveFile.MODE_READ_ONLY);

            openFileTask.continueWithTask(new Continuation<DriveContents, Task<Void>>()
            {
                @Override
                public Task<Void> then(@NonNull Task<DriveContents> task) throws Exception
                {
                    Log.d("RESTORE: ", "open File task");

                    DriveContents driveContents = task.getResult();
                    //TODO download file an add to database

                    InputStream inputStream = driveContents.getInputStream();

                    byte[] buf = new byte[8192];

                    int c = 0;



String baseDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
                        String fileName = DatabaseHelper.DATABASE_NAME;

                        Log.d("RESTORE: ", baseDir + "/" +fileName);


                        File f = new File(baseDir+File.pathSeparator+fileName);
                        if(f.canWrite())
                        {
                            Log.d("RESTORE: ", "File writable");

                            OutputStream outputStream = new FileOutputStream(f);

                            while ((c = inputStream.read(buf, 0, buf.length)) > 0)
                            {
                                outputStream.write(buf, 0, c);
                                outputStream.flush();
                            }
                            outputStream.close();
                        }
                        else
                        {
                            Log.d("RESTORE: ", "File not writable");
                        }

                    return mDriveResourceClient.discardContents(driveContents);
                }
            })
            .addOnFailureListener(new OnFailureListener()
            {
                @Override
                public void onFailure(@NonNull Exception e)
                {

                }
            });


        }
        else
        {
            Toast.makeText(this, "Backup does not exists", Toast.LENGTH_SHORT).show();
        }

    }

In the above code the the control always reaches to Log.d("RESTORE: ", "File not writable");. I have the write permissions defined in the manifest and also runtime permission is granted. Also there is no error in the log.

Below is the backup function for reference.

public void startBackup(View view)
    {
        final ProgressDialog progressDialog = new ProgressDialog(this);

        final File currentDB = this.getDatabasePath(DatabaseHelper.DATABASE_NAME);

        Log.d("DATABASE: ", currentDB.getAbsolutePath());
        Log.d("DATABASE: ", currentDB.getName());

        progressDialog.setMessage("Backing Up!!!!");
        progressDialog.show();

        final Task<DriveFolder> appFolderTask = mDriveResourceClient.getAppFolder();
        final Task<DriveContents> createContentsTask = mDriveResourceClient.createContents();

        Tasks.whenAll(appFolderTask, createContentsTask)
                .continueWithTask(new Continuation<Void, Task<DriveFile>>()
                                  {
                                      @Override
                                      public Task<DriveFile> then(@NonNull Task<Void> task) throws Exception
                                      {
                                          DriveFolder parent = appFolderTask.getResult();
                                          DriveContents contents = createContentsTask.getResult();

                                          InputStream inputStream = null;

                                          try
                                          {
                                              inputStream = new FileInputStream(currentDB);
                                          }
                                          catch (FileNotFoundException e)
                                          {
                                              e.printStackTrace();
                                          }

                                          OutputStream outputStream = contents.getOutputStream();
                                          int c = 0;
                                          byte[] buf = new byte[8192];
                                          if (inputStream != null)
                                          {

                                              while ((c = inputStream.read(buf, 0, buf.length)) > 0)
                                              {
                                                  outputStream.write(buf, 0, c);
                                                  outputStream.flush();
                                              }
                                              outputStream.close();
                                          }
                                          else
                                          {
                                              Toast.makeText(BackupActivity.this, "Some error occurred", Toast.LENGTH_SHORT).show();
                                          }

                                          MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
                                                  .setMimeType("application/x-sqlite3")
                                                  .setTitle(currentDB.getName())
                                                  .build();


                                          return mDriveResourceClient.createFile(parent, changeSet, contents);
                                      }
                                  })
                .addOnSuccessListener(this, new OnSuccessListener<DriveFile>() {
                    @Override
                    public void onSuccess(DriveFile driveFile)
                    {
                        progressDialog.dismiss();

                        String driveFileID = driveFile.getDriveId().encodeToString();

                        String dateTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date());

                        SharedPreferences.Editor editor = sharedPreferences.edit();
                        editor.putString("dbBackupDriveFileID", driveFileID);
                        editor.putString("lastDbBackupTime", dateTime);
                        editor.apply();

                        Log.d("DRIVE_FILE", driveFileID);

                        String d = getString(R.string.last_backup) + dateTime;

                        textView.setText(d);

                        Toast.makeText(BackupActivity.this, "Backup Successful. File "+driveFile.getDriveId()
                                .encodeToString(), Toast.LENGTH_LONG).show();

                    }
                })
                .addOnFailureListener(this, new OnFailureListener() {
                    @Override
                    public void onFailure(@NonNull Exception e)
                    {
                        progressDialog.dismiss();
                        Log.e("DRIVE ", "Unable to create file", e);
                        Toast.makeText(BackupActivity.this, "Unable to backup", Toast.LENGTH_SHORT).show();
                    }
                });
    }

Upvotes: 0

Views: 569

Answers (2)

Pushkar
Pushkar

Reputation: 770

Instead of using the File f = new File(baseDir+File.pathSeparator+fileName); I replaced the File usage with an FileOutputStream. Modified part of the restore function:

DriveContents driveContents = task.getResult();
                    //TODO download file an add to database

                    InputStream inputStream = driveContents.getInputStream();

                    byte[] buf = new byte[8192];

                    int c = 0;
                    if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
                    {

                        Log.d("RESTORE: ", "External DIR mounted");

                        String baseDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
                        String fileName = DatabaseHelper.DATABASE_NAME;


                        String fileFullName = baseDir + File.separator + fileName;

                        Log.d("RESTORE: ", fileFullName);


                                FileOutputStream outputStream;
                                outputStream = new FileOutputStream(fileFullName, false);

                                while ((c = inputStream.read(buf, 0, buf.length)) > 0) {
                                    outputStream.write(buf, 0, c);
                                    outputStream.flush();
                                }
                                outputStream.close();
                    }
                    else
                    {
                        Log.d("RESTORE: ", "External DIR not mounted");
                    }

This solved my issue.

Upvotes: 1

MikeT
MikeT

Reputation: 57043

The answer could be as simple as adding f.mkdirs() immediately after File f = new File(baseDir+File.pathSeparator+fileName); and before if(f.canWrite()).

However there are numerous reasons why canWrite can return false, So you should check the STATE (probably before you try canWrite)

Personally I utilise the following rather long winded code :-

class StoreData {

    private String directory; //Note built internally and includes subdirectory
    private String subdirectory;
    private String filename;
    private boolean mounted;
    private boolean inerror;
    private boolean fileexists;
    private boolean direxists;
    private long errorcode;
    private ArrayList<String> errorlist = new ArrayList<>();
    private ArrayList<File> otherfilesindirectory = new ArrayList<>();

    // Need to be aware of the API
    @SuppressWarnings("unused")
    public static final int API_VERSION = Build.VERSION.SDK_INT;
    private static final long UNMOUNTED = 1;
    private static final long FILEIOERR = 2;
    private static final long READERR = 4;
    private static final String NEWLINE = "\r\n";

    /**
     * Sole Constructor for a StoreData object
     * Note instantiating creates but the deletes a file, assuming that
     * no prior errors left the instance in an unusable state (as initially set)
     * Note instantiating, if existcheck (3rd param) is true, does not create
     * and delete the file, rather it checks that the file exists
     *     typically for reading an existing file.
     *
     * @param subdirectory - Sub directory in which to create file
     * @param filename - the file name where actual data will be stored
     * @param existcheck - whether or not to check for the existence of the file
     *
     *  Note!! existcheck, if true, will not try to create the file
     */
    public StoreData(String subdirectory, @SuppressWarnings("SameParameterValue") String filename, boolean existcheck) {
        fileexists = false;
        direxists = false;
        mounted = false;
        inerror = false;
        errorcode = 0;

        this.directory = "";
        this.subdirectory = subdirectory;
        this.filename = filename;

        // External Storage must be mounted.
        String chkmnt = Environment.getExternalStorageState();

        if(!(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))) {
            switch (Environment.getExternalStorageState()) {
                case Environment.MEDIA_SHARED : {
                    errorlist.add(
                            "Although External Storage is present." +
                                    " It cannot be used as it's in use via USB." +
                                    "\nDisconnect the USB cable and then try again."
                    );
                    break;
                }
                case Environment.MEDIA_REMOVED : {
                    errorlist.add(
                            "External Storage is not present." +
                                    "\nInsert an SD Card."
                    );
                    break;
                }
                case Environment.MEDIA_EJECTING : {
                    errorlist.add(
                            "External Storage is being ejected." +
                                    "\nRe-insert the SD Card."
                    );
                    break;
                }
                case Environment.MEDIA_NOFS : {
                    errorlist.add(
                            "External Storage is blank or does not have the correct" +
                                    " filesystem present." +
                                    "\nUse a valid SDCard."
                    );
                    break;
                }
                case Environment.MEDIA_BAD_REMOVAL : {
                    errorlist.add(
                            "External Storage was removed incorrectly." +
                                    "\nRe-insert the SD Card, if this fails then" +
                                    " try restarting the device."
                    );
                    break;
                }
                case Environment.MEDIA_CHECKING : {
                    errorlist.add(
                            "External Storage is unavailable as it is being checked." +
                                    "\nTry again."
                    );
                }
                case Environment.MEDIA_MOUNTED_READ_ONLY : {
                    errorlist.add(
                            "External Storage is READ ONLY." +
                                    "\nInsert an SD card that is not protected."
                    );
                }
                case Environment.MEDIA_UNKNOWN : {
                    errorlist.add(
                            "External Storage state is UNKNOWN." +
                                    "\ntry a different SD Card."
                    );
                }
                case Environment.MEDIA_UNMOUNTABLE : {
                    errorlist.add(
                            "External Storage cannot be mounted." +
                                    "\nTry re-inserting the SD Card or using a different SD Card."
                    );
                }
                case Environment.MEDIA_UNMOUNTED : {
                    errorlist.add(
                            "External Storage is not mounted." +
                                    "\nTry re-inserting the SD Card or using a different SD Card."
                    );

                }
                default: {
                    errorlist.add(
                            "Undefined Error"
                    );
                }
            }
            this.errorcode = UNMOUNTED;
            return;
        } else {
            this.mounted = true;
        }

        // Get the required directory and specified sub directory
        File dir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),subdirectory);
        this.directory = dir.getPath();

        // If existcheck is true check that the directories exist
        if (existcheck && dir.exists()) {
            direxists = true;

        }
        // If the directories do not exist try to create them and redo check
        // Note! existcheck is more for file level so always try to create
        // directories
        else {
            boolean x = dir.mkdirs();
            if(dir.exists()) {
                direxists = true;
            }
        }
        if(direxists) {
            refreshOtherFilesInDirectory();
        }

        // File level
        File f = new File(directory,filename);
        // Check if the file exists if requested and return if it does
        if (existcheck) {
            if (f.exists()) {
                fileexists = true;
            }
            return;
        }

        try {
            boolean x = f.createNewFile();
        }
        catch (IOException e) {
            e.printStackTrace();
            this.errorcode = FILEIOERR ;
            errorlist.add(
                    "File Error " + e.getMessage()
            );
            return;
        }
        boolean x = f.delete();
    }

    @SuppressWarnings({"ConstantConditions", "UnusedReturnValue"})
    public boolean refreshOtherFilesInDirectory() {
        boolean rv = true;
        File dir = new File(directory);
        File[] dirlist = dir.listFiles();
        if((dirlist.length) > 0) {
            // Sort the list
            Arrays.sort(dirlist, new Comparator<File>() {
                @Override
                public int compare(File object1, File object2) {
                    return object1.getName().compareTo(object2.getName());
                }
            });
            otherfilesindirectory.clear();
            for (File aDirlist : dirlist) {
                if (!(aDirlist.getName().equals(this.filename))) {
                    otherfilesindirectory.add(aDirlist);
                }
            }
        }
        return rv;
    }

    /**
     * writeData - Write data to the file from String Arraylist passed
     * Note!! a linefeed is added to each string
     * @param datatowrite - strng ArrayList holding data to write
     * @return result flag
     */
    @SuppressWarnings("unused")
    public boolean writeData(ArrayList<String> datatowrite) {
        // Check that this instance is OK
        if (!this.isOK()) {
            this.errorlist.add(
                    "\nError prior to call to writeData method."
            );
            return false;
        }
        // Prepare to write
        this.errorlist.clear();
        File f = new File(this.directory,File.separator + this.filename);
        try {
            boolean x =  f.createNewFile();
            FileOutputStream fos = new FileOutputStream(f);
            OutputStreamWriter osw = new OutputStreamWriter(fos);
            for (int i = 0; i < datatowrite.size(); i++) {
                osw.write(datatowrite.get(i) + NEWLINE);
            }
            osw.flush();
            osw.close();
            fos.flush();
            fos.close();
            this.fileexists = true;
        }
        catch (IOException e) {
            e.printStackTrace();
            this.errorcode = FILEIOERR;
            errorlist.add(
                    "File Error " + e.getMessage()
            );
            return false;
        }
        return true;
    }

    /**
     * readData - Populate a String ArrayList from the data in the file
     * Note! Assumes linefeeds in the file separate strings of data
     * @return - result flag
     */
    @SuppressWarnings("unused")
    public ArrayList<String> readData() {
        ArrayList<String> rv = new ArrayList<>();

        if(!this.isOKandExists()) {
            this.errorlist.add(
                    "\nError prior to call to readData method or the file doesn't exist."
            );
            this.errorcode = READERR;
            return rv;
        }

        this.errorlist.clear();
        File f = new File(this.directory,File.separator + this.filename);
        try {
            FileInputStream fis = new FileInputStream(f);
            InputStreamReader isr = new InputStreamReader(fis);
            BufferedReader br = new BufferedReader(isr);
            String line;
            while((line = br.readLine()) != null) {
                rv.add(line);
            }
        }
        catch (IOException e) {
            e.printStackTrace();
            this.errorcode = READERR;
            errorlist.add(
                    "File Read Error" + e.getMessage()
            );
            return rv;
        }
        return rv;
    }

    /**
     * isOK - Check if object is usable
     * @return true if OK else false
     */
    public boolean isOK() {
        return !(errorcode != 0 || !mounted || inerror);
    }

    /**
     * exists = Check if the file exists
     * @return - Result of check
     */
    @SuppressWarnings("unused")
    public boolean exists() {
        return this.fileexists;
    }

    public boolean isOKandExists() {
        return this.isOK() && this.fileexists;
    }

    /**
     * Return a string displaying the instances details
     * @return string displaying object's members
     */
    public String Display() {
        String rv;

        rv = "Directory path=" + directory + "\n" +
                "SubDirectory=" + subdirectory + "\n" +
                "Filename=" + filename + "\n" +
                "Mounted =" + Boolean.toString(mounted) + "\n" +
                "Directory Exists=" + Boolean.toString(this.direxists) + "\n" +
                "File Exists=" + Boolean.toString(this.fileexists) + "\n" +
                "In Error=" + Boolean.toString(inerror) + "\n" +
                "Last Error Code=" + Long.toString(errorcode);
        return rv;
    }

    @SuppressWarnings("unused")
    public String DisplayWithOtherFiles() {
        String rv;
        rv = this.Display() + "\nOther Files in Directory (" + this.directory + ") ";

        for(int i = 0; i < otherfilesindirectory.size(); i++) {
            rv = rv + "\n\t" + otherfilesindirectory.get(i).getName();
        }
        return rv;
    }

    /**
     * Retrieve generated error messages. if any
     * @return sting comprised of all error messages generated
     */
    @SuppressWarnings("unused")
    public String getErrorMessages() {
        String rv = "";
        for(int i = 0; i < errorlist.size(); i++) {
            rv = rv + errorlist.get(i);
        }
        return rv;
    }

    /**
     * Method: getDirectory - get the backup directory as a String
     * @return Directory as a String
     */
    public String getDirectory() {
        return this.directory;
    }

    /**
     * Method: getFilename - get the filename of the object as a String
     * @return Filename as a String
     */
    @SuppressWarnings("unused")
    public String getFilename() {
        return this.filename;
    }

    /**
     * Method: getSubDirectory - get the sub-directory as a string
     * @return Sub-Directory as a String
     */
    @SuppressWarnings("unused")
    public String getSubDirectory() {
        return this.subdirectory;
    }

    /**
     * Method: getFilesInDirectory - return an ArrayList of type File
     * @return List of files in the directory as an ArrayList<File>
     */
    public ArrayList<File> getFilesInDirectory() {
        return this.otherfilesindirectory;
    }
}

Upvotes: 0

Related Questions