Export and import SQLite database - java

Good evening, I'm developing an android app, it has SQLite database, I need a way to copy the database to the external storage of the device, where I can copy to another device and so I can import the database into the another device.
For example:
Suppose the application calls "example", the database is in the "/data/data/com.gnd.example/databases" folder and it is called data.db, need to copy it to the "example / backup" folder ", for example" / storage / emulated / 0 / Example / Backup ". This is the first part.
The second part is the import, where the application should copy the file from the "example / import" folder to the folder "/data/data/com.gnd.example/databases"
For this I have a two button activity, btn_export and btn_import.
I have already relied on the following solutions:
import / export to android sqlite database
Simple export and import of SQLite database on Android
I already inserted it in AndroidManifest
How do I ask the user for permission?
I tried copying using this code that I took in one of the examples
private void backupDatabase () throws IOException {
   String inFileName = "/data/data/com.gnd.example/databases/dados.db";
   File dbFile = new File (inFileName);
   FileInputStream fis = new FileInputStream (dbFile);
   String outFileName = Environment.getExternalStorageDirectory () + "/ example / backup / data.db";
   OutputStream output = new FileOutputStream (outFileName);
   byte [] buffer = new byte [1024];
   int length;
   while ((length = fis.read (buffer))> 0) {
       output.write (buffer, 0, length);
   }
   output.flush ();
   output.close ();
   fis.close ();
}
The button looks like this:
   #Override
   public void onClick (View view) {
       try {
           backupDatabase ();
       } catch (IOException e1) {
           e1.printStackTrace ();
       }
   
});
Log when I press the button:
07/01 19:35:39: Launching app
$ adb shell am start -n "com.gnd.keepkey / com.gnd.keepkey.Telephone" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Client not ready yet..Waiting for process to come online
Waiting for process to come online
Connected to process 27724 on device motorola-moto_z2_play-0039635857
Capturing and displaying logcat messages from application. This behavior can be disabled in the "Logcat output" section of the "Debugger" settings page.
I / zygote: Do partial code cache collection, code = 20KB, data = 29KB
I / zygote: After code cache collection, code = 20KB, data = 29KB
    Increasing code cache capacity to 128KB
I / zygote: Do partial code cache collection, code = 20KB, data = 47KB
I / zygote: After code cache collection, code = 20KB, data = 47KB
    Increasing code cache capacity to 256KB
I / zygote: Compiler allocated 4MB to compile void android.widget.TextView. <Init> (android.content.Context, android.util.AttributeSet, int, int)
I / zygote: Full code cache collection, code = 120KB, data = 82KB
I / zygote: After code cache collection, code = 117KB, data = 62KB
I / zygote: Do partial code cache collection, code = 125KB, date = 79KB
I / zygote: After code cache collection, code = 125KB, data = 79KB
    Increasing code cache capacity to 512KB
W / System.err: java.io.FileNotFoundException: /storage/emulated/0/teste/dados.db (No such file or directory)
        at java.io.FileOutputStream.open0 (Native Method)
W / System.err: at java.io.FileOutputStream.open (FileOutputStream.java:287)
        at java.io.FileOutputStream. <init> (FileOutputStream.java:223)
        at java.io.FileOutputStream. <init> (FileOutputStream.java:110)
        at com.gnd.keepkey.funcoes.Exportar_Importar.backupDatabase (Export_Importar.java:87)
        at com.gnd.keepkey.funcoes.Exportar_Importar.access $ 000 (Export_Importar.java:42)
        at com.gnd.keepkey.funcoes.Export_Import $ 1.onClick (Export_Import.java:69)
W / System.err: at android.view.View.performClick (View.java:6259)
        at android.view.View $ PerformClick.run (View.java:24732)
        at android.os.Handler.handleCallback (Handler.java:789)
        at android.os.Handler.dispatchMessage (Handler.java:98)
        at android.os.Looper.loop (Looper.java:164)
        at android.app.ActivityThread.main (ActivityThread.java:6592)
        at java.lang.reflect.Method.invoke (Native Method)
W / System.err: at com.android.internal.os.Zygote $ MethodAndArgsCaller.run (Zygote.java:240)
        at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:769)

