From 98b5603d34bb99a40b65d04b367454ca7fa07d2b Mon Sep 17 00:00:00 2001 From: AMIT SHEKHAR Date: Sun, 19 Nov 2017 10:15:38 +0530 Subject: [PATCH 01/74] Create CONTRIBUTING.md --- CONTRIBUTING.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..3741d94 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,9 @@ +# Contributing + +1. Fork it! +2. Checkout the development branch: `git checkout development` +3. Create your feature branch: `git checkout -b my-new-feature` +4. Add your changes to the index: `git add .` +5. Commit your changes: `git commit -m 'Add some feature'` +6. Push to the branch: `git push origin my-new-feature` +7. Submit a pull request against the `development` branch From 9cad770e7c1f8e191bcc1d17df0910da0f840d79 Mon Sep 17 00:00:00 2001 From: AMIT SHEKHAR Date: Mon, 20 Nov 2017 11:04:22 +0530 Subject: [PATCH 02/74] Add contribution guide --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 08e43fd..d24f328 100644 --- a/README.md +++ b/README.md @@ -149,5 +149,6 @@ public static void setCustomDatabaseFiles(Context context) { limitations under the License. ``` -### Contributing to Android Debug Database -Just make pull request. You're in! +### Contributing to PRDownloader +All pull requests are welcome, make sure to follow the [contribution guidelines](CONTRIBUTING.md) +when you submit pull request. From 3aa6430c0815d2f6d9ed8216815817c53d869a98 Mon Sep 17 00:00:00 2001 From: AMIT SHEKHAR Date: Mon, 20 Nov 2017 11:04:52 +0530 Subject: [PATCH 03/74] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d24f328..6fda95f 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,6 @@ public static void setCustomDatabaseFiles(Context context) { limitations under the License. ``` -### Contributing to PRDownloader +### Contributing to Android Debug Database All pull requests are welcome, make sure to follow the [contribution guidelines](CONTRIBUTING.md) when you submit pull request. From 7f8879a9bf9b9eb0e3e4ec468a8aa2f641415bd9 Mon Sep 17 00:00:00 2001 From: amitshekhariitbhu Date: Fri, 29 Dec 2017 13:55:13 +0530 Subject: [PATCH 04/74] Update desc for encrypted database --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6fda95f..1bad2fd 100644 --- a/README.md +++ b/README.md @@ -103,11 +103,11 @@ public static void setCustomDatabaseFiles(Context context) { Class debugDB = Class.forName("com.amitshekhar.DebugDB"); Class[] argTypes = new Class[]{HashMap.class}; Method setCustomDatabaseFiles = debugDB.getMethod("setCustomDatabaseFiles", argTypes); - HashMap customDatabaseFiles = new HashMap<>(); + HashMap> customDatabaseFiles = new HashMap<>(); // set your custom database files customDatabaseFiles.put(ExtTestDBHelper.DATABASE_NAME, - new File(context.getFilesDir() + "/" + ExtTestDBHelper.DIR_NAME + - "/" + ExtTestDBHelper.DATABASE_NAME)); + new Pair<>(new File(context.getFilesDir() + "/" + ExtTestDBHelper.DIR_NAME + + "/" + ExtTestDBHelper.DATABASE_NAME), "")); setCustomDatabaseFiles.invoke(null, customDatabaseFiles); } catch (Exception ignore) { From ef4ad89fdfa0b47d88d3906f197262dfd3487215 Mon Sep 17 00:00:00 2001 From: amitshekhariitbhu Date: Fri, 29 Dec 2017 14:04:22 +0530 Subject: [PATCH 05/74] Modify equals() --- app/build.gradle | 2 +- .../src/main/java/com/amitshekhar/server/RequestHandler.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index c5b4dd2..185e01e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -32,7 +32,7 @@ android { } buildTypes { debug { - resValue("string", "PORT_NUMBER", "8081") + resValue("string", "PORT_NUMBER", "8080") resValue("string", "DB_PASSWORD_PERSON", "a_password") } release { diff --git a/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java b/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java index 7161024..9535bd7 100644 --- a/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java +++ b/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java @@ -191,7 +191,7 @@ private String getDBListResponse() { Response response = new Response(); if (mDatabaseFiles != null) { for (HashMap.Entry> entry : mDatabaseFiles.entrySet()) { - String[] dbEntry = { entry.getKey(), entry.getValue().second != "" ? "true" : "false" }; + String[] dbEntry = { entry.getKey(), !entry.getValue().second.equals("") ? "true" : "false" }; response.rows.add(dbEntry); } } From c6d6b0ccbc15c65b36cb7a0c3300f62a6f26a328 Mon Sep 17 00:00:00 2001 From: amitshekhariitbhu Date: Fri, 29 Dec 2017 14:06:52 +0530 Subject: [PATCH 06/74] Remove unused import --- .../java/com/sample/database/PersonDBHelper.java | 7 +++---- debug-db/src/main/java/com/amitshekhar/DebugDB.java | 7 +++---- .../com/amitshekhar/model/TableDataResponse.java | 1 + .../java/com/amitshekhar/server/ClientServer.java | 5 ++--- .../java/com/amitshekhar/server/RequestHandler.java | 13 +++++++------ .../com/amitshekhar/utils/DatabaseFileProvider.java | 4 ---- .../java/com/amitshekhar/utils/DatabaseHelper.java | 7 +++---- 7 files changed, 19 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/com/sample/database/PersonDBHelper.java b/app/src/main/java/com/sample/database/PersonDBHelper.java index f3e390e..a9c33dc 100644 --- a/app/src/main/java/com/sample/database/PersonDBHelper.java +++ b/app/src/main/java/com/sample/database/PersonDBHelper.java @@ -19,10 +19,10 @@ package com.sample.database; -import android.app.Application; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; + import net.sqlcipher.DatabaseUtils; import net.sqlcipher.database.SQLiteDatabase; import net.sqlcipher.database.SQLiteOpenHelper; @@ -110,7 +110,7 @@ public ArrayList getAllPerson() { while (!res.isAfterLast()) { arrayList.add( res.getString(res.getColumnIndex(PERSON_COLUMN_FIRST_NAME)) + " " + - res.getString(res.getColumnIndex(PERSON_COLUMN_LAST_NAME))); + res.getString(res.getColumnIndex(PERSON_COLUMN_LAST_NAME))); res.moveToNext(); } res.close(); @@ -128,8 +128,7 @@ public int count() { } else { return 0; } - } - finally { + } finally { cursor.close(); db.close(); } diff --git a/debug-db/src/main/java/com/amitshekhar/DebugDB.java b/debug-db/src/main/java/com/amitshekhar/DebugDB.java index d0b6ea1..8ec31cc 100644 --- a/debug-db/src/main/java/com/amitshekhar/DebugDB.java +++ b/debug-db/src/main/java/com/amitshekhar/DebugDB.java @@ -28,7 +28,6 @@ import java.io.File; import java.util.HashMap; -import java.util.Map; /** * Created by amitshekhar on 15/11/16. @@ -74,12 +73,12 @@ public static void shutDown() { } } - public static void setCustomDatabaseFiles(HashMap> customDatabaseFiles){ - if(clientServer!=null){ + public static void setCustomDatabaseFiles(HashMap> customDatabaseFiles) { + if (clientServer != null) { clientServer.setCustomDatabaseFiles(customDatabaseFiles); } } - + public static boolean isServerRunning() { return clientServer != null && clientServer.isRunning(); } diff --git a/debug-db/src/main/java/com/amitshekhar/model/TableDataResponse.java b/debug-db/src/main/java/com/amitshekhar/model/TableDataResponse.java index 241bded..a2ffff4 100644 --- a/debug-db/src/main/java/com/amitshekhar/model/TableDataResponse.java +++ b/debug-db/src/main/java/com/amitshekhar/model/TableDataResponse.java @@ -38,6 +38,7 @@ public static class TableInfo { public String title; public boolean isPrimary; } + public static class ColumnData { public String dataType; public Object value; diff --git a/debug-db/src/main/java/com/amitshekhar/server/ClientServer.java b/debug-db/src/main/java/com/amitshekhar/server/ClientServer.java index 89619cc..952e87e 100644 --- a/debug-db/src/main/java/com/amitshekhar/server/ClientServer.java +++ b/debug-db/src/main/java/com/amitshekhar/server/ClientServer.java @@ -34,7 +34,6 @@ import java.net.Socket; import java.net.SocketException; import java.util.HashMap; -import java.util.Map; public class ClientServer implements Runnable { @@ -88,8 +87,8 @@ public void run() { } } - public void setCustomDatabaseFiles(HashMap> customDatabaseFiles){ - mRequestHandler.setCustomDatabaseFiles(customDatabaseFiles); + public void setCustomDatabaseFiles(HashMap> customDatabaseFiles) { + mRequestHandler.setCustomDatabaseFiles(customDatabaseFiles); } public boolean isRunning() { diff --git a/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java b/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java index 9535bd7..ddec8dc 100644 --- a/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java +++ b/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java @@ -21,7 +21,6 @@ import android.content.Context; import android.content.res.AssetManager; -import net.sqlcipher.database.SQLiteDatabase; import android.net.Uri; import android.text.TextUtils; import android.util.Pair; @@ -39,6 +38,8 @@ import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; +import net.sqlcipher.database.SQLiteDatabase; + import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -155,7 +156,7 @@ public void handle(Socket socket) throws IOException { } } - public void setCustomDatabaseFiles(HashMap> customDatabaseFiles){ + public void setCustomDatabaseFiles(HashMap> customDatabaseFiles) { mCustomDatabaseFiles = customDatabaseFiles; } @@ -185,17 +186,17 @@ private void closeDatabase() { private String getDBListResponse() { mDatabaseFiles = DatabaseFileProvider.getDatabaseFiles(mContext); - if(mCustomDatabaseFiles!=null){ + if (mCustomDatabaseFiles != null) { mDatabaseFiles.putAll(mCustomDatabaseFiles); } Response response = new Response(); if (mDatabaseFiles != null) { for (HashMap.Entry> entry : mDatabaseFiles.entrySet()) { - String[] dbEntry = { entry.getKey(), !entry.getValue().second.equals("") ? "true" : "false" }; + String[] dbEntry = {entry.getKey(), !entry.getValue().second.equals("") ? "true" : "false"}; response.rows.add(dbEntry); } } - response.rows.add(new String[] { Constants.APP_SHARED_PREFERENCES, "false" }); + response.rows.add(new String[]{Constants.APP_SHARED_PREFERENCES, "false"}); response.isSuccessful = true; return mGson.toJson(response); } @@ -238,7 +239,7 @@ private String executeQueryAndGetResponse(String route) { if (query != null) { String[] statements = query.split(";"); - for (int i=0; i Date: Mon, 8 Jan 2018 19:38:26 +0530 Subject: [PATCH 07/74] Bump version --- README.md | 2 +- debug-db/debug-db-upload.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1bad2fd..52b7db7 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ ### Using Android Debug Database Library in your application Add this to your app's build.gradle ```groovy -debugCompile 'com.amitshekhar.android:debug-db:1.0.1' +debugCompile 'com.amitshekhar.android:debug-db:1.0.2' ``` Use `debugCompile` so that it will only compile in your debug build and not in your release build. diff --git a/debug-db/debug-db-upload.gradle b/debug-db/debug-db-upload.gradle index 24f61c8..21b4784 100755 --- a/debug-db/debug-db-upload.gradle +++ b/debug-db/debug-db-upload.gradle @@ -24,7 +24,7 @@ def siteUrl = 'https://github.com/amitshekhariitbhu/Android-Debug-Database' def gitUrl = 'https://github.com/amitshekhariitbhu/Android-Debug-Database.git' group = "com.amitshekhar.android" -version = '1.0.1' +version = '1.0.2' install { repositories.mavenInstaller { From e109c647397475a4d146a316132c56435137e6bc Mon Sep 17 00:00:00 2001 From: amitshekhariitbhu Date: Mon, 8 Jan 2018 19:43:04 +0530 Subject: [PATCH 08/74] Change debugCompile to compile --- app/build.gradle | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 185e01e..488fc36 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,6 @@ dependencies { }) compile 'com.android.support:appcompat-v7:25.0.0' testCompile 'junit:junit:4.12' + compile 'net.zetetic:android-database-sqlcipher:3.5.7@aar' debugCompile project(':debug-db') - - debugCompile 'net.zetetic:android-database-sqlcipher:3.5.7@aar' } From 39cb04402f637f92ae2014236ae6fdaa4100fbea Mon Sep 17 00:00:00 2001 From: amitshekhariitbhu Date: Mon, 12 Feb 2018 14:28:20 +0530 Subject: [PATCH 09/74] Update build version and add room db --- app/build.gradle | 10 ++++++---- build.gradle | 4 +++- debug-db/build.gradle | 9 +++++---- gradle/wrapper/gradle-wrapper.properties | 2 +- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 488fc36..7a1b962 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,12 +20,12 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 25 - buildToolsVersion "24.0.3" + compileSdkVersion 27 + buildToolsVersion '25.0.3' defaultConfig { applicationId "com.sample" minSdkVersion 15 - targetSdkVersion 25 + targetSdkVersion 27 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -47,8 +47,10 @@ dependencies { androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) - compile 'com.android.support:appcompat-v7:25.0.0' + compile 'com.android.support:appcompat-v7:27.0.0' testCompile 'junit:junit:4.12' compile 'net.zetetic:android-database-sqlcipher:3.5.7@aar' + compile "android.arch.persistence.room:runtime:1.0.0" + annotationProcessor "android.arch.persistence.room:compiler:1.0.0" debugCompile project(':debug-db') } diff --git a/build.gradle b/build.gradle index 43bc990..ba230a0 100644 --- a/build.gradle +++ b/build.gradle @@ -22,9 +22,10 @@ buildscript { repositories { jcenter() + google() } dependencies { - classpath 'com.android.tools.build:gradle:2.2.0' + classpath 'com.android.tools.build:gradle:2.3.3' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' @@ -36,6 +37,7 @@ buildscript { allprojects { repositories { jcenter() + google() } } diff --git a/debug-db/build.gradle b/debug-db/build.gradle index 93a8738..7e64d75 100644 --- a/debug-db/build.gradle +++ b/debug-db/build.gradle @@ -20,12 +20,12 @@ apply plugin: 'com.android.library' android { - compileSdkVersion 25 - buildToolsVersion "24.0.3" + compileSdkVersion 27 + buildToolsVersion '25.0.3' defaultConfig { - minSdkVersion 9 - targetSdkVersion 25 + minSdkVersion 14 + targetSdkVersion 27 versionCode 1 versionName "1.0" @@ -50,6 +50,7 @@ dependencies { testCompile 'junit:junit:4.12' compile 'com.google.code.gson:gson:2.8.0' compile 'net.zetetic:android-database-sqlcipher:3.5.7@aar' + compile "android.arch.persistence.room:runtime:1.0.0" } //apply from: 'debug-db-upload.gradle' \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 859a010..d103af0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -22,4 +22,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip From e2b0cab42fc43c6d688df6f7eee32386d4645f9b Mon Sep 17 00:00:00 2001 From: anandgaurav10 Date: Mon, 12 Feb 2018 17:46:18 +0530 Subject: [PATCH 10/74] Add example for room database --- .../main/java/com/sample/MainActivity.java | 17 ++++++++++ .../com/sample/database/room/AppDatabase.java | 14 ++++++++ .../java/com/sample/database/room/User.java | 17 ++++++++++ .../sample/database/room/UserDBHelper.java | 30 +++++++++++++++++ .../com/sample/database/room/UserDao.java | 33 +++++++++++++++++++ 5 files changed, 111 insertions(+) create mode 100644 app/src/main/java/com/sample/database/room/AppDatabase.java create mode 100644 app/src/main/java/com/sample/database/room/User.java create mode 100644 app/src/main/java/com/sample/database/room/UserDBHelper.java create mode 100644 app/src/main/java/com/sample/database/room/UserDao.java diff --git a/app/src/main/java/com/sample/MainActivity.java b/app/src/main/java/com/sample/MainActivity.java index d860940..bc3683d 100644 --- a/app/src/main/java/com/sample/MainActivity.java +++ b/app/src/main/java/com/sample/MainActivity.java @@ -31,9 +31,13 @@ import com.sample.database.ContactDBHelper; import com.sample.database.ExtTestDBHelper; import com.sample.database.PersonDBHelper; +import com.sample.database.room.User; +import com.sample.database.room.UserDBHelper; import com.sample.utils.Utils; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; public class MainActivity extends AppCompatActivity { @@ -108,6 +112,19 @@ protected void onCreate(Bundle savedInstanceState) { } } + // Room database + UserDBHelper userDBHelper = new UserDBHelper(getApplicationContext()); + if (userDBHelper.count() == 0) { + List userList = new ArrayList<>(); + for (int i = 0; i < 20; i++) { + User user = new User(); + user.id = (long) (i + 1); + user.name = "user_" + i; + userList.add(user); + } + userDBHelper.insertUser(userList); + } + Utils.setCustomDatabaseFiles(getApplicationContext()); } diff --git a/app/src/main/java/com/sample/database/room/AppDatabase.java b/app/src/main/java/com/sample/database/room/AppDatabase.java new file mode 100644 index 0000000..ef0ce31 --- /dev/null +++ b/app/src/main/java/com/sample/database/room/AppDatabase.java @@ -0,0 +1,14 @@ +package com.sample.database.room; + +import android.arch.persistence.room.Database; +import android.arch.persistence.room.RoomDatabase; + +/** + * Created by anandgaurav on 12/02/18. + */ +@Database(entities = {User.class}, version = 1) +public abstract class AppDatabase extends RoomDatabase { + + public abstract UserDao userDao(); + +} diff --git a/app/src/main/java/com/sample/database/room/User.java b/app/src/main/java/com/sample/database/room/User.java new file mode 100644 index 0000000..7869ce8 --- /dev/null +++ b/app/src/main/java/com/sample/database/room/User.java @@ -0,0 +1,17 @@ +package com.sample.database.room; + +import android.arch.persistence.room.Entity; +import android.arch.persistence.room.PrimaryKey; + +/** + * Created by anandgaurav on 12/02/18. + */ +@Entity(tableName = "users") +public class User { + + @PrimaryKey + public Long id; + + public String name; + +} diff --git a/app/src/main/java/com/sample/database/room/UserDBHelper.java b/app/src/main/java/com/sample/database/room/UserDBHelper.java new file mode 100644 index 0000000..cc7f54b --- /dev/null +++ b/app/src/main/java/com/sample/database/room/UserDBHelper.java @@ -0,0 +1,30 @@ +package com.sample.database.room; + +import android.arch.persistence.room.Room; +import android.content.Context; + +import java.util.List; + +/** + * Created by anandgaurav on 12/02/18. + */ + +public class UserDBHelper { + + private final AppDatabase appDatabase; + + public UserDBHelper(Context context) { + appDatabase = Room.databaseBuilder(context, AppDatabase.class, "User-Database") + .allowMainThreadQueries() + .build(); + } + + public void insertUser(List userList) { + appDatabase.userDao().insertAll(userList); + } + + public int count() { + return appDatabase.userDao().loadAll().size(); + } + +} diff --git a/app/src/main/java/com/sample/database/room/UserDao.java b/app/src/main/java/com/sample/database/room/UserDao.java new file mode 100644 index 0000000..e07b271 --- /dev/null +++ b/app/src/main/java/com/sample/database/room/UserDao.java @@ -0,0 +1,33 @@ +package com.sample.database.room; + +import android.arch.persistence.room.Dao; +import android.arch.persistence.room.Delete; +import android.arch.persistence.room.Insert; +import android.arch.persistence.room.OnConflictStrategy; +import android.arch.persistence.room.Query; + +import java.util.List; + +/** + * Created by anandgaurav on 12/02/18. + */ + +@Dao +public interface UserDao { + + @Query("SELECT * FROM users") + List loadAll(); + + @Query("SELECT * FROM users WHERE id IN (:userIds)") + List loadAllByIds(List userIds); + + @Insert(onConflict = OnConflictStrategy.REPLACE) + void insert(User user); + + @Insert(onConflict = OnConflictStrategy.REPLACE) + void insertAll(List users); + + @Delete + void delete(User user); + +} From 1952173a00c8a8ae8b032a7f1191cd25e1dd17cc Mon Sep 17 00:00:00 2001 From: anandgaurav10 Date: Mon, 12 Feb 2018 17:53:20 +0530 Subject: [PATCH 11/74] Add example for inMemory room database --- app/src/main/java/com/sample/MainActivity.java | 12 ++++++++++++ .../java/com/sample/database/room/UserDBHelper.java | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/app/src/main/java/com/sample/MainActivity.java b/app/src/main/java/com/sample/MainActivity.java index bc3683d..a239570 100644 --- a/app/src/main/java/com/sample/MainActivity.java +++ b/app/src/main/java/com/sample/MainActivity.java @@ -125,6 +125,18 @@ protected void onCreate(Bundle savedInstanceState) { userDBHelper.insertUser(userList); } + // Room inMemory database + if (userDBHelper.countInMemory() == 0) { + List userList = new ArrayList<>(); + for (int i = 0; i < 20; i++) { + User user = new User(); + user.id = (long) (i + 1); + user.name = "in_memory_user_" + i; + userList.add(user); + } + userDBHelper.insertUserInMemory(userList); + } + Utils.setCustomDatabaseFiles(getApplicationContext()); } diff --git a/app/src/main/java/com/sample/database/room/UserDBHelper.java b/app/src/main/java/com/sample/database/room/UserDBHelper.java index cc7f54b..d92a5a3 100644 --- a/app/src/main/java/com/sample/database/room/UserDBHelper.java +++ b/app/src/main/java/com/sample/database/room/UserDBHelper.java @@ -12,19 +12,29 @@ public class UserDBHelper { private final AppDatabase appDatabase; + private final AppDatabase inMemoryAppDatabase; public UserDBHelper(Context context) { appDatabase = Room.databaseBuilder(context, AppDatabase.class, "User-Database") .allowMainThreadQueries() .build(); + inMemoryAppDatabase = Room.inMemoryDatabaseBuilder(context, AppDatabase.class).build(); } public void insertUser(List userList) { appDatabase.userDao().insertAll(userList); } + public void insertUserInMemory(List userList) { + inMemoryAppDatabase.userDao().insertAll(userList); + } + public int count() { return appDatabase.userDao().loadAll().size(); } + public int countInMemory() { + return inMemoryAppDatabase.userDao().loadAll().size(); + } + } From 310a794dc6be61177970685a7fcc0a06b1badc52 Mon Sep 17 00:00:00 2001 From: anandgaurav10 Date: Mon, 12 Feb 2018 17:54:42 +0530 Subject: [PATCH 12/74] Add example for inMemory room database --- app/src/main/java/com/sample/database/room/UserDBHelper.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/sample/database/room/UserDBHelper.java b/app/src/main/java/com/sample/database/room/UserDBHelper.java index d92a5a3..6f854b7 100644 --- a/app/src/main/java/com/sample/database/room/UserDBHelper.java +++ b/app/src/main/java/com/sample/database/room/UserDBHelper.java @@ -18,7 +18,9 @@ public UserDBHelper(Context context) { appDatabase = Room.databaseBuilder(context, AppDatabase.class, "User-Database") .allowMainThreadQueries() .build(); - inMemoryAppDatabase = Room.inMemoryDatabaseBuilder(context, AppDatabase.class).build(); + inMemoryAppDatabase = Room.inMemoryDatabaseBuilder(context, AppDatabase.class) + .allowMainThreadQueries() + .build(); } public void insertUser(List userList) { From 1fc1d233871add9de6776399a0a3ae9edc9c3d85 Mon Sep 17 00:00:00 2001 From: anandgaurav10 Date: Mon, 12 Feb 2018 18:46:11 +0530 Subject: [PATCH 13/74] Provide database instance through interface --- .../amitshekhar/server/RequestHandler.java | 26 ++++---- .../com/amitshekhar/sqlite/DebugSQLiteDB.java | 60 +++++++++++++++++++ .../java/com/amitshekhar/sqlite/SQLiteDB.java | 30 ++++++++++ .../com/amitshekhar/utils/DatabaseHelper.java | 21 ++++--- 4 files changed, 114 insertions(+), 23 deletions(-) create mode 100644 debug-db/src/main/java/com/amitshekhar/sqlite/DebugSQLiteDB.java create mode 100644 debug-db/src/main/java/com/amitshekhar/sqlite/SQLiteDB.java diff --git a/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java b/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java index ddec8dc..e96c3c7 100644 --- a/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java +++ b/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java @@ -29,6 +29,8 @@ import com.amitshekhar.model.RowDataRequest; import com.amitshekhar.model.TableDataResponse; import com.amitshekhar.model.UpdateRowResponse; +import com.amitshekhar.sqlite.DebugSQLiteDB; +import com.amitshekhar.sqlite.SQLiteDB; import com.amitshekhar.utils.Constants; import com.amitshekhar.utils.DatabaseFileProvider; import com.amitshekhar.utils.DatabaseHelper; @@ -60,7 +62,7 @@ public class RequestHandler { private final Gson mGson; private final AssetManager mAssets; private boolean isDbOpened; - private SQLiteDatabase mDatabase; + private SQLiteDB sqLiteDB; private HashMap> mDatabaseFiles; private HashMap> mCustomDatabaseFiles; private String mSelectedDatabase = null; @@ -172,15 +174,15 @@ private void openDatabase(String database) { SQLiteDatabase.loadLibs(mContext); - mDatabase = SQLiteDatabase.openOrCreateDatabase(databaseFile.getAbsolutePath(), password, null); + sqLiteDB = new DebugSQLiteDB(SQLiteDatabase.openOrCreateDatabase(databaseFile.getAbsolutePath(), password, null)); isDbOpened = true; } private void closeDatabase() { - if (mDatabase != null && mDatabase.isOpen()) { - mDatabase.close(); + if (sqLiteDB != null && sqLiteDB.isOpen()) { + sqLiteDB.close(); } - mDatabase = null; + sqLiteDB = null; isDbOpened = false; } @@ -213,7 +215,7 @@ private String getAllDataFromTheTableResponse(String route) { if (isDbOpened) { String sql = "SELECT * FROM " + tableName; - response = DatabaseHelper.getTableData(mDatabase, sql, tableName); + response = DatabaseHelper.getTableData(sqLiteDB, sql, tableName); } else { response = PrefHelper.getAllPrefData(mContext, tableName); } @@ -244,13 +246,13 @@ private String executeQueryAndGetResponse(String route) { String aQuery = statements[i].trim(); first = aQuery.split(" ")[0].toLowerCase(); if (first.equals("select") || first.equals("pragma")) { - TableDataResponse response = DatabaseHelper.getTableData(mDatabase, aQuery, null); + TableDataResponse response = DatabaseHelper.getTableData(sqLiteDB, aQuery, null); data = mGson.toJson(response); if (!response.isSuccessful) { break; } } else { - TableDataResponse response = DatabaseHelper.exec(mDatabase, aQuery); + TableDataResponse response = DatabaseHelper.exec(sqLiteDB, aQuery); data = mGson.toJson(response); if (!response.isSuccessful) { break; @@ -285,7 +287,7 @@ private String getTableListResponse(String route) { mSelectedDatabase = Constants.APP_SHARED_PREFERENCES; } else { openDatabase(database); - response = DatabaseHelper.getAllTableName(mDatabase); + response = DatabaseHelper.getAllTableName(sqLiteDB); mSelectedDatabase = database; } return mGson.toJson(response); @@ -303,7 +305,7 @@ private String addTableDataAndGetResponse(String route) { if (Constants.APP_SHARED_PREFERENCES.equals(mSelectedDatabase)) { response = PrefHelper.addOrUpdateRow(mContext, tableName, rowDataRequests); } else { - response = DatabaseHelper.addRow(mDatabase, tableName, rowDataRequests); + response = DatabaseHelper.addRow(sqLiteDB, tableName, rowDataRequests); } return mGson.toJson(response); } catch (Exception e) { @@ -325,7 +327,7 @@ private String updateTableDataAndGetResponse(String route) { if (Constants.APP_SHARED_PREFERENCES.equals(mSelectedDatabase)) { response = PrefHelper.addOrUpdateRow(mContext, tableName, rowDataRequests); } else { - response = DatabaseHelper.updateRow(mDatabase, tableName, rowDataRequests); + response = DatabaseHelper.updateRow(sqLiteDB, tableName, rowDataRequests); } return mGson.toJson(response); } catch (Exception e) { @@ -348,7 +350,7 @@ private String deleteTableDataAndGetResponse(String route) { if (Constants.APP_SHARED_PREFERENCES.equals(mSelectedDatabase)) { response = PrefHelper.deleteRow(mContext, tableName, rowDataRequests); } else { - response = DatabaseHelper.deleteRow(mDatabase, tableName, rowDataRequests); + response = DatabaseHelper.deleteRow(sqLiteDB, tableName, rowDataRequests); } return mGson.toJson(response); } catch (Exception e) { diff --git a/debug-db/src/main/java/com/amitshekhar/sqlite/DebugSQLiteDB.java b/debug-db/src/main/java/com/amitshekhar/sqlite/DebugSQLiteDB.java new file mode 100644 index 0000000..385934d --- /dev/null +++ b/debug-db/src/main/java/com/amitshekhar/sqlite/DebugSQLiteDB.java @@ -0,0 +1,60 @@ +package com.amitshekhar.sqlite; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.SQLException; + +import net.sqlcipher.database.SQLiteDatabase; + +/** + * Created by anandgaurav on 12/02/18. + */ + +public class DebugSQLiteDB implements SQLiteDB { + + private final SQLiteDatabase database; + + public DebugSQLiteDB(SQLiteDatabase database) { + this.database = database; + } + + @Override + public int delete(String table, String whereClause, String[] whereArgs) { + return database.delete(table, whereClause, whereArgs); + } + + @Override + public boolean isOpen() { + return database.isOpen(); + } + + @Override + public void close() { + database.close(); + } + + @Override + public Cursor rawQuery(String sql, String[] selectionArgs) { + return database.rawQuery(sql, selectionArgs); + } + + @Override + public void execSQL(String sql) throws SQLException { + database.execSQL(sql); + } + + @Override + public long insert(String table, String nullColumnHack, ContentValues values) { + return database.insert(table, nullColumnHack, values); + } + + @Override + public int update(String table, ContentValues values, String whereClause, String[] whereArgs) { + return database.update(table, values, whereClause, whereArgs); + } + + @Override + public int getVersion() { + return database.getVersion(); + } +} diff --git a/debug-db/src/main/java/com/amitshekhar/sqlite/SQLiteDB.java b/debug-db/src/main/java/com/amitshekhar/sqlite/SQLiteDB.java new file mode 100644 index 0000000..df21e4e --- /dev/null +++ b/debug-db/src/main/java/com/amitshekhar/sqlite/SQLiteDB.java @@ -0,0 +1,30 @@ +package com.amitshekhar.sqlite; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.SQLException; + + +/** + * Created by anandgaurav on 12/02/18. + */ + +public interface SQLiteDB { + + int delete(String table, String whereClause, String[] whereArgs); + + boolean isOpen(); + + void close(); + + Cursor rawQuery(String sql, String[] selectionArgs); + + void execSQL(String sql) throws SQLException; + + long insert(String table, String nullColumnHack, ContentValues values); + + int update(String table, ContentValues values, String whereClause, String[] whereArgs); + + int getVersion(); + +} diff --git a/debug-db/src/main/java/com/amitshekhar/utils/DatabaseHelper.java b/debug-db/src/main/java/com/amitshekhar/utils/DatabaseHelper.java index b32f75f..531157a 100644 --- a/debug-db/src/main/java/com/amitshekhar/utils/DatabaseHelper.java +++ b/debug-db/src/main/java/com/amitshekhar/utils/DatabaseHelper.java @@ -20,15 +20,14 @@ package com.amitshekhar.utils; import android.content.ContentValues; +import android.database.Cursor; import android.text.TextUtils; import com.amitshekhar.model.Response; import com.amitshekhar.model.RowDataRequest; import com.amitshekhar.model.TableDataResponse; import com.amitshekhar.model.UpdateRowResponse; - -import net.sqlcipher.Cursor; -import net.sqlcipher.database.SQLiteDatabase; +import com.amitshekhar.sqlite.SQLiteDB; import java.util.ArrayList; import java.util.HashSet; @@ -44,7 +43,7 @@ private DatabaseHelper() { // This class in not publicly instantiable } - public static Response getAllTableName(SQLiteDatabase database) { + public static Response getAllTableName(SQLiteDB database) { Response response = new Response(); Cursor c = database.rawQuery("SELECT name FROM sqlite_master WHERE type='table' OR type='view' ORDER BY name COLLATE NOCASE", null); if (c.moveToFirst()) { @@ -63,7 +62,7 @@ public static Response getAllTableName(SQLiteDatabase database) { return response; } - public static TableDataResponse getTableData(SQLiteDatabase db, String selectQuery, String tableName) { + public static TableDataResponse getTableData(SQLiteDB db, String selectQuery, String tableName) { TableDataResponse tableData = new TableDataResponse(); tableData.isSelectQuery = true; @@ -173,7 +172,7 @@ private static String getQuotedTableName(String tableName) { return String.format("[%s]", tableName); } - private static List getTableInfo(SQLiteDatabase db, String pragmaQuery) { + private static List getTableInfo(SQLiteDB db, String pragmaQuery) { Cursor cursor; try { @@ -219,7 +218,7 @@ private static List getTableInfo(SQLiteDatabase db, } - public static UpdateRowResponse addRow(SQLiteDatabase db, String tableName, + public static UpdateRowResponse addRow(SQLiteDB db, String tableName, List rowDataRequests) { UpdateRowResponse updateRowResponse = new UpdateRowResponse(); @@ -261,7 +260,7 @@ public static UpdateRowResponse addRow(SQLiteDatabase db, String tableName, } - public static UpdateRowResponse updateRow(SQLiteDatabase db, String tableName, List rowDataRequests) { + public static UpdateRowResponse updateRow(SQLiteDB db, String tableName, List rowDataRequests) { UpdateRowResponse updateRowResponse = new UpdateRowResponse(); @@ -316,7 +315,7 @@ public static UpdateRowResponse updateRow(SQLiteDatabase db, String tableName, L } - public static UpdateRowResponse deleteRow(SQLiteDatabase db, String tableName, + public static UpdateRowResponse deleteRow(SQLiteDB db, String tableName, List rowDataRequests) { UpdateRowResponse updateRowResponse = new UpdateRowResponse(); @@ -363,7 +362,7 @@ public static UpdateRowResponse deleteRow(SQLiteDatabase db, String tableName, } - public static TableDataResponse exec(SQLiteDatabase database, String sql) { + public static TableDataResponse exec(SQLiteDB database, String sql) { TableDataResponse tableDataResponse = new TableDataResponse(); tableDataResponse.isSelectQuery = false; try { @@ -387,7 +386,7 @@ public static TableDataResponse exec(SQLiteDatabase database, String sql) { } private static String getTableName(String selectQuery) { - // TODO: 24/4/17 Handle JOIN Query + // TODO: Handle JOIN Query TableNameParser tableNameParser = new TableNameParser(selectQuery); HashSet tableNames = (HashSet) tableNameParser.tables(); From 7de8194c724a78777642fbcf20f9428a1b2746a3 Mon Sep 17 00:00:00 2001 From: anandgaurav10 Date: Mon, 12 Feb 2018 20:02:46 +0530 Subject: [PATCH 14/74] Add support for room inMemory databases --- .../main/java/com/sample/MainActivity.java | 1 + .../sample/database/room/UserDBHelper.java | 6 +- app/src/main/java/com/sample/utils/Utils.java | 17 ++++++ .../main/java/com/amitshekhar/DebugDB.java | 7 +++ .../com/amitshekhar/server/ClientServer.java | 10 ++-- .../amitshekhar/server/RequestHandler.java | 27 +++++++-- .../sqlite/InMemoryDebugSQLiteDB.java | 59 +++++++++++++++++++ .../java/com/amitshekhar/utils/Utils.java | 2 +- 8 files changed, 117 insertions(+), 12 deletions(-) create mode 100644 debug-db/src/main/java/com/amitshekhar/sqlite/InMemoryDebugSQLiteDB.java diff --git a/app/src/main/java/com/sample/MainActivity.java b/app/src/main/java/com/sample/MainActivity.java index a239570..62744d0 100644 --- a/app/src/main/java/com/sample/MainActivity.java +++ b/app/src/main/java/com/sample/MainActivity.java @@ -138,6 +138,7 @@ protected void onCreate(Bundle savedInstanceState) { } Utils.setCustomDatabaseFiles(getApplicationContext()); + Utils.setInMemoryRoomDatabases(userDBHelper.getInMemoryDatabase()); } public void showDebugDbAddress(View view) { diff --git a/app/src/main/java/com/sample/database/room/UserDBHelper.java b/app/src/main/java/com/sample/database/room/UserDBHelper.java index 6f854b7..c406ac7 100644 --- a/app/src/main/java/com/sample/database/room/UserDBHelper.java +++ b/app/src/main/java/com/sample/database/room/UserDBHelper.java @@ -1,5 +1,6 @@ package com.sample.database.room; +import android.arch.persistence.db.SupportSQLiteDatabase; import android.arch.persistence.room.Room; import android.content.Context; @@ -15,7 +16,7 @@ public class UserDBHelper { private final AppDatabase inMemoryAppDatabase; public UserDBHelper(Context context) { - appDatabase = Room.databaseBuilder(context, AppDatabase.class, "User-Database") + appDatabase = Room.databaseBuilder(context, AppDatabase.class, "User.db") .allowMainThreadQueries() .build(); inMemoryAppDatabase = Room.inMemoryDatabaseBuilder(context, AppDatabase.class) @@ -39,4 +40,7 @@ public int countInMemory() { return inMemoryAppDatabase.userDao().loadAll().size(); } + public SupportSQLiteDatabase getInMemoryDatabase() { + return inMemoryAppDatabase.getOpenHelper().getWritableDatabase(); + } } diff --git a/app/src/main/java/com/sample/utils/Utils.java b/app/src/main/java/com/sample/utils/Utils.java index de75ca8..f71e60d 100644 --- a/app/src/main/java/com/sample/utils/Utils.java +++ b/app/src/main/java/com/sample/utils/Utils.java @@ -19,6 +19,7 @@ package com.sample.utils; +import android.arch.persistence.db.SupportSQLiteDatabase; import android.content.Context; import android.util.Pair; import android.widget.Toast; @@ -71,4 +72,20 @@ public static void setCustomDatabaseFiles(Context context) { } } + public static void setInMemoryRoomDatabases(SupportSQLiteDatabase... database) { + if (BuildConfig.DEBUG) { + try { + Class debugDB = Class.forName("com.amitshekhar.DebugDB"); + Class[] argTypes = new Class[]{HashMap.class}; + HashMap inMemoryDatabases = new HashMap<>(); + // set your inMemory database + inMemoryDatabases.put("InMemoryOne.db", database[0]); + Method setRoomInMemoryDatabase = debugDB.getMethod("setInMemoryRoomDatabases", argTypes); + setRoomInMemoryDatabase.invoke(null, inMemoryDatabases); + } catch (Exception ignore) { + + } + } + } + } diff --git a/debug-db/src/main/java/com/amitshekhar/DebugDB.java b/debug-db/src/main/java/com/amitshekhar/DebugDB.java index 8ec31cc..b6463e1 100644 --- a/debug-db/src/main/java/com/amitshekhar/DebugDB.java +++ b/debug-db/src/main/java/com/amitshekhar/DebugDB.java @@ -19,6 +19,7 @@ package com.amitshekhar; +import android.arch.persistence.db.SupportSQLiteDatabase; import android.content.Context; import android.util.Log; import android.util.Pair; @@ -79,6 +80,12 @@ public static void setCustomDatabaseFiles(HashMap> cu } } + public static void setInMemoryRoomDatabases(HashMap databases) { + if (clientServer != null) { + clientServer.setInMemoryRoomDatabases(databases); + } + } + public static boolean isServerRunning() { return clientServer != null && clientServer.isRunning(); } diff --git a/debug-db/src/main/java/com/amitshekhar/server/ClientServer.java b/debug-db/src/main/java/com/amitshekhar/server/ClientServer.java index 952e87e..552e5a3 100644 --- a/debug-db/src/main/java/com/amitshekhar/server/ClientServer.java +++ b/debug-db/src/main/java/com/amitshekhar/server/ClientServer.java @@ -24,6 +24,7 @@ */ +import android.arch.persistence.db.SupportSQLiteDatabase; import android.content.Context; import android.util.Log; import android.util.Pair; @@ -40,13 +41,10 @@ public class ClientServer implements Runnable { private static final String TAG = "ClientServer"; private final int mPort; - + private final RequestHandler mRequestHandler; private boolean mIsRunning; - private ServerSocket mServerSocket; - private final RequestHandler mRequestHandler; - public ClientServer(Context context, int port) { mRequestHandler = new RequestHandler(context); mPort = port; @@ -91,6 +89,10 @@ public void setCustomDatabaseFiles(HashMap> customDat mRequestHandler.setCustomDatabaseFiles(customDatabaseFiles); } + public void setInMemoryRoomDatabases(HashMap databases) { + mRequestHandler.setInMemoryRoomDatabases(databases); + } + public boolean isRunning() { return mIsRunning; } diff --git a/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java b/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java index e96c3c7..d8b71f3 100644 --- a/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java +++ b/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java @@ -19,6 +19,7 @@ package com.amitshekhar.server; +import android.arch.persistence.db.SupportSQLiteDatabase; import android.content.Context; import android.content.res.AssetManager; import android.net.Uri; @@ -30,6 +31,7 @@ import com.amitshekhar.model.TableDataResponse; import com.amitshekhar.model.UpdateRowResponse; import com.amitshekhar.sqlite.DebugSQLiteDB; +import com.amitshekhar.sqlite.InMemoryDebugSQLiteDB; import com.amitshekhar.sqlite.SQLiteDB; import com.amitshekhar.utils.Constants; import com.amitshekhar.utils.DatabaseFileProvider; @@ -66,6 +68,7 @@ public class RequestHandler { private HashMap> mDatabaseFiles; private HashMap> mCustomDatabaseFiles; private String mSelectedDatabase = null; + private HashMap mRoomInMemoryDatabases = new HashMap<>(); public RequestHandler(Context context) { mContext = context; @@ -162,6 +165,10 @@ public void setCustomDatabaseFiles(HashMap> customDat mCustomDatabaseFiles = customDatabaseFiles; } + public void setInMemoryRoomDatabases(HashMap databases) { + mRoomInMemoryDatabases = databases; + } + private void writeServerError(PrintStream output) { output.println("HTTP/1.0 500 Internal Server Error"); output.flush(); @@ -169,12 +176,14 @@ private void writeServerError(PrintStream output) { private void openDatabase(String database) { closeDatabase(); - File databaseFile = mDatabaseFiles.get(database).first; - String password = mDatabaseFiles.get(database).second; - - SQLiteDatabase.loadLibs(mContext); - - sqLiteDB = new DebugSQLiteDB(SQLiteDatabase.openOrCreateDatabase(databaseFile.getAbsolutePath(), password, null)); + if (mRoomInMemoryDatabases.containsKey(database)) { + sqLiteDB = new InMemoryDebugSQLiteDB(mRoomInMemoryDatabases.get(database)); + } else { + File databaseFile = mDatabaseFiles.get(database).first; + String password = mDatabaseFiles.get(database).second; + SQLiteDatabase.loadLibs(mContext); + sqLiteDB = new DebugSQLiteDB(SQLiteDatabase.openOrCreateDatabase(databaseFile.getAbsolutePath(), password, null)); + } isDbOpened = true; } @@ -198,6 +207,12 @@ private String getDBListResponse() { response.rows.add(dbEntry); } } + if (mRoomInMemoryDatabases != null) { + for (HashMap.Entry entry : mRoomInMemoryDatabases.entrySet()) { + String[] dbEntry = {entry.getKey(), "false"}; + response.rows.add(dbEntry); + } + } response.rows.add(new String[]{Constants.APP_SHARED_PREFERENCES, "false"}); response.isSuccessful = true; return mGson.toJson(response); diff --git a/debug-db/src/main/java/com/amitshekhar/sqlite/InMemoryDebugSQLiteDB.java b/debug-db/src/main/java/com/amitshekhar/sqlite/InMemoryDebugSQLiteDB.java new file mode 100644 index 0000000..8e0081d --- /dev/null +++ b/debug-db/src/main/java/com/amitshekhar/sqlite/InMemoryDebugSQLiteDB.java @@ -0,0 +1,59 @@ +package com.amitshekhar.sqlite; + +import android.arch.persistence.db.SupportSQLiteDatabase; +import android.content.ContentValues; +import android.database.Cursor; +import android.database.SQLException; + +/** + * Created by anandgaurav on 12/02/18. + */ + +public class InMemoryDebugSQLiteDB implements SQLiteDB { + + private final SupportSQLiteDatabase database; + + public InMemoryDebugSQLiteDB(SupportSQLiteDatabase database) { + this.database = database; + } + + @Override + public int delete(String table, String whereClause, String[] whereArgs) { + return database.delete(table, whereClause, whereArgs); + } + + @Override + public boolean isOpen() { + return database.isOpen(); + } + + @Override + public void close() { + // no ops + } + + @Override + public Cursor rawQuery(String sql, String[] selectionArgs) { + return database.query(sql, selectionArgs); + } + + @Override + public void execSQL(String sql) throws SQLException { + database.execSQL(sql); + } + + @Override + public long insert(String table, String nullColumnHack, ContentValues values) { + return database.insert(table, 0, values); + } + + @Override + public int update(String table, ContentValues values, String whereClause, String[] whereArgs) { + return database.update(table, 0, values, whereClause, whereArgs); + } + + @Override + public int getVersion() { + return database.getVersion(); + } +} diff --git a/debug-db/src/main/java/com/amitshekhar/utils/Utils.java b/debug-db/src/main/java/com/amitshekhar/utils/Utils.java index 6446b97..394d711 100644 --- a/debug-db/src/main/java/com/amitshekhar/utils/Utils.java +++ b/debug-db/src/main/java/com/amitshekhar/utils/Utils.java @@ -84,7 +84,7 @@ public static byte[] loadContent(String fileName, AssetManager assetManager) thr } public static byte[] getDatabase(String selectedDatabase, HashMap> databaseFiles) { - if (TextUtils.isEmpty(selectedDatabase)) { + if (TextUtils.isEmpty(selectedDatabase) || !databaseFiles.containsKey(selectedDatabase)) { return null; } From 98d89bdd56c63323d62fc150d0383288f97d67d4 Mon Sep 17 00:00:00 2001 From: anandgaurav10 Date: Mon, 12 Feb 2018 21:35:01 +0530 Subject: [PATCH 15/74] Disable download for inMemoryDatabase --- debug-db/src/main/assets/app.js | 25 +++++++++++++------ .../amitshekhar/server/RequestHandler.java | 6 ++--- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/debug-db/src/main/assets/app.js b/debug-db/src/main/assets/app.js index 581879b..0c82d88 100644 --- a/debug-db/src/main/assets/app.js +++ b/debug-db/src/main/assets/app.js @@ -70,12 +70,13 @@ function getDBList() { for(var count = 0; count < dbList.length; count++){ var dbName = dbList[count][0]; var isEncrypted = dbList[count][1]; + var isDownloadable = dbList[count][2]; var dbAttribute = isEncrypted == "true" ? ' ' : ""; if(dbName.indexOf("journal") == -1){ - $("#db-list").append("" + dbName + dbAttribute + ""); + $("#db-list").append("" + dbName + dbAttribute + ""); if(!isSelectionDone){ isSelectionDone = true; - $('#db-list').find('a').trigger('click'); + $('#db-list').find('a').trigger('click'); } } } @@ -85,7 +86,7 @@ function getDBList() { } var lastTableName = getHashValue('table'); -function openDatabaseAndGetTableList(db) { +function openDatabaseAndGetTableList(db, isDownloadable) { if("APP_SHARED_PREFERENCES" == db) { $('#run-query').removeClass('active'); @@ -97,10 +98,16 @@ function openDatabaseAndGetTableList(db) { } else { $('#run-query').removeClass('disabled'); $('#run-query').addClass('active'); - $('#selected-db-info').removeClass('disabled'); - $('#selected-db-info').addClass('active'); + if("true" == isDownloadable) { + $('#selected-db-info').removeClass('disabled'); + $('#selected-db-info').addClass('active'); + $("#selected-db-info").text("Export Selected Database : "+db); + } else { + $('#selected-db-info').removeClass('active'); + $('#selected-db-info').addClass('disabled'); + $("#selected-db-info").text("Selected Database : "+db); + } isDatabaseSelected = true; - $("#selected-db-info").text("Export Selected Database : "+db); } @@ -110,7 +117,11 @@ function openDatabaseAndGetTableList(db) { var tableList = result.rows; var dbVersion = result.dbVersion; if("APP_SHARED_PREFERENCES" != db) { - $("#selected-db-info").text("Export Selected Database : "+db +" Version : "+dbVersion); + if("true" == isDownloadable) { + $("#selected-db-info").text("Export Selected Database : "+db +" Version : "+dbVersion); + } else { + $("#selected-db-info").text("Selected Database : "+db +" Version : "+dbVersion); + } } $('#table-list').empty() for(var count = 0; count < tableList.length; count++){ diff --git a/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java b/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java index d8b71f3..aa0d9b5 100644 --- a/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java +++ b/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java @@ -203,17 +203,17 @@ private String getDBListResponse() { Response response = new Response(); if (mDatabaseFiles != null) { for (HashMap.Entry> entry : mDatabaseFiles.entrySet()) { - String[] dbEntry = {entry.getKey(), !entry.getValue().second.equals("") ? "true" : "false"}; + String[] dbEntry = {entry.getKey(), !entry.getValue().second.equals("") ? "true" : "false", "true"}; response.rows.add(dbEntry); } } if (mRoomInMemoryDatabases != null) { for (HashMap.Entry entry : mRoomInMemoryDatabases.entrySet()) { - String[] dbEntry = {entry.getKey(), "false"}; + String[] dbEntry = {entry.getKey(), "false", "false"}; response.rows.add(dbEntry); } } - response.rows.add(new String[]{Constants.APP_SHARED_PREFERENCES, "false"}); + response.rows.add(new String[]{Constants.APP_SHARED_PREFERENCES, "false", "false"}); response.isSuccessful = true; return mGson.toJson(response); } From 4fb23289b402dbd50241d9080c6a7d020ccd395b Mon Sep 17 00:00:00 2001 From: anandgaurav10 Date: Mon, 12 Feb 2018 21:41:36 +0530 Subject: [PATCH 16/74] Add desc for room inMemory db and bump version --- README.md | 24 +++++++++++++++++-- app/src/main/java/com/sample/utils/Utils.java | 2 +- debug-db/debug-db-upload.gradle | 2 +- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 52b7db7..1f5608b 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ ### Using Android Debug Database Library in your application Add this to your app's build.gradle ```groovy -debugCompile 'com.amitshekhar.android:debug-db:1.0.2' +debugCompile 'com.amitshekhar.android:debug-db:1.0.3' ``` Use `debugCompile` so that it will only compile in your debug build and not in your release build. @@ -95,7 +95,7 @@ public static void showDebugDBAddressLogToast(Context context) { ``` ### Adding custom database files -As this library is auto-initialize, if you want to add custom database files, add the following method and call +As this library is auto-initialize, if you want to debug custom database files, add the following method and call ```java public static void setCustomDatabaseFiles(Context context) { if (BuildConfig.DEBUG) { @@ -116,6 +116,26 @@ public static void setCustomDatabaseFiles(Context context) { } ``` +### Adding InMemory Room databases +As this library is auto-initialize, if you want to debug inMemory Room databases, add the following method and call +```java +public static void setInMemoryRoomDatabases(SupportSQLiteDatabase... database) { + if (BuildConfig.DEBUG) { + try { + Class debugDB = Class.forName("com.amitshekhar.DebugDB"); + Class[] argTypes = new Class[]{HashMap.class}; + HashMap inMemoryDatabases = new HashMap<>(); + // set your inMemory databases + inMemoryDatabases.put("InMemoryOne.db", database[0]); + Method setRoomInMemoryDatabase = debugDB.getMethod("setInMemoryRoomDatabases", argTypes); + setRoomInMemoryDatabase.invoke(null, inMemoryDatabases); + } catch (Exception ignore) { + + } + } +} +``` + ### Find this project useful ? :heart: * Support it by clicking the :star: button on the upper right of this page. :v: diff --git a/app/src/main/java/com/sample/utils/Utils.java b/app/src/main/java/com/sample/utils/Utils.java index f71e60d..e4edb4d 100644 --- a/app/src/main/java/com/sample/utils/Utils.java +++ b/app/src/main/java/com/sample/utils/Utils.java @@ -78,7 +78,7 @@ public static void setInMemoryRoomDatabases(SupportSQLiteDatabase... database) { Class debugDB = Class.forName("com.amitshekhar.DebugDB"); Class[] argTypes = new Class[]{HashMap.class}; HashMap inMemoryDatabases = new HashMap<>(); - // set your inMemory database + // set your inMemory databases inMemoryDatabases.put("InMemoryOne.db", database[0]); Method setRoomInMemoryDatabase = debugDB.getMethod("setInMemoryRoomDatabases", argTypes); setRoomInMemoryDatabase.invoke(null, inMemoryDatabases); diff --git a/debug-db/debug-db-upload.gradle b/debug-db/debug-db-upload.gradle index 21b4784..05d6d12 100755 --- a/debug-db/debug-db-upload.gradle +++ b/debug-db/debug-db-upload.gradle @@ -24,7 +24,7 @@ def siteUrl = 'https://github.com/amitshekhariitbhu/Android-Debug-Database' def gitUrl = 'https://github.com/amitshekhariitbhu/Android-Debug-Database.git' group = "com.amitshekhar.android" -version = '1.0.2' +version = '1.0.3' install { repositories.mavenInstaller { From 38fd1c96b34fbe2763dd98d38c369271c4740ed6 Mon Sep 17 00:00:00 2001 From: AMIT SHEKHAR Date: Mon, 12 Feb 2018 22:07:39 +0530 Subject: [PATCH 17/74] Create CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1 @@ + From c8a5862e4e2998283015655a976674c03104db37 Mon Sep 17 00:00:00 2001 From: AMIT SHEKHAR Date: Mon, 12 Feb 2018 22:25:55 +0530 Subject: [PATCH 18/74] Update CHANGELOG.md --- CHANGELOG.md | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b13789..552d668 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,72 @@ +Change Log +========== +Version 1.0.3 *(2018-02-12)* +---------------------------- + +* New: Add support for debugging inMemory Room Database +* Add example for Room Database + + +Version 1.0.2 *(2018-01-08)* +---------------------------- + +* New: Add SqlCipher support +* New: List table name in non case sensitive alphabetical order + + +Version 1.0.1 *(2017-06-23)* +---------------------------- + +* New: Add insert row feature +* New: Add custom database files support +* New: Add method for checking isServerRunning +* New: Add pragma support +* Fix: Minor bug fixes + + +Version 1.0.0 *(2017-02-08)* +---------------------------- + +* New: Add support for editing database directly +* New: Delete rows directly +* New: Delete Shared Pref +* New: Edit shared preferences directly +* New: Add standard code for checking databases files +* New: Complete offline support +* Refactor library code + + +Version 0.5.0 *(2017-01-21)* +---------------------------- + +* New: Export DB +* New: Method to get DB version +* Fix: Fix prouard issue and other minor issues + + +Version 0.4.0 *(2016-11-29)* +---------------------------- + +* Optimizations +* Fix: Fix few minor bugs + + +Version 0.3.0 *(2016-11-23)* +---------------------------- + +* New: Add support for custom port +* Fix: Fix few minor bugs + + +Version 0.2.0 *(2016-11-17)* +---------------------------- + +* New: Add method for getting address +* Fix: Fix few minor bugs + + +Version 0.1.0 *(2016-11-16)* +---------------------------- + +Initial release. From 319e25b056268d26001a0ce7ae1db7108dc18853 Mon Sep 17 00:00:00 2001 From: Antoine GRAVELOT Date: Wed, 14 Feb 2018 12:26:04 +0100 Subject: [PATCH 19/74] Use 'debugImplementation' instead 'debugCompile' Compile dependencies for 'debug' sources (deprecated: use 'debugImplementation' instead). https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_separation --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1f5608b..1bd3bd9 100644 --- a/README.md +++ b/README.md @@ -36,10 +36,10 @@ ### Using Android Debug Database Library in your application Add this to your app's build.gradle ```groovy -debugCompile 'com.amitshekhar.android:debug-db:1.0.3' +debugImplementation 'com.amitshekhar.android:debug-db:1.0.3' ``` -Use `debugCompile` so that it will only compile in your debug build and not in your release build. +Use `debugImplementation` so that it will only compile in your debug build and not in your release build. That’s all, just start the application, you will see in the logcat an entry like follows : From b0dda3a9b5b2fca256203047807982ffe5b3b109 Mon Sep 17 00:00:00 2001 From: AMIT SHEKHAR Date: Wed, 21 Feb 2018 22:08:40 +0530 Subject: [PATCH 20/74] Add line for debug room inMemory db --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1bd3bd9..fa8c233 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ * Search in your data. * Sort data. * Download database. +* Debug Room inMemory database. ### All these features work without rooting your device -> No need of rooted device From 9e39cbd8e0e69e5b0f4c70d4a44b6b94a194a9ec Mon Sep 17 00:00:00 2001 From: wrong Date: Wed, 28 Mar 2018 11:42:49 -0300 Subject: [PATCH 21/74] Add filesystem navigation (seen from the app perspective) --- build.gradle | 2 +- debug-db/build.gradle | 2 + debug-db/src/main/AndroidManifest.xml | 3 + debug-db/src/main/assets/app.js | 26 ++- .../src/main/assets/images/application.png | Bin 0 -> 464 bytes debug-db/src/main/assets/images/code.png | Bin 0 -> 603 bytes debug-db/src/main/assets/images/css.png | Bin 0 -> 618 bytes debug-db/src/main/assets/images/db.png | Bin 0 -> 579 bytes debug-db/src/main/assets/images/directory.png | Bin 0 -> 537 bytes debug-db/src/main/assets/images/doc.png | Bin 0 -> 651 bytes debug-db/src/main/assets/images/file.png | Bin 0 -> 294 bytes debug-db/src/main/assets/images/film.png | Bin 0 -> 653 bytes debug-db/src/main/assets/images/flash.png | Bin 0 -> 582 bytes .../src/main/assets/images/folder_open.png | Bin 0 -> 583 bytes debug-db/src/main/assets/images/html.png | Bin 0 -> 734 bytes debug-db/src/main/assets/images/java.png | Bin 0 -> 633 bytes debug-db/src/main/assets/images/linux.png | Bin 0 -> 668 bytes debug-db/src/main/assets/images/music.png | Bin 0 -> 385 bytes debug-db/src/main/assets/images/pdf.png | Bin 0 -> 591 bytes debug-db/src/main/assets/images/php.png | Bin 0 -> 538 bytes debug-db/src/main/assets/images/picture.png | Bin 0 -> 606 bytes debug-db/src/main/assets/images/ppt.png | Bin 0 -> 588 bytes debug-db/src/main/assets/images/psd.png | Bin 0 -> 856 bytes debug-db/src/main/assets/images/ruby.png | Bin 0 -> 626 bytes debug-db/src/main/assets/images/script.png | Bin 0 -> 859 bytes debug-db/src/main/assets/images/spinner.gif | Bin 0 -> 2530 bytes debug-db/src/main/assets/images/txt.png | Bin 0 -> 342 bytes debug-db/src/main/assets/images/xls.png | Bin 0 -> 663 bytes debug-db/src/main/assets/images/zip.png | Bin 0 -> 386 bytes debug-db/src/main/assets/index.html | 26 ++- debug-db/src/main/assets/jquery.easing.js | 146 ++++++++++++ debug-db/src/main/assets/jqueryFileTree.css | 91 ++++++++ debug-db/src/main/assets/jqueryFileTree.js | 95 ++++++++ .../com/amitshekhar/DebugDBInitProvider.java | 13 ++ .../RequestPermissionsActivity.java | 28 +++ .../amitshekhar/server/RequestHandler.java | 219 +++++++++++++++++- 36 files changed, 647 insertions(+), 4 deletions(-) create mode 100644 debug-db/src/main/assets/images/application.png create mode 100644 debug-db/src/main/assets/images/code.png create mode 100644 debug-db/src/main/assets/images/css.png create mode 100644 debug-db/src/main/assets/images/db.png create mode 100644 debug-db/src/main/assets/images/directory.png create mode 100644 debug-db/src/main/assets/images/doc.png create mode 100644 debug-db/src/main/assets/images/file.png create mode 100644 debug-db/src/main/assets/images/film.png create mode 100644 debug-db/src/main/assets/images/flash.png create mode 100644 debug-db/src/main/assets/images/folder_open.png create mode 100644 debug-db/src/main/assets/images/html.png create mode 100644 debug-db/src/main/assets/images/java.png create mode 100644 debug-db/src/main/assets/images/linux.png create mode 100644 debug-db/src/main/assets/images/music.png create mode 100644 debug-db/src/main/assets/images/pdf.png create mode 100644 debug-db/src/main/assets/images/php.png create mode 100644 debug-db/src/main/assets/images/picture.png create mode 100644 debug-db/src/main/assets/images/ppt.png create mode 100644 debug-db/src/main/assets/images/psd.png create mode 100644 debug-db/src/main/assets/images/ruby.png create mode 100644 debug-db/src/main/assets/images/script.png create mode 100644 debug-db/src/main/assets/images/spinner.gif create mode 100644 debug-db/src/main/assets/images/txt.png create mode 100644 debug-db/src/main/assets/images/xls.png create mode 100644 debug-db/src/main/assets/images/zip.png create mode 100644 debug-db/src/main/assets/jquery.easing.js create mode 100644 debug-db/src/main/assets/jqueryFileTree.css create mode 100644 debug-db/src/main/assets/jqueryFileTree.js create mode 100644 debug-db/src/main/java/com/amitshekhar/RequestPermissionsActivity.java diff --git a/build.gradle b/build.gradle index ba230a0..ac91450 100644 --- a/build.gradle +++ b/build.gradle @@ -25,7 +25,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.3' + classpath 'com.android.tools.build:gradle:3.0.1' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' diff --git a/debug-db/build.gradle b/debug-db/build.gradle index 7e64d75..dbf8b6e 100644 --- a/debug-db/build.gradle +++ b/debug-db/build.gradle @@ -51,6 +51,8 @@ dependencies { compile 'com.google.code.gson:gson:2.8.0' compile 'net.zetetic:android-database-sqlcipher:3.5.7@aar' compile "android.arch.persistence.room:runtime:1.0.0" + compile 'com.android.support:appcompat-v7:27.0.0' + } //apply from: 'debug-db-upload.gradle' \ No newline at end of file diff --git a/debug-db/src/main/AndroidManifest.xml b/debug-db/src/main/AndroidManifest.xml index bb8d603..95c0bb2 100644 --- a/debug-db/src/main/AndroidManifest.xml +++ b/debug-db/src/main/AndroidManifest.xml @@ -22,6 +22,7 @@ + + + diff --git a/debug-db/src/main/assets/app.js b/debug-db/src/main/assets/app.js index 0c82d88..8d3b897 100644 --- a/debug-db/src/main/assets/app.js +++ b/debug-db/src/main/assets/app.js @@ -21,8 +21,32 @@ $( document ).ready(function() { $(this).addClass('selected'); }); - + $('#fileTree').fileTree({ root: '/', script: 'fileTree', folderEvent: 'click', expandSpeed: 750, collapseSpeed: 750, multiFolder: false }, function(file) { + console.log(file); + $.ajax({url: "getFileData?fileName="+file, success: function(result){ + $("#file-data").empty(); + $("#file-data").append("
" + hexdump(result) + "
"); + }}); + }); }); +function hexdump(buffer, blockSize) { + blockSize = blockSize || 16; + var lines = []; + var hex = "0123456789ABCDEF"; + for (var b = 0; b < buffer.length; b += blockSize) { + var block = buffer.slice(b, Math.min(b + blockSize, buffer.length)); + var addr = ("0000" + b.toString(16)).slice(-4); + var codes = block.split('').map(function (ch) { + var code = ch.charCodeAt(0); + return " " + hex[(0xF0 & code) >> 4] + hex[0x0F & code]; + }).join(""); + codes += "   ".repeat(blockSize - block.length); + var chars = block.replace(/[\x00-\x1F\x20]/g, '.'); + chars += " ".repeat(blockSize - block.length); + lines.push(addr + "  " + codes + "  " + chars); + } + return lines.join("
"); +} var isDatabaseSelected = true; diff --git a/debug-db/src/main/assets/images/application.png b/debug-db/src/main/assets/images/application.png new file mode 100644 index 0000000000000000000000000000000000000000..1dee9e366094e87db68c606d0522d72d4b939818 GIT binary patch literal 464 zcmV;>0WbcEP)8e6`gpm!y1M!N^ZV(=IC*t) z{^;nqJv-tM$9J1L2QJ2DN!#51=1_l@G`2=6e0lehL%sic%`_4--LFM}IF!KzJCseW zq1I3__Z40|e?qyK1__gzP(qrBf-G7SQbQ`#Lw94WVe(o`qg+f4hy;Qju)q#I(9{`% zQmAGomzhQ!b|gq>KqL@IkO~$=Koi}a$u6d07kiS}NoYVMJjAeZpaB*;wwcDdEbK@K zNP;B7RzhQ|H9AlUO<`J>m1(5R)Pb-iLBb@7Jp)}LHdAb-VVgYxVoTzGoqu{~a>6uj zeqCRFI9pC#h09bGwy9;oHcp6(RB%jeY^F=Ll!S+9JkVe4nDG7tJMQiP0000^~*-1fljz_B$LUvK}k?BNXe#Y!m=zM!!V#}8bncK5m;8VP zw86G*RI63?Cd%b9bX|ueNlZ|wR6rj|r_)VIP@r2imh3?SN+^{|kY%~8B{maJ@F*OK z&VH9LwOeGt#DRjj0~v~8`>iO7!Ybi;zE$va`A^T#yW`y44;k^#O~K5*jD=qcUhPSc zvyy~q;5H_1WT1l~cqje9yfa+l!hu6xjdOJ8s;8E^+=QQ$tw p?%p!Hy#YapB=@+^9(46X{{RQg%9y;OKjr`c002ovPDHLkV1g7l326WT literal 0 HcmV?d00001 diff --git a/debug-db/src/main/assets/images/css.png b/debug-db/src/main/assets/images/css.png new file mode 100644 index 0000000000000000000000000000000000000000..f907e44b3330e3ed1763e42746a2943234adb94d GIT binary patch literal 618 zcmV-w0+s!VP)Z@gK0z$EtPV#+`^OH`R@cE-cuE&_!D)SZGxmQxeobP_Zwq zMEgi6ePN45N`|V1so0uE8^}1xw8s;VM%Ai@7} z2-&Cyvez_-O4?6uv{zTaj|YeYEk34i~K@`8YW2g{x* zc;7z3lItpVy_et{Z-ZZ)<@*%{l7Ao8mu@V7*gz<_1##mwW*%LEwCdzNsVLYx2*T-J z#HeQ*_a=R~KDdVNk$EVgAIRl$oQi_(`_IrdJciDpH|Xe{K-YsMtc!cRnFi$qzsr4z z5*$;ecov%3->1{YNy6-Gf(Ecy&_I$CjI#laeuE+S120^|Vjsf)W&i*H07*qoM6N<$ Ef)^>s`J(VpX#y^kqzQ;#=2x({YMw9Q&ndHT&`BD$#%Ql?{+)-OuSA`r}MWJ zVg+2Gc(GW}a=BERPNy^;kEz$|38dTYlFQ{%5S!g@|8f8D_!Nu9_Ni2glF1}xG8xi! zorc39&F6EPOeWOt_XS`W2H_Bo$MXugy}SEctJQj=(TLXTHL(jRXfzs>NF=0SHk;94 zF!&HjdZNX(3U3;LY64IMX__Xv%_wjLC!J2`0Jw?X=zPK$C$`&dYPDKaC={e16bcE@ zgun^<0k;ak*=xLE)@(Lqu~MmsFoMCLY&0Qog`NO(h@kyxaA%EbwJLy8sU*Vi`~52K zX0wrqW;_LmMq@evX4iAM9Od(Q0eHP$1%L|xAh@vrqB`HPQLon}f3aAka=9!3hr=O- z5F9`#J_7Jhah=U(4RjaRhkS4Xkk98kDz-`i!r|~~AQ1TFcDw(@<8g{aBE)l)PNxNE zI(RPyc>9e{@WGSMU%i7*v{!&P$WLz25)0oc=Dl-yy%xYZAm4b-rttL7UjR#%`#j_F R;_mx(K@^6+>g^d@v4;gkbWsEoXE%32*i1tcpTNXd5CcIl)ECgqz|2rE6EW}s7R?kl za1q`0GCkMruC6-2LANtwVlsgzsp4?{@7$`KBv!G66>Vie3h?3OmEEkjwdLG0PgLVi z`!N((f$A@n17Ldj#`};0I3@iHJ5M{#IZz|UIYRm4(!uV7eYIYIwQf&}_2J~}>pQ^n z6o8--^T(=hkBNQ_k{-_GWE;FMW7!p}f{NG3nHZ{D5<3d8&tLh%a4AqqnjMkr3m&fkMdECD3N5}Unig5wy40;>lo4j~k+e}v)` zR6)J8Mk*u=SpB`p6o)7j?S0T@9?bz#m@l>gc*zk__|*!FMcHwP!gwLJvS~9c0px8E zWhkjP zNW|QGv-YFNLN^qH@tJycPNG5ti6B7;r4mEr#lr@*T8*M85D`{ZR^BWwF23T<%MYIh zdC)S*p=|xk^!~H=+HSZ183~y8v4|mYmZxt&)5{{~>J`>E223Q5>T$=~mtA71q-jdG z+eJhOAyBW^0k9Gk1+rX8)zFx((CG^&tDY>6XaS~Fy!WJON|Gdujg5^~Vzt@o%BcYLiNiTQSD`zL^ociBz_>bDlpw3kriQ@Z`bVsGz-_6N>$&gTDiKDTKR^ z-hB*tHa^>!oD~5TK^0UK5rZ}RBm50Bv}S-yA%s=Ha5RYb{)!z2N&$&64gfhybBu8p lh~_|?8^bu;BRYt{<}Yrwd83Y=s?Goa002ovPDHLkV1l%3CP4rI literal 0 HcmV?d00001 diff --git a/debug-db/src/main/assets/images/file.png b/debug-db/src/main/assets/images/file.png new file mode 100644 index 0000000000000000000000000000000000000000..8b8b1ca0000bc8fa8d0379926736029f8fabe364 GIT binary patch literal 294 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^zbpD<_bdI{u9mbgZg z1m~xflqVLYGB~E>C#5QQ<|d}62BjvZR2H60wE-&H;pyTSqH(@-Vl>|&1p(LP>kg~E zYiz5X^`c$+%8#zC{u)yfe-5 zmgid={Z3k(ERKCKrE7DF;=x4^O+ pzO8rLO8p|Ip=x)jHOtWj`bJBmKdh_V<`47(gQu&X%Q~loCIFbEay|e6 literal 0 HcmV?d00001 diff --git a/debug-db/src/main/assets/images/film.png b/debug-db/src/main/assets/images/film.png new file mode 100644 index 0000000000000000000000000000000000000000..b0ce7bb198a3b268bd634d2b26e9b710f3797d37 GIT binary patch literal 653 zcmV;80&@L{P)WO3(`_cf+b25@DJ#zdQm}8GzWtq2-QnZ8W6mB^kfeK5f%S{ zUW%tGMCwrwic~ZrQcG=4f?5bkV+3dRk8hw6bk~y$KX#b!y*J4EJ~>;dRASqrSu;ZpM>?P}K~6AT zWv6Dmq?v&9LdXC(m%WCO6ma_di$R(v$@ad_>@R41N3N5lSJq9@6CGhX84-$%Xrd_6 z;){?{E|Ytt5$S-&Au>t4wDlIxdkfe-a22LMj``McG};r8@{GsRPm*+8fFey6C)@ifDBXVyTw(N@Xd41b45OFg6x_QA zpwLiigyy~cVoPxW^r~C7ZQpr%>1$*HKmv~AY-qJw4;gUecS--wnqslISSS=^KA&Ic n@BK|Onfz#3R%n{$a)0j^sqv5F(1NTL00000NkvXXu0mjf3S}fX literal 0 HcmV?d00001 diff --git a/debug-db/src/main/assets/images/flash.png b/debug-db/src/main/assets/images/flash.png new file mode 100644 index 0000000000000000000000000000000000000000..5769120b1b6d38019b505c9167498ea199212cc1 GIT binary patch literal 582 zcmV-M0=fN(P)OL3{+!MJa`3kv~Q#QfQ%c z)1s}QE<_XaYBG;IuRF=td#+}fi4h(6HgoUyJLi0t(*dA^B)%@8kkG&bdM5P5^Z5WF z%d%>m^SbN0XeV)wbUOXn5Ag#A$gJx+7-OCkMM1S%MWIlTkbFLmOeW(&n&wUd&;`>p zVcRy$Z{K0=?SpNnP^;BYEEXleFbq(UY&LrXX$6qkJ~)8+b{=jj3HEXds;Z(?D%}}L zX3`39&dy=Zyar!ehA}e>w)(*vrCct{PI9^2Jpj&OZS8<3-@{0(gNv%1{)zAiLY+_^ zl}e>Ofd4&#Irj#7>=o=Uhv5IJ@?sN0^J|(WL2Uun$4}si6}TG-s3T#p&6GE<<2W)O zf{^Y2HlO#*QDvTp3v&d@;8*}aUC4lisG9(w7@d5Y8y)}U#FwCkqp*Mcgme4{&gGRf zlBfd`nF9cQBKB2_L{F8G2)7pAf$i)Ds`|}-c>pc^LRW{w4SQ)3N^BbZx)6BlCZts! zKph%`(m#xg-q3I7=(us;9<)*2%iuQ1J`oV3gU6V~T}^JU5714JN33&GwEEru0d}Uo U{MPL+lmGw#07*qoM6N<$f^vibe*gdg literal 0 HcmV?d00001 diff --git a/debug-db/src/main/assets/images/folder_open.png b/debug-db/src/main/assets/images/folder_open.png new file mode 100644 index 0000000000000000000000000000000000000000..4e3548352fc4a82e91ebc7e79907565e40aae457 GIT binary patch literal 583 zcmV-N0=WH&P)tH$n+Bzw$Yklv;`N=nc;V( z;&KN-35T@gWRBGYeR_y$`8BZn{9n>tC)YRdy9cbn)8Q%P55|`*)XQ7AP<#e&a6A$G zs{)EE$ezXf)f=5ALOICxp0jR!e;wEEZgZGsRUZ-h>_^9-(AanOoA53^Oy2?&k5kk} z?afPEzOxMFZ*s5?tr|9&QDTtD0JKnEUBuwz(q0E3;No;>T`j*QjDE}-H4^67sukD9 z^-tZq)fps7^kHQIm*y8axDHaac(>L_z>J-M4kBj1UrI?JsT;`QylT2EJnsJT{hN;C#tgf#9krG=I>5!<*aE1_(spcgF}<`n4i zJi-}^6UUeU4jUFwdCiVPDm%`Zx^UBa8J(mnR6wEgz^}o8;)M*Y(@l_!Kfv)}4+NuM zaPXE50z)r)9=D=SR|RIqfQ^j}Hu!fzMeQBo+@PZk1G8hOw|vBTvkx`HM)Xe9q3xao z@`p0`NO!2904FHSLA6E@Y-O6zH$DQzvq@aHsz}}<(!v(Z_+EodX%R&NZW75g+nENo zV0020rxE^;7d!067AN>6*+&YLp$9uH6F-=In`XC{Cn%+o|5)b&boEPr02w@|P*oGm QmjD0&07*qoM6N<$g78X0Q~&?~ literal 0 HcmV?d00001 diff --git a/debug-db/src/main/assets/images/java.png b/debug-db/src/main/assets/images/java.png new file mode 100644 index 0000000000000000000000000000000000000000..b7bfcd15fb2f7ce658185bd7ef4729815e2f18b9 GIT binary patch literal 633 zcmV-<0*3vGP)DAD_Iw@kR+3LLZX?3 zyhGkEvUzKYKpu0v_u>?yY~y}#nAbh$p6}dm6adQqK)FzXa=E-qP)ntfS|}9Md_I4Y z%jHh8*{qt$WYlyzt)^0`-M<7XmC7CwTlIP!6`0meW$j(o%*9*fiupyDJZx7&n@&K>z*>QCDhTzTp zTlnzwp?s_A3C!nn*-|JJ`k^?T&hB6^z;e07X0ws&YPAxuvwwlMjasdSU@%xy91e#I zzZYAh^udB=a16G~?^>-EC_Ew4q~Q1a4;7oucH0!-LK+*Y)he3JCK`a7P}Mc?vlj7AM;VkzMEhOwnj`hl1MBj6s4Nlz_+^kH@of;eX>V_G&2` Tt35or00000NkvXXu0mjf&s-qj literal 0 HcmV?d00001 diff --git a/debug-db/src/main/assets/images/linux.png b/debug-db/src/main/assets/images/linux.png new file mode 100644 index 0000000000000000000000000000000000000000..52699bfee0ce434356c6a9576f32fb6633b01866 GIT binary patch literal 668 zcmV;N0%QG&P)Q{U6)pC|tRy7$(5JQ<@7eB8yk=XcNf-aBIe#;8c_B$^=N z{-Iq&o3%O}V4~G($=zcP(LI|+6dq{?rby~MXwJQ*=!bOvl%?k zYY;jP^@M_k03MHL+-9?_3W5MN=moFW3xmPHU=-4Bw;62MrIhg_lwHEsv)V9U4x>+9cG2kIz8fWo`WyMMfz zdg-)p!<(hFR{VYSDJHEJn09O@#)%q0l?GUg9eS2~vKPUtd+=ak5lWLd-jI=;cjEf# zt$1;~?G!t@s+VLwL=P+Ks;E z!Jkh#NeohG;&02OFD7^EYklCE>?a@fNhGaV ftv%qM$TQzJ6;XjO8erVL00000NkvXXu0mjfw}q7O literal 0 HcmV?d00001 diff --git a/debug-db/src/main/assets/images/pdf.png b/debug-db/src/main/assets/images/pdf.png new file mode 100644 index 0000000000000000000000000000000000000000..8f8095e46fa4965700afe1f9d065d8a37b101676 GIT binary patch literal 591 zcmV-V0~O9lw>B8WRlD)Gm}Jrz31u-X&&gn2lvjs=i{7nIaL6v2==uw+8Lcs(8j27 z;|c`rmSv@Lx!heopGP^^Ieb3f=R!%Lpp$}iMS-&P3EJ)s48wrJ_Ni0~k|c47D2nj= z{jS6bt|kFpFf|p5cM`_&0Zh|`rfEp0(}=}lT#(6RpzAsUfxv^LSYX>WlAaN$>)*J5 z0#sE+JRUD8iT9*fz{)_^7@6P&!sEjTcD+I9Z4YjT1`wH@fV{cEvneYGFU%maIEU2s55&K(LixD|{p-uiS@?KNj zk-Go8G$hH6g002ovPDHLkV1hVj1#|!a literal 0 HcmV?d00001 diff --git a/debug-db/src/main/assets/images/php.png b/debug-db/src/main/assets/images/php.png new file mode 100644 index 0000000000000000000000000000000000000000..7868a25945cd5e5cb7daaca9591927511ca65c0f GIT binary patch literal 538 zcmV+#0_FXQP)vYep8SaFV10Q$h+;hIUPX_=v5b}%>Tm<(&j1&5;I!55C)oN0s(P%ZB zP3Q#ahfpXKWF@S?jm4U#fv)QovMhrriclyNs6-G12#3R##4PSZ0VY(dRWJ;Lwuq{# zAW0Gwi$yA^R4RZ!;W+L`f&%x{=D^VK#BBWL4Ys{;*!A7Q;!=dN<&D8*GzGaF4`hV4 zDbY0{NrMX>ZqF=0((gR5-zL$kC*b)!fwu{Euru|XrG<$^n#@)7i_>rCmRxnDq>$Y%gJaCkRd|tE*a2x05Pe!I^e13o69#&RQZ36s0 zB=O|K2Yi(jsMqThn}9t?f5E-)L^naZ+db$&%M$!bCdm=jv7?t_lB?3&%Ltq(>ESw? c;MI421LCcoDG!2@;{X5v07*qoM6N<$f`UZt7XSbN literal 0 HcmV?d00001 diff --git a/debug-db/src/main/assets/images/picture.png b/debug-db/src/main/assets/images/picture.png new file mode 100644 index 0000000000000000000000000000000000000000..4a158fef7e0da8fd19525f574f2c4966443866cf GIT binary patch literal 606 zcmV-k0-^nhP)Q2rnAt>LM%-F zK|rtwgcU)}7x~z1Hrcs5bH*ZO$!>xO8K#?==bZPQ_ecnV>#P`H`QzGaRhd62G_&rC zTLU$c7_x*nFP_dW#Q+*);mMHE?j)HexK784D4x9l_tfpz2$@1y}9rkF+ zI+J5NMWeZyObc!d+rUc=>D+uOdAOg#%+Ej6h+wn5^xPmVVH*Eu446Y0A_@ zo$rlds-+sL10DbHU2HvUSp%6 z*n}iP63IK?dpo;h@sj9~pcxo;VVTc-XLiP@DgefqE#NE=@oyUd-&HjLpsLIuSFXV-EMck)oQ(A`s%*^&wf0(rNiNHsU%=0Rw;WC z(kbc37l6fo`-0uR!pYkYv8U^3?nsh^@pw!K0TH3uYyx1_2>|JbXPmfskJ|1YAw9w! z9`N)1^Aesr;y5Nr5-ODn)oOL|CGi}f9!&iVwpK$khlIX10X$H6^A_stBJqvLhU$?V`QXqKme*s~gVDJ4A;LTs_e15jhc1;By a82kqHEPVYFAD2!500006nP)PbXFR5;6x zQ`<|^Q5@e>|3Fq06ckj13SKAzrBX7=2LmNa=VjAGbC_kBmo!b(h0(lh&dtlb&FNfD zmuquN(K1t)x?D|-M01+g{cLIqvEiq)K|Q2K51*IA`CQKDe7`gRw4E?o66yPi8&Nn? z6l%~H1nUQ%T@&i^1O5KKw~aW)k2+%HG368DWXx)cVb)1u?vucz;=oXT3q3jhU%Im| z1?(s=#*a)g@)(;l>H@5fX<;>n14d_9M|`nnP-6AN3z#%q4DzF(Nk5nWM_|14<{lEc zYPuy4KilYl*ag-mA8dVe2Tbuq#03kAEX>O@@Lksg>YW6tl(V+B2dCW}0wgkfx|Qv4 z-D>azy5k)pmaZ6BhrO`cMMsS?2x;x!C{kX=N`E6pB`Ii0bgLj_hNq!8%(hX_#U?!j zKiGk=Hh5$0c^KwfGl^?D=47dOtiAw2KNAm$$b9o7%%}<>OK>*QNXr%9w1k|LfQ{U2 zSbTi2+))I}Pzx-B&9LZXSZvF~o2CeGlzvc4uVc3ME*4%1(7-xD(S|uUCaD1_sK#7S ztY9n_un1WWgXlK28Rs!v7K;_~=D2b`P734*5Qt!^h`~~a5OuM~Hfisl25mar^a_}} zg_v*2Bt^;WEyLcAGWiUsw{`FkNI_Ti5|L(1@j^*TA*!PfgVy)tDpxOv#%F0dOfNa0 zdc|1jYs3&aBQ0Zq#{l)Z8sgGaTq&(WaY+Uil^l#q(oh+BaNO|=WH5X?GktSNWOaL* z*ZNp}GN_(f2a)O)VOE@kDJ~t8MZuVBOvi$ni}tLGC}Zpk**1wJva900hs{igrph=+ z+^OPVd@W&MNyEpqoQ;kge+VO-3U?f3JDTsf%0@_u%bjJIsY%3=wv>=dpyM%#>wcRc z+$m+(U9dwMf67h5I&QPOO-M5_z-8e1J$HL#g&=&d>pz$2H|k=JCX$xdMNxZS(e|#C2>JN4>y}l*tQ*E7zP@R2CCJnkW?xa6bgk%(hgtZ z0=~d?U3i`+Mvi4!&~+WPT1^NX#{u6&QIx+DE(oR{&T5&-ovF?@wGw)P&AtpHZa|G%V*GUUqL@@!d4V$`8=##4)ytY959JG zdc&Kho)&AL70^i z!PEmeeDWCB-UbK(*4JST44^tV2z_J(dn~+vBMJT97_7rzFio=~XczIv?PQ5$v%u~y zu(bteXb5I1h2zCV{Jc2~V{{yzZipgsP6;k264$*#5q?GzCm|CPa9CKqm4b116h3Pu z?+%Cm52plC8|5P0@igf2GV1KkCfk{Zecu=G@VNrf>s%g9c5D%@cfxVb6$nY`1IW=4 zt10QqSps_2JLp0f3I0j0u>#qA;v!+T))KEbCg|mo3q0pG{OR}p0fPds8+K~d>Hq)$ M07*qoM6N<$g1S2e3jhEB literal 0 HcmV?d00001 diff --git a/debug-db/src/main/assets/images/script.png b/debug-db/src/main/assets/images/script.png new file mode 100644 index 0000000000000000000000000000000000000000..63fe6ceff5bfcedb9670279d4bb8d25807f6ecee GIT binary patch literal 859 zcmV-h1El5>jf6w4x#gTU%_MMNlkNp$oSbvBp&uHw9M;u0-4@=t5BI zP6Hx#-C_{5RMJ z0_P+Xkumexn8%)S+Y)#l(gR;YJP<6#1-=jjK0LONWPdJQIR8uK1HpvVIxBIQ2ztt+ zqoEx_X9S%QGMe=~(k#sebCL-an)%CR%a7YtUOQUgv+G>~?N~XSWhx=? z@$fx}0MB;$`JWcQ-Re{XV~5|{DvU(#*+NF*g)j^qk#b~G9_O!i*y&mZVZ=a3;Go(K z`DkskYn56Nhu+k@1Ke*uY|x zI&k6j$JfNe_a{GH%=n2rZOz$Z8R9V?Pe36hIk}jo+A-`;dt9vyvBu#Xm@veu&@v`| zzt%mwc_$nd0-sMVx2d)b0!MqGxmfCumx7yB#nIUWvA{!HOMfslMyW1iV&nY>zxwyj z8^JfLN|kT z4m^Q1mhO(_r4w@`V?H=YNkOf(i&bHT3Auc3bryK1_{hDSetLoLN{VLB^78ULiNFy^ zkUqqG$fjVkJj5tfWkOn|P5`HVEp5@-mGnc0wvJGHC=+39MC2TWT#i?t*~fNch*he_ zgtS^8dH$(KlW)EF1b4Fzv~?&0IQaNdg;W5&{t&Bmg9&N1-rBBr_;Rg8ekw^mn;@T# zlS{|Rq+-Nlg18i%UY;i|q1NnSwf>I@85#4U4002ovPDHLkV1mEDi4_0< literal 0 HcmV?d00001 diff --git a/debug-db/src/main/assets/images/spinner.gif b/debug-db/src/main/assets/images/spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..85b99d46b9911ba53a792d716d9f688f3bb3c784 GIT binary patch literal 2530 zcmb8xdrVVT90%}oAH9#>bK6p?cF>G=h0@JhTLdf!(gIdN1QeV+L;)2XD9^zNN@xod zwcvQjRvj8}y3J)yFv=3A2@i*l5Fc|hs2f3R;^QAN{$aL%*eO}IEP(~izxV$0$@lmB zp0hT=YKq8{00}@xHO(cjNl%Njq^=3k8whNA8mRstISPn@&imc_Tf_3@%d4xaj~qF2 z`t<3uXU|@|c=5`WE2E>Mw{G3KfB*i(#Kf~_&tAQH_4e)C>6t?woVxLBCtLOMhuB(t zGPLP@xf$T%5Zyz3iOuKI^?SO*zNFJ?wrL@=YKNznIpw!L(PPjKYQ7%}eBCama_c91pIJ1~(b1Kal`fZSz8V+OJY|r^QHxE6+WMoDB(^~{XdW=!#+uo_ zaF>U{k0Hho~yN%NrSyV`soxiBvd>cj#}5&$KDA)3{S^?ffCPkeDi5Vo>T8sy}Wj_Hf}N$J0Hz& zv|)_<-f+~-Hi-jgzIkG5vY*T|>7-8J0U*@$d8~ScFqA)f4R7yaDBs3j(<2>@V?*ZD zy6{Vy+~KLc_m5He=9(fsy~0c&4a-*{&i)i(02j`@8?In8dBM!Y(Znu4jtn@VMj|y5 zrTNa5ki}vtEiGN(#07>Rk#HaVI5ZB4@@yk%765mBN(yieTXgm{oK}vXn!h0oZG3gj;>>W?MbZnG6Q_cX~h&<^+R)8g9 zSd!4lZEm4K#ma0{aTpMHv%wr`1VF@rrKl5H7Hyn$;=;nh3JVMEcKbqhXnqO(3z}-# zu=v1OV7q%OY`*dVI>Y}97i4s2yl$S zmSto?fO|=u8h;vSPKY2zqaEs;LlhJgv~%aqwzjtUie_4FUNiT>r-& zOO#{4cBb6f^ zov;AUp?UAne>@_GHz5+(221Xlve$OHK)tvvi!~l<%)zr4PhU@ z(CY^us$O5!${6Pje5gy?*WtA2j>{<;$_PvVQNKa&69IX)a|Fw5>C&=WQ6s37gabVh SjHs`-`=fdj^ILtus=om5Wbpz3 literal 0 HcmV?d00001 diff --git a/debug-db/src/main/assets/images/txt.png b/debug-db/src/main/assets/images/txt.png new file mode 100644 index 0000000000000000000000000000000000000000..813f712f726c935f9adf8d2f2dd0d7683791ef11 GIT binary patch literal 342 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^zbpD<_bdI{u9mbgZg z1m~xflqVLYGB~E>C#5QQ<|d}62BjvZR2H60wE-%6;pyTSA|c6o&@eC9QG)Hj&ExYL zO&oVL^)+cM^qd@ApywS>pwx0H@RDN}hq;7mU-SKczYQ-hnrr=;iDAQMZQ+*g=YOM= z!QlMQEn7FbaD->uKAYgo_j9)W&$$zS*W9}m(ey0q$&7l-XEWO0Y(9M=SnhLbwy;d>@~SY$Ku*0xPvIOQeV1x7u_z-2-X>_74(yfh7C znXL|3GZ+d2`3re2hs?MK^@R5;6x zlTS!gQ5431_q{u#M2 zg&W%y6a}>qj1Z|7Vu&-DW6d~k-n;jnHsjb-q#u0C^W!_5^C=MlKq<8oNCQ6qS00!X z5eI;XP=g!^f}j{hku}E1zZ?XCjE;`p19k(Rh%^AQQ54xysU+ocx$c#f61Z4HnT#3u~FR(3>BnZniMIF4DouI8Hi4u>cAK%EN)5PO(ip3(% zIgBx+QYirR){Z8QwV$9Z(Mpt=L-Or3#bf-G@66}txq0yc*T(zNTBDT0T8rO^JeNbSI-Tzf5!pBioy4NwAN^?iN#{;fH1Jke4Xa`^fR8m z%h6dq%xX)S?7`zae))(Xst^Scp6B8FejQW?RLTM8@0=vnnntuRGBM2dpo>gbCnTD= z^<;=JuqdSf@O>Z8^XdR?s+KEfhDdB_#ahFj^giCtzT(s8kA$AViyTqaAR;KGaLzUU z<=GqA4bRwpX|IG~*x>pZ!@zLr`XQ`od>m(`;jz|M_*1GDO#$7;n74ppb8=eiqh760 x0yt}J1#p`gw$`o!R{d7zU9~!Un@nJV{4bstt4Au+Up@c;002ovPDHLkV1kWhGjjj{ literal 0 HcmV?d00001 diff --git a/debug-db/src/main/assets/images/zip.png b/debug-db/src/main/assets/images/zip.png new file mode 100644 index 0000000000000000000000000000000000000000..fd4bbccdf1643f4ff5022fbc59b82546e259317e GIT binary patch literal 386 zcmV-|0e$|7P)_QM!1S$Bhw4w+iRuFWf;tfR6D%SMJrb+tx zC9R6{2>Ou6#juIy6u(I?|;&Owi$sRB4^20apB5xE2 z#B9XekY66S6lzfCL!eEQRgo0LokTA55@Y#%_wN!T Android Debug Database + @@ -32,6 +33,8 @@ + + @@ -39,7 +42,7 @@ - + @@ -156,6 +159,27 @@
Data Updated Successfully
+ +
+ +
+
+
Filesystem
+
+
+
+
+
+ +
+
+
Data
+
+
+
+
+
+ diff --git a/debug-db/src/main/assets/jquery.easing.js b/debug-db/src/main/assets/jquery.easing.js new file mode 100644 index 0000000..9e4c736 --- /dev/null +++ b/debug-db/src/main/assets/jquery.easing.js @@ -0,0 +1,146 @@ +/* + * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/ + * + * Uses the built In easIng capabilities added In jQuery 1.1 + * to offer multiple easIng options + * + * Copyright (c) 2007 George Smith + * Licensed under the MIT License: + * http://www.opensource.org/licenses/mit-license.php + */ + +// t: current time, b: begInnIng value, c: change In value, d: duration +jQuery.easing['jswing'] = jQuery.easing['swing']; + +jQuery.extend( jQuery.easing, +{ + def: 'easeOutQuad', + swing: function (x, t, b, c, d) { + //alert(jQuery.easing.default); + return jQuery.easing[jQuery.easing.def](x, t, b, c, d); + }, + easeInQuad: function (x, t, b, c, d) { + return c*(t/=d)*t + b; + }, + easeOutQuad: function (x, t, b, c, d) { + return -c *(t/=d)*(t-2) + b; + }, + easeInOutQuad: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t + b; + return -c/2 * ((--t)*(t-2) - 1) + b; + }, + easeInCubic: function (x, t, b, c, d) { + return c*(t/=d)*t*t + b; + }, + easeOutCubic: function (x, t, b, c, d) { + return c*((t=t/d-1)*t*t + 1) + b; + }, + easeInOutCubic: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t*t + b; + return c/2*((t-=2)*t*t + 2) + b; + }, + easeInQuart: function (x, t, b, c, d) { + return c*(t/=d)*t*t*t + b; + }, + easeOutQuart: function (x, t, b, c, d) { + return -c * ((t=t/d-1)*t*t*t - 1) + b; + }, + easeInOutQuart: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t*t*t + b; + return -c/2 * ((t-=2)*t*t*t - 2) + b; + }, + easeInQuint: function (x, t, b, c, d) { + return c*(t/=d)*t*t*t*t + b; + }, + easeOutQuint: function (x, t, b, c, d) { + return c*((t=t/d-1)*t*t*t*t + 1) + b; + }, + easeInOutQuint: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b; + return c/2*((t-=2)*t*t*t*t + 2) + b; + }, + easeInSine: function (x, t, b, c, d) { + return -c * Math.cos(t/d * (Math.PI/2)) + c + b; + }, + easeOutSine: function (x, t, b, c, d) { + return c * Math.sin(t/d * (Math.PI/2)) + b; + }, + easeInOutSine: function (x, t, b, c, d) { + return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b; + }, + easeInExpo: function (x, t, b, c, d) { + return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b; + }, + easeOutExpo: function (x, t, b, c, d) { + return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; + }, + easeInOutExpo: function (x, t, b, c, d) { + if (t==0) return b; + if (t==d) return b+c; + if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b; + return c/2 * (-Math.pow(2, -10 * --t) + 2) + b; + }, + easeInCirc: function (x, t, b, c, d) { + return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b; + }, + easeOutCirc: function (x, t, b, c, d) { + return c * Math.sqrt(1 - (t=t/d-1)*t) + b; + }, + easeInOutCirc: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b; + return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b; + }, + easeInElastic: function (x, t, b, c, d) { + var s=1.70158;var p=0;var a=c; + if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; + if (a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; + }, + easeOutElastic: function (x, t, b, c, d) { + var s=1.70158;var p=0;var a=c; + if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; + if (a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b; + }, + easeInOutElastic: function (x, t, b, c, d) { + var s=1.70158;var p=0;var a=c; + if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5); + if (a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; + return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; + }, + easeInBack: function (x, t, b, c, d, s) { + if (s == undefined) s = 1.70158; + return c*(t/=d)*t*((s+1)*t - s) + b; + }, + easeOutBack: function (x, t, b, c, d, s) { + if (s == undefined) s = 1.70158; + return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; + }, + easeInOutBack: function (x, t, b, c, d, s) { + if (s == undefined) s = 1.70158; + if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; + return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; + }, + easeInBounce: function (x, t, b, c, d) { + return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b; + }, + easeOutBounce: function (x, t, b, c, d) { + if ((t/=d) < (1/2.75)) { + return c*(7.5625*t*t) + b; + } else if (t < (2/2.75)) { + return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; + } else if (t < (2.5/2.75)) { + return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; + } else { + return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; + } + }, + easeInOutBounce: function (x, t, b, c, d) { + if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b; + return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b; + } +}); \ No newline at end of file diff --git a/debug-db/src/main/assets/jqueryFileTree.css b/debug-db/src/main/assets/jqueryFileTree.css new file mode 100644 index 0000000..b1d887a --- /dev/null +++ b/debug-db/src/main/assets/jqueryFileTree.css @@ -0,0 +1,91 @@ +UL.jqueryFileTree { + font-family: Verdana, sans-serif; + font-size: 11px; + line-height: 18px; + padding: 0px; + margin: 0px; +} + +UL.jqueryFileTree LI { + list-style: none; + padding: 0px; + padding-left: 20px; + margin: 0px; + white-space: nowrap; +} + +UL.jqueryFileTree A { + color: #333; + text-decoration: none; + display: block; + padding: 0px 2px; +} + +UL.jqueryFileTree A:hover { + background: #BDF; +} + +/* Core Styles */ +.jqueryFileTree LI.directory { background: url(images/directory.png) left top no-repeat; } +.jqueryFileTree LI.expanded { background: url(images/folder_open.png) left top no-repeat; } +.jqueryFileTree LI.file { background: url(images/file.png) left top no-repeat; } +.jqueryFileTree LI.wait { background: url(images/spinner.gif) left top no-repeat; } +/* File Extensions*/ +.jqueryFileTree LI.ext_3gp { background: url(images/film.png) left top no-repeat; } +.jqueryFileTree LI.ext_afp { background: url(images/code.png) left top no-repeat; } +.jqueryFileTree LI.ext_afpa { background: url(images/code.png) left top no-repeat; } +.jqueryFileTree LI.ext_asp { background: url(images/code.png) left top no-repeat; } +.jqueryFileTree LI.ext_aspx { background: url(images/code.png) left top no-repeat; } +.jqueryFileTree LI.ext_avi { background: url(images/film.png) left top no-repeat; } +.jqueryFileTree LI.ext_bat { background: url(images/application.png) left top no-repeat; } +.jqueryFileTree LI.ext_bmp { background: url(images/picture.png) left top no-repeat; } +.jqueryFileTree LI.ext_c { background: url(images/code.png) left top no-repeat; } +.jqueryFileTree LI.ext_cfm { background: url(images/code.png) left top no-repeat; } +.jqueryFileTree LI.ext_cgi { background: url(images/code.png) left top no-repeat; } +.jqueryFileTree LI.ext_com { background: url(images/application.png) left top no-repeat; } +.jqueryFileTree LI.ext_cpp { background: url(images/code.png) left top no-repeat; } +.jqueryFileTree LI.ext_css { background: url(images/css.png) left top no-repeat; } +.jqueryFileTree LI.ext_doc { background: url(images/doc.png) left top no-repeat; } +.jqueryFileTree LI.ext_exe { background: url(images/application.png) left top no-repeat; } +.jqueryFileTree LI.ext_gif { background: url(images/picture.png) left top no-repeat; } +.jqueryFileTree LI.ext_fla { background: url(images/flash.png) left top no-repeat; } +.jqueryFileTree LI.ext_h { background: url(images/code.png) left top no-repeat; } +.jqueryFileTree LI.ext_htm { background: url(images/html.png) left top no-repeat; } +.jqueryFileTree LI.ext_html { background: url(images/html.png) left top no-repeat; } +.jqueryFileTree LI.ext_jar { background: url(images/java.png) left top no-repeat; } +.jqueryFileTree LI.ext_jpg { background: url(images/picture.png) left top no-repeat; } +.jqueryFileTree LI.ext_jpeg { background: url(images/picture.png) left top no-repeat; } +.jqueryFileTree LI.ext_js { background: url(images/script.png) left top no-repeat; } +.jqueryFileTree LI.ext_lasso { background: url(images/code.png) left top no-repeat; } +.jqueryFileTree LI.ext_log { background: url(images/txt.png) left top no-repeat; } +.jqueryFileTree LI.ext_m4p { background: url(images/music.png) left top no-repeat; } +.jqueryFileTree LI.ext_mov { background: url(images/film.png) left top no-repeat; } +.jqueryFileTree LI.ext_mp3 { background: url(images/music.png) left top no-repeat; } +.jqueryFileTree LI.ext_mp4 { background: url(images/film.png) left top no-repeat; } +.jqueryFileTree LI.ext_mpg { background: url(images/film.png) left top no-repeat; } +.jqueryFileTree LI.ext_mpeg { background: url(images/film.png) left top no-repeat; } +.jqueryFileTree LI.ext_ogg { background: url(images/music.png) left top no-repeat; } +.jqueryFileTree LI.ext_pcx { background: url(images/picture.png) left top no-repeat; } +.jqueryFileTree LI.ext_pdf { background: url(images/pdf.png) left top no-repeat; } +.jqueryFileTree LI.ext_php { background: url(images/php.png) left top no-repeat; } +.jqueryFileTree LI.ext_png { background: url(images/picture.png) left top no-repeat; } +.jqueryFileTree LI.ext_ppt { background: url(images/ppt.png) left top no-repeat; } +.jqueryFileTree LI.ext_psd { background: url(images/psd.png) left top no-repeat; } +.jqueryFileTree LI.ext_pl { background: url(images/script.png) left top no-repeat; } +.jqueryFileTree LI.ext_py { background: url(images/script.png) left top no-repeat; } +.jqueryFileTree LI.ext_rb { background: url(images/ruby.png) left top no-repeat; } +.jqueryFileTree LI.ext_rbx { background: url(images/ruby.png) left top no-repeat; } +.jqueryFileTree LI.ext_rhtml { background: url(images/ruby.png) left top no-repeat; } +.jqueryFileTree LI.ext_rpm { background: url(images/linux.png) left top no-repeat; } +.jqueryFileTree LI.ext_ruby { background: url(images/ruby.png) left top no-repeat; } +.jqueryFileTree LI.ext_sql { background: url(images/db.png) left top no-repeat; } +.jqueryFileTree LI.ext_swf { background: url(images/flash.png) left top no-repeat; } +.jqueryFileTree LI.ext_tif { background: url(images/picture.png) left top no-repeat; } +.jqueryFileTree LI.ext_tiff { background: url(images/picture.png) left top no-repeat; } +.jqueryFileTree LI.ext_txt { background: url(images/txt.png) left top no-repeat; } +.jqueryFileTree LI.ext_vb { background: url(images/code.png) left top no-repeat; } +.jqueryFileTree LI.ext_wav { background: url(images/music.png) left top no-repeat; } +.jqueryFileTree LI.ext_wmv { background: url(images/film.png) left top no-repeat; } +.jqueryFileTree LI.ext_xls { background: url(images/xls.png) left top no-repeat; } +.jqueryFileTree LI.ext_xml { background: url(images/code.png) left top no-repeat; } +.jqueryFileTree LI.ext_zip { background: url(images/zip.png) left top no-repeat; } \ No newline at end of file diff --git a/debug-db/src/main/assets/jqueryFileTree.js b/debug-db/src/main/assets/jqueryFileTree.js new file mode 100644 index 0000000..4870467 --- /dev/null +++ b/debug-db/src/main/assets/jqueryFileTree.js @@ -0,0 +1,95 @@ +// jQuery File Tree Plugin +// +// Version 1.01 +// +// Cory S.N. LaViska +// A Beautiful Site (http://abeautifulsite.net/) +// 24 March 2008 +// +// Visit http://abeautifulsite.net/notebook.php?article=58 for more information +// +// Usage: $('.fileTreeDemo').fileTree( options, callback ) +// +// Options: root - root folder to display; default = / +// script - location of the serverside AJAX file to use; default = jqueryFileTree.php +// folderEvent - event to trigger expand/collapse; default = click +// expandSpeed - default = 500 (ms); use -1 for no animation +// collapseSpeed - default = 500 (ms); use -1 for no animation +// expandEasing - easing function to use on expand (optional) +// collapseEasing - easing function to use on collapse (optional) +// multiFolder - whether or not to limit the browser to one subfolder at a time +// loadMessage - Message to display while initial tree loads (can be HTML) +// +// History: +// +// 1.01 - updated to work with foreign characters in directory/file names (12 April 2008) +// 1.00 - released (24 March 2008) +// +// TERMS OF USE +// +// This plugin is dual-licensed under the GNU General Public License and the MIT License and +// is copyright 2008 A Beautiful Site, LLC. +// +if(jQuery) (function($){ + + $.extend($.fn, { + fileTree: function(o, h) { + // Defaults + if( !o ) var o = {}; + if( o.root == undefined ) o.root = '/'; + if( o.script == undefined ) o.script = 'jqueryFileTree.php'; + if( o.folderEvent == undefined ) o.folderEvent = 'click'; + if( o.expandSpeed == undefined ) o.expandSpeed= 500; + if( o.collapseSpeed == undefined ) o.collapseSpeed= 500; + if( o.expandEasing == undefined ) o.expandEasing = null; + if( o.collapseEasing == undefined ) o.collapseEasing = null; + if( o.multiFolder == undefined ) o.multiFolder = true; + if( o.loadMessage == undefined ) o.loadMessage = 'Loading...'; + + $(this).each( function() { + + function showTree(c, t) { + $(c).addClass('wait'); + $(".jqueryFileTree.start").remove(); + $.post(o.script, { dir: t }, function(data) { + $(c).find('.start').html(''); + $(c).removeClass('wait').append(data); + if( o.root == t ) $(c).find('UL:hidden').show(); else $(c).find('UL:hidden').slideDown({ duration: o.expandSpeed, easing: o.expandEasing }); + bindTree(c); + }); + } + + function bindTree(t) { + $(t).find('LI A').bind(o.folderEvent, function() { + if( $(this).parent().hasClass('directory') ) { + if( $(this).parent().hasClass('collapsed') ) { + // Expand + if( !o.multiFolder ) { + $(this).parent().parent().find('UL').slideUp({ duration: o.collapseSpeed, easing: o.collapseEasing }); + $(this).parent().parent().find('LI.directory').removeClass('expanded').addClass('collapsed'); + } + $(this).parent().find('UL').remove(); // cleanup + showTree( $(this).parent(), escape($(this).attr('rel').match( /.*\// )) ); + $(this).parent().removeClass('collapsed').addClass('expanded'); + } else { + // Collapse + $(this).parent().find('UL').slideUp({ duration: o.collapseSpeed, easing: o.collapseEasing }); + $(this).parent().removeClass('expanded').addClass('collapsed'); + } + } else { + h($(this).attr('rel')); + } + return false; + }); + // Prevent A from triggering the # on non-click events + if( o.folderEvent.toLowerCase != 'click' ) $(t).find('LI A').bind('click', function() { return false; }); + } + // Loading message + $(this).html('
  • ' + o.loadMessage + '
