From cc549a6c498b578ce397c6eb7dba3911ebf70e16 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 8 Jul 2015 23:03:34 +0200 Subject: [PATCH] Basic database import and export (to external storage) (In the Debug Activity) --- .../gadgetbridge/DebugActivity.java | 79 ++++++++++++++++--- .../gadgetbridge/GBApplication.java | 15 ++-- .../gadgetbridge/database/DBHelper.java | 67 ++++++++++++++++ .../gadgetbridge/util/FileUtils.java | 45 +++++++++++ app/src/main/res/layout/activity_debug.xml | 19 +++++ 5 files changed, 205 insertions(+), 20 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/DebugActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/DebugActivity.java index d52beb670..b58773aec 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/DebugActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/DebugActivity.java @@ -13,20 +13,34 @@ import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.EditText; +import android.widget.Toast; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; + +import nodomain.freeyourgadget.gadgetbridge.database.ActivityDatabaseHandler; +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; public class DebugActivity extends Activity { - Button sendSMSButton; - Button sendEmailButton; - Button incomingCallButton; - Button outgoingCallButton; - Button startCallButton; - Button endCallButton; - Button testNotificationButton; - Button setMusicInfoButton; - Button setTimeButton; - Button rebootButton; - EditText editContent; + private static final Logger LOG = LoggerFactory.getLogger(DebugActivity.class); + + private Button sendSMSButton; + private Button sendEmailButton; + private Button incomingCallButton; + private Button outgoingCallButton; + private Button startCallButton; + private Button endCallButton; + private Button testNotificationButton; + private Button setMusicInfoButton; + private Button setTimeButton; + private Button rebootButton; + private Button exportDBButton; + private Button importDBButton; + private EditText editContent; private BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override @@ -114,6 +128,21 @@ public class DebugActivity extends Activity { } }); + exportDBButton = (Button) findViewById(R.id.exportDBButton); + exportDBButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + exportDB(); + } + }); + importDBButton = (Button) findViewById(R.id.importDBButton); + importDBButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + importDB(); + } + }); + rebootButton = (Button) findViewById(R.id.rebootButton); rebootButton.setOnClickListener(new View.OnClickListener() { @Override @@ -156,6 +185,34 @@ public class DebugActivity extends Activity { }); } + private void exportDB() { + try { + ActivityDatabaseHandler dbHandler = GBApplication.getActivityDatabaseHandler(); + DBHelper helper = new DBHelper(this); + File dir = FileUtils.getExternalFilesDir(); + File destFile = helper.exportDB(dbHandler, dir); + Toast.makeText(this, "Exported to: " + destFile.getAbsolutePath(), Toast.LENGTH_LONG).show(); + } catch (Exception ex) { + LOG.error("Unable to export db", ex); + Toast.makeText(this, "Error exporting DB: " + ex.getMessage(), Toast.LENGTH_LONG).show(); + } + } + + private void importDB() { + try { + ActivityDatabaseHandler dbHandler = GBApplication.getActivityDatabaseHandler(); + DBHelper helper = new DBHelper(this); + File dir = FileUtils.getExternalFilesDir(); + File sourceFile = new File(dir, dbHandler.getDatabaseName()); + helper.importDB(dbHandler, sourceFile); + helper.validateDB(dbHandler); + Toast.makeText(this, "Import successful.", Toast.LENGTH_LONG).show(); + } catch (Exception ex) { + LOG.error("Unable to import db", ex); + Toast.makeText(this, "Error importing DB: " + ex.getMessage(), Toast.LENGTH_LONG).show(); + } + } + private void testNotification() { NotificationManager nManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); NotificationCompat.Builder ncomp = new NotificationCompat.Builder(this); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index a512087e5..9bf18f1be 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -12,8 +12,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; +import java.io.IOException; import nodomain.freeyourgadget.gadgetbridge.database.ActivityDatabaseHandler; +import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; public class GBApplication extends Application { private static GBApplication context; @@ -47,20 +49,15 @@ public class GBApplication extends Application { private void setupLogging() { if (isFileLoggingEnabled()) { - File dir = getExternalFilesDir(null); - if (dir != null) { - if (!dir.exists()) { - dir.mkdirs(); - } + try { + File dir = FileUtils.getExternalFilesDir(); // used by assets/logback.xml since the location cannot be statically determined System.setProperty("GB_LOGFILES_DIR", dir.getAbsolutePath()); - } else { - Log.e("GBApplication", "External files dir is null, cannot log to file"); + } catch (IOException ex) { + Log.e("GBApplication", "External files dir not available, cannot log to file, ex"); System.setProperty("GB_LOGFILES_DIR", "/dev/null"); - } } else { - System.setProperty("GB_LOGFILES_DIR", "/dev/null"); // just to please logback configuration, not used at all try { ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); root.detachAppender("FILE"); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java new file mode 100644 index 000000000..ff445285c --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java @@ -0,0 +1,67 @@ +package nodomain.freeyourgadget.gadgetbridge.database; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; + +import java.io.File; +import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; + +public class DBHelper { + private final Context context; + + public DBHelper(Context context) { + this.context = context; + } + + private String getClosedDBPath(SQLiteOpenHelper dbHandler) throws IllegalStateException { + SQLiteDatabase db = dbHandler.getReadableDatabase(); + String path = db.getPath(); + db.close(); // reference counted, so may still be open + if (db.isOpen()) { + throw new IllegalStateException("Database must be closed"); + } + return path; + } + + public File exportDB(SQLiteOpenHelper dbHandler, File toDir) throws IllegalStateException, IOException { + String dbPath = getClosedDBPath(dbHandler); + File sourceFile = new File(dbPath); + File destFile = new File(toDir, sourceFile.getName()); + if (destFile.exists()) { + File backup = new File(toDir, destFile.getName() + "_" + getDate()); + destFile.renameTo(backup); + } else if (!toDir.exists()) { + if (!toDir.mkdirs()) { + throw new IOException("Unable to create directory: " + toDir.getAbsolutePath()); + } + } + + FileUtils.copyFile(sourceFile, destFile); + return destFile; + } + + private String getDate() { + return new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.US).format(new Date()); + } + + public void importDB(SQLiteOpenHelper dbHandler, File fromFile) throws IllegalStateException, IOException { + String dbPath = getClosedDBPath(dbHandler); + File toFile = new File(dbPath); + FileUtils.copyFile(fromFile, toFile); + } + + public void validateDB(SQLiteOpenHelper dbHandler) throws IOException { + try (SQLiteDatabase db = dbHandler.getReadableDatabase()) { + if (!db.isDatabaseIntegrityOk()) { + throw new IOException("Database integrity is not OK"); + } + } + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java new file mode 100644 index 000000000..d3fbd9b9d --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java @@ -0,0 +1,45 @@ +package nodomain.freeyourgadget.gadgetbridge.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.channels.FileChannel; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; + +public class FileUtils { + /** + * Copies the the given sourceFile to destFile, overwriting it, in case it exists. + * @param sourceFile + * @param destFile + * @throws IOException + */ + public static void copyFile(File sourceFile, File destFile) throws IOException { + if (!sourceFile.exists()) { + throw new IOException("Does not exist: " + sourceFile.getAbsolutePath()); + } + copyFile(new FileInputStream(sourceFile), new FileOutputStream(destFile)); + } + + private static void copyFile(FileInputStream sourceStream, FileOutputStream destStream) throws IOException { + try (FileChannel fromChannel = sourceStream.getChannel(); FileChannel toChannel = destStream.getChannel()) { + fromChannel.transferTo(0, fromChannel.size(), toChannel); + } + } + + /** + * Returns the existing external storage dir. + * @throws IOException when the directory is not available + */ + public static File getExternalFilesDir() throws IOException { + File dir = GBApplication.getContext().getExternalFilesDir(null); + if (dir == null) { + throw new IOException("Unable to access external files dir: null"); + } + if (!dir.exists() && !dir.mkdirs()) { + throw new IOException("Unable to access external files dir: does not exist"); + } + return dir; + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_debug.xml b/app/src/main/res/layout/activity_debug.xml index 49353c518..108fa7117 100644 --- a/app/src/main/res/layout/activity_debug.xml +++ b/app/src/main/res/layout/activity_debug.xml @@ -109,4 +109,23 @@ android:layout_alignParentStart="true" android:layout_alignEnd="@+id/endCallButton" /> +