FileNotFoundException is likely due to the directory teste not existing, perhaps due to permissions.
Using :-
private void backupDatabase () throws IOException {
String inFileName = "/data/data/com.gnd.example/databases/dados.db";
File dbFile = new File (inFileName);
FileInputStream fis = new FileInputStream (dbFile);
String outFileName = Environment.getExternalStorageDirectory () + "/ example / backup / data.db";
//<<<<<<<<<<< CODE ADDED >>>>>>>>>>
File os = new File(outFileName);
if (!os.getParentFile().exists()) {
os.getParentFile().mkdirs();
}
//<<<<<<<<<< END Of ADDED CODE >>>>>>>>>>
OutputStream output = new FileOutputStream(os); //<<<<<<<<<< CHANGED
byte [] buffer = new byte [1024];
int length;
while ((length = fis.read (buffer))> 0) {
output.write (buffer, 0, length);
}
output.flush ();
output.close ();
fis.close ();
}
will create the directories if they do not exist (assuming permissions are correct)
Working Example :-
The following is a working app
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="aso.so56843045backup">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Note the <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> (for earlier devices)
ExternalStoragePermissions.java
class ExternalStoragePermissions {
public int API_VERSION = Build.VERSION.SDK_INT;
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
//Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
public static final String THISCLASS = ExternalStoragePermissions.class.getSimpleName();
private static final String LOGTAG = "SW_ESP";
public ExternalStoragePermissions() {}
// Note call this method
public static void verifyStoragePermissions(Activity activity) {
int permission = ActivityCompat.checkSelfPermission(
activity,
Manifest.permission.WRITE_EXTERNAL_STORAGE);
if(permission != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
activity,
PERMISSIONS_STORAGE,
REQUEST_EXTERNAL_STORAGE
);
}
}
}
If permissions not given then the directories cannot be created resulting in FileNotFoundException.
DBHelper.java
public class DBHelper extends SQLiteOpenHelper {
public static final String DBNAME = "dados.db";
public static final int DBVERSION = 1;
public DBHelper(Context context) {
super(context, DBNAME, null, DBVERSION);
this.getWritableDatabase();
}
#Override
public void onCreate(SQLiteDatabase db) {
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
A Very basic empty database (exception android_metadata table), enough to check backup.
MainActivity.java
public class MainActivity extends AppCompatActivity {
DBHelper mDBHlpr;
Button mBackup;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBackup = this.findViewById(R.id.backup);
mBackup.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mDBHlpr.close();
try {
backupDatabase();
} catch (IOException e) {
e.printStackTrace();
}
}
});
ExternalStoragePermissions.verifyStoragePermissions(this);
mDBHlpr = new DBHelper(this);
}
private void backupDatabase () throws IOException {
FileInputStream fis = new FileInputStream (this.getDatabasePath("dados.db").getPath());
String outFileName = Environment.getExternalStorageDirectory () + "/example/backup/" + String.valueOf(System.currentTimeMillis()) + "data.db";
Log.d("OSFILEPATH",outFileName);
File os = new File(outFileName);
if (!os.getParentFile().exists()) {
os.getParentFile().mkdirs();
}
OutputStream output = new FileOutputStream(os);
byte [] buffer = new byte [1024];
int length;
while ((length = fis.read (buffer))> 0) {
output.write (buffer, 0, length);
}
output.flush ();
output.close ();
fis.close ();
}
}
Notes
When first run after install permission will be requested for later devices (click allow).
Backup has been named with a timestamp so multiple backups can exist.
Database is closed, (this should cope with Android Pie+ wehere default is WAL mode, the close should empty (commit the changes) the -wal and -shm files, thus negating the need to backup the additional files).
Result

With respect to your crash:
You have not created the directory, at least not via your code. Create a File object pointing to the directory that you want, then call mkdirs() on that object.
You might not be holding the WRITE_EXTERNAL_STORAGE permission, including requesting it at runtime. See https://developer.android.com/training/permissions/requesting.
Other problems here include:
You will not have the ability to write to your requested location in Android Q (by default) and Android R (for all apps). I recommend that you write to getExternalFilesDir() (found on Context) or use the Storage Access Framework.
You are doing disk I/O on the main application thread. This will cause your UI to freeze while that I/O is occurring. Users may think that your app is broken. There are many ways to address this, though the Jetpack approach would be to use a ViewModel and LiveData.
You are not arranging to get your file indexed by the MediaStore, so the user will not be able to see it in their desktop file manager. Use MediaScannerConnection.scanFile() to index the file.
"/data/data/com.gnd.example/databases/dados.db" is the wrong path on many Android devices. Never hardcode paths. Use getDatabasePath() on Context to get the path to your database.
This sample Java app shows how a lot of this is done, in the context of a text editor instead of a database backup solution.
In general, I recommend that you put this project aside for a while and read an up-to-date book on Android app development. Most of the problems that I mention here are on topics that would be covered in a decent book on Android.

Related

Android studio download with downloadmanager and then open downloaded file

