diff --git a/README.md b/README.md
index 0bb48770..553657e5 100644
--- a/README.md
+++ b/README.md
@@ -230,7 +230,16 @@ npm install --save react-native-sqlite-storage
...
include ':react-native-sqlite-storage'
-project(':react-native-sqlite-storage').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sqlite-storage/src/android')
+project(':react-native-sqlite-storage').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sqlite-storage/src/android/storage')
+
+include ':sqlite-plugin-provider'
+project(':sqlite-plugin-provider').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sqlite-storage/src/android/sqlite-plugin-provider')
+
+// for sqlitecipher support:
+include ':sql-cipher-plugin'
+project(':sql-cipher-plugin').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sqlite-storage/src/android/sql-cipher-plugin')
+
+
```
#### Step 3 - Update app Gradle Build
@@ -242,6 +251,9 @@ project(':react-native-sqlite-storage').projectDir = new File(rootProject.projec
dependencies {
...
compile project(':react-native-sqlite-storage')
+ compile project(':sqlite-plugin-provider')
+ // To add sqlite cipher support:
+ compile project(':sql-cipher-plugin')
}
```
@@ -265,7 +277,7 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
.setBundleAssetName("index.android.bundle") // this is dependant on how you name you JS files, example assumes index.android.js
.setJSMainModuleName("index.android") // this is dependant on how you name you JS files, example assumes index.android.js
.addPackage(new MainReactPackage())
- .addPackage(new SQLitePluginPackage()) // register SQLite Plugin here
+ .addPackage(new SQLitePluginPackage()) // register SQLite Plugin here. For SQLiteCipher support pass SqliteCipherConnectionProvider to PluginPackage.
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
diff --git a/src/android/build.gradle b/src/android/build.gradle
index e42f502a..c4705857 100644
--- a/src/android/build.gradle
+++ b/src/android/build.gradle
@@ -1,34 +1,16 @@
buildscript {
repositories {
+ mavenLocal()
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:1.3.0'
+ classpath 'com.android.tools.build:gradle:2.0.0'
}
}
-apply plugin: 'com.android.library'
-
-android {
- compileSdkVersion 23
- buildToolsVersion "23.0.1"
-
- defaultConfig {
- minSdkVersion 16
- targetSdkVersion 22
- versionCode 1
- versionName "1.0"
- }
- lintOptions {
- abortOnError false
+allprojects {
+ repositories {
+ jcenter()
}
}
-
-repositories {
- mavenCentral()
-}
-
-dependencies {
- compile 'com.facebook.react:react-native:0.14.+'
-}
diff --git a/src/android/settings.gradle b/src/android/settings.gradle
index 74e11f66..66e94f43 100644
--- a/src/android/settings.gradle
+++ b/src/android/settings.gradle
@@ -1,2 +1,4 @@
rootProject.name = 'SQLitePlugin'
-include ':app'
\ No newline at end of file
+include ':sqlite-plugin-provider'
+include ':sql-cipher-plugin'
+include ':storage'
diff --git a/src/android/sql-cipher-plugin/.gitignore b/src/android/sql-cipher-plugin/.gitignore
new file mode 100644
index 00000000..796b96d1
--- /dev/null
+++ b/src/android/sql-cipher-plugin/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/src/android/sql-cipher-plugin/build.gradle b/src/android/sql-cipher-plugin/build.gradle
new file mode 100644
index 00000000..975329d6
--- /dev/null
+++ b/src/android/sql-cipher-plugin/build.gradle
@@ -0,0 +1,24 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion "25.0.0"
+
+ defaultConfig {
+ minSdkVersion 16
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile project(':sqlite-plugin-provider')
+ compile 'net.zetetic:android-database-sqlcipher:3.5.7@aar'
+}
diff --git a/src/android/sql-cipher-plugin/proguard-rules.pro b/src/android/sql-cipher-plugin/proguard-rules.pro
new file mode 100644
index 00000000..10c22872
--- /dev/null
+++ b/src/android/sql-cipher-plugin/proguard-rules.pro
@@ -0,0 +1,25 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/sergeyd/Library/Android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/src/android/sql-cipher-plugin/src/main/AndroidManifest.xml b/src/android/sql-cipher-plugin/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..e9de8f58
--- /dev/null
+++ b/src/android/sql-cipher-plugin/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
diff --git a/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherConnectionProvider.java b/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherConnectionProvider.java
new file mode 100644
index 00000000..4900727b
--- /dev/null
+++ b/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherConnectionProvider.java
@@ -0,0 +1,37 @@
+package org.pgsqlite.sqlcipher;
+
+import android.content.Context;
+
+import org.pgsqlite.sqlite.plugin.Database;
+import org.pgsqlite.sqlite.plugin.DatabaseConnectionProvider;
+
+import net.sqlcipher.DatabaseErrorHandler;
+import net.sqlcipher.SQLException;
+import net.sqlcipher.database.SQLiteDatabase;
+
+/**
+ * Written by Sergei Dryganets Jul 24/2017
+ */
+public class SqliteCipherConnectionProvider implements DatabaseConnectionProvider {
+ private static volatile boolean sNativeLibraryLoaded;
+
+ public SqliteCipherConnectionProvider(Context context) {
+ if (!sNativeLibraryLoaded) {
+ sNativeLibraryLoaded = true;
+ SQLiteDatabase.loadLibs(context);
+ }
+ }
+
+ @Override
+ public Database openDatabase(String databasePath, String password, int openFlags) {
+ return new SqliteCipherDatabase(SQLiteDatabase.openDatabase(databasePath, password, null,
+ openFlags, null, new DBErrorHandler()));
+ }
+
+ private class DBErrorHandler implements DatabaseErrorHandler {
+ @Override
+ public void onCorruption(SQLiteDatabase dbObj) {
+ throw new SQLException("Database is corrupted");
+ }
+ }
+}
diff --git a/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherCursor.java b/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherCursor.java
new file mode 100644
index 00000000..698cac9c
--- /dev/null
+++ b/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherCursor.java
@@ -0,0 +1,71 @@
+package org.pgsqlite.sqlcipher;
+
+import org.pgsqlite.sqlite.plugin.Cursor;
+
+import java.io.IOException;
+
+/**
+ * Written by Sergei Dryganets Jul 24/2017
+ */
+public class SqliteCipherCursor implements Cursor {
+ private final net.sqlcipher.Cursor cursor;
+
+ public SqliteCipherCursor(net.sqlcipher.Cursor cursor) {
+ this.cursor = cursor;
+ }
+
+ @Override
+ public boolean moveToFirst() {
+ return cursor.moveToFirst();
+ }
+
+ @Override
+ public boolean moveToNext() {
+ return cursor.moveToNext();
+ }
+
+ @Override
+ public int getColumnCount() {
+ return cursor.getColumnCount();
+ }
+
+ @Override
+ public String getColumnName(int i) {
+ return cursor.getColumnName(i);
+ }
+
+ @Override
+ public int getType(int i) {
+ return cursor.getType(i);
+ }
+
+ @Override
+ public long getLong(int i) {
+ return cursor.getLong(i);
+ }
+
+ @Override
+ public double getDouble(int i) {
+ return cursor.getDouble(i);
+ }
+
+ @Override
+ public String getString(int i) {
+ return cursor.getString(i);
+ }
+
+ @Override
+ public byte[] getBlob(int i) {
+ return cursor.getBlob(i);
+ }
+
+ @Override
+ public int getCount() {
+ return cursor.getCount();
+ }
+
+ @Override
+ public void close() throws IOException {
+ cursor.close();
+ }
+}
diff --git a/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherDatabase.java b/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherDatabase.java
new file mode 100644
index 00000000..3e083b6d
--- /dev/null
+++ b/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherDatabase.java
@@ -0,0 +1,60 @@
+package org.pgsqlite.sqlcipher;
+
+import net.sqlcipher.database.SQLiteDatabase;
+
+import org.pgsqlite.sqlite.plugin.Database;
+import org.pgsqlite.sqlite.plugin.Cursor;
+import org.pgsqlite.sqlite.plugin.SQLStatement;
+
+import java.io.IOException;
+
+/**
+ * Written by Sergei Dryganets Jul 24/2017
+ */
+public class SqliteCipherDatabase implements Database {
+ private SQLiteDatabase database;
+
+ SqliteCipherDatabase(SQLiteDatabase database) {
+ this.database = database;
+ }
+
+ @Override
+ public void execSQL(String sql) {
+ database.execSQL(sql);
+ }
+
+ @Override
+ public SQLStatement compileStatement(String sql) {
+ return new SqliteCipherStatement(database.compileStatement(sql));
+ }
+
+ @Override
+ public Cursor rawQuery(String sql, String[] params) {
+ return new SqliteCipherCursor(database.rawQuery(sql, params));
+ }
+
+ @Override
+ public boolean isOpen() {
+ return database.isOpen();
+ }
+
+ @Override
+ public void beginTransaction() {
+ database.beginTransaction();
+ }
+
+ @Override
+ public void setTransactionSuccessful() {
+ database.setTransactionSuccessful();
+ }
+
+ @Override
+ public void endTransaction() {
+ database.endTransaction();
+ }
+
+ @Override
+ public void close() throws IOException {
+ database.close();
+ }
+}
diff --git a/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherStatement.java b/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherStatement.java
new file mode 100644
index 00000000..34a87870
--- /dev/null
+++ b/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherStatement.java
@@ -0,0 +1,50 @@
+package org.pgsqlite.sqlcipher;
+
+import org.pgsqlite.sqlite.plugin.SQLStatement;
+
+import java.io.IOException;
+
+/**
+ * Written by Sergei Dryganets Jul 24/2017
+ */
+public class SqliteCipherStatement implements SQLStatement {
+ private final net.sqlcipher.database.SQLiteStatement statement;
+ public SqliteCipherStatement(net.sqlcipher.database.SQLiteStatement statement) {
+ this.statement = statement;
+ }
+
+ @Override
+ public void bindDouble(int i, double value) {
+ statement.bindDouble(i, value);
+ }
+
+ @Override
+ public void bindString(int i, String value) {
+ statement.bindString(i, value);
+ }
+
+ @Override
+ public void bindNull(int i) {
+ statement.bindNull(i);
+ }
+
+ @Override
+ public void bindLong(int i, long value) {
+ statement.bindLong(i, value);
+ }
+
+ @Override
+ public long executeInsert() {
+ return statement.executeInsert();
+ }
+
+ @Override
+ public int executeUpdateDelete() {
+ return statement.executeUpdateDelete();
+ }
+
+ @Override
+ public void close() throws IOException {
+ statement.close();
+ }
+}
diff --git a/src/android/sqlite-plugin-provider/.gitignore b/src/android/sqlite-plugin-provider/.gitignore
new file mode 100644
index 00000000..796b96d1
--- /dev/null
+++ b/src/android/sqlite-plugin-provider/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/src/android/sqlite-plugin-provider/build.gradle b/src/android/sqlite-plugin-provider/build.gradle
new file mode 100644
index 00000000..fb28c00a
--- /dev/null
+++ b/src/android/sqlite-plugin-provider/build.gradle
@@ -0,0 +1,20 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion "25.0.0"
+
+ defaultConfig {
+ minSdkVersion 16
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
diff --git a/src/android/sqlite-plugin-provider/proguard-rules.pro b/src/android/sqlite-plugin-provider/proguard-rules.pro
new file mode 100644
index 00000000..10c22872
--- /dev/null
+++ b/src/android/sqlite-plugin-provider/proguard-rules.pro
@@ -0,0 +1,25 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/sergeyd/Library/Android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/src/android/sqlite-plugin-provider/src/main/AndroidManifest.xml b/src/android/sqlite-plugin-provider/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..76853997
--- /dev/null
+++ b/src/android/sqlite-plugin-provider/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
diff --git a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/Cursor.java b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/Cursor.java
new file mode 100644
index 00000000..3fca06a2
--- /dev/null
+++ b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/Cursor.java
@@ -0,0 +1,48 @@
+package org.pgsqlite.sqlite.plugin;
+
+import java.io.Closeable;
+
+/**
+ * Written by Sergei Dryganets Jul 24/2017
+ */
+public interface Cursor extends Closeable {
+
+ /*
+ * Values returned by {@link #getType(int)}.
+ * These should be consistent with the corresponding types defined in CursorWindow.h
+ */
+ /** Value returned by {@link #getType(int)} if the specified column is null */
+ int FIELD_TYPE_NULL = 0;
+
+ /** Value returned by {@link #getType(int)} if the specified column type is integer */
+ int FIELD_TYPE_INTEGER = 1;
+
+ /** Value returned by {@link #getType(int)} if the specified column type is float */
+ int FIELD_TYPE_FLOAT = 2;
+
+ /** Value returned by {@link #getType(int)} if the specified column type is string */
+ int FIELD_TYPE_STRING = 3;
+
+ /** Value returned by {@link #getType(int)} if the specified column type is blob */
+ int FIELD_TYPE_BLOB = 4;
+
+ boolean moveToFirst();
+
+ boolean moveToNext();
+
+ int getColumnCount();
+
+ String getColumnName(int i);
+
+ int getType(int i);
+
+ long getLong(int i);
+
+ double getDouble(int i);
+
+ String getString(int i);
+
+ byte[] getBlob(int i);
+
+ int getCount();
+}
diff --git a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/Database.java b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/Database.java
new file mode 100644
index 00000000..e3732b9e
--- /dev/null
+++ b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/Database.java
@@ -0,0 +1,17 @@
+package org.pgsqlite.sqlite.plugin;
+
+import java.io.Closeable;
+
+/**
+ * Written by Sergei Dryganets Jul 24/2017
+ */
+public interface Database extends Closeable {
+ void execSQL(String sql);
+ SQLStatement compileStatement(String sql);
+ Cursor rawQuery(String sql, String[] params);
+ boolean isOpen();
+
+ void beginTransaction();
+ void setTransactionSuccessful();
+ void endTransaction();
+}
diff --git a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DatabaseConnectionProvider.java b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DatabaseConnectionProvider.java
new file mode 100644
index 00000000..5310feba
--- /dev/null
+++ b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DatabaseConnectionProvider.java
@@ -0,0 +1,40 @@
+package org.pgsqlite.sqlite.plugin;
+
+import android.content.Context;
+
+/**
+ * Written by Sergei Dryganets Jul 24/2017
+ */
+public interface DatabaseConnectionProvider {
+
+ int OPEN_READWRITE = 0x00000000;
+ int OPEN_READONLY = 0x00000001;
+
+ /**
+ * Open flag: Flag for {@link #openDatabase} to open the database without support for
+ * localized collators.
+ *
+ * {@more} This causes the collator LOCALIZED
not to be created.
+ * You must be consistent when using this flag to use the setting the database was
+ * created with. If this is set, {@link #setLocale} will do nothing.
+ */
+ int NO_LOCALIZED_COLLATORS = 0x00000010;
+
+ /**
+ * Open flag: Flag for {@link #openDatabase} to create the database file if it does not
+ * already exist.
+ */
+ int CREATE_IF_NECESSARY = 0x10000000;
+
+ /**
+ * Open flag: Flag for {@link #openDatabase} to open the database file with
+ * write-ahead logging enabled by default.
+ * Write-ahead logging cannot be used with read-only databases so the value of
+ * this flag is ignored if the database is opened read-only.
+ *
+ */
+ int ENABLE_WRITE_AHEAD_LOGGING = 0x20000000;
+
+ Database openDatabase(String databasePath, String password, int openFlags);
+}
+
diff --git a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultConnectionProvider.java b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultConnectionProvider.java
new file mode 100644
index 00000000..3d63ca94
--- /dev/null
+++ b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultConnectionProvider.java
@@ -0,0 +1,24 @@
+package org.pgsqlite.sqlite.plugin;
+
+import android.content.Context;
+import android.database.DatabaseErrorHandler;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+
+/**
+ * Written by Sergei Dryganets Jul 24/2017
+ */
+public class DefaultConnectionProvider implements DatabaseConnectionProvider {
+
+ @Override
+ public Database openDatabase(String databasePath, String password, int openFlags) {
+ return new DefaultDatabase(SQLiteDatabase.openDatabase(databasePath, null, openFlags, new DBErrorHandler()));
+ }
+
+ private class DBErrorHandler implements DatabaseErrorHandler {
+ @Override
+ public void onCorruption(SQLiteDatabase dbObj) {
+ throw new SQLException("Database is corrupted");
+ }
+ }
+}
diff --git a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultCursor.java b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultCursor.java
new file mode 100644
index 00000000..f4f19baa
--- /dev/null
+++ b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultCursor.java
@@ -0,0 +1,69 @@
+package org.pgsqlite.sqlite.plugin;
+
+import java.io.IOException;
+
+/**
+ * Written by Sergei Dryganets Jul 24/2017
+ */
+public class DefaultCursor implements Cursor {
+ private final android.database.Cursor mCursor;
+
+ public DefaultCursor(android.database.Cursor cursor) {
+ mCursor = cursor;
+ }
+
+ @Override
+ public boolean moveToFirst() {
+ return mCursor.moveToFirst();
+ }
+
+ @Override
+ public boolean moveToNext() {
+ return mCursor.moveToNext();
+ }
+
+ @Override
+ public int getColumnCount() {
+ return mCursor.getColumnCount();
+ }
+
+ @Override
+ public String getColumnName(int i) {
+ return mCursor.getColumnName(i);
+ }
+
+ @Override
+ public int getType(int i) {
+ return mCursor.getType(i);
+ }
+
+ @Override
+ public long getLong(int i) {
+ return mCursor.getLong(i);
+ }
+
+ @Override
+ public double getDouble(int i) {
+ return mCursor.getDouble(i);
+ }
+
+ @Override
+ public String getString(int i) {
+ return mCursor.getString(i);
+ }
+
+ @Override
+ public byte[] getBlob(int i) {
+ return mCursor.getBlob(i);
+ }
+
+ @Override
+ public int getCount() {
+ return mCursor.getCount();
+ }
+
+ @Override
+ public void close() throws IOException {
+ mCursor.close();
+ }
+}
diff --git a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultDatabase.java b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultDatabase.java
new file mode 100644
index 00000000..197aa5e3
--- /dev/null
+++ b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultDatabase.java
@@ -0,0 +1,56 @@
+package org.pgsqlite.sqlite.plugin;
+
+import android.database.sqlite.SQLiteDatabase;
+
+import java.io.IOException;
+
+/**
+ * Written by Sergei Dryganets Jul 24/2017
+ */
+public class DefaultDatabase implements Database {
+ private final SQLiteDatabase database;
+
+ public DefaultDatabase(SQLiteDatabase database) {
+ this.database = database;
+ }
+
+ @Override
+ public void execSQL(String sql) {
+ database.execSQL(sql);
+ }
+
+ @Override
+ public SQLStatement compileStatement(String sql) {
+ return new DefaultStatement(database.compileStatement(sql));
+ }
+
+ @Override
+ public Cursor rawQuery(String sql, String[] params) {
+ return new DefaultCursor(database.rawQuery(sql, params));
+ }
+
+ @Override
+ public boolean isOpen() {
+ return database.isOpen();
+ }
+
+ @Override
+ public void beginTransaction() {
+ database.beginTransaction();
+ }
+
+ @Override
+ public void setTransactionSuccessful() {
+ database.setTransactionSuccessful();
+ }
+
+ @Override
+ public void endTransaction() {
+ database.endTransaction();
+ }
+
+ @Override
+ public void close() throws IOException {
+ database.close();
+ }
+}
diff --git a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultStatement.java b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultStatement.java
new file mode 100644
index 00000000..d9b748f3
--- /dev/null
+++ b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultStatement.java
@@ -0,0 +1,51 @@
+package org.pgsqlite.sqlite.plugin;
+
+import android.database.sqlite.SQLiteStatement;
+
+import java.io.IOException;
+
+/**
+ * Written by Sergei Dryganets Jul 24/2017
+ */
+public class DefaultStatement implements SQLStatement {
+ private final SQLiteStatement mStatement;
+
+ public DefaultStatement(SQLiteStatement statement) {
+ mStatement = statement;
+ }
+
+ @Override
+ public void bindDouble(int i, double value) {
+ mStatement.bindDouble(i, value);
+ }
+
+ @Override
+ public void bindString(int i, String value) {
+ mStatement.bindString(i, value);
+ }
+
+ @Override
+ public void bindNull(int i) {
+ mStatement.bindNull(i);
+ }
+
+ @Override
+ public void bindLong(int i, long value) {
+ mStatement.bindLong(i, value);
+ }
+
+ @Override
+ public long executeInsert() {
+ return mStatement.executeInsert();
+ }
+
+ @Override
+ public int executeUpdateDelete() {
+ return mStatement.executeUpdateDelete();
+ }
+
+ @Override
+ public void close() throws IOException {
+ mStatement.close();
+ }
+}
diff --git a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/SQLStatement.java b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/SQLStatement.java
new file mode 100644
index 00000000..fcc0994d
--- /dev/null
+++ b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/SQLStatement.java
@@ -0,0 +1,20 @@
+package org.pgsqlite.sqlite.plugin;
+
+import java.io.Closeable;
+
+/**
+ * Written by Sergei Dryganets Jul 24/2017
+ */
+public interface SQLStatement extends Closeable {
+ void bindDouble(int i, double value);
+
+ void bindString(int i, String value);
+
+ void bindNull(int i);
+
+ void bindLong(int i, long value);
+
+ long executeInsert();
+
+ int executeUpdateDelete();
+}
diff --git a/src/android/sqlite-plugin-provider/src/main/res/values/strings.xml b/src/android/sqlite-plugin-provider/src/main/res/values/strings.xml
new file mode 100644
index 00000000..82b4713d
--- /dev/null
+++ b/src/android/sqlite-plugin-provider/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ SqlitePluginProvider
+
diff --git a/src/android/storage/build.gradle b/src/android/storage/build.gradle
new file mode 100644
index 00000000..52a24fba
--- /dev/null
+++ b/src/android/storage/build.gradle
@@ -0,0 +1,26 @@
+
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion "25.0.0"
+
+ defaultConfig {
+ minSdkVersion 16
+ targetSdkVersion 22
+ versionCode 1
+ versionName "1.0"
+ }
+ lintOptions {
+ abortOnError false
+ }
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ compile 'com.facebook.react:react-native:0.14.+'
+ compile project(':sqlite-plugin-provider')
+}
diff --git a/src/android/src/main/AndroidManifest.xml b/src/android/storage/src/main/AndroidManifest.xml
similarity index 100%
rename from src/android/src/main/AndroidManifest.xml
rename to src/android/storage/src/main/AndroidManifest.xml
diff --git a/src/android/src/main/java/org/pgsqlite/CallbackContext.java b/src/android/storage/src/main/java/org/pgsqlite/CallbackContext.java
similarity index 100%
rename from src/android/src/main/java/org/pgsqlite/CallbackContext.java
rename to src/android/storage/src/main/java/org/pgsqlite/CallbackContext.java
diff --git a/src/android/src/main/java/org/pgsqlite/SQLiteArray.java b/src/android/storage/src/main/java/org/pgsqlite/SQLiteArray.java
similarity index 100%
rename from src/android/src/main/java/org/pgsqlite/SQLiteArray.java
rename to src/android/storage/src/main/java/org/pgsqlite/SQLiteArray.java
diff --git a/src/android/src/main/java/org/pgsqlite/SQLiteObject.java b/src/android/storage/src/main/java/org/pgsqlite/SQLiteObject.java
similarity index 100%
rename from src/android/src/main/java/org/pgsqlite/SQLiteObject.java
rename to src/android/storage/src/main/java/org/pgsqlite/SQLiteObject.java
diff --git a/src/android/src/main/java/org/pgsqlite/SQLitePlugin.java b/src/android/storage/src/main/java/org/pgsqlite/SQLitePlugin.java
similarity index 92%
rename from src/android/src/main/java/org/pgsqlite/SQLitePlugin.java
rename to src/android/storage/src/main/java/org/pgsqlite/SQLitePlugin.java
index 9988dfc9..e364f74e 100755
--- a/src/android/src/main/java/org/pgsqlite/SQLitePlugin.java
+++ b/src/android/storage/src/main/java/org/pgsqlite/SQLitePlugin.java
@@ -8,10 +8,6 @@
package org.pgsqlite;
import android.annotation.SuppressLint;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
-import android.database.sqlite.SQLiteStatement;
import android.content.Context;
import android.util.Base64;
@@ -39,6 +35,10 @@
import org.json.JSONException;
import org.json.JSONArray;
import org.json.JSONObject;
+import org.pgsqlite.sqlite.plugin.Database;
+import org.pgsqlite.sqlite.plugin.DatabaseConnectionProvider;
+import org.pgsqlite.sqlite.plugin.Cursor;
+import org.pgsqlite.sqlite.plugin.SQLStatement;
import java.io.FileOutputStream;
import java.io.InputStream;
@@ -62,20 +62,20 @@ public class SQLitePlugin extends ReactContextBaseJavaModule {
*/
static ConcurrentHashMap dbrmap = new ConcurrentHashMap();
- /**
- * Linked activity
- */
- protected Context context = null;
+ private Context context = null;
/**
* Thread pool for database operations
*/
- protected ExecutorService threadPool;
+ private ExecutorService threadPool;
- public SQLitePlugin(ReactApplicationContext reactContext) {
+ private DatabaseConnectionProvider provider;
+
+ public SQLitePlugin(ReactApplicationContext reactContext, DatabaseConnectionProvider provider) {
super(reactContext);
this.context = reactContext.getApplicationContext();
this.threadPool = Executors.newCachedThreadPool();
+ this.provider = provider;
}
/**
@@ -231,8 +231,13 @@ private boolean executeAndPossiblyThrow(Action action, JSONArray args, CallbackC
case open:
o = args.getJSONObject(0);
dbname = o.getString("name");
+ String password = null;
+
+ if (o.has("key")) {
+ password = o.getString("key");
+ }
// open database and start reading its queue
- this.startDatabase(dbname, o, cbc);
+ this.startDatabase(dbname, password, o, cbc);
break;
case close:
@@ -334,10 +339,11 @@ public void closeAllOpenDatabases() {
/**
*
* @param dbname - The name of the database file
+ * @param password - password used to encrypt database
* @param options - options passed in from JS
* @param cbc - JS callback context
*/
- private void startDatabase(String dbname, JSONObject options, CallbackContext cbc) {
+ private void startDatabase(String dbname, String password, JSONObject options, CallbackContext cbc) {
// TODO: is it an issue that we can orphan an existing thread? What should we do here?
// If we re-use the existing DBRunner it might be in the process of closing...
DBRunner r = dbrmap.get(dbname);
@@ -349,7 +355,7 @@ private void startDatabase(String dbname, JSONObject options, CallbackContext cb
// than orphaning the old DBRunner.
cbc.success("database started");
} else {
- r = new DBRunner(dbname, options, cbc);
+ r = new DBRunner(dbname, password, options, cbc);
dbrmap.put(dbname, r);
this.getThreadPool().execute(r);
}
@@ -365,11 +371,11 @@ private void startDatabase(String dbname, JSONObject options, CallbackContext cb
* @return instance of SQLite database
* @throws Exception
*/
- private SQLiteDatabase openDatabase(String dbname, String assetFilePath, int openFlags, CallbackContext cbc) throws Exception {
+ private Database openDatabase(String dbname, String password, String assetFilePath, int openFlags, CallbackContext cbc) throws Exception {
InputStream in = null;
File dbfile = null;
try {
- SQLiteDatabase database = this.getDatabase(dbname);
+ Database database = this.getDatabase(dbname);
if (database != null && database.isOpen()) {
//this only happens when DBRunner is cycling the db for the locking work around.
// otherwise, this should not happen - should be blocked at the execute("open") level
@@ -392,7 +398,7 @@ private SQLiteDatabase openDatabase(String dbname, String assetFilePath, int ope
File assetFile = new File(filesDir, assetFilePath);
in = new FileInputStream(assetFile);
FLog.v(TAG, "Located pre-populated DB asset in Files subdirectory: " + assetFile.getCanonicalPath());
- if (openFlags == SQLiteDatabase.OPEN_READONLY) {
+ if (openFlags == DatabaseConnectionProvider.OPEN_READONLY) {
dbfile = assetFile;
FLog.v(TAG, "Detected read-only mode request for external asset.");
}
@@ -400,7 +406,8 @@ private SQLiteDatabase openDatabase(String dbname, String assetFilePath, int ope
}
if (dbfile == null) {
- openFlags = SQLiteDatabase.OPEN_READWRITE | SQLiteDatabase.CREATE_IF_NECESSARY;
+ openFlags = DatabaseConnectionProvider.OPEN_READWRITE |
+ DatabaseConnectionProvider.CREATE_IF_NECESSARY;
dbfile = this.getContext().getDatabasePath(dbname);
if (!dbfile.exists() && in != null) {
@@ -415,13 +422,13 @@ private SQLiteDatabase openDatabase(String dbname, String assetFilePath, int ope
FLog.v(TAG, "Opening sqlite db: " + dbfile.getAbsolutePath());
- SQLiteDatabase mydb = SQLiteDatabase.openDatabase(dbfile.getAbsolutePath(), null, openFlags);
+ Database mydb = provider.openDatabase(dbfile.getAbsolutePath(), password, openFlags);
if (cbc != null) // needed for Android locking/closing workaround
cbc.success("database open");
return mydb;
- } catch (SQLiteException ex) {
+ } catch (Exception ex) {
if (cbc != null) // needed for Android locking/closing workaround
cbc.error("can't open database " + ex);
throw ex;
@@ -498,11 +505,8 @@ private void closeDatabase(String dbName, CallbackContext cbc) {
* @param dbName The name of the database file
*/
private void closeDatabaseNow(String dbName) {
- SQLiteDatabase mydb = this.getDatabase(dbName);
-
- if (mydb != null) {
- mydb.close();
- }
+ Database mydb = this.getDatabase(dbName);
+ closeQuietly(mydb);
}
/**
@@ -576,7 +580,7 @@ private boolean deleteDatabaseNow(String dbname) {
*
* @param dbname The name of the database.
*/
- private SQLiteDatabase getDatabase(String dbname) {
+ private Database getDatabase(String dbname) {
DBRunner r = dbrmap.get(dbname);
return (r == null) ? null : r.mydb;
}
@@ -594,7 +598,7 @@ private SQLiteDatabase getDatabase(String dbname) {
private void executeSqlBatch(String dbname, String[] queryarr, JSONArray[] jsonparams,
String[] queryIDs, CallbackContext cbc) {
- SQLiteDatabase mydb = getDatabase(dbname);
+ Database mydb = getDatabase(dbname);
if (mydb == null) {
// not allowed - can only happen if someone has closed (and possibly deleted) a database and then re-used the database
@@ -621,7 +625,7 @@ private void executeSqlBatch(String dbname, String[] queryarr, JSONArray[] jsonp
QueryType queryType = getQueryType(query);
if (queryType == QueryType.update || queryType == QueryType.delete) {
- SQLiteStatement myStatement = null;
+ SQLStatement myStatement = null;
int rowsAffected = -1; // (assuming invalid)
try {
@@ -633,7 +637,7 @@ private void executeSqlBatch(String dbname, String[] queryarr, JSONArray[] jsonp
rowsAffected = myStatement.executeUpdateDelete();
// Indicate valid results:
needRawQuery = false;
- } catch (SQLiteException ex) {
+ } catch (Exception ex) {
// Indicate problem & stop this query:
errorMessage = ex.getMessage();
FLog.e(TAG, "SQLiteStatement.executeUpdateDelete() failed", ex);
@@ -652,7 +656,7 @@ private void executeSqlBatch(String dbname, String[] queryarr, JSONArray[] jsonp
if (queryType == QueryType.insert && jsonparams != null) {
needRawQuery = false;
- SQLiteStatement myStatement = mydb.compileStatement(query);
+ SQLStatement myStatement = mydb.compileStatement(query);
bindArgsToStatement(myStatement, jsonparams[i]);
@@ -669,7 +673,7 @@ private void executeSqlBatch(String dbname, String[] queryarr, JSONArray[] jsonp
} else {
queryResult.put("rowsAffected", 0);
}
- } catch (SQLiteException ex) {
+ } catch (Exception ex) {
// report error result with the error message
// could be constraint violation or some other error
errorMessage = ex.getMessage();
@@ -686,7 +690,7 @@ private void executeSqlBatch(String dbname, String[] queryarr, JSONArray[] jsonp
queryResult = new JSONObject();
queryResult.put("rowsAffected", 0);
- } catch (SQLiteException ex) {
+ } catch (Exception ex) {
errorMessage = ex.getMessage();
FLog.e(TAG, "SQLiteDatabase.beginTransaction() failed", ex);
}
@@ -700,7 +704,7 @@ private void executeSqlBatch(String dbname, String[] queryarr, JSONArray[] jsonp
queryResult = new JSONObject();
queryResult.put("rowsAffected", 0);
- } catch (SQLiteException ex) {
+ } catch (Exception ex) {
errorMessage = ex.getMessage();
FLog.e(TAG, "SQLiteDatabase.setTransactionSuccessful/endTransaction() failed", ex);
}
@@ -713,7 +717,7 @@ private void executeSqlBatch(String dbname, String[] queryarr, JSONArray[] jsonp
queryResult = new JSONObject();
queryResult.put("rowsAffected", 0);
- } catch (SQLiteException ex) {
+ } catch (Exception ex) {
errorMessage = ex.getMessage();
FLog.e(TAG, "SQLiteDatabase.endTransaction() failed", ex);
}
@@ -768,7 +772,7 @@ private QueryType getQueryType(String query) {
return QueryType.other;
}
- private void bindArgsToStatement(SQLiteStatement myStatement, JSONArray sqlArgs) throws JSONException {
+ private void bindArgsToStatement(SQLStatement myStatement, JSONArray sqlArgs) throws JSONException {
for (int i = 0; i < sqlArgs.length(); i++) {
if (sqlArgs.get(i) instanceof Float || sqlArgs.get(i) instanceof Double) {
myStatement.bindDouble(i + 1, sqlArgs.getDouble(i));
@@ -792,7 +796,7 @@ private void bindArgsToStatement(SQLiteStatement myStatement, JSONArray sqlArgs)
*
* @return results in string form
*/
- private JSONObject executeSqlStatementQuery(SQLiteDatabase mydb,
+ private JSONObject executeSqlStatementQuery(Database mydb,
String query, JSONArray paramsAsJson,
CallbackContext cbc) throws Exception {
JSONObject rowsResult = new JSONObject();
@@ -889,22 +893,25 @@ private void closeQuietly(Closeable closeable) {
private class DBRunner implements Runnable {
final String dbname;
+ final String password;
final int openFlags;
private String assetFilename;
private boolean androidLockWorkaround;
final BlockingQueue q;
final CallbackContext openCbc;
- SQLiteDatabase mydb;
+ Database mydb;
- DBRunner(final String dbname, JSONObject options, CallbackContext cbc) {
+ DBRunner(final String dbname, final String password, JSONObject options, CallbackContext cbc) {
this.dbname = dbname;
- int openFlags = SQLiteDatabase.OPEN_READWRITE | SQLiteDatabase.CREATE_IF_NECESSARY;
+ this.password = password;
+ int openFlags = DatabaseConnectionProvider.OPEN_READWRITE |
+ DatabaseConnectionProvider.CREATE_IF_NECESSARY;
try {
this.assetFilename = options.has("assetFilename") ? options.getString("assetFilename") : null;
if (this.assetFilename != null && this.assetFilename.length() > 0) {
boolean readOnly = options.has("readOnly") && options.getBoolean("readOnly");
- openFlags = readOnly ? SQLiteDatabase.OPEN_READONLY : openFlags;
+ openFlags = readOnly ? DatabaseConnectionProvider.OPEN_READONLY : openFlags;
}
} catch (Exception ex){
FLog.e(TAG,"Error retrieving assetFilename this.mode from options:",ex);
@@ -920,7 +927,7 @@ private class DBRunner implements Runnable {
public void run() {
try {
- this.mydb = openDatabase(dbname, this.assetFilename, this.openFlags, this.openCbc);
+ this.mydb = openDatabase(dbname, password, this.assetFilename, this.openFlags, this.openCbc);
} catch (Exception e) {
FLog.e(TAG, "unexpected error, stopping db thread", e);
dbrmap.remove(dbname);
@@ -939,7 +946,7 @@ public void run() {
if (androidLockWorkaround && dbq.queries.length == 1 && dbq.queries[0].equals("COMMIT")) {
// FLog.v(TAG, "close and reopen db");
closeDatabaseNow(dbname);
- this.mydb = openDatabase(dbname, "", this.openFlags, null);
+ this.mydb = openDatabase(dbname, password, "", this.openFlags, null);
// FLog.v(TAG, "close and reopen db finished");
}
diff --git a/src/android/src/main/java/org/pgsqlite/SQLitePluginConverter.java b/src/android/storage/src/main/java/org/pgsqlite/SQLitePluginConverter.java
similarity index 100%
rename from src/android/src/main/java/org/pgsqlite/SQLitePluginConverter.java
rename to src/android/storage/src/main/java/org/pgsqlite/SQLitePluginConverter.java
diff --git a/src/android/src/main/java/org/pgsqlite/SQLitePluginPackage.java b/src/android/storage/src/main/java/org/pgsqlite/SQLitePluginPackage.java
similarity index 73%
rename from src/android/src/main/java/org/pgsqlite/SQLitePluginPackage.java
rename to src/android/storage/src/main/java/org/pgsqlite/SQLitePluginPackage.java
index be250278..9dde94c5 100644
--- a/src/android/src/main/java/org/pgsqlite/SQLitePluginPackage.java
+++ b/src/android/storage/src/main/java/org/pgsqlite/SQLitePluginPackage.java
@@ -5,14 +5,15 @@
*/
package org.pgsqlite;
-import android.app.Activity;
-
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
+import org.pgsqlite.sqlite.plugin.DatabaseConnectionProvider;
+import org.pgsqlite.sqlite.plugin.DefaultConnectionProvider;
+
import java.util.ArrayList;
import java.util.Collections;
@@ -20,15 +21,14 @@
public class SQLitePluginPackage implements ReactPackage {
- /**
- * @deprecated, use method without activity
- * activity parameter is ignored
- */
- public SQLitePluginPackage(Activity activity){
- this();
- }
+ private final DatabaseConnectionProvider provider;
public SQLitePluginPackage() {
+ this(new DefaultConnectionProvider());
+ }
+
+ public SQLitePluginPackage(DatabaseConnectionProvider provider) {
+ this.provider = provider;
}
@Override
@@ -36,7 +36,7 @@ public List createNativeModules(
ReactApplicationContext reactContext) {
List modules = new ArrayList<>();
- modules.add(new SQLitePlugin(reactContext));
+ modules.add(new SQLitePlugin(reactContext, provider));
return modules;
}