'); + // Get the initial file list + showTree( $(this), escape(o.root) ); + }); + } + }); + +})(jQuery); \ No newline at end of file diff --git a/debug-db/src/main/java/com/amitshekhar/DebugDBInitProvider.java b/debug-db/src/main/java/com/amitshekhar/DebugDBInitProvider.java index c99f634..cc94ccf 100644 --- a/debug-db/src/main/java/com/amitshekhar/DebugDBInitProvider.java +++ b/debug-db/src/main/java/com/amitshekhar/DebugDBInitProvider.java @@ -19,12 +19,17 @@ package com.amitshekhar; +import android.Manifest; import android.content.ContentProvider; import android.content.ContentValues; import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.database.Cursor; import android.net.Uri; +import android.os.Build; +import android.support.v4.content.ContextCompat; /** * Created by amitshekhar on 16/11/16. @@ -38,6 +43,14 @@ public DebugDBInitProvider() { @Override public boolean onCreate() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (ContextCompat.checkSelfPermission(this.getContext(), + Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) { + //ask for permission + Intent intent = new Intent(getContext(), RequestPermissionsActivity.class); + getContext().startActivity(intent); + } + } DebugDB.initialize(getContext()); return true; } diff --git a/debug-db/src/main/java/com/amitshekhar/RequestPermissionsActivity.java b/debug-db/src/main/java/com/amitshekhar/RequestPermissionsActivity.java new file mode 100644 index 0000000..dfa92b3 --- /dev/null +++ b/debug-db/src/main/java/com/amitshekhar/RequestPermissionsActivity.java @@ -0,0 +1,28 @@ +package com.amitshekhar; + +import android.Manifest; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.AppCompatActivity; + +/** + * Created by adji on 3/28/18. + */ + +public class RequestPermissionsActivity extends AppCompatActivity { + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (ContextCompat.checkSelfPermission(this, + Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) { + //ask for permission + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1); + } + } + finish(); + } +} diff --git a/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java b/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java index aa0d9b5..c7c1219 100644 --- a/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java +++ b/debug-db/src/main/java/com/amitshekhar/server/RequestHandler.java @@ -23,8 +23,11 @@ import android.content.Context; import android.content.res.AssetManager; import android.net.Uri; +import android.os.Build; +import android.os.Environment; import android.text.TextUtils; import android.util.Pair; +import android.webkit.MimeTypeMap; import com.amitshekhar.model.Response; import com.amitshekhar.model.RowDataRequest; @@ -45,12 +48,20 @@ import net.sqlcipher.database.SQLiteDatabase; import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintStream; +import java.io.UnsupportedEncodingException; import java.net.Socket; import java.net.URLDecoder; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -60,6 +71,7 @@ public class RequestHandler { + private static final long MAX_FILE_BYTES = 1024; private final Context mContext; private final Gson mGson; private final AssetManager mAssets; @@ -70,10 +82,52 @@ public class RequestHandler { private String mSelectedDatabase = null; private HashMap mRoomInMemoryDatabases = new HashMap<>(); + private final String emulatedRootDir; + public RequestHandler(Context context) { mContext = context; mAssets = context.getResources().getAssets(); mGson = new GsonBuilder().serializeNulls().create(); + + String[] types = {Environment.DIRECTORY_MUSIC, + Environment.DIRECTORY_PODCASTS, + Environment.DIRECTORY_RINGTONES, + Environment.DIRECTORY_ALARMS, + Environment.DIRECTORY_NOTIFICATIONS, + Environment.DIRECTORY_PICTURES, + Environment.DIRECTORY_MOVIES, + Environment.DIRECTORY_DOWNLOADS, + Environment.DIRECTORY_DOCUMENTS, + Environment.DIRECTORY_DCIM}; + StringBuffer ss = new StringBuffer(); + ss.append("
    "); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + for (String type : types) { + appendToFileTree(ss, context.getExternalFilesDirs(type), "getExternalFilesDirs(" + type + ")"); + } + appendToFileTree(ss, context.getExternalCacheDirs(), "getExternalCacheDirs"); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + appendToFileTree(ss, context.getExternalMediaDirs(), "getExternalMediaDirs"); + appendToFileTree(ss, context.getCodeCacheDir(), "getCodeCacheDir"); + appendToFileTree(ss, context.getNoBackupFilesDir(), "getNoBackupFilesDir"); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + appendToFileTree(ss, context.getDataDir(), "getDataDir"); + } + for (String type : types) { + appendToFileTree(ss, Environment.getExternalStoragePublicDirectory(type), "Environment.getExternalStoragePublicDirectory( " + type + " )"); + } + appendToFileTree(ss, Environment.getDataDirectory(), "Environment.getDataDirectory"); + appendToFileTree(ss, Environment.getExternalStorageDirectory(), "Environment.getExternalStorageDirectory"); + appendToFileTree(ss, Environment.getRootDirectory(), "Environment.getRootDirectory"); + appendToFileTree(ss, Environment.getDownloadCacheDirectory(), "Environment.getDownloadCacheDirectory"); + appendToFileTree(ss, context.getFilesDir(), "getFilesDir"); + appendToFileTree(ss, context.getCacheDir(), "getCacheDir"); + appendToFileTree(ss, context.getExternalCacheDir(), "getExternalCacheDir"); + appendToFileTree(ss, context.getObbDir(), "getObbDir"); + ss.append("