(to make things clearer :) I am using WebView. The idea is that users can visit any page and download any file, the idea is to support as many basic features as i can to get closer feel to built in web browser.
As the title says, i am trying to make my app download files and open them once theyre downloaded. There are some bullet points i want to mention :
user should be able to download any kind of file (i guess its called mimeType), as they would in they default internet browser app
since users download items from webview, it would only make sense if downloads would appear in downloads folder
would be awesome if every users device could decide on itself which app to use to open downloaded file, but, for example, my samsung s9 asks which app to use, thats not a bad option either
i ran across some problems like sucessful download, but the file was empty/corrupted, the downlaoded file was missing in file browser even if app told me that the download is completed
So i have been on not 2nd, not 3rd but even 5th page of google search results, so ive seen a lot, these are couple versions of my code so far, every single one of them are not working as i expected them in my dreams.
Just to be clear, the code you are about to see is under webview setDownloadListener(new DownloadListener() ...
So my latest version, without file opening after download :
public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimeType, long contentLength) {
//used to call a class to in previous code version to make some downloads
//new DownloadTask(webview_base.this, url, mimeType);
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url)).setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI |
DownloadManager.Request.NETWORK_MOBILE);
// in order for this if to run, you must use the android 3.2 to compile your app
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
}
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, url.substring(url.lastIndexOf('/')));
// get download service and enqueue file
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);
}
});
the problem with ^ this version was that i was able to download files, but it downlaoded pages HTML instead of actual pdf or whatever i was aiming for, thats weird.
previous version was a bit smoother, i was able to download files, and i used a dialog to offer to open a file. Everything worked fine, but documents were empty/corrupted and i got offered to open them with weird apps, for example, once i got asked to open pdf with my smartwatch app, but that mustve gone away since i edited a code time by time. The problem i landed here was that even after couple of ctrl+z i wasnt able to get my app back on feets - i was stuck at download screen and logcat shout at me that it couldnt fine a download directory. at that point i got mad and started the version you can see above. Anyway, here is the whole class :
package com.example.viaapp_v2;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import androidx.core.content.FileProvider;
public class DownloadTask extends FileProvider {
private static final String TAG = "Download Task";
private Context context;
String mimeType;
private String downloadUrl = "", downloadFileName = "";
private ProgressDialog progressDialog;
public DownloadTask() {
//required empty constructor
}
public DownloadTask(Context context, String downloadUrl, String mime) {
this.context = context;
this.downloadUrl = downloadUrl;
this.mimeType = mime;
//Create file name by picking download file name from URL
downloadFileName = downloadUrl.substring(downloadUrl.lastIndexOf('/'));
//Start Downloading Task
new DownloadingTask().execute();
}
private class DownloadingTask extends AsyncTask<Void, Void, Void> {
File apkStorage = null;
File outputFile = null;
#Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog = new ProgressDialog(context);
progressDialog.setMessage("Downloading...");
progressDialog.setCancelable(false);
progressDialog.show();
}
#Override
protected void onPostExecute(Void result) {
try {
if (outputFile != null) {
progressDialog.dismiss();
final ContextThemeWrapper ctw = new ContextThemeWrapper( context, R.style.Theme_AppCompat_Light_Dialog);
final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(ctw);
alertDialogBuilder.setTitle("Document ");
alertDialogBuilder.setMessage(downloadFileName+" Downloaded Successfully ");
alertDialogBuilder.setCancelable(false);
alertDialogBuilder.setPositiveButton("ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
}
});
alertDialogBuilder.setNegativeButton("Open file",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
try {
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString()
+ downloadFileName);//name here is the name of any string you want to pass to the method
if (!file.isDirectory())
file.mkdir();
Intent testIntent = new Intent();
testIntent.setType(mimeType);
testIntent.setAction(Intent.ACTION_VIEW);
Uri uri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", file);
testIntent.setDataAndType(uri, mimeType);
ctw.startActivity(testIntent);
} catch (Exception e) {
e.printStackTrace();
}
}
});
alertDialogBuilder.show();
} else {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
}
}, 3000);
Log.e(TAG, "Download Failed");
}
} catch (Exception e) {
e.printStackTrace();
//Change button text if exception occurs
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
}
}, 3000);
Log.e(TAG, "Download Failed with Exception - " + e.getLocalizedMessage());
}
super.onPostExecute(result);
}
#Override
protected Void doInBackground(Void... arg0) {
try {
URL url = new URL(downloadUrl);//Create Download URl
HttpURLConnection c = (HttpURLConnection) url.openConnection();//Open Url Connection
c.setRequestMethod("GET");//Set Request Method to "GET" since we are grtting data
c.connect();//connect the URL Connection
//If Connection response is not OK then show Logs
if (c.getResponseCode() != HttpURLConnection.HTTP_OK) {
Log.e(TAG, "Server returned HTTP " + c.getResponseCode()
+ " " + c.getResponseMessage());
}
//Get File if SD card is present
if (new CheckForSDCard().isSDCardPresent()) {
apkStorage = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString());
} else
Toast.makeText(context, "Oops!! There is no SD Card.", Toast.LENGTH_SHORT).show();
//If File is not present create directory
if (!apkStorage.exists()) {
apkStorage.mkdir();
Log.e(TAG, "Directory Created.");
}
outputFile = new File(apkStorage, downloadFileName);//Create Output file in Main File
//Create New File if not present
if (!outputFile.exists()) {
outputFile.createNewFile();
Log.e(TAG, "File Created");
}
FileOutputStream fos = new FileOutputStream(outputFile);//Get OutputStream for NewFile Location
InputStream is = c.getInputStream();//Get InputStream for connection
byte[] buffer = new byte[1024];//Set buffer type
int len1 = 0;//init length
while ((len1 = is.read(buffer)) != -1) {
fos.write(buffer, 0, len1);//Write new file
}
//Close all connection after doing task
fos.close();
is.close();
} catch (Exception e) {
//Read exception if something went wrong
e.printStackTrace();
outputFile = null;
Log.e(TAG, "Download Error Exception " + e.getMessage());
}
return null;
}
}
private class CheckForSDCard {
//Check If SD Card is present or not method
boolean isSDCardPresent() {
return Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED);
}
}
}
Looking for my manifest file ? Well here it goes .. ive put in some support for file upload, file download, internet acces, ect
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-feature android:name="android.hardware.camera"/>
<uses-permission android:name="android.permission.CAMERA"/>
i even tried to put some provider stuff in application
<provider
android:name=".DownloadTask"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths"/>
</provider>
But deep inside i had no idea what to put in provider path file
<paths><external-path name="external_files" path="."/></paths>
Sorry that this text is so long. Im just desperate and almost done with my project, but i have to seek for help somewhere to keep going. I hope ive explained my problem enough and provided enough code, would be happy to know which code is better / more possible ( make a class or write everything down right under listener ).
--- EDIT ---
So.. i tried to run my old version of code, which is that long block of code above, class file "DownloadTask.java", which is called by constructor, passing utl, mimetype and context. You can see the line commented in the first block of code in this post, 3rd line. Here is what happened : i visited a page, clicked on a link to download pdf file, but then i got stuck on dialog that shows "downloading.." while logcat said this :
D/ViewRootImpl#19478bd[webview_base]: MSG_RESIZED: frame=Rect(18, 617 - 701, 821) ci=Rect(0, 0 - 0, 0) vi=Rect(0, 0 - 0, 0) or=1
D/ViewRootImpl#c47c382[webview_base]: Relayout returned: old=[0,0][720,1480] new=[0,0][720,1480] result=0x1 surface={valid=true 484837122048} changed=false
I/ample.viaapp_v: Compiler allocated 4MB to compile void android.widget.TextView.<init>(android.content.Context, android.util.AttributeSet, int, int)
I/System.out: (HTTPLog)-Static: isSBSettingEnabled false
I/System.out: (HTTPLog)-Static: isSBSettingEnabled false
W/System.err: java.io.FileNotFoundException: /storage/emulated/0/Download/Datu%20p%C4%81rraides%20t%C4%ABkli-III_LV.pdf (Is a directory)
at java.io.FileOutputStream.open0(Native Method)
at java.io.FileOutputStream.open(FileOutputStream.java:308)
at java.io.FileOutputStream.<init>(FileOutputStream.java:238)
at java.io.FileOutputStream.<init>(FileOutputStream.java:180)
at com.example.viaapp_v2.DownloadTask$DownloadingTask.doInBackground(DownloadTask.java:173)
at com.example.viaapp_v2.DownloadTask$DownloadingTask.doInBackground(DownloadTask.java:54)
W/System.err: at android.os.AsyncTask$2.call(AsyncTask.java:333)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:764)
E/Download Task: Download Error Exception /storage/emulated/0/Download/Datu%20p%C4%81rraides%20t%C4%ABkli-III_LV.pdf (Is a directory)
E/Download Task: Download Failed
D/ViewRootImpl#19478bd[webview_base]: MSG_WINDOW_FOCUS_CHANGED 1 1
And yes, as you can see, the file name contais accented letters, thats why its so weird, 99% of users which are willing to use the app, will work around content which uses accented letters so im wondering mayeb i should replace filenames without all these letters like "āēīģčķ" to "aeigck".

