diff --git a/android/build.gradle b/android/build.gradle
index 7d10878c0..28ec5c5c5 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -11,6 +11,7 @@ apply plugin: 'maven'
android {
compileSdkVersion rootProject.compileSdkVersion
buildToolsVersion rootProject.buildToolsVersion
+ testOptions.unitTests.includeAndroidResources = true
defaultConfig {
minSdkVersion rootProject.minSdkVersion
@@ -67,6 +68,8 @@ android {
testImplementation deps.mockito
testImplementation deps.robolectric
+ testImplementation deps.testCore
+ testImplementation deps.testRules
testImplementation deps.hamcrest
testImplementation deps.junit
}
diff --git a/android/sample/src/debug/java/com/facebook/flipper/sample/FlipperInitializer.java b/android/sample/src/debug/java/com/facebook/flipper/sample/FlipperInitializer.java
index da6e5385d..199823c39 100644
--- a/android/sample/src/debug/java/com/facebook/flipper/sample/FlipperInitializer.java
+++ b/android/sample/src/debug/java/com/facebook/flipper/sample/FlipperInitializer.java
@@ -1,14 +1,15 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
- * This source code is licensed under the MIT license found in the LICENSE
- * file in the root directory of this source tree.
+ *
This source code is licensed under the MIT license found in the LICENSE file in the root
+ * directory of this source tree.
*/
package com.facebook.flipper.sample;
import android.content.Context;
import com.facebook.flipper.core.FlipperClient;
import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
+import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
import com.facebook.flipper.plugins.example.ExampleFlipperPlugin;
import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
import com.facebook.flipper.plugins.inspector.DescriptorMapping;
@@ -51,6 +52,7 @@ public final class FlipperInitializer {
client.addPlugin(new FrescoFlipperPlugin());
client.addPlugin(new ExampleFlipperPlugin());
client.addPlugin(CrashReporterPlugin.getInstance());
+ client.addPlugin(new DatabasesFlipperPlugin(context));
client.start();
final OkHttpClient okHttpClient =
diff --git a/android/sample/src/main/java/com/facebook/flipper/sample/Database1Helper.java b/android/sample/src/main/java/com/facebook/flipper/sample/Database1Helper.java
new file mode 100644
index 000000000..e43d929d7
--- /dev/null
+++ b/android/sample/src/main/java/com/facebook/flipper/sample/Database1Helper.java
@@ -0,0 +1,84 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ *
This source code is licensed under the MIT license found in the LICENSE file in the root
+ * directory of this source tree.
+ */
+package com.facebook.flipper.sample;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+public class Database1Helper extends SQLiteOpenHelper {
+
+ // If you change the database schema, you must increment the database version.
+ public static final int DATABASE_VERSION = 4;
+ private static final String SQL_CREATE_FIRST_TABLE =
+ "CREATE TABLE "
+ + "db1_first_table"
+ + " ("
+ + "_id INTEGER PRIMARY KEY,"
+ + "db1_col0_text TEXT,"
+ + "db1_col1_integer INTEGER,"
+ + "db1_col2_float FLOAT,"
+ + "db1_col3_blob TEXT,"
+ + "db1_col4_null TEXT DEFAULT NULL,"
+ + "db1_col5 TEXT,"
+ + "db1_col6 TEXT,"
+ + "db1_col7 TEXT,"
+ + "db1_col8 TEXT,"
+ + "db1_col9 TEXT"
+ + ")";
+ private static final String SQL_CREATE_SECOND_TABLE =
+ "CREATE TABLE "
+ + "db1_empty_table"
+ + " ("
+ + "_id INTEGER PRIMARY KEY,"
+ + "column1 TEXT,"
+ + "column2 TEXT)";
+
+ public Database1Helper(Context context) {
+ super(context, "database1.db", null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL(SQL_CREATE_FIRST_TABLE);
+ db.execSQL(SQL_CREATE_SECOND_TABLE);
+ insertSampleData(db);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ // This database is only a cache for online data, so its upgrade policy is
+ // to simply to discard the data and start over
+ db.execSQL("DROP TABLE IF EXISTS first_table");
+ db.execSQL("DROP TABLE IF EXISTS second_table");
+ db.execSQL("DROP TABLE IF EXISTS db1_first_table");
+ db.execSQL("DROP TABLE IF EXISTS db1_empty_table");
+ onCreate(db);
+ }
+
+ @Override
+ public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ onUpgrade(db, oldVersion, newVersion);
+ }
+
+ public void insertSampleData(SQLiteDatabase db) {
+ for (int i = 0; i < 100; i++) {
+ ContentValues contentValues = new ContentValues();
+ contentValues.put("db1_col0_text", "Long text data for testing resizing");
+ contentValues.put("db1_col1_integer", 1000 + i);
+ contentValues.put("db1_col2_float", 1000.465f + i);
+ contentValues.put("db1_col3_blob", new byte[] {0, 0, 0, 1, 1, 0, 1, 1});
+ contentValues.put("db1_col5", "db_1_column5_value");
+ contentValues.put("db1_col6", "db_1_column6_value");
+ contentValues.put("db1_col7", "db_1_column7_value");
+ contentValues.put("db1_col8", "db_1_column8_value");
+ contentValues.put("db1_col9", "db_1_column9_value");
+ db.insert("db1_first_table", null, contentValues);
+ }
+ }
+}
diff --git a/android/sample/src/main/java/com/facebook/flipper/sample/Database2Helper.java b/android/sample/src/main/java/com/facebook/flipper/sample/Database2Helper.java
new file mode 100644
index 000000000..ce6315d52
--- /dev/null
+++ b/android/sample/src/main/java/com/facebook/flipper/sample/Database2Helper.java
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ *
This source code is licensed under the MIT license found in the LICENSE file in the root
+ * directory of this source tree.
+ */
+package com.facebook.flipper.sample;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+public class Database2Helper extends SQLiteOpenHelper {
+
+ // If you change the database schema, you must increment the database version.
+ public static final int DATABASE_VERSION = 4;
+ private static final String SQL_CREATE_FIRST_TABLE =
+ "CREATE TABLE "
+ + "db2_first_table"
+ + " ("
+ + "_id INTEGER PRIMARY KEY,"
+ + "column1 TEXT,"
+ + "column2 TEXT)";
+ private static final String SQL_CREATE_SECOND_TABLE =
+ "CREATE TABLE "
+ + "db2_second_table"
+ + " ("
+ + "_id INTEGER PRIMARY KEY,"
+ + "column1 TEXT,"
+ + "column2 TEXT)";
+
+ public Database2Helper(Context context) {
+ super(context, "database2.db", null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL(SQL_CREATE_FIRST_TABLE);
+ db.execSQL(SQL_CREATE_SECOND_TABLE);
+ insertSampleData(db);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ // This database is only a cache for online data, so its upgrade policy is
+ // to simply to discard the data and start over
+ db.execSQL("DROP TABLE IF EXISTS first_table");
+ db.execSQL("DROP TABLE IF EXISTS second_table");
+ db.execSQL("DROP TABLE IF EXISTS db2_first_table");
+ db.execSQL("DROP TABLE IF EXISTS db2_second_table");
+ onCreate(db);
+ }
+
+ @Override
+ public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ onUpgrade(db, oldVersion, newVersion);
+ }
+
+ public void insertSampleData(SQLiteDatabase db) {
+ for (int i = 0; i < 10; i++) {
+ ContentValues contentValues = new ContentValues();
+ contentValues.put("column1", "Long text data for testing resizing");
+ contentValues.put(
+ "column2",
+ "extra extra extra extra extra extra extra extra extra extra extra extra extra extra extra extra extra extra extra extra extra extra extra extra extra extra extra extra extra extra extra extra extra extra extra extra Long text data for testing resizing");
+ db.insert("db2_first_table", null, contentValues);
+ db.insert("db2_second_table", null, contentValues);
+ }
+ }
+}
diff --git a/android/sample/src/main/java/com/facebook/flipper/sample/FlipperSampleApplication.java b/android/sample/src/main/java/com/facebook/flipper/sample/FlipperSampleApplication.java
index 7fd2786c0..b9a90c3f6 100644
--- a/android/sample/src/main/java/com/facebook/flipper/sample/FlipperSampleApplication.java
+++ b/android/sample/src/main/java/com/facebook/flipper/sample/FlipperSampleApplication.java
@@ -1,13 +1,14 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
- * This source code is licensed under the MIT license found in the LICENSE
- * file in the root directory of this source tree.
+ *
This source code is licensed under the MIT license found in the LICENSE file in the root
+ * directory of this source tree.
*/
package com.facebook.flipper.sample;
import android.app.Application;
import android.content.Context;
+import android.database.DatabaseUtils;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.flipper.android.AndroidFlipperClient;
import com.facebook.flipper.core.FlipperClient;
@@ -33,5 +34,11 @@ public class FlipperSampleApplication extends Application {
.edit()
.putInt("SomeKey", 1337)
.apply();
+
+ Database1Helper db1Helper = new Database1Helper(this);
+ Database2Helper db2Helper = new Database2Helper(this);
+
+ DatabaseUtils.queryNumEntries(db1Helper.getReadableDatabase(), "db1_first_table", null, null);
+ DatabaseUtils.queryNumEntries(db2Helper.getReadableDatabase(), "db2_first_table", null, null);
}
}
diff --git a/android/src/main/java/com/facebook/flipper/plugins/databases/DatabaseDescriptor.java b/android/src/main/java/com/facebook/flipper/plugins/databases/DatabaseDescriptor.java
new file mode 100644
index 000000000..28a6def0a
--- /dev/null
+++ b/android/src/main/java/com/facebook/flipper/plugins/databases/DatabaseDescriptor.java
@@ -0,0 +1,15 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ *
This source code is licensed under the MIT license found in the LICENSE file in the root
+ * directory of this source tree.
+ */
+package com.facebook.flipper.plugins.databases;
+
+/**
+ * Interface to describe a Database object. The DatabaseDescriptor#name() is visible and displayed
+ * to the user
+ */
+public interface DatabaseDescriptor {
+ String name();
+}
diff --git a/android/src/main/java/com/facebook/flipper/plugins/databases/DatabaseDriver.java b/android/src/main/java/com/facebook/flipper/plugins/databases/DatabaseDriver.java
new file mode 100644
index 000000000..bd0dad0b8
--- /dev/null
+++ b/android/src/main/java/com/facebook/flipper/plugins/databases/DatabaseDriver.java
@@ -0,0 +1,148 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ *
This source code is licensed under the MIT license found in the LICENSE file in the root
+ * directory of this source tree.
+ */
+package com.facebook.flipper.plugins.databases;
+
+import android.content.Context;
+import androidx.annotation.StringDef;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * Abstract class allowing to implement different drivers interfacing with Databases.
+ *
+ * @param A DatabaseDescriptor object that is called for each databases provider by the
+ * driver
+ */
+public abstract class DatabaseDriver {
+
+ private final Context mContext;
+
+ public DatabaseDriver(final Context context) {
+ mContext = context;
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public abstract List getDatabases();
+
+ public abstract List getTableNames(DESCRIPTOR databaseDescriptor);
+
+ public abstract DatabaseGetTableDataResponse getTableData(
+ DESCRIPTOR databaseDescriptor,
+ String table,
+ String order,
+ boolean reverse,
+ int start,
+ int count);
+
+ public abstract DatabaseGetTableStructureResponse getTableStructure(
+ DESCRIPTOR databaseDescriptor, String table);
+
+ public abstract DatabaseExecuteSqlResponse executeSQL(
+ DESCRIPTOR databaseDescriptor, String query);
+
+ public static class DatabaseGetTableDataResponse {
+
+ public final List columns;
+ public final List> values;
+ public final Integer start;
+ public final Integer count;
+ public final Long total;
+
+ public DatabaseGetTableDataResponse(
+ final List columns,
+ final List> values,
+ int start,
+ int count,
+ long total) {
+ this.columns = columns;
+ this.values = values;
+ this.start = start;
+ this.count = count;
+ this.total = total;
+ }
+ }
+
+ public static class DatabaseGetTableStructureResponse {
+
+ public final List structureColumns;
+ public final List> structureValues;
+ public final List indexesColumns;
+ public final List> indexesValues;
+ public final String definition;
+
+ public DatabaseGetTableStructureResponse(
+ final List structureColumns,
+ final List> structureValues,
+ final List indexesColumns,
+ final List> indexesValues,
+ String definition) {
+ this.structureColumns = structureColumns;
+ this.structureValues = structureValues;
+ this.indexesColumns = indexesColumns;
+ this.indexesValues = indexesValues;
+ this.definition = definition;
+ }
+ }
+
+ public static class DatabaseExecuteSqlResponse {
+
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef({TYPE_SELECT, TYPE_INSERT, TYPE_UPDATE_DELETE, TYPE_RAW})
+ public @interface Type {}
+
+ public static final String TYPE_SELECT = "select";
+ public static final String TYPE_INSERT = "insert";
+ public static final String TYPE_UPDATE_DELETE = "update_delete";
+ public static final String TYPE_RAW = "raw";
+
+ public final @Type String type;
+
+ // Select
+ public final List columns;
+ public final List> values;
+
+ // insert
+ public final Long insertedId;
+
+ // update/delete
+ public final Integer affectedCount;
+
+ private DatabaseExecuteSqlResponse(
+ final @Type String type,
+ final List columns,
+ final List> values,
+ Long insertedId,
+ Integer affectedCount) {
+ this.type = type;
+ this.columns = columns;
+ this.values = values;
+ this.insertedId = insertedId;
+ this.affectedCount = affectedCount;
+ }
+
+ public static DatabaseExecuteSqlResponse successfulSelect(
+ List columns, List> values) {
+ return new DatabaseExecuteSqlResponse(TYPE_SELECT, columns, values, null, null);
+ }
+
+ public static DatabaseExecuteSqlResponse successfulInsert(long insertedId) {
+ return new DatabaseExecuteSqlResponse(TYPE_INSERT, null, null, insertedId, null);
+ }
+
+ public static DatabaseExecuteSqlResponse successfulUpdateDelete(int affectedRows) {
+ return new DatabaseExecuteSqlResponse(TYPE_UPDATE_DELETE, null, null, null, affectedRows);
+ }
+
+ public static DatabaseExecuteSqlResponse successfulRawQuery() {
+ return new DatabaseExecuteSqlResponse(TYPE_RAW, null, null, null, null);
+ }
+ }
+}
diff --git a/android/src/main/java/com/facebook/flipper/plugins/databases/DatabasesErrorCodes.java b/android/src/main/java/com/facebook/flipper/plugins/databases/DatabasesErrorCodes.java
new file mode 100644
index 000000000..218c14f8f
--- /dev/null
+++ b/android/src/main/java/com/facebook/flipper/plugins/databases/DatabasesErrorCodes.java
@@ -0,0 +1,16 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ *
This source code is licensed under the MIT license found in the LICENSE file in the root
+ * directory of this source tree.
+ */
+package com.facebook.flipper.plugins.databases;
+
+public class DatabasesErrorCodes {
+
+ public static final int ERROR_INVALID_REQUEST = 1;
+ public static final String ERROR_INVALID_REQUEST_MESSAGE = "The request received was invalid";
+ public static final int ERROR_DATABASE_INVALID = 2;
+ public static final String ERROR_DATABASE_INVALID_MESSAGE = "Could not access database";
+ public static final int ERROR_SQL_EXECUTION_EXCEPTION = 3;
+}
diff --git a/android/src/main/java/com/facebook/flipper/plugins/databases/DatabasesFlipperPlugin.java b/android/src/main/java/com/facebook/flipper/plugins/databases/DatabasesFlipperPlugin.java
new file mode 100644
index 000000000..9768518b6
--- /dev/null
+++ b/android/src/main/java/com/facebook/flipper/plugins/databases/DatabasesFlipperPlugin.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ *
This source code is licensed under the MIT license found in the LICENSE file in the root
+ * directory of this source tree.
+ */
+package com.facebook.flipper.plugins.databases;
+
+import android.content.Context;
+import com.facebook.flipper.core.FlipperConnection;
+import com.facebook.flipper.core.FlipperPlugin;
+import com.facebook.flipper.plugins.databases.impl.SqliteDatabaseDriver;
+import java.util.Collections;
+import java.util.List;
+
+public class DatabasesFlipperPlugin implements FlipperPlugin {
+
+ private static final String ID = "Databases";
+
+ private final DatabasesManager databasesManager;
+
+ public DatabasesFlipperPlugin(Context context) {
+ this(new SqliteDatabaseDriver(context));
+ }
+
+ public DatabasesFlipperPlugin(DatabaseDriver databaseDriver) {
+ this(Collections.singletonList(databaseDriver));
+ }
+
+ public DatabasesFlipperPlugin(List databaseDriverList) {
+ databasesManager = new DatabasesManager(databaseDriverList);
+ }
+
+ @Override
+ public String getId() {
+ return ID;
+ }
+
+ @Override
+ public void onConnect(FlipperConnection connection) {
+ databasesManager.setConnection(connection);
+ }
+
+ @Override
+ public void onDisconnect() {
+ databasesManager.setConnection(null);
+ }
+
+ @Override
+ public boolean runInBackground() {
+ return false;
+ }
+}
diff --git a/android/src/main/java/com/facebook/flipper/plugins/databases/DatabasesManager.java b/android/src/main/java/com/facebook/flipper/plugins/databases/DatabasesManager.java
new file mode 100644
index 000000000..f309be15d
--- /dev/null
+++ b/android/src/main/java/com/facebook/flipper/plugins/databases/DatabasesManager.java
@@ -0,0 +1,260 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ *
This source code is licensed under the MIT license found in the LICENSE file in the root
+ * directory of this source tree.
+ */
+package com.facebook.flipper.plugins.databases;
+
+import android.util.SparseArray;
+import com.facebook.flipper.core.FlipperArray;
+import com.facebook.flipper.core.FlipperConnection;
+import com.facebook.flipper.core.FlipperObject;
+import com.facebook.flipper.core.FlipperReceiver;
+import com.facebook.flipper.core.FlipperResponder;
+import com.facebook.flipper.plugins.databases.DatabaseDriver.DatabaseExecuteSqlResponse;
+import com.facebook.flipper.plugins.databases.DatabaseDriver.DatabaseGetTableDataResponse;
+import com.facebook.flipper.plugins.databases.DatabaseDriver.DatabaseGetTableStructureResponse;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import javax.annotation.Nullable;
+
+public class DatabasesManager {
+
+ private static final String DATABASE_LIST_COMMAND = "databaseList";
+ private static final String GET_TABLE_DATA_COMMAND = "getTableData";
+ private static final String GET_TABLE_STRUCTURE_COMMAND = "getTableStructure";
+ private static final String EXECUTE_COMMAND = "execute";
+
+ private final List mDatabaseDriverList;
+ private final SparseArray mDatabaseDescriptorHolderSparseArray;
+ private final Set mDatabaseDescriptorHolderSet;
+
+ private FlipperConnection mConnection;
+
+ public DatabasesManager(List databaseDriverList) {
+ this.mDatabaseDriverList = databaseDriverList;
+ this.mDatabaseDescriptorHolderSparseArray = new SparseArray<>();
+ this.mDatabaseDescriptorHolderSet =
+ new TreeSet<>(
+ new Comparator() {
+ @Override
+ public int compare(DatabaseDescriptorHolder o1, DatabaseDescriptorHolder o2) {
+ return o1.databaseDescriptor.name().compareTo(o2.databaseDescriptor.name());
+ }
+ });
+ }
+
+ public void setConnection(@Nullable FlipperConnection connection) {
+ this.mConnection = connection;
+ if (connection != null) {
+ listenForCommands(connection);
+ }
+ }
+
+ public boolean isConnected() {
+ return mConnection != null;
+ }
+
+ private void listenForCommands(FlipperConnection connection) {
+ connection.receive(
+ DATABASE_LIST_COMMAND,
+ new FlipperReceiver() {
+ @Override
+ public void onReceive(FlipperObject params, FlipperResponder responder) {
+ int databaseId = 1;
+ mDatabaseDescriptorHolderSparseArray.clear();
+ mDatabaseDescriptorHolderSet.clear();
+ for (DatabaseDriver> databaseDriver : mDatabaseDriverList) {
+ List extends DatabaseDescriptor> databaseDescriptorList =
+ databaseDriver.getDatabases();
+ for (DatabaseDescriptor databaseDescriptor : databaseDescriptorList) {
+ int id = databaseId++;
+ DatabaseDescriptorHolder databaseDescriptorHolder =
+ new DatabaseDescriptorHolder(id, databaseDriver, databaseDescriptor);
+ mDatabaseDescriptorHolderSparseArray.put(id, databaseDescriptorHolder);
+ mDatabaseDescriptorHolderSet.add(databaseDescriptorHolder);
+ }
+ }
+ FlipperArray result =
+ ObjectMapper.databaseListToFlipperArray(mDatabaseDescriptorHolderSet);
+ responder.success(result);
+ }
+ });
+ connection.receive(
+ GET_TABLE_DATA_COMMAND,
+ new FlipperReceiver() {
+ @Override
+ public void onReceive(FlipperObject params, FlipperResponder responder) {
+ GetTableDataRequest getTableDataRequest =
+ ObjectMapper.flipperObjectToGetTableDataRequest(params);
+ if (getTableDataRequest == null) {
+ responder.error(
+ ObjectMapper.toErrorFlipperObject(
+ DatabasesErrorCodes.ERROR_INVALID_REQUEST,
+ DatabasesErrorCodes.ERROR_INVALID_REQUEST_MESSAGE));
+ } else {
+ DatabaseDescriptorHolder databaseDescriptorHolder =
+ mDatabaseDescriptorHolderSparseArray.get(getTableDataRequest.databaseId);
+ if (databaseDescriptorHolder == null) {
+ responder.error(
+ ObjectMapper.toErrorFlipperObject(
+ DatabasesErrorCodes.ERROR_DATABASE_INVALID,
+ DatabasesErrorCodes.ERROR_DATABASE_INVALID_MESSAGE));
+ } else {
+ try {
+ DatabaseGetTableDataResponse databaseGetTableDataResponse =
+ databaseDescriptorHolder.databaseDriver.getTableData(
+ databaseDescriptorHolder.databaseDescriptor,
+ getTableDataRequest.table,
+ getTableDataRequest.order,
+ getTableDataRequest.reverse,
+ getTableDataRequest.start,
+ getTableDataRequest.count);
+ responder.success(
+ ObjectMapper.databaseGetTableDataReponseToFlipperObject(
+ databaseGetTableDataResponse));
+ } catch (Exception e) {
+ responder.error(
+ ObjectMapper.toErrorFlipperObject(
+ DatabasesErrorCodes.ERROR_SQL_EXECUTION_EXCEPTION, e.getMessage()));
+ }
+ }
+ }
+ }
+ });
+ connection.receive(
+ GET_TABLE_STRUCTURE_COMMAND,
+ new FlipperReceiver() {
+ @Override
+ public void onReceive(FlipperObject params, FlipperResponder responder) {
+ GetTableStructureRequest getTableStructureRequest =
+ ObjectMapper.flipperObjectToGetTableStructureRequest(params);
+ if (getTableStructureRequest == null) {
+ responder.error(
+ ObjectMapper.toErrorFlipperObject(
+ DatabasesErrorCodes.ERROR_INVALID_REQUEST,
+ DatabasesErrorCodes.ERROR_INVALID_REQUEST_MESSAGE));
+ } else {
+ DatabaseDescriptorHolder databaseDescriptorHolder =
+ mDatabaseDescriptorHolderSparseArray.get(getTableStructureRequest.databaseId);
+ if (databaseDescriptorHolder == null) {
+ responder.error(
+ ObjectMapper.toErrorFlipperObject(
+ DatabasesErrorCodes.ERROR_DATABASE_INVALID,
+ DatabasesErrorCodes.ERROR_DATABASE_INVALID_MESSAGE));
+ } else {
+ try {
+ DatabaseGetTableStructureResponse databaseGetTableStructureResponse =
+ databaseDescriptorHolder.databaseDriver.getTableStructure(
+ databaseDescriptorHolder.databaseDescriptor,
+ getTableStructureRequest.table);
+ responder.success(
+ ObjectMapper.databaseGetTableStructureReponseToFlipperObject(
+ databaseGetTableStructureResponse));
+ } catch (Exception e) {
+ responder.error(
+ ObjectMapper.toErrorFlipperObject(
+ DatabasesErrorCodes.ERROR_SQL_EXECUTION_EXCEPTION, e.getMessage()));
+ }
+ }
+ }
+ }
+ });
+ connection.receive(
+ EXECUTE_COMMAND,
+ new FlipperReceiver() {
+ @Override
+ public void onReceive(FlipperObject params, FlipperResponder responder) {
+ ExecuteSqlRequest executeSqlRequest =
+ ObjectMapper.flipperObjectToExecuteSqlRequest(params);
+ if (executeSqlRequest == null) {
+ responder.error(
+ ObjectMapper.toErrorFlipperObject(
+ DatabasesErrorCodes.ERROR_INVALID_REQUEST,
+ DatabasesErrorCodes.ERROR_INVALID_REQUEST_MESSAGE));
+ } else {
+ DatabaseDescriptorHolder databaseDescriptorHolder =
+ mDatabaseDescriptorHolderSparseArray.get(executeSqlRequest.databaseId);
+ if (databaseDescriptorHolder == null) {
+ responder.error(
+ ObjectMapper.toErrorFlipperObject(
+ DatabasesErrorCodes.ERROR_DATABASE_INVALID,
+ DatabasesErrorCodes.ERROR_DATABASE_INVALID_MESSAGE));
+ } else {
+ try {
+ DatabaseExecuteSqlResponse databaseExecuteSqlResponse =
+ databaseDescriptorHolder.databaseDriver.executeSQL(
+ databaseDescriptorHolder.databaseDescriptor, executeSqlRequest.value);
+ responder.success(
+ ObjectMapper.databaseExecuteSqlResponseToFlipperObject(
+ databaseExecuteSqlResponse));
+ } catch (Exception e) {
+ responder.error(
+ ObjectMapper.toErrorFlipperObject(
+ DatabasesErrorCodes.ERROR_SQL_EXECUTION_EXCEPTION, e.getMessage()));
+ }
+ }
+ }
+ }
+ });
+ }
+
+ static class DatabaseDescriptorHolder {
+
+ public final int id;
+ public final DatabaseDriver databaseDriver;
+ public final DatabaseDescriptor databaseDescriptor;
+
+ public DatabaseDescriptorHolder(
+ int id, DatabaseDriver databaseDriver, DatabaseDescriptor databaseDescriptor) {
+ this.id = id;
+ this.databaseDriver = databaseDriver;
+ this.databaseDescriptor = databaseDescriptor;
+ }
+ }
+
+ static class ExecuteSqlRequest {
+
+ public final int databaseId;
+ public final String value;
+
+ ExecuteSqlRequest(int databaseId, String value) {
+ this.databaseId = databaseId;
+ this.value = value;
+ }
+ }
+
+ static class GetTableDataRequest {
+
+ public final int databaseId;
+ public final String table;
+ public final String order;
+ public final boolean reverse;
+ public final int start;
+ public final int count;
+
+ GetTableDataRequest(
+ int databaseId, String table, String order, boolean reverse, int start, int count) {
+ this.databaseId = databaseId;
+ this.table = table;
+ this.order = order;
+ this.reverse = reverse;
+ this.start = start;
+ this.count = count;
+ }
+ }
+
+ static class GetTableStructureRequest {
+
+ public final int databaseId;
+ public final String table;
+
+ GetTableStructureRequest(int databaseId, String table) {
+ this.databaseId = databaseId;
+ this.table = table;
+ }
+ }
+}
diff --git a/android/src/main/java/com/facebook/flipper/plugins/databases/ObjectMapper.java b/android/src/main/java/com/facebook/flipper/plugins/databases/ObjectMapper.java
new file mode 100644
index 000000000..00160010d
--- /dev/null
+++ b/android/src/main/java/com/facebook/flipper/plugins/databases/ObjectMapper.java
@@ -0,0 +1,226 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ *
This source code is licensed under the MIT license found in the LICENSE file in the root
+ * directory of this source tree.
+ */
+package com.facebook.flipper.plugins.databases;
+
+import android.text.TextUtils;
+import com.facebook.flipper.core.FlipperArray;
+import com.facebook.flipper.core.FlipperArray.Builder;
+import com.facebook.flipper.core.FlipperObject;
+import com.facebook.flipper.plugins.databases.DatabaseDriver.DatabaseExecuteSqlResponse;
+import com.facebook.flipper.plugins.databases.DatabaseDriver.DatabaseGetTableDataResponse;
+import com.facebook.flipper.plugins.databases.DatabaseDriver.DatabaseGetTableStructureResponse;
+import com.facebook.flipper.plugins.databases.DatabasesManager.DatabaseDescriptorHolder;
+import com.facebook.flipper.plugins.databases.DatabasesManager.ExecuteSqlRequest;
+import com.facebook.flipper.plugins.databases.DatabasesManager.GetTableDataRequest;
+import com.facebook.flipper.plugins.databases.DatabasesManager.GetTableStructureRequest;
+import java.io.UnsupportedEncodingException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+public class ObjectMapper {
+
+ private static final int MAX_BLOB_LENGTH = 512;
+ private static final String UNKNOWN_BLOB_LABEL = "{blob}";
+
+ public static FlipperArray databaseListToFlipperArray(
+ Collection databaseDescriptorHolderList) {
+ FlipperArray.Builder builder = new FlipperArray.Builder();
+
+ for (DatabaseDescriptorHolder databaseDescriptorHolder : databaseDescriptorHolderList) {
+
+ List tableNameList =
+ databaseDescriptorHolder.databaseDriver.getTableNames(
+ databaseDescriptorHolder.databaseDescriptor);
+ Collections.sort(tableNameList);
+ FlipperArray.Builder tableBuilder = new Builder();
+ for (String tablename : tableNameList) {
+ tableBuilder.put(tablename);
+ }
+
+ builder.put(
+ new FlipperObject.Builder()
+ .put("id", databaseDescriptorHolder.id)
+ .put("name", databaseDescriptorHolder.databaseDescriptor.name())
+ .put("tables", tableBuilder.build())
+ .build());
+ }
+
+ return builder.build();
+ }
+
+ public static GetTableDataRequest flipperObjectToGetTableDataRequest(FlipperObject params) {
+ int databaseId = params.getInt("databaseId");
+ String table = params.getString("table");
+ String order = params.getString("order");
+ boolean reverse = params.getBoolean("reverse");
+ int start = params.getInt("start");
+ int count = params.getInt("count");
+ if (databaseId <= 0 || TextUtils.isEmpty(table)) {
+ return null;
+ }
+ return new GetTableDataRequest(databaseId, table, order, reverse, start, count);
+ }
+
+ public static GetTableStructureRequest flipperObjectToGetTableStructureRequest(
+ FlipperObject params) {
+ int databaseId = params.getInt("databaseId");
+ String table = params.getString("table");
+ if (databaseId <= 0 || TextUtils.isEmpty(table)) {
+ return null;
+ }
+ return new GetTableStructureRequest(databaseId, table);
+ }
+
+ public static ExecuteSqlRequest flipperObjectToExecuteSqlRequest(FlipperObject params) {
+ int databaseId = params.getInt("databaseId");
+ String value = params.getString("value");
+ if (databaseId <= 0 || TextUtils.isEmpty(value)) {
+ return null;
+ }
+ return new ExecuteSqlRequest(databaseId, value);
+ }
+
+ public static FlipperObject databaseGetTableDataReponseToFlipperObject(
+ DatabaseGetTableDataResponse databaseGetTableDataResponse) {
+
+ FlipperArray.Builder columnBuilder = new FlipperArray.Builder();
+ for (String columnName : databaseGetTableDataResponse.columns) {
+ columnBuilder.put(columnName);
+ }
+
+ FlipperArray.Builder rowBuilder = new FlipperArray.Builder();
+ for (List