"); + emulatedRootDir = ss.toString(); } public void handle(Socket socket) throws IOException { @@ -81,6 +135,7 @@ public void handle(Socket socket) throws IOException { PrintStream output = null; try { String route = null; + String body = null; // Read HTTP headers and parse out the route. reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); @@ -91,6 +146,32 @@ public void handle(Socket socket) throws IOException { int end = line.indexOf(' ', start); route = line.substring(start, end); break; + } else if (line.startsWith("POST /")) { + int start = line.indexOf('/') + 1; + int end = line.indexOf(' ', start); + route = line.substring(start, end); + + Integer contentLength = null; + while (!TextUtils.isEmpty(line = reader.readLine())) { + if (line.toUpperCase().startsWith("CONTENT-LENGTH")) { + int clStart = line.indexOf(":") + 1; + String cl = line.substring(clStart); + contentLength = Integer.parseInt(cl.trim()); + } + if (line.equals("\r\n")) { + break; + } + } + if (contentLength != null) { + char[] content = new char[contentLength]; + if (reader.read(content) == contentLength) { + body = String.valueOf(content); + } + } else { + // TODO: read till the end if necesary. + body = reader.readLine(); + } + break; } } @@ -103,7 +184,12 @@ public void handle(Socket socket) throws IOException { byte[] bytes; - if (route.startsWith("getDbList")) { + if (route.startsWith("fileTree")) { + final String response = getFileListResponse(route, body); + bytes = response.getBytes(); + } else if (route.startsWith("getFileData")) { + bytes = getFileContent(route); + } else if (route.startsWith("getDbList")) { final String response = getDBListResponse(); bytes = response.getBytes(); } else if (route.startsWith("getAllDataFromTheTable")) { @@ -147,6 +233,8 @@ public void handle(Socket socket) throws IOException { output.println(); output.write(bytes); output.flush(); + } catch (Exception e) { + e.printStackTrace(); } finally { try { if (null != output) { @@ -195,6 +283,135 @@ private void closeDatabase() { isDbOpened = false; } + private byte[] getFileContent(String route) { + String query = null; + if (route.contains("?fileName=")) { + query = route.substring(route.indexOf("=") + 1, route.length()); + } + try { + query = URLDecoder.decode(query, "UTF-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + if (query == null) { + return new byte[0]; + } + File f = new File(query); + ByteArrayOutputStream ous = null; + InputStream ios = null; + long readed = 0; + try { + int bufSize = (MAX_FILE_BYTES < 4096) ? (int) MAX_FILE_BYTES : 4096; + byte[] buffer = new byte[bufSize]; + ous = new ByteArrayOutputStream(); + ios = new FileInputStream(f); + int read = 0; + while ((read = ios.read(buffer)) != -1) { + readed += read; + if (readed > MAX_FILE_BYTES) { + break; + } + ous.write(buffer, 0, read); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (ous != null) + ous.close(); + } catch (IOException e) { + } + try { + if (ios != null) + ios.close(); + } catch (IOException e) { + } + } + if (ous != null) { + return ous.toByteArray(); + } + return new byte[0]; + } + + private String getFileListResponse(String route, String body) { + String query = null; + if (route.contains("?dir=")) { + query = route.substring(route.indexOf("=") + 1, route.length()); + } else if (body != null && body.contains("dir=")) { + query = body.substring(body.indexOf("=") + 1, body.length()); + } + if (query != null) { + try { + query = URLDecoder.decode(query, "UTF-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + if (query.equals("/")) { + return emulatedRootDir; + } + + StringBuffer ss = new StringBuffer(); + ss.append("
    "); + try { + File dir = new File(query); + if (dir.exists()) { + File[] fileList = dir.listFiles(); + if (fileList != null) { + for (File entry : fileList) { + appendToFileTree(ss, entry); + } + } + + } else { + ss.append("Directory not found : " + query); + } + } catch (Exception e) { + e.printStackTrace(); + ss.append("Could not load directory: " + query); + } finally { + ss.append("
"); + } + return ss.toString(); + } + + private void appendToFileTree(StringBuffer ss, File[] fs, String extra) { + if (fs == null) { + return; + } + for (File entry : fs) { + appendToFileTree(ss, entry, extra); + } + } + + private void appendToFileTree(StringBuffer ss, File entry) { + appendToFileTree(ss, entry, null); + + } + + private void appendToFileTree(StringBuffer ss, File entry, String rootDirExtra) { + String ff = entry.getAbsolutePath(); + String f; + if (entry.isDirectory()) { + // An empty root directory. + if (rootDirExtra != null && (entry.list() == null || entry.list().length == 0)) { + return; + } + ss.append("
  • "); + f = entry.getName(); + } else { + String extension = MimeTypeMap.getFileExtensionFromUrl(ff); + ss.append("
  • "); + f = entry.getName() + " ( " + entry.length() + " ) "; + } + if (rootDirExtra != null) { + ss.append(rootDirExtra + " "); + } + ss.append(f + "
  • "); + } + private String getDBListResponse() { mDatabaseFiles = DatabaseFileProvider.getDatabaseFiles(mContext); if (mCustomDatabaseFiles != null) { From 2437a2443f22bd37155deb5532f9941416283e38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rube=CC=81n=20Gonza=CC=81lez=20Mun=CC=83oz?= Date: Tue, 15 May 2018 12:13:27 +0200 Subject: [PATCH 22/74] Changed compile to implementation in the .gradle files --- app/build.gradle | 14 +++++++------- debug-db/build.gradle | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 7a1b962..8460424 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -43,14 +43,14 @@ android { } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + implementation fileTree(dir: 'libs', include: ['*.jar']) + androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) - compile 'com.android.support:appcompat-v7:27.0.0' - testCompile 'junit:junit:4.12' - compile 'net.zetetic:android-database-sqlcipher:3.5.7@aar' - compile "android.arch.persistence.room:runtime:1.0.0" + implementation 'com.android.support:appcompat-v7:27.0.0' + testImplementation 'junit:junit:4.12' + implementation 'net.zetetic:android-database-sqlcipher:3.5.7@aar' + implementation "android.arch.persistence.room:runtime:1.0.0" annotationProcessor "android.arch.persistence.room:compiler:1.0.0" - debugCompile project(':debug-db') + debugImplementation project(':debug-db') } diff --git a/debug-db/build.gradle b/debug-db/build.gradle index dbf8b6e..3e35fc0 100644 --- a/debug-db/build.gradle +++ b/debug-db/build.gradle @@ -43,15 +43,15 @@ android { } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + implementation fileTree(dir: 'libs', include: ['*.jar']) + androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) - testCompile 'junit:junit:4.12' - compile 'com.google.code.gson:gson:2.8.0' - compile 'net.zetetic:android-database-sqlcipher:3.5.7@aar' - compile "android.arch.persistence.room:runtime:1.0.0" - compile 'com.android.support:appcompat-v7:27.0.0' + testImplementation 'junit:junit:4.12' + implementation 'com.google.code.gson:gson:2.8.0' + implementation 'net.zetetic:android-database-sqlcipher:3.5.7@aar' + implementation "android.arch.persistence.room:runtime:1.0.0" + implementation 'com.android.support:appcompat-v7:27.0.0' } From c16b14738df841525c992143927f190f4d8652c6 Mon Sep 17 00:00:00 2001 From: amitshekhariitbhu Date: Sat, 23 Jun 2018 11:09:19 +0530 Subject: [PATCH 23/74] Add check for -wal and -shm --- debug-db/src/main/assets/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug-db/src/main/assets/app.js b/debug-db/src/main/assets/app.js index 0c82d88..79835c5 100644 --- a/debug-db/src/main/assets/app.js +++ b/debug-db/src/main/assets/app.js @@ -72,7 +72,7 @@ function getDBList() { var isEncrypted = dbList[count][1]; var isDownloadable = dbList[count][2]; var dbAttribute = isEncrypted == "true" ? ' ' : ""; - if(dbName.indexOf("journal") == -1){ + if(dbName.indexOf("journal") == -1 && dbName.indexOf("-wal") == -1 && dbName.indexOf("-shm") == -1){ $("#db-list").append("" + dbName + dbAttribute + ""); if(!isSelectionDone){ isSelectionDone = true; From 0353d9772dec877ccc57724179b0d3b7022d7ce5 Mon Sep 17 00:00:00 2001 From: amitshekhariitbhu Date: Sat, 23 Jun 2018 11:18:40 +0530 Subject: [PATCH 24/74] Update room-db version --- app/build.gradle | 6 +++--- debug-db/build.gradle | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 7a1b962..8a3a976 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -47,10 +47,10 @@ dependencies { androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) - compile 'com.android.support:appcompat-v7:27.0.0' + compile 'com.android.support:appcompat-v7:27.1.1' testCompile 'junit:junit:4.12' compile 'net.zetetic:android-database-sqlcipher:3.5.7@aar' - compile "android.arch.persistence.room:runtime:1.0.0" - annotationProcessor "android.arch.persistence.room:compiler:1.0.0" + compile "android.arch.persistence.room:runtime:1.1.0" + annotationProcessor "android.arch.persistence.room:compiler:1.1.0" debugCompile project(':debug-db') } diff --git a/debug-db/build.gradle b/debug-db/build.gradle index 7e64d75..062c9e3 100644 --- a/debug-db/build.gradle +++ b/debug-db/build.gradle @@ -48,9 +48,9 @@ dependencies { exclude group: 'com.android.support', module: 'support-annotations' }) testCompile 'junit:junit:4.12' - compile 'com.google.code.gson:gson:2.8.0' + compile 'com.google.code.gson:gson:2.8.2' compile 'net.zetetic:android-database-sqlcipher:3.5.7@aar' - compile "android.arch.persistence.room:runtime:1.0.0" + compile "android.arch.persistence.room:runtime:1.1.0" } //apply from: 'debug-db-upload.gradle' \ No newline at end of file From 73d53e5f25e1fe39ff98e4bb064be5e03a157f0c Mon Sep 17 00:00:00 2001 From: amitshekhariitbhu Date: Sat, 23 Jun 2018 11:21:36 +0530 Subject: [PATCH 25/74] Update to 1.0.4 --- README.md | 2 +- debug-db/debug-db-upload.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fa8c233..349c827 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ ### Using Android Debug Database Library in your application Add this to your app's build.gradle ```groovy -debugImplementation 'com.amitshekhar.android:debug-db:1.0.3' +debugImplementation 'com.amitshekhar.android:debug-db:1.0.4' ``` Use `debugImplementation` so that it will only compile in your debug build and not in your release build. diff --git a/debug-db/debug-db-upload.gradle b/debug-db/debug-db-upload.gradle index 05d6d12..98739e2 100755 --- a/debug-db/debug-db-upload.gradle +++ b/debug-db/debug-db-upload.gradle @@ -24,7 +24,7 @@ def siteUrl = 'https://github.com/amitshekhariitbhu/Android-Debug-Database' def gitUrl = 'https://github.com/amitshekhariitbhu/Android-Debug-Database.git' group = "com.amitshekhar.android" -version = '1.0.3' +version = '1.0.4' install { repositories.mavenInstaller { From f95db2f64cc4221a6e35ee645f293a76fe9cb06e Mon Sep 17 00:00:00 2001 From: amitshekhariitbhu Date: Sat, 23 Jun 2018 11:36:52 +0530 Subject: [PATCH 26/74] Update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 552d668..dcdfbf1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ Change Log ========== +Version 1.0.4 *(2018-06-23)* +---------------------------- + +* Fix: Fix issue of Room Database + + Version 1.0.3 *(2018-02-12)* ---------------------------- From 79fbefbb3e7addd399cac5b7f9191962f74c24ee Mon Sep 17 00:00:00 2001 From: Evgeniy Vigurskiy Date: Mon, 2 Jul 2018 16:23:06 +0300 Subject: [PATCH 27/74] Add support for database delete --- debug-db/src/main/assets/app.js | 40 +++++++++++++------ debug-db/src/main/assets/index.html | 8 +++- .../amitshekhar/server/RequestHandler.java | 29 ++++++++++++++ 3 files changed, 63 insertions(+), 14 deletions(-) diff --git a/debug-db/src/main/assets/app.js b/debug-db/src/main/assets/app.js index 8d3b897..5d5e240 100644 --- a/debug-db/src/main/assets/app.js +++ b/debug-db/src/main/assets/app.js @@ -82,6 +82,21 @@ function downloadDb() { } } +function deleteDb() { + if (isDatabaseSelected) { + $.ajax({url: "deleteDb", success: function(result){ + result = JSON.parse(result); + if(result.isSuccessful){ + console.log("Database deleted successfully"); + showSuccessInfo("Database Deleted Successfully"); + getDBList(); + } else { + console.log("Database delete failed"); + showErrorInfo("Database Delete Failed"); + } + }}); + } +} function getDBList() { @@ -115,21 +130,26 @@ function openDatabaseAndGetTableList(db, isDownloadable) { if("APP_SHARED_PREFERENCES" == db) { $('#run-query').removeClass('active'); $('#run-query').addClass('disabled'); - $('#selected-db-info').removeClass('active'); - $('#selected-db-info').addClass('disabled'); + $('#selected-db-download').removeClass('active'); + $('#selected-db-delete').removeClass('active'); + $('#selected-db-download').addClass('disabled'); + $('#selected-db-delete').addClass('disabled'); isDatabaseSelected = false; $("#selected-db-info").text("SharedPreferences"); } else { $('#run-query').removeClass('disabled'); $('#run-query').addClass('active'); + $("#selected-db-info").text("Selected Database : "+db); if("true" == isDownloadable) { - $('#selected-db-info').removeClass('disabled'); - $('#selected-db-info').addClass('active'); - $("#selected-db-info").text("Export Selected Database : "+db); + $('#selected-db-download').addClass('active'); + $('#selected-db-delete').addClass('active'); + $('#selected-db-download').removeClass('disabled'); + $('#selected-db-delete').removeClass('disabled'); } else { - $('#selected-db-info').removeClass('active'); - $('#selected-db-info').addClass('disabled'); - $("#selected-db-info").text("Selected Database : "+db); + $('#selected-db-download').removeClass('active'); + $('#selected-db-delete').removeClass('active'); + $('#selected-db-download').addClass('disabled'); + $('#selected-db-delete').addClass('disabled'); } isDatabaseSelected = true; } @@ -141,11 +161,7 @@ function openDatabaseAndGetTableList(db, isDownloadable) { var tableList = result.rows; var dbVersion = result.dbVersion; if("APP_SHARED_PREFERENCES" != db) { - if("true" == isDownloadable) { - $("#selected-db-info").text("Export Selected Database : "+db +" Version : "+dbVersion); - } else { $("#selected-db-info").text("Selected Database : "+db +" Version : "+dbVersion); - } } $('#table-list').empty() for(var count = 0; count < tableList.length; count++){ diff --git a/debug-db/src/main/assets/index.html b/debug-db/src/main/assets/index.html index 846b136..f8eeb36 100644 --- a/debug-db/src/main/assets/index.html +++ b/debug-db/src/main/assets/index.html @@ -121,9 +121,13 @@ - + + + + + Customize UI + @@ -151,10 +157,131 @@ + + + +
    Data Updated Successfully
    + diff --git a/debug-db-base/src/main/assets/ui-customization.js b/debug-db-base/src/main/assets/ui-customization.js new file mode 100644 index 0000000..0d1c81f --- /dev/null +++ b/debug-db-base/src/main/assets/ui-customization.js @@ -0,0 +1,487 @@ +/** + * UI customization for Android Debug Database. + * + * - Stored in browser localStorage (per-browser). + * - Visual-only settings. + * - Safe defaults that replicate existing UI. + */ +(function (global) { + "use strict"; + + var STORAGE_KEY = "addb.uiCustomization.v1"; + + var DEFAULT_SETTINGS = { + tableWidth: "fixed", // fixed | full | fluid + rowDensity: "normal", // compact | normal | comfortable + wrapLongText: false, + maxColumnWidthPx: null, // number | null + jsonMode: "raw", // raw | wrapped | pretty + stickyHeader: false, + pageLength: 10 // 10 | 25 | 50 | 100 + }; + + var allowedTableWidths = { fixed: true, full: true, fluid: true }; + var allowedRowDensities = { compact: true, normal: true, comfortable: true }; + var allowedJsonModes = { raw: true, wrapped: true, pretty: true }; + var allowedPageLengths = { 10: true, 25: true, 50: true, 100: true }; + + var currentSettings = null; + var currentDataTable = null; + var currentDataTableDrawHandler = null; + var pendingDataTableRender = false; + var jsonCleanupNeeded = false; + + function safeParseJson(json) { + try { + return JSON.parse(json); + } catch (e) { + return null; + } + } + + function safeGetLocalStorageItem(key) { + try { + if (!global.localStorage) return null; + return global.localStorage.getItem(key); + } catch (e) { + return null; + } + } + + function safeSetLocalStorageItem(key, value) { + try { + if (!global.localStorage) return false; + global.localStorage.setItem(key, value); + return true; + } catch (e) { + return false; + } + } + + function safeRemoveLocalStorageItem(key) { + try { + if (!global.localStorage) return false; + global.localStorage.removeItem(key); + return true; + } catch (e) { + return false; + } + } + + function normalizeBoolean(value, fallback) { + if (value === true || value === false) return value; + if (value === "true") return true; + if (value === "false") return false; + return fallback; + } + + function clampInt(value, min, max) { + if (typeof value !== "number") return null; + if (value < min) return min; + if (value > max) return max; + return value; + } + + function normalizeSettings(raw) { + var settings = $.extend({}, DEFAULT_SETTINGS); + if (!raw || typeof raw !== "object") return settings; + + if (allowedTableWidths[raw.tableWidth]) settings.tableWidth = raw.tableWidth; + if (allowedRowDensities[raw.rowDensity]) settings.rowDensity = raw.rowDensity; + if (allowedJsonModes[raw.jsonMode]) settings.jsonMode = raw.jsonMode; + + settings.wrapLongText = normalizeBoolean(raw.wrapLongText, DEFAULT_SETTINGS.wrapLongText); + settings.stickyHeader = normalizeBoolean(raw.stickyHeader, DEFAULT_SETTINGS.stickyHeader); + + var pageLength = parseInt(raw.pageLength, 10); + if (allowedPageLengths[pageLength]) settings.pageLength = pageLength; + + var maxColumnWidthPx = raw.maxColumnWidthPx; + if (maxColumnWidthPx === "" || maxColumnWidthPx === null || typeof maxColumnWidthPx === "undefined") { + settings.maxColumnWidthPx = null; + } else { + var parsedMaxWidth = parseInt(maxColumnWidthPx, 10); + if (!isNaN(parsedMaxWidth)) { + settings.maxColumnWidthPx = clampInt(parsedMaxWidth, 120, 2000); + } + } + + return settings; + } + + function loadSettings() { + var raw = safeGetLocalStorageItem(STORAGE_KEY); + if (!raw) return normalizeSettings(null); + return normalizeSettings(safeParseJson(raw)); + } + + function saveSettings(settings) { + return safeSetLocalStorageItem(STORAGE_KEY, JSON.stringify(settings)); + } + + function clearSettings() { + return safeRemoveLocalStorageItem(STORAGE_KEY); + } + + function applyContainerWidth(settings) { + var container = $("#app-container"); + if (!container.length) return; + + container.removeClass("container container-fluid addb-container-fluid-max"); + + if (settings.tableWidth === "fixed") { + container.addClass("container"); + } else { + container.addClass("container-fluid"); + if (settings.tableWidth === "fluid") { + container.addClass("addb-container-fluid-max"); + } + } + } + + function applyBodyClasses(settings) { + var body = $("body"); + + body.removeClass("addb-density-compact addb-density-normal addb-density-comfortable"); + body.addClass("addb-density-" + settings.rowDensity); + + body.toggleClass("addb-wrap-long-text", !!settings.wrapLongText); + + body.removeClass("addb-json-mode-raw addb-json-mode-wrapped addb-json-mode-pretty"); + body.addClass("addb-json-mode-" + settings.jsonMode); + + body.toggleClass("addb-sticky-header", !!settings.stickyHeader); + } + + function applyMaxColumnWidth(settings) { + var shouldApplyMaxWidth = + typeof settings.maxColumnWidthPx === "number" && + settings.maxColumnWidthPx > 0 && + (settings.wrapLongText || settings.jsonMode !== "raw"); + + var body = $("body"); + if (shouldApplyMaxWidth) { + document.documentElement.style.setProperty("--addb-max-col-width", settings.maxColumnWidthPx + "px"); + body.addClass("addb-max-col-width"); + } else { + document.documentElement.style.removeProperty("--addb-max-col-width"); + body.removeClass("addb-max-col-width"); + } + } + + function looksLikeJson(value) { + if (typeof value !== "string") return false; + var trimmed = value.trim(); + if (trimmed.length < 2) return false; + var startsOk = trimmed[0] === "{" || trimmed[0] === "["; + var endsOk = trimmed[trimmed.length - 1] === "}" || trimmed[trimmed.length - 1] === "]"; + return startsOk && endsOk; + } + + function cleanupJsonClassesForCurrentPage(dataTableApi) { + if (!dataTableApi) return; + + var rows = dataTableApi.rows({ page: "current" }).nodes(); + for (var rowIndex = 0; rowIndex < rows.length; rowIndex++) { + var cells = rows[rowIndex].cells; + for (var cellIndex = 0; cellIndex < cells.length; cellIndex++) { + var td = cells[cellIndex]; + td.classList.remove("addb-json-cell"); + } + } + } + + function applyJsonFormattingToCurrentPage(dataTableApi, settings) { + if (!dataTableApi) return; + + if (settings.jsonMode === "raw") { + if (jsonCleanupNeeded) { + cleanupJsonClassesForCurrentPage(dataTableApi); + jsonCleanupNeeded = false; + } + return; + } + + jsonCleanupNeeded = false; + + var rows = dataTableApi.rows({ page: "current" }).nodes(); + for (var rowIndex = 0; rowIndex < rows.length; rowIndex++) { + var cells = rows[rowIndex].cells; + for (var cellIndex = 0; cellIndex < cells.length; cellIndex++) { + var td = cells[cellIndex]; + td.classList.remove("addb-json-cell"); + + var cellData = dataTableApi.cell(td).data(); + if (!looksLikeJson(cellData)) continue; + + var parsed = safeParseJson(cellData); + if (parsed === null) continue; + + td.classList.add("addb-json-cell"); + + if (settings.jsonMode === "pretty") { + td.textContent = JSON.stringify(parsed, null, 2); + } + } + } + } + + function applyDataTableSettings(dataTableApi, settings, reason) { + if (!dataTableApi) return; + + try { + if (typeof dataTableApi.page === "function" && typeof dataTableApi.page.len === "function") { + var currentLen = dataTableApi.page.len(); + if (currentLen !== settings.pageLength) { + dataTableApi.page.len(settings.pageLength); + } + } + + if (reason === "jsonMode") { + dataTableApi.rows().invalidate(); + } + + var tableNode = $(dataTableApi.table().node()); + var isVisible = tableNode.is(":visible"); + if (!isVisible) { + pendingDataTableRender = true; + return; + } + + dataTableApi.columns.adjust(); + dataTableApi.draw(false); + } catch (e) { + // Best-effort only; never break the main UI. + } + } + + function applySettings(settings, reason) { + var previousSettings = currentSettings || DEFAULT_SETTINGS; + currentSettings = normalizeSettings(settings); + + applyContainerWidth(currentSettings); + applyBodyClasses(currentSettings); + applyMaxColumnWidth(currentSettings); + + if (previousSettings.jsonMode !== currentSettings.jsonMode && currentSettings.jsonMode === "raw") { + jsonCleanupNeeded = true; + } + + var dataTableReason = reason; + if (previousSettings.jsonMode === "pretty" && currentSettings.jsonMode !== "pretty") { + dataTableReason = "jsonMode"; + } + + applyDataTableSettings(currentDataTable, currentSettings, dataTableReason); + } + + function setCurrentDataTable(dataTableApi) { + var previousTableNode = currentDataTable ? $(currentDataTable.table().node()) : null; + if (previousTableNode && currentDataTableDrawHandler) { + previousTableNode.off("draw.dt", currentDataTableDrawHandler); + } + + currentDataTable = dataTableApi || null; + if (!currentDataTable) return; + + var tableNode = $(currentDataTable.table().node()); + currentDataTableDrawHandler = function () { + applyJsonFormattingToCurrentPage(currentDataTable, currentSettings || DEFAULT_SETTINGS); + }; + tableNode.on("draw.dt", currentDataTableDrawHandler); + + applyDataTableSettings(currentDataTable, currentSettings || DEFAULT_SETTINGS, "init"); + } + + function getQueryValue(key) { + var query = global.location.search; + if (!query) return null; + var keyEscaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + var matches = query.match(new RegExp("[?&]" + keyEscaped + "=([^&]*)")); + return matches ? decodeURIComponent(matches[1].replace(/\+/g, " ")) : null; + } + + function setQueryValue(key, value) { + var currentSearch = global.location.search || ""; + var hash = global.location.hash || ""; + var base = global.location.pathname; + + var query = currentSearch.replace(/^\?/, ""); + var parts = query ? query.split("&") : []; + var updated = []; + var found = false; + + for (var i = 0; i < parts.length; i++) { + if (!parts[i]) continue; + var kv = parts[i].split("="); + var k = decodeURIComponent(kv[0] || ""); + if (k !== key) { + updated.push(parts[i]); + continue; + } + found = true; + if (value !== null && value !== "") { + updated.push(encodeURIComponent(key) + "=" + encodeURIComponent(value)); + } + } + + if (!found && value !== null && value !== "") { + updated.push(encodeURIComponent(key) + "=" + encodeURIComponent(value)); + } + + var newSearch = updated.length ? "?" + updated.join("&") : ""; + return base + newSearch + hash; + } + + function showView(viewName) { + var mainView = $("#addb-main-view"); + var customizeView = $("#addb-customize-view"); + + if (viewName === "customize") { + mainView.addClass("display-none"); + customizeView.removeClass("display-none"); + global.scrollTo(0, 0); + } else { + customizeView.addClass("display-none"); + mainView.removeClass("display-none"); + if (pendingDataTableRender && currentDataTable) { + pendingDataTableRender = false; + applyDataTableSettings(currentDataTable, currentSettings || DEFAULT_SETTINGS, "resume"); + } + global.scrollTo(0, 0); + } + } + + function syncViewFromUrl() { + var view = getQueryValue("view"); + showView(view === "customize" ? "customize" : "main"); + } + + function navigateTo(viewName) { + if (!global.history || typeof global.history.pushState !== "function") { + if (viewName === "customize") global.location.href = setQueryValue("view", "customize"); + else global.location.href = setQueryValue("view", null); + return; + } + + var url = viewName === "customize" ? setQueryValue("view", "customize") : setQueryValue("view", null); + global.history.pushState({ view: viewName }, "", url); + showView(viewName); + } + + function setActiveCategory(category) { + var categories = $("#addb-customize-categories .list-group-item"); + categories.removeClass("active"); + categories.filter("[data-category='" + category + "']").addClass("active"); + + $(".addb-customize-category").each(function () { + var el = $(this); + el.toggleClass("display-none", el.attr("data-category") !== category); + }); + } + + function readDraftFromForm() { + var maxWidthRaw = $("#addb-max-column-width").val(); + var settings = { + tableWidth: $("input[name='addb-table-width']:checked").val(), + rowDensity: $("input[name='addb-row-density']:checked").val(), + wrapLongText: $("#addb-wrap-long-text").is(":checked"), + maxColumnWidthPx: maxWidthRaw === "" ? null : maxWidthRaw, + jsonMode: $("#addb-json-mode").val(), + stickyHeader: $("#addb-sticky-header").is(":checked"), + pageLength: $("#addb-page-length").val() + }; + return normalizeSettings(settings); + } + + function writeFormFromSettings(settings) { + $("input[name='addb-table-width'][value='" + settings.tableWidth + "']").prop("checked", true); + $("input[name='addb-row-density'][value='" + settings.rowDensity + "']").prop("checked", true); + $("#addb-wrap-long-text").prop("checked", !!settings.wrapLongText); + $("#addb-max-column-width").val(settings.maxColumnWidthPx === null ? "" : settings.maxColumnWidthPx); + $("#addb-json-mode").val(settings.jsonMode); + $("#addb-sticky-header").prop("checked", !!settings.stickyHeader); + $("#addb-page-length").val(String(settings.pageLength)); + } + + function initCustomizationUi() { + $("#open-ui-customization").on("click", function (e) { + e.preventDefault(); + navigateTo("customize"); + }); + + $("#addb-customize-back").on("click", function (e) { + e.preventDefault(); + navigateTo("main"); + }); + + $("#addb-customize-categories").on("click", ".list-group-item", function (e) { + e.preventDefault(); + setActiveCategory($(this).attr("data-category")); + }); + + $("#addb-customize-form").on("change input", "input, select", function () { + var draft = readDraftFromForm(); + applySettings(draft, "preview"); + }); + + $("#addb-ui-apply").on("click", function () { + var draft = readDraftFromForm(); + applySettings(draft, "apply"); + + var saved = saveSettings(currentSettings); + if (saved && typeof global.showSuccessInfo === "function") { + global.showSuccessInfo("UI settings saved"); + } else if (!saved && typeof global.showErrorInfo === "function") { + global.showErrorInfo("Could not save UI settings"); + } + }); + + $("#addb-ui-reset").on("click", function () { + clearSettings(); + applySettings(DEFAULT_SETTINGS, "reset"); + writeFormFromSettings(DEFAULT_SETTINGS); + if (typeof global.showSuccessInfo === "function") { + global.showSuccessInfo("UI settings reset to defaults"); + } + }); + + // Initial form + view state + writeFormFromSettings(currentSettings || DEFAULT_SETTINGS); + setActiveCategory("layout"); + syncViewFromUrl(); + + $(global).on("popstate", function () { + syncViewFromUrl(); + }); + } + + function init() { + currentSettings = loadSettings(); + applySettings(currentSettings, "init"); + initCustomizationUi(); + } + + global.AddbUiCustomization = { + defaults: DEFAULT_SETTINGS, + getSettings: function () { + return $.extend({}, currentSettings || DEFAULT_SETTINGS); + }, + apply: function (settings) { + applySettings(settings, "external"); + }, + save: function (settings) { + applySettings(settings, "external"); + return saveSettings(currentSettings); + }, + clear: function () { + clearSettings(); + applySettings(DEFAULT_SETTINGS, "external"); + }, + onDataTableCreated: function (dataTableApi) { + setCurrentDataTable(dataTableApi); + applyJsonFormattingToCurrentPage(currentDataTable, currentSettings || DEFAULT_SETTINGS); + }, + init: init + }; +})(window); diff --git a/debug-db-base/src/main/java/com/amitshekhar/server/RequestHandler.java b/debug-db-base/src/main/java/com/amitshekhar/server/RequestHandler.java index 3f967ea..e7ba526 100644 --- a/debug-db-base/src/main/java/com/amitshekhar/server/RequestHandler.java +++ b/debug-db-base/src/main/java/com/amitshekhar/server/RequestHandler.java @@ -102,36 +102,47 @@ public void handle(Socket socket) throws IOException { route = "index.html"; } + // Strip query params for static asset lookups and MIME type detection. + // (API routes still receive the full route, including query params.) + String routePath = route; + final int queryIndex = route.indexOf('?'); + if (queryIndex >= 0) { + routePath = route.substring(0, queryIndex); + if (routePath.isEmpty()) { + routePath = "index.html"; + } + } + byte[] bytes; - if (route.startsWith("getDbList")) { + if (routePath.startsWith("getDbList")) { final String response = getDBListResponse(); bytes = response.getBytes(); - } else if (route.startsWith("getAllDataFromTheTable")) { + } else if (routePath.startsWith("getAllDataFromTheTable")) { final String response = getAllDataFromTheTableResponse(route); bytes = response.getBytes(); - } else if (route.startsWith("getTableList")) { + } else if (routePath.startsWith("getTableList")) { final String response = getTableListResponse(route); bytes = response.getBytes(); - } else if (route.startsWith("addTableData")) { + } else if (routePath.startsWith("addTableData")) { final String response = addTableDataAndGetResponse(route); bytes = response.getBytes(); - } else if (route.startsWith("updateTableData")) { + } else if (routePath.startsWith("updateTableData")) { final String response = updateTableDataAndGetResponse(route); bytes = response.getBytes(); - } else if (route.startsWith("deleteTableData")) { + } else if (routePath.startsWith("deleteTableData")) { final String response = deleteTableDataAndGetResponse(route); bytes = response.getBytes(); - } else if (route.startsWith("query")) { + } else if (routePath.startsWith("query")) { final String response = executeQueryAndGetResponse(route); bytes = response.getBytes(); - } else if (route.startsWith("deleteDb")) { + } else if (routePath.startsWith("deleteDb")) { final String response = deleteSelectedDatabaseAndGetResponse(); bytes = response.getBytes(); - } else if (route.startsWith("downloadDb")) { + } else if (routePath.startsWith("downloadDb")) { bytes = Utils.getDatabase(mSelectedDatabase, mDatabaseFiles); } else { - bytes = Utils.loadContent(route, mAssets); + bytes = Utils.loadContent(routePath, mAssets); } if (null == bytes) { @@ -141,9 +152,9 @@ public void handle(Socket socket) throws IOException { // Send out the content. output.println("HTTP/1.0 200 OK"); - output.println("Content-Type: " + Utils.detectMimeType(route)); + output.println("Content-Type: " + Utils.detectMimeType(routePath)); - if (route.startsWith("downloadDb")) { + if (routePath.startsWith("downloadDb")) { output.println("Content-Disposition: attachment; filename=" + mSelectedDatabase); } else { output.println("Content-Length: " + bytes.length); From 8e42800a968f4414adac906e13bbed057f64da6a Mon Sep 17 00:00:00 2001 From: Skylar Graika Date: Sun, 14 Dec 2025 09:40:13 -0800 Subject: [PATCH 68/74] Update custom ui icon --- debug-db-base/src/main/assets/custom.css | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/debug-db-base/src/main/assets/custom.css b/debug-db-base/src/main/assets/custom.css index 1ccc6dc..6181036 100644 --- a/debug-db-base/src/main/assets/custom.css +++ b/debug-db-base/src/main/assets/custom.css @@ -28,6 +28,17 @@ margin-right: 8px; } +@font-face { + font-family: "AddbGlyphicons"; + src: url("fonts/glyphicons-halflings-regular.ttf") format("truetype"); + font-weight: normal; + font-style: normal; +} + +.glyphicon { + font-family: "AddbGlyphicons"; +} + .addb-customize-heading .btn-link { padding-top: 0; padding-bottom: 0; From 40d939af1d084dccfd03ab4d16b2ef0c7ea073da Mon Sep 17 00:00:00 2001 From: Skylar Graika Date: Sun, 14 Dec 2025 13:40:12 -0800 Subject: [PATCH 69/74] Fix JSON pretty-print + wrapped formatting --- debug-db-base/src/main/assets/app.js | 155 ++++++++++++- debug-db-base/src/main/assets/custom.css | 70 +++++- .../src/main/assets/ui-customization.js | 218 +++++++++++++++--- 3 files changed, 411 insertions(+), 32 deletions(-) diff --git a/debug-db-base/src/main/assets/app.js b/debug-db-base/src/main/assets/app.js index cf6819c..2a5685e 100644 --- a/debug-db-base/src/main/assets/app.js +++ b/debug-db-base/src/main/assets/app.js @@ -2,7 +2,11 @@ $( document ).ready(function() { if (window.AddbUiCustomization && typeof window.AddbUiCustomization.init === "function") { window.AddbUiCustomization.init(); } - getDBList(); + if (shouldUseDemoData()) { + initDemoMode(); + } else { + getDBList(); + } $("#query").keypress(function(e){ if(e.which == 13) { queryFunction(); @@ -28,6 +32,150 @@ $( document ).ready(function() { }); var isDatabaseSelected = true; +var isDemoModeEnabled = false; + +function shouldUseDemoData() { + try { + var search = window.location.search || ""; + if (/(^|[?&])demo=1(&|$)/.test(search)) return true; + return window.location.protocol === "file:"; + } catch (e) { + return false; + } +} + +function demoRepeat(str, count) { + var out = ""; + for (var i = 0; i < count; i++) out += str; + return out; +} + +function demoCell(value, dataType) { + return { value: value, dataType: dataType }; +} + +function buildDemoResult() { + var longToken = "tok_" + demoRepeat("A", 72) + "." + demoRepeat("B", 72) + "." + demoRepeat("C", 72); + var veryLongUrl = + "https://example.com/" + + demoRepeat("path/", 8) + + "resource?query=" + + demoRepeat("x", 48) + + "&more=" + + demoRepeat("y", 48); + + var json1 = JSON.stringify({ + user: { + id: 42, + name: "Ada Lovelace", + flags: { isAdmin: false, isTester: true, beta: true } + }, + session: { + token: longToken, + expiresAt: "2025-12-14T12:34:56Z" + }, + features: ["ui_customization", "json_tree", "sticky_header"] + }); + + var json2 = JSON.stringify({ + request: { + method: "POST", + url: veryLongUrl, + headers: { + "content-type": "application/json", + "x-request-id": "req_" + demoRepeat("7", 24) + } + }, + timingsMs: { dns: 12, tcp: 34, tls: 56, ttfb: 78, total: 234 }, + tags: ["android", "debug-db", "datatable"] + }); + + var json3 = JSON.stringify([ + { type: "event", ts: 1734150000, data: { name: "launch", coldStart: true } }, + { type: "event", ts: 1734150030, data: { name: "tap", target: "settings_button" } }, + { type: "event", ts: 1734150055, data: { name: "network", status: 200, bytes: 2048 } } + ]); + + var json4 = JSON.stringify({ + emptyObject: {}, + emptyArray: [], + nested: { + level2: { + level3: { + message: "Expand/collapse should keep 2nd-level collapsed by default.", + numbers: [1, 2, 3, 4, 5] + } + } + } + }); + + return { + isSuccessful: true, + isSelectQuery: true, + isEditable: false, + tableInfos: [ + { title: "id", isPrimary: true }, + { title: "name", isPrimary: false }, + { title: "is_active", isPrimary: false }, + { title: "json_payload", isPrimary: false }, + { title: "notes", isPrimary: false } + ], + rows: [ + [ + demoCell(1, "number"), + demoCell("Alpha", "string"), + demoCell(true, "boolean"), + demoCell(json1, "string"), + demoCell("Try JSON mode: Pretty-printed", "string") + ], + [ + demoCell(2, "number"), + demoCell("Beta", "string"), + demoCell(false, "boolean"), + demoCell(json2, "string"), + demoCell("Includes a very long URL + token", "string") + ], + [ + demoCell(3, "number"), + demoCell("Gamma", "string"), + demoCell(true, "boolean"), + demoCell(json3, "string"), + demoCell("Root JSON array (children collapsed at level 2)", "string") + ], + [ + demoCell(4, "number"), + demoCell("Delta", "string"), + demoCell(true, "boolean"), + demoCell(json4, "string"), + demoCell("Has empty containers + deep nesting", "string") + ] + ] + }; +} + +function loadDemoData() { + var result = buildDemoResult(); + inflateData(result); +} + +function initDemoMode() { + isDemoModeEnabled = true; + isDatabaseSelected = false; + + $("#selected-db-info").text("Demo mode (local file) — showing sample data for UI testing"); + + $("#run-query").removeClass("active").addClass("disabled").prop("disabled", true); + $("#selected-db-download").removeClass("active").addClass("disabled").prop("disabled", true); + $("#selected-db-delete").removeClass("active").addClass("disabled").prop("disabled", true); + + $("#db-list").empty().append("DEMO_DB"); + $("#table-list") + .empty() + .append("demo_table"); + + loadDemoData(); + showSuccessInfo("Loaded demo data (open Customize UI → Text & JSON to try Pretty-printed)"); +} function getData(tableName) { @@ -44,6 +192,11 @@ function queryFunction() { var query = $('#query').val(); + if (isDemoModeEnabled) { + showErrorInfo("Demo mode: database queries are not available (UI only)"); + return; + } + $.ajax({url: "query?query="+escape(query), success: function(result){ result = JSON.parse(result); diff --git a/debug-db-base/src/main/assets/custom.css b/debug-db-base/src/main/assets/custom.css index 6181036..027e5c9 100644 --- a/debug-db-base/src/main/assets/custom.css +++ b/debug-db-base/src/main/assets/custom.css @@ -106,15 +106,75 @@ body.addb-max-col-width #parent-data-div table.dataTable td { body.addb-json-mode-wrapped #parent-data-div table.dataTable td.addb-json-cell { white-space: normal; - overflow-wrap: anywhere; - word-break: break-word; + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + overflow-wrap: break-word; + word-break: normal; + vertical-align: top; } body.addb-json-mode-pretty #parent-data-div table.dataTable td.addb-json-cell { - white-space: pre-wrap; + white-space: pre; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; - overflow-wrap: anywhere; - word-break: break-word; + overflow-wrap: normal; + word-break: normal; + vertical-align: top; +} + +body.addb-max-col-width.addb-json-mode-pretty #parent-data-div table.dataTable td.addb-json-cell { + white-space: pre-wrap; + overflow-wrap: break-word; +} + +body.addb-json-mode-pretty #parent-data-div table.dataTable td.addb-json-cell .addb-json-tree { + display: inline-block; +} + +#parent-data-div table.dataTable td.addb-json-cell .addb-json-line { + display: block; + margin: 0; +} + +#parent-data-div table.dataTable td.addb-json-cell .addb-json-gutter { + display: inline-block; + width: 14px; +} + +#parent-data-div table.dataTable td.addb-json-cell details.addb-json-node { + margin: 0; + padding: 0; +} + +#parent-data-div table.dataTable td.addb-json-cell details.addb-json-node > summary { + cursor: pointer; + list-style: none; +} + +#parent-data-div table.dataTable td.addb-json-cell details.addb-json-node > summary::-webkit-details-marker { + display: none; +} + +#parent-data-div table.dataTable td.addb-json-cell details.addb-json-node > summary::marker { + content: ""; +} + +#parent-data-div table.dataTable td.addb-json-cell details.addb-json-node > summary .addb-json-gutter::before { + content: "▸"; +} + +#parent-data-div table.dataTable td.addb-json-cell details.addb-json-node[open] > summary .addb-json-gutter::before { + content: "▾"; +} + +#parent-data-div table.dataTable td.addb-json-cell details.addb-json-node > summary .addb-json-summary-open { + display: none; +} + +#parent-data-div table.dataTable td.addb-json-cell details.addb-json-node[open] > summary .addb-json-summary-open { + display: inline; +} + +#parent-data-div table.dataTable td.addb-json-cell details.addb-json-node[open] > summary .addb-json-summary-closed { + display: none; } #snackbar { diff --git a/debug-db-base/src/main/assets/ui-customization.js b/debug-db-base/src/main/assets/ui-customization.js index 0d1c81f..f4f4d58 100644 --- a/debug-db-base/src/main/assets/ui-customization.js +++ b/debug-db-base/src/main/assets/ui-customization.js @@ -28,8 +28,11 @@ var currentSettings = null; var currentDataTable = null; var currentDataTableDrawHandler = null; + var currentDataTableContainer = null; var pendingDataTableRender = false; - var jsonCleanupNeeded = false; + var pendingColumnsAdjust = false; + + var JSON_INDENT_PX = 16; function safeParseJson(json) { try { @@ -178,31 +181,168 @@ return startsOk && endsOk; } - function cleanupJsonClassesForCurrentPage(dataTableApi) { - if (!dataTableApi) return; + function formatJsonPrimitive(value) { + if (value === null) return "null"; + var t = typeof value; + if (t === "string" || t === "number" || t === "boolean") return JSON.stringify(value); + try { + return JSON.stringify(value); + } catch (e) { + return String(value); + } + } - var rows = dataTableApi.rows({ page: "current" }).nodes(); - for (var rowIndex = 0; rowIndex < rows.length; rowIndex++) { - var cells = rows[rowIndex].cells; - for (var cellIndex = 0; cellIndex < cells.length; cellIndex++) { - var td = cells[cellIndex]; - td.classList.remove("addb-json-cell"); + function createJsonLine(tagName, depth, extraClassName) { + var el = document.createElement(tagName || "div"); + el.className = "addb-json-line" + (extraClassName ? " " + extraClassName : ""); + el.style.paddingLeft = depth * JSON_INDENT_PX + "px"; + + var gutter = document.createElement("span"); + gutter.className = "addb-json-gutter"; + el.appendChild(gutter); + + return { el: el, gutter: gutter }; + } + + function createJsonDetails(depth, openByDefault) { + var details = document.createElement("details"); + details.className = "addb-json-node"; + if (openByDefault) details.open = true; + + var summaryLine = createJsonLine("summary", depth, "addb-json-summary"); + + var closedSpan = document.createElement("span"); + closedSpan.className = "addb-json-summary-closed"; + + var openSpan = document.createElement("span"); + openSpan.className = "addb-json-summary-open"; + + summaryLine.el.appendChild(closedSpan); + summaryLine.el.appendChild(openSpan); + + var children = document.createElement("div"); + children.className = "addb-json-children"; + + details.appendChild(summaryLine.el); + details.appendChild(children); + + return { + details: details, + closedSpan: closedSpan, + openSpan: openSpan, + children: children + }; + } + + function jsonContainerInfo(value) { + if (Array.isArray(value)) { + return { kind: "array", open: "[", close: "]", count: value.length }; + } + var keys = Object.keys(value); + return { kind: "object", open: "{", close: "}", count: keys.length, keys: keys }; + } + + function appendJsonValue(parent, value, depth, isLast, keyPrefix) { + var comma = isLast ? "" : ","; + var prefix = keyPrefix || ""; + + var isContainer = value !== null && typeof value === "object"; + if (!isContainer) { + var primitiveLine = createJsonLine("div", depth); + primitiveLine.el.appendChild(document.createTextNode(prefix + formatJsonPrimitive(value) + comma)); + parent.appendChild(primitiveLine.el); + return; + } + + var info = jsonContainerInfo(value); + var isEmpty = info.count === 0; + if (isEmpty) { + var emptyLine = createJsonLine("div", depth); + emptyLine.el.appendChild(document.createTextNode(prefix + info.open + info.close + comma)); + parent.appendChild(emptyLine.el); + return; + } + + // Collapse all 2nd-level (depth=1) container values by default. + var openByDefault = depth !== 1; + var node = createJsonDetails(depth, openByDefault); + + var collapsedPreview = info.kind === "array" ? "[\u2026] (" + info.count + ")" : "{\u2026} (" + info.count + ")"; + node.closedSpan.textContent = prefix + collapsedPreview + comma; + node.openSpan.textContent = prefix + info.open; + + if (info.kind === "array") { + for (var index = 0; index < value.length; index++) { + appendJsonValue(node.children, value[index], depth + 1, index === value.length - 1, ""); + } + } else { + var keys = info.keys; + for (var keyIndex = 0; keyIndex < keys.length; keyIndex++) { + var key = keys[keyIndex]; + var childPrefix = JSON.stringify(key) + ": "; + appendJsonValue(node.children, value[key], depth + 1, keyIndex === keys.length - 1, childPrefix); } } + + var closingLine = createJsonLine("div", depth); + closingLine.el.appendChild(document.createTextNode(info.close + comma)); + node.children.appendChild(closingLine.el); + + parent.appendChild(node.details); } - function applyJsonFormattingToCurrentPage(dataTableApi, settings) { - if (!dataTableApi) return; + function buildJsonTree(parsedJson) { + var root = document.createElement("div"); + root.className = "addb-json-tree"; - if (settings.jsonMode === "raw") { - if (jsonCleanupNeeded) { - cleanupJsonClassesForCurrentPage(dataTableApi); - jsonCleanupNeeded = false; + var info = jsonContainerInfo(parsedJson); + + var openLine = createJsonLine("div", 0); + openLine.el.appendChild(document.createTextNode(info.open)); + root.appendChild(openLine.el); + + if (info.kind === "array") { + for (var index = 0; index < parsedJson.length; index++) { + appendJsonValue(root, parsedJson[index], 1, index === parsedJson.length - 1, ""); + } + } else { + var keys = info.keys; + for (var keyIndex = 0; keyIndex < keys.length; keyIndex++) { + var key = keys[keyIndex]; + var childPrefix = JSON.stringify(key) + ": "; + appendJsonValue(root, parsedJson[key], 1, keyIndex === keys.length - 1, childPrefix); } - return; } - jsonCleanupNeeded = false; + var closeLine = createJsonLine("div", 0); + closeLine.el.appendChild(document.createTextNode(info.close)); + root.appendChild(closeLine.el); + + return root; + } + + function scheduleColumnsAdjust(dataTableApi) { + if (!dataTableApi) return; + if (pendingColumnsAdjust) return; + pendingColumnsAdjust = true; + + global.setTimeout(function () { + pendingColumnsAdjust = false; + try { + var tableNode = $(dataTableApi.table().node()); + if (!tableNode.is(":visible")) return; + dataTableApi.columns.adjust(); + } catch (e) { + // Best-effort only. + } + }, 0); + } + + function applyJsonFormattingToCurrentPage(dataTableApi, settings) { + if (!dataTableApi) return; + + var mode = settings.jsonMode; + var didMutateDom = false; var rows = dataTableApi.rows({ page: "current" }).nodes(); for (var rowIndex = 0; rowIndex < rows.length; rowIndex++) { @@ -214,16 +354,39 @@ var cellData = dataTableApi.cell(td).data(); if (!looksLikeJson(cellData)) continue; + if (mode === "raw") { + // Restore raw JSON text (undo tree view, if present). + if (td.textContent !== cellData || td.childNodes.length !== 1 || td.firstChild.nodeType !== 3) { + td.textContent = cellData; + didMutateDom = true; + } + continue; + } + var parsed = safeParseJson(cellData); if (parsed === null) continue; td.classList.add("addb-json-cell"); - if (settings.jsonMode === "pretty") { - td.textContent = JSON.stringify(parsed, null, 2); + if (mode === "wrapped") { + // Keep raw JSON text, but apply JSON cell styling. + if (td.textContent !== cellData || td.childNodes.length !== 1 || td.firstChild.nodeType !== 3) { + td.textContent = cellData; + didMutateDom = true; + } + continue; } + + // Pretty mode: render a collapsible JSON tree with 2nd-level containers collapsed. + td.textContent = ""; + td.appendChild(buildJsonTree(parsed)); + didMutateDom = true; } } + + if (didMutateDom) { + scheduleColumnsAdjust(dataTableApi); + } } function applyDataTableSettings(dataTableApi, settings, reason) { @@ -263,14 +426,8 @@ applyBodyClasses(currentSettings); applyMaxColumnWidth(currentSettings); - if (previousSettings.jsonMode !== currentSettings.jsonMode && currentSettings.jsonMode === "raw") { - jsonCleanupNeeded = true; - } - var dataTableReason = reason; - if (previousSettings.jsonMode === "pretty" && currentSettings.jsonMode !== "pretty") { - dataTableReason = "jsonMode"; - } + if (previousSettings.jsonMode !== currentSettings.jsonMode) dataTableReason = "jsonMode"; applyDataTableSettings(currentDataTable, currentSettings, dataTableReason); } @@ -280,6 +437,10 @@ if (previousTableNode && currentDataTableDrawHandler) { previousTableNode.off("draw.dt", currentDataTableDrawHandler); } + if (currentDataTableContainer) { + currentDataTableContainer.off("click.addb-json"); + currentDataTableContainer = null; + } currentDataTable = dataTableApi || null; if (!currentDataTable) return; @@ -290,6 +451,11 @@ }; tableNode.on("draw.dt", currentDataTableDrawHandler); + currentDataTableContainer = $(currentDataTable.table().container()); + currentDataTableContainer.on("click.addb-json", "details.addb-json-node > summary", function () { + scheduleColumnsAdjust(currentDataTable); + }); + applyDataTableSettings(currentDataTable, currentSettings || DEFAULT_SETTINGS, "init"); } From 9c3dde7b2bc0577b0a36de63d75b80d5945348ba Mon Sep 17 00:00:00 2001 From: Skylar Graika Date: Sun, 14 Dec 2025 13:56:32 -0800 Subject: [PATCH 70/74] Add compact sidebar configuration --- debug-db-base/src/main/assets/custom.css | 16 ++++++++ debug-db-base/src/main/assets/index.html | 15 +++++++- .../src/main/assets/ui-customization.js | 37 +++++++++++++++++++ 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/debug-db-base/src/main/assets/custom.css b/debug-db-base/src/main/assets/custom.css index 027e5c9..364f33d 100644 --- a/debug-db-base/src/main/assets/custom.css +++ b/debug-db-base/src/main/assets/custom.css @@ -24,6 +24,22 @@ word-break: normal; } +body.addb-sidebar-compact #addb-db-column .panel-heading, +body.addb-sidebar-compact #addb-table-column .panel-heading { + padding: 6px 8px; + font-size: 12px; +} + +body.addb-sidebar-compact #db-list .list-group-item, +body.addb-sidebar-compact #table-list .list-group-item { + padding: 6px 8px; + font-size: 12px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + word-break: normal; +} + .addb-customize-link { margin-right: 8px; } diff --git a/debug-db-base/src/main/assets/index.html b/debug-db-base/src/main/assets/index.html index 2bda545..169e14d 100644 --- a/debug-db-base/src/main/assets/index.html +++ b/debug-db-base/src/main/assets/index.html @@ -134,7 +134,7 @@
    -
    +
    Databases
    @@ -142,7 +142,7 @@
    -
    +
    Tables
    @@ -199,6 +199,17 @@

    Controls how much horizontal space the UI uses.

    +
    + +
    + +
    +
    + +
    +

    Controls how much space the database/table lists use (compact gives more room to the data table).

    +
    +
    diff --git a/debug-db-base/src/main/assets/ui-customization.js b/debug-db-base/src/main/assets/ui-customization.js index f4f4d58..9a877d1 100644 --- a/debug-db-base/src/main/assets/ui-customization.js +++ b/debug-db-base/src/main/assets/ui-customization.js @@ -12,6 +12,7 @@ var DEFAULT_SETTINGS = { tableWidth: "fixed", // fixed | full | fluid + sidebarWidth: "standard", // standard | compact rowDensity: "normal", // compact | normal | comfortable wrapLongText: false, maxColumnWidthPx: null, // number | null @@ -21,6 +22,7 @@ }; var allowedTableWidths = { fixed: true, full: true, fluid: true }; + var allowedSidebarWidths = { standard: true, compact: true }; var allowedRowDensities = { compact: true, normal: true, comfortable: true }; var allowedJsonModes = { raw: true, wrapped: true, pretty: true }; var allowedPageLengths = { 10: true, 25: true, 50: true, 100: true }; @@ -90,6 +92,7 @@ if (!raw || typeof raw !== "object") return settings; if (allowedTableWidths[raw.tableWidth]) settings.tableWidth = raw.tableWidth; + if (allowedSidebarWidths[raw.sidebarWidth]) settings.sidebarWidth = raw.sidebarWidth; if (allowedRowDensities[raw.rowDensity]) settings.rowDensity = raw.rowDensity; if (allowedJsonModes[raw.jsonMode]) settings.jsonMode = raw.jsonMode; @@ -142,6 +145,35 @@ } } + function setBootstrapSmColumn(el, span) { + if (!el || !el.length) return; + if (typeof span !== "number") return; + if (span < 1) span = 1; + if (span > 12) span = 12; + + var className = el.attr("class") || ""; + var classes = className.split(/\s+/).filter(Boolean); + var updated = []; + for (var i = 0; i < classes.length; i++) { + if (/^col-sm-\d+$/.test(classes[i])) continue; + updated.push(classes[i]); + } + updated.push("col-sm-" + span); + el.attr("class", updated.join(" ")); + } + + function applySidebarWidth(settings) { + var dbCol = $("#addb-db-column"); + var tableCol = $("#addb-table-column"); + var dataCol = $("#parent-data-div"); + if (!dbCol.length || !tableCol.length || !dataCol.length) return; + + var isCompact = settings.sidebarWidth === "compact"; + setBootstrapSmColumn(dbCol, isCompact ? 1 : 2); + setBootstrapSmColumn(tableCol, isCompact ? 1 : 2); + setBootstrapSmColumn(dataCol, isCompact ? 10 : 8); + } + function applyBodyClasses(settings) { var body = $("body"); @@ -154,6 +186,8 @@ body.addClass("addb-json-mode-" + settings.jsonMode); body.toggleClass("addb-sticky-header", !!settings.stickyHeader); + + body.toggleClass("addb-sidebar-compact", settings.sidebarWidth === "compact"); } function applyMaxColumnWidth(settings) { @@ -423,6 +457,7 @@ currentSettings = normalizeSettings(settings); applyContainerWidth(currentSettings); + applySidebarWidth(currentSettings); applyBodyClasses(currentSettings); applyMaxColumnWidth(currentSettings); @@ -550,6 +585,7 @@ var maxWidthRaw = $("#addb-max-column-width").val(); var settings = { tableWidth: $("input[name='addb-table-width']:checked").val(), + sidebarWidth: $("input[name='addb-sidebar-width']:checked").val(), rowDensity: $("input[name='addb-row-density']:checked").val(), wrapLongText: $("#addb-wrap-long-text").is(":checked"), maxColumnWidthPx: maxWidthRaw === "" ? null : maxWidthRaw, @@ -562,6 +598,7 @@ function writeFormFromSettings(settings) { $("input[name='addb-table-width'][value='" + settings.tableWidth + "']").prop("checked", true); + $("input[name='addb-sidebar-width'][value='" + settings.sidebarWidth + "']").prop("checked", true); $("input[name='addb-row-density'][value='" + settings.rowDensity + "']").prop("checked", true); $("#addb-wrap-long-text").prop("checked", !!settings.wrapLongText); $("#addb-max-column-width").val(settings.maxColumnWidthPx === null ? "" : settings.maxColumnWidthPx); From aa9860c0396777d443a043eabcbd866900628ef4 Mon Sep 17 00:00:00 2001 From: Skylar Graika Date: Mon, 15 Dec 2025 08:45:43 -0800 Subject: [PATCH 71/74] Add config for max number of rows for json before scrollinga --- debug-db-base/src/main/assets/custom.css | 14 ++++++ debug-db-base/src/main/assets/index.html | 9 ++++ .../src/main/assets/ui-customization.js | 49 +++++++++++++++++-- 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/debug-db-base/src/main/assets/custom.css b/debug-db-base/src/main/assets/custom.css index 364f33d..f775258 100644 --- a/debug-db-base/src/main/assets/custom.css +++ b/debug-db-base/src/main/assets/custom.css @@ -78,6 +78,7 @@ body.addb-sidebar-compact #table-list .list-group-item { :root { --addb-max-col-width: 480px; --addb-sticky-header-top: 50px; + --addb-json-max-lines: 10; } body.addb-sticky-header #parent-data-div .dataTables_scrollHead { @@ -128,6 +129,14 @@ body.addb-json-mode-wrapped #parent-data-div table.dataTable td.addb-json-cell { vertical-align: top; } +body.addb-json-mode-wrapped #parent-data-div table.dataTable td.addb-json-cell .addb-json-text { + display: block; + line-height: 1.25; + max-height: calc(var(--addb-json-max-lines) * 1.25em); + overflow-y: auto; + padding-right: 6px; +} + body.addb-json-mode-pretty #parent-data-div table.dataTable td.addb-json-cell { white-space: pre; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; @@ -143,6 +152,11 @@ body.addb-max-col-width.addb-json-mode-pretty #parent-data-div table.dataTable t body.addb-json-mode-pretty #parent-data-div table.dataTable td.addb-json-cell .addb-json-tree { display: inline-block; + line-height: 1.25; + max-height: calc(var(--addb-json-max-lines) * 1.25em); + overflow-y: auto; + overflow-x: visible; + padding-right: 6px; } #parent-data-div table.dataTable td.addb-json-cell .addb-json-line { diff --git a/debug-db-base/src/main/assets/index.html b/debug-db-base/src/main/assets/index.html index 169e14d..28e6187 100644 --- a/debug-db-base/src/main/assets/index.html +++ b/debug-db-base/src/main/assets/index.html @@ -260,6 +260,15 @@

    Applies to cells that contain valid JSON objects/arrays.

    + +
    + +
    + + lines +
    +

    Limits row height when JSON is wrapped or pretty-printed.

    +