File Input Stream not working on Api level 29

I have a method that tries to extract m3u files, but it doesn't work at api level 29. I get the following error on the Logcat screen:
2019-10-03 20:46:53.165 32591-417/? E/Google: java.io.IOException: Cleartext HTTP traffic to mysite.tk not permitted
My Method:
#SuppressLint("StaticFieldLeak")
class _loadFile extends AsyncTask<String, Void, Boolean> {
#Override
protected void onPreExecute() {
super.onPreExecute();
spinner.setVisibility(View.VISIBLE);
}
#Override
protected Boolean doInBackground(String... strings) {
try { //new FileInputStream (new File(name)
is = new FileInputStream(new File(strings[0])); // if u r trying to open file from asstes InputStream is = getassets.open(); InputStream
M3UPlaylist playlist = parser.parseFile(is);
mAdapter.update(playlist.getPlaylistItems());
return true;
} catch (Exception e) {
Log.d("Google", "_loadFile: " + e.toString());
return false;
}
}
#Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
spinner.setVisibility(View.GONE);
}
}
EDİT:
I gave ClearText permission, but a new error appeared.
Google: _loadFile: java.io.FileNotFoundException: /storage/emulated/0/Netuptv/data.m3u: open failed: ENOENT (No such file or directory)
EDİT:
My file path. I think the problem is here. But I don't have the information to solve it.
static final File DEFA = Environment.getExternalStorageDirectory();
public static final File dir = new File(DEFA.getPath() + "/Netuptv");
static final File filepath = new File(dir.getPath() + "/data.m3u");
Please check if you added this line android:usesCleartextTraffic="true" to your Manifest.
Since the file access API is deprecated on Android Q, I highly recommend to you to read this article by CommaneWare , as mention on this article, try to use android:requestLegacyExternalStorage="true" to avoid the file access issues
Try the following :
String fileName = "data.m3u";
File storageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES)
+ "/Netuptv");
boolean success = true;
if (!storageDir.exists()) {
success = storageDir.mkdirs();
}
if (success) {
File file = new File(storageDir,fileName);
filepath = file.getAbsolutePath();
}
With Android 10 (api level 29) file access is deprecated, it's called scoped storage.
A quick but not permanent fix for testing on Android 11:
Adding android:requestLegacyExternalStorage="true"
and changing
compileSdkVersion 29
targetSdkVersion 29
both to 29 in build.gradle

How can I access a file that I have created externally? AKA where is the file?

I'm in the process of trying to create a backup/restore (export/import) process for an SQLite Database App.
Although I appear to have created and populated the file (OK I now know that I have). I cannot see the file in DDMS nor in Windows Explorer. I'd really like to be able to do the latter (see the bottom for a more specific question).
I've successfully written the file and read the file using the following code:
package mjt.sqlitetutorial;
import android.database.Cursor; //+++++ Added
import android.os.Build;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log; //+++++ Added
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class MainActivity extends AppCompatActivity {
public int API_VERSION = Build.VERSION.SDK_INT;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (API_VERSION >= 23) {
ExternalStoragePermissions.verifyStoragePermissions(this);
}
final String EXTSTGTAG = "EXTERNSTG";
File file = getExternalFilesDir("File");
Log.i(EXTSTGTAG,file.toString());
//String extstgdirabs = Environment.getExternalStorageDirectory().getAbsolutePath();
String extstgdirpth = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
Log.i(EXTSTGTAG,"<=\nEXTERN STG PUB DIR=>" + extstgdirpth);
String filepath = extstgdirpth + File.separator + "myfile.txt";
Log.i(EXTSTGTAG,"Full File Path and name is\n\t" + filepath);
File f = new File(filepath);
if(!f.exists() ) {
Log.i(EXTSTGTAG,"File did not exist (path=" + filepath + ")");
try {
f.createNewFile();
}
catch (IOException e) {
Log.e(EXTSTGTAG,"Failure Creating New File MSG=" + e.getMessage());
}
}
if(f.exists()) {
Log.i(EXTSTGTAG,"File Already Exists (" + filepath + ")");
try {
Log.i(EXTSTGTAG,"Creating FileOutputStream instance.");
FileOutputStream fos = new FileOutputStream(f);
Log.i(EXTSTGTAG,"Creating OutputStreamWriter instance from FileOutputStream.");
OutputStreamWriter osw = new OutputStreamWriter(fos);
Log.i(EXTSTGTAG,"Adding Data to OutputStreamWriter.");
osw.append("My Test Data.");
Log.i(EXTSTGTAG,"Closing OutputStreamWriter.");
osw.close();
Log.i(EXTSTGTAG,"Flushing FileOutputStream.");
fos.flush();
Log.i(EXTSTGTAG,"Closing FileOutputStream");
fos.close();
Log.i(EXTSTGTAG,"All Done OK.");
} catch (IOException e) {
Log.e(EXTSTGTAG, "Failure Trying to write to file." + e.getMessage());
e.printStackTrace();
}
} else {
Log.i(EXTSTGTAG,"File doesn't appear to exist when it should????");
}
f.setReadable(true);
f.setWritable(true);
if(f.exists()) {
try {
byte[] bytes;
FileInputStream fis = new FileInputStream(f);
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr);
StringBuilder sb = new StringBuilder();
String line = null;
while((line = br.readLine()) != null) {
sb.append(line).append("\n");
}
br.close();
Log.i(EXTSTGTAG,"Read the following data:\n" + sb.toString());
}
catch (IOException e) {
Log.e(EXTSTGTAG,"Failure trying to read file." + e.getMessage());
e.printStackTrace();
}
}
}
}
Output to the log (using EXTERN as the filter) shows (note the first run when installing the App run fails but requests and sets permissions. I don't believe this is an issue/cause) :-
10-18 12:54:40.159 32393-32393/? I/EXTERNSTG: /storage/emulated/0/Android/data/mjt.sqlitetutorial/files/File
10-18 12:54:40.159 32393-32393/? I/EXTERNSTG: <=
EXTERN STG PUB DIR=>/storage/emulated/0/Download
10-18 12:54:40.159 32393-32393/? I/EXTERNSTG: Full File Path and name is
/storage/emulated/0/Download/myfile.txt
10-18 12:54:40.159 32393-32393/? I/EXTERNSTG: File Already Exists (/storage/emulated/0/Download/myfile.txt)
10-18 12:54:40.159 32393-32393/? I/EXTERNSTG: Creating FileOutputStream instance.
10-18 12:54:40.159 32393-32393/? I/EXTERNSTG: Creating OutputStreamWriter instance from FileOutputStream.
10-18 12:54:40.159 32393-32393/? I/EXTERNSTG: Adding Data to OutputStreamWriter.
10-18 12:54:40.159 32393-32393/? I/EXTERNSTG: Closing OutputStreamWriter.
10-18 12:54:40.159 32393-32393/? I/EXTERNSTG: Flushing FileOutputStream.
10-18 12:54:40.159 32393-32393/? I/EXTERNSTG: Closing FileOutputStream
10-18 12:54:40.169 32393-32393/? I/EXTERNSTG: All Done OK.
10-18 12:54:40.169 32393-32393/? I/EXTERNSTG: Read the following data:
My Test Data.
The last line indicating that it has read the file (I assume). There are no other log messages during the provided messages (many before and after though).
the device that I'm testing this on is a non-rooted HTC One M8s with an SDcard. However, I believe that /storage/emulated/0/Download, the directory where the file is being written to is on the internal memory.
With DDMS I don't appear to be able to see this (the actual SD card has a Downloads directory as opposed to a Download directory).
In Windows explorer I can see both Internal Storage and SD card as devices under the HTC_0PKV1 device.
In Windows Explorer the Download directory has (via properties) 0 Directories and Files. Neither read only nor hidden are ticked.
I've tried both with and without the setReadable and setWritable().
I've just tried using the file manager on the phone and can now see the file. So more specifically, the question is; Is there any way excluding rooting the phone and moving the file via file manager on the phone, to access the file via Windows Explorer?
I should also state that the App will run on tablets, so the method should be generic rather than specific to a device.
The file becomes visible in Windows explorer after disconnecting and re-connecting the USB cable. I'm not sure if this how MTP is meant to work or it could perhaps be due to ADB as per this snippet:-
However, if you’ve ever attempted to unlock your device such as to
install a new ROM or root it, then you may have at one time or another
installed the Android Debug Bridge (ADB) driver on your computer. This
driver works great for being able to use the computer to send commands
to your device, but it may mess up your easy-peasy file manipulation.
found at How to Get Your Android Device to Show up in File Explorer (If It Isn’t)

JDBC Lotus-Oracle error: Java.lang.ArrayIndexOutOfBoundsException: Array index out of range: -1

I am using jdbc to connect to an Oracle 10g database. Building the connection in Eclipse/Java works fine. However when I move the code to a Lotus 8.5.2 agent I end up with the following error(s):
Java.lang.ArrayIndexOutOfBoundsException: Array index out of range: -1
at oracle.jdbc.driver.T4CTTIoauthenticate.setSessionFields(T4CTTIoauthenticate.java:1019)
at oracle.jdbc.driver.T4CTTIoauthenticate.<init>(T4CTTIoauthenticate.java:186)
at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:354)
at oracle.jdbc.driver.PhysicalConnection.<init>(PhysicalConnection.java:454)
at oracle.jdbc.driver.T4CConnection.<init>(T4CConnection.java:165)
at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:35)
at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:802)
at oracle.jdbc.pool.OracleDataSource.getPhysicalConnection(OracleDataSource.java:298)
at oracle.jdbc.pool.OracleDataSource.getConnection(OracleDataSource.java:222)
at oracle.jdbc.pool.OracleDataSource.getConnection(OracleDataSource.java:166)
at JavaAgent.NotesMain(Unknown Source)
at lotus.domino.AgentBase.runNotes(Unknown Source)
at lotus.domino.NotesThread.run(Unknown Source)
This is the code used to connect:
Class.forName("oracle.jdbc.OracleDriver");
Connection conn = DriverManager.getConnection(
"jdbc:oracle:thin:#:xx.xx.xx.xx:1521:xx", "xx", "xx");
I have tried to solve this in different ways:
- use the Lotus JVM in eclipse
- use different jdbc jars in eclipse
- use different ways to build the connection in Lotus
- use different jdbc jars jars in lotus
Finally I moved the ojdbc14.jar file Lotus\Notes\jvm\lib\ext directory and it works fine now.
This solution will work, but obviously I prefer to distribute this jar along with the nsf. Is there a way I can make this happen?
As suggested by leyrer. I tried adding the following line to the "/jvm/lib/security/java.policy" file
permission java.security.AllPermission;
This does results in the same error message.
For now I will stick with placing the ojdbc5.jar in the /ext directory.
If you are using the ojdbc jar unpacked, be sure you are not excluding the oracle/sql/converter_xcharset/*.glb files. I was getting this same error when my executable jar was built using Maven, but was not including these files. The block below explicitly includes them.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<excludeTypes>pom</excludeTypes>
<includes>**/*.class,**/*.glb</includes>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
</configuration>
</execution>
</executions>
</plugin>
I would guess, that the JVM's Security Manager is not allowing access to the network because the security policy does not specify to allow this action.
See Flying Saucer in Lotus Notes for more details.
Long time ago this issue arise... But I had to discover it for a customer this month. And nowhere I found a solution but some false pretences and incomplete analyses. Therefore and for all of you run into it, I will share my findings, the root cause of the issue and your opportunities to get it solved. I have tested it with the version 11.2.0.4 of the driver (ojdbc6.jar). Further, if your database uses UTF-8 encoding, it seems to work just with the java.policy adjustments. In my case, it was a database with windows 1252 encoding.
First of all, the oracle jdbc driver needs some security adjustments... it's better to set them explicitly and not by permission java.security.AllPermission;. Use this permissions, taken from the ocacle jdbc driver download page (ojdbc.policy file):
permission java.util.PropertyPermission "user.name", "read";
permission java.util.PropertyPermission "oracle.jdbc.*", "read";
permission java.util.PropertyPermission "oracle.net.wallet_location", "read";
permission java.util.PropertyPermission "oracle.net.tns_admin", "read";
permission javax.management.MBeanServerPermission "createMBeanServer";
permission javax.management.MBeanPermission "oracle.jdbc.driver.OracleDiagnosabilityMBean#[com.oracle.jdbc:type=diagnosability,*]", "registerMBean";
permission javax.management.MBeanTrustPermission "register";
After this settings are in place, you will run into the Java.lang.ArrayIndexOutOfBoundsException: Array index out of range: -1 issue. The root cause of that is, the the class loader of java agents (lotus.domino.AgentLoader) does not implement getResource(String name) and that leads to always returning null to the calling method. Since the orcale jdbc driver needs the glb files from the oracle.sql.converter_xcharset folder within the jar to work properly and they were loaded by the getRousource method mentioned above, this will not work! The result is the ArrayIndexOutOfBoundsException.
So the only solutions are either to use the driver from the file system (and use a jvm default class loader) or you change the class loading process as follows:
create a custom class loader:
public class CustomLoader extends ClassLoader {
private final AgentLoader loader;
public CustomLoader(AgentLoader agentLoader, ClassLoader parent) {
super(parent);
loader = agentLoader;
}
#Override
public URL getResource(String name) {
InputStream is = loader.getResourceAsStream(name);
if (is == null) {
return super.getResource(name);
}
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
return null;
}
try {
URL url = new URL("dominoinmemory", "", -1, name, new DominoInMemoryStreamHandler(name));
System.out.println(url);
return url;
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
}
}
private class DominoInMemoryStreamHandler extends URLStreamHandler {
private String resName;
byte[] content = null;
public DominoInMemoryStreamHandler(String resName) {
this.resName = resName;
}
#Override
protected URLConnection openConnection(final URL u) throws IOException {
if (!u.getProtocol().equals("dominoinmemory"))
throw new IOException("Cannot handle protocol: " + u.getProtocol());
InputStream is = loader.getResourceAsStream(resName);
content = toByteArray(is);
return new URLConnection(u) {
#Override
public int getContentLength() {
if (content != null) {
return content.length;
} else {
return super.getContentLength();
}
}
#Override
public void connect() throws IOException {
if (content != null) {
connected = true;
} else {
throw new IOException("The resource '" + resName + "' was not found");
}
}
#Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(content);
}
};
}
}
public static byte[] toByteArray(InputStream input) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
long count = 0;
int n = 0;
while (-1 != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
}
return output.toByteArray();
}
In the domino agent, before any other operation occurs, change the parent class loader of the AgentLoader with reflection
public void NotesMain() {
try {
AgentLoader agentLoader = (AgentLoader) getClass().getClassLoader();
Field f1 = agentLoader.getClass().getSuperclass().getDeclaredField("parent");
f1.setAccessible(true);
ClassLoader parent = (ClassLoader) f1.get(agentLoader);
f1.set(agentLoader, new CustomLoader(agentLoader, parent));
...
Attention:
Use this at your own risk!
This code required two additional entries in the policy file:
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
permission java.net.NetPermission "specifyStreamHandler";

Exporting a SQLite database to an XML file in Android

I know this is possible but I'm not really sure where to start. Has anyone been able to achieve this?
Thanks.
The DataXmlExporter class described in this article will export a SQL lite DB to an XML file.
http://www.screaming-penguin.com/node/7749
The full example is available in this SVN repo. The ManageData class invokes the export.
http://totsp.com/svn/repo/AndroidExamples/trunk/
You will need to create an application class that exposes the DB and referenced as the application name in the AndroidManifest.xml file. Then use that DB as the argument to the DataXmlExporter constructor.
Here's the application class I use. You should already have a class (probably not named DatabaseHelper) that extends SQLiteOpenHelper
package com.billybobbain.android.someapp;
import android.app.Application;
import android.util.Log;
public class MyApplication extends Application {
public static final String APP_NAME = "SomeApp";
private DatabaseHelper dataHelper;
#Override
public void onCreate() {
super.onCreate();
Log.d(APP_NAME, "APPLICATION onCreate");
this.dataHelper = new DatabaseHelper(this);
}
#Override
public void onTerminate() {
Log.d(APP_NAME, "APPLICATION onTerminate");
super.onTerminate();
}
public DatabaseHelper getDataHelper() {
return this.dataHelper;
}
public void setDataHelper(DatabaseHelper dataHelper) {
this.dataHelper = dataHelper;
}
}
Have a look at the source code here exporting-a-sqlite-database-to-an-xml-file-in-android
The only change I had to make (to stop a few Eclipse warnings) was to close a cursor in the exportData( ) method. To make the code more portable, I also passed the XML file and location as an argument rather then as a declared final field.
The code writes the XML file to the SD card. Now, #mmaitlen who listed the source code on his blog doesn't add in any features to test for the existence of an external storage unit. So that's left for you to do.
However, you can embed some simple code to test for the existence of a writeable memory card with the following snippet (untested):
sdOkToWrite = false;
String sdTest = Environment.getExternalStorageState();
if (sdTest.equals(Environment.MEDIA_MOUNTED)) {
sdOkToWrite = true;
} else {
// Here's where you can code what to do without the external storage
}
Testing for the external storage is useful if you have large files to create that may exceed internal capacity.
I found this very helpful:
http://www.phonesdevelopers.com/1788273/
Using it the following way to export it to the sd card:
File sd = Environment.getExternalStorageDirectory();
String path = sd + "/" + DB_NAME + ".xml";
DatabaseDump databaseDump = new DatabaseDump(mySQLiteOpenHelperObject.getReadableDatabase(), path);
databaseDump.exportData();
Of course don't forget:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Categories